added FFmpeg support for A/V capture compression

This commit is contained in:
myT 2023-11-27 02:53:15 +01:00
parent 9a6e253dc3
commit 3cea7e0c3d
8 changed files with 631 additions and 530 deletions

View File

@ -4,6 +4,16 @@ See the end of this file for known issues.
DD Mmm YY - 1.54
add: FFmpeg piping support for compression of audio/video captures
cl_ffmpeg <0|1> (default: 0) uses FFmpeg instead of writing raw .avi files
cl_ffmpegCommand <string> are the FFmpeg command-line options for the output file
cl_ffmpegExePath <string> (default: "ffmpeg") is the path of the FFmpeg executable to use
note that the path cannot contain spaces on Windows
cl_ffmpegOutPath <string> (default: "") is the video output directory
leave empty to write to cpma/videos as with .avi files
cl_ffmpegOutExt <string> (default: "mp4") is the output file extension
cl_ffmpegLog <0|1> (default: 0) enables the creation of 1 log file per capture
add: togglegui and toggleguiinput to toggle (the mouse input of) the built-in GUI system
add: key binds starting with "keycatchgui" always take precedence over everything else

File diff suppressed because it is too large Load Diff

View File

@ -698,7 +698,7 @@ static intptr_t CL_CgameSystemCalls( intptr_t *args )
case CG_EXT_NDP_STARTVIDEO:
Cvar_Set( cl_aviFrameRate->name, va( "%d", (int)args[2] ) );
return CL_OpenAVIForWriting( va( "videos/%s", (const char*)VMA(1) ), qfalse );
return CL_OpenAVIForWriting( VMA(1) );
case CG_EXT_NDP_STOPVIDEO:
CL_CloseAVI();

View File

@ -41,6 +41,12 @@ cvar_t* cl_showSend;
cvar_t *cl_timedemo;
cvar_t *cl_aviFrameRate;
cvar_t *cl_aviMotionJpeg;
cvar_t *cl_ffmpeg;
cvar_t *cl_ffmpegCommand;
cvar_t *cl_ffmpegExePath;
cvar_t *cl_ffmpegOutPath;
cvar_t *cl_ffmpegOutExt;
cvar_t *cl_ffmpegLog;
cvar_t *cl_allowDownload;
cvar_t *cl_inGameVideo;
@ -1951,7 +1957,7 @@ static void CL_Video_f()
1900+t.tm_year, 1+t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec );
}
CL_OpenAVIForWriting( va( "videos/%s", s ), qfalse );
CL_OpenAVIForWriting( s );
}
@ -2148,6 +2154,41 @@ static const cvarTableItem_t cl_cvars[] =
&cl_aviMotionJpeg, "cl_aviMotionJpeg", "1", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, help_cl_aviMotionJpeg,
"AVI motion JPEG", CVARCAT_DEMO, "", ""
},
{
&cl_ffmpeg, "cl_ffmpeg", "0", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL,
"use FFmpeg for video export\n"
"Pipes video through FFmpeg instead of writing raw .avi files.",
"Use FFmpeg", CVARCAT_DEMO, "Uses FFmpeg instead of writing raw .avi files", ""
},
{
&cl_ffmpegCommand, "cl_ffmpegCommand", "-movflags faststart -bf 2 -c:v libx264 -preset medium -crf 23 -vf format=yuv420p -c:a aac -b:a 320k",
CVAR_ARCHIVE, CVART_STRING, NULL, NULL,
"FFmpeg encode settings\n"
"The full command-line options for the output file.",
"FFmpeg encode settings", CVARCAT_DEMO, "Command-line options for the output file", ""
},
{
&cl_ffmpegExePath, "cl_ffmpegExePath", "ffmpeg", CVAR_ARCHIVE, CVART_STRING, NULL, NULL,
"FFmpeg executable path\n"
"The path cannot contain spaces.",
"FFmpeg executable path", CVARCAT_DEMO, "The path cannot contain spaces", ""
},
{
&cl_ffmpegOutPath, "cl_ffmpegOutPath", "", CVAR_ARCHIVE, CVART_STRING, NULL, NULL,
"FFmpeg output directory\n"
"Leave empty to write to cpma/videos as with .avi files.",
"FFmpeg output directory", CVARCAT_DEMO, "Leave empty to write to cpma/videos as with .avi files", ""
},
{
&cl_ffmpegOutExt, "cl_ffmpegOutExt", "mp4", CVAR_ARCHIVE, CVART_STRING, NULL, NULL, "FFmpeg output file extension",
"FFmpeg output file extension", CVARCAT_DEMO, "", ""
},
{
&cl_ffmpegLog, "cl_ffmpegLog", "0", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL,
"FFmpeg log file creation\n"
"Creates 1 log file per capture.",
"FFmpeg log file creation", CVARCAT_DEMO, "Creates 1 log file per capture", ""
},
{ &rconAddress, "rconAddress", "", 0, CVART_STRING, NULL, NULL, help_rconAddress },
{
&cl_maxpackets, "cl_maxpackets", "125", CVAR_ARCHIVE, CVART_INTEGER, "15", "125", "max. packet upload rate",

View File

@ -371,6 +371,12 @@ extern cvar_t *cl_serverStatusResendTime;
extern cvar_t *cl_timedemo;
extern cvar_t *cl_aviFrameRate;
extern cvar_t *cl_aviMotionJpeg;
extern cvar_t *cl_ffmpeg;
extern cvar_t *cl_ffmpegCommand;
extern cvar_t *cl_ffmpegExePath;
extern cvar_t *cl_ffmpegOutPath;
extern cvar_t *cl_ffmpegOutExt;
extern cvar_t *cl_ffmpegLog;
extern cvar_t *cl_allowDownload; // 0=off, 1=CNQ3, -1=id
extern cvar_t *cl_inGameVideo;
@ -547,12 +553,12 @@ qbool CL_Netchan_Process( netchan_t *chan, msg_t *msg );
//
// cl_avi.c
//
qbool CL_OpenAVIForWriting( const char *fileNameNoExt, qbool reOpen );
void CL_TakeVideoFrame( void );
qbool CL_OpenAVIForWriting( const char *fileNameNoExt );
void CL_TakeVideoFrame();
void CL_WriteAVIVideoFrame( const byte *imageBuffer, int size );
void CL_WriteAVIAudioFrame( const byte *pcmBuffer, int size );
qbool CL_CloseAVI( void );
qbool CL_VideoRecording( void );
qbool CL_CloseAVI();
qbool CL_VideoRecording();
//
// cl_download.cpp

View File

@ -174,6 +174,14 @@ or configs will never get loaded from disk!
#define MAX_SEARCH_PATHS 4096
#define MAX_FILEHASH_SIZE 1024
#if defined( _WIN32 )
#define Sys_OpenPipeWrite(Command) _popen(Command, "wb")
#define Sys_ClosePipe(Pipe) _pclose(Pipe)
#else
#define Sys_OpenPipeWrite(Command) popen(Command, "w")
#define Sys_ClosePipe(Pipe) pclose(Pipe)
#endif
typedef struct fileInPack_s {
char *name; // name of the file
unsigned long pos; // file info position in zip
@ -228,6 +236,7 @@ typedef union {
typedef struct {
qfile_gut file;
qbool unique;
qbool isPipe;
} qfile_ut;
typedef struct {
@ -316,6 +325,7 @@ static fileHandle_t FS_HandleForFile()
{
for ( int i = 1; i < MAX_FILE_HANDLES; ++i ) {
if ( fsh[i].handleFiles.file.o == NULL ) {
Com_Memset( &fsh[i], 0, sizeof( fsh[i] ) );
return i;
}
}
@ -378,7 +388,7 @@ FS_ReplaceSeparators
Fix things up differently for win/unix/mac
====================
*/
static void FS_ReplaceSeparators( char *path ) {
void FS_ReplaceSeparators( char *path ) {
char *s;
for ( s = path ; *s ; s++ ) {
@ -711,7 +721,11 @@ void FS_FCloseFile( fileHandle_t f ) {
// we didn't find it as a pak, so close it as a unique file
if (fsh[f].handleFiles.file.o) {
fclose (fsh[f].handleFiles.file.o);
if (fsh[f].handleFiles.isPipe) {
Sys_ClosePipe( fsh[f].handleFiles.file.o );
} else {
fclose( fsh[f].handleFiles.file.o );
}
}
Com_Memset( &fsh[f], 0, sizeof( fsh[f] ) );
}
@ -757,6 +771,21 @@ fileHandle_t FS_FOpenFileWrite( const char *filename ) {
return f;
}
fileHandle_t FS_OpenPipeWrite( const char* command ) {
fileHandle_t f = FS_HandleForFile();
fsh[f].zipFile = qfalse;
fsh[f].handleFiles.file.o = Sys_OpenPipeWrite( command );
fsh[f].handleFiles.isPipe = qtrue;
Q_strncpyz( fsh[f].name, "$pipe", sizeof(fsh[f].name));
fsh[f].handleSync = qfalse;
if ( !fsh[f].handleFiles.file.o ) {
f = 0;
}
return f;
}
/*
===========
FS_FOpenFileAppend

View File

@ -677,6 +677,8 @@ void FS_FreeFileList( char **list );
qbool FS_FileExists( const char *file ); // checks in current game dir
qbool FS_FileExistsEx( const char *file, qbool curGameDir ); // if curGameDir is qfalse, checks in "baseq3"
void FS_ReplaceSeparators( char *path );
char* FS_BuildOSPath( const char *base, const char *game, const char *qpath );
int FS_LoadStack();
@ -687,6 +689,8 @@ int FS_GetModList( char *listbuf, int bufsize );
fileHandle_t FS_FOpenFileWrite( const char *qpath );
// will properly create any needed paths and deal with seperater character issues
fileHandle_t FS_OpenPipeWrite( const char* command );
fileHandle_t FS_SV_FOpenFileWrite( const char *filename );
int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp );
void FS_SV_Rename( const char *from, const char *to );

View File

@ -1059,6 +1059,12 @@ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
return 0;
}
// prevent child processes from spawning a command prompt window
#ifndef DEDICATED
AllocConsole();
ShowWindow( GetConsoleWindow(), SW_HIDE );
#endif
// done here so the early console can be shown on the primary monitor
WIN_InitMonitorList();