From 98c8cf0f19d89b729825d6d37b924a84025ec07b Mon Sep 17 00:00:00 2001 From: tma Date: Wed, 4 Jan 2006 03:12:12 +0000 Subject: * AVI video output - Uses motion jpeg codec by default - Use cl_avidemo to set a framerate - \video [filename] to start capture - \stopvideo to stop capture - Audio capture is a bit ropey git-svn-id: svn://svn.icculus.org/quake3/trunk@454 edf5b092-35ff-0310-97b2-ce42778d08ea --- code/renderer/tr_backend.c | 3 +++ code/renderer/tr_cmds.c | 27 +++++++++++++++++++++ code/renderer/tr_image.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++ code/renderer/tr_init.c | 47 +++++++++++++++++++++++++++++++++++++ code/renderer/tr_local.h | 18 +++++++++++++- code/renderer/tr_public.h | 3 +++ 6 files changed, 155 insertions(+), 1 deletion(-) (limited to 'code/renderer') diff --git a/code/renderer/tr_backend.c b/code/renderer/tr_backend.c index 6c93c14..2061dd8 100644 --- a/code/renderer/tr_backend.c +++ b/code/renderer/tr_backend.c @@ -1081,6 +1081,9 @@ void RB_ExecuteRenderCommands( const void *data ) { case RC_SCREENSHOT: data = RB_TakeScreenshotCmd( data ); break; + case RC_VIDEOFRAME: + data = RB_TakeVideoFrameCmd( data ); + break; case RC_END_OF_LIST: default: diff --git a/code/renderer/tr_cmds.c b/code/renderer/tr_cmds.c index 54f8150..3be865c 100644 --- a/code/renderer/tr_cmds.c +++ b/code/renderer/tr_cmds.c @@ -445,3 +445,30 @@ void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { backEnd.pc.msec = 0; } +/* +============= +RE_TakeVideoFrame +============= +*/ +void RE_TakeVideoFrame( int width, int height, + byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ) +{ + videoFrameCommand_t *cmd; + + if( !tr.registered ) { + return; + } + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if( !cmd ) { + return; + } + + cmd->commandId = RC_VIDEOFRAME; + + cmd->width = width; + cmd->height = height; + cmd->captureBuffer = captureBuffer; + cmd->encodeBuffer = encodeBuffer; + cmd->motionJpeg = motionJpeg; +} diff --git a/code/renderer/tr_image.c b/code/renderer/tr_image.c index 15d6c7e..c227388 100644 --- a/code/renderer/tr_image.c +++ b/code/renderer/tr_image.c @@ -1852,6 +1852,64 @@ void SaveJPG(char * filename, int quality, int image_width, int image_height, un /* And we're done! */ } +/* +================= +SaveJPGToBuffer +================= +*/ +int SaveJPGToBuffer( byte *buffer, int quality, + int image_width, int image_height, + byte *image_buffer ) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + int row_stride; /* physical row width in image buffer */ + + /* Step 1: allocate and initialize JPEG compression object */ + cinfo.err = jpeg_std_error(&jerr); + /* Now we can initialize the JPEG compression object. */ + jpeg_create_compress(&cinfo); + + /* Step 2: specify data destination (eg, a file) */ + /* Note: steps 2 and 3 can be done in either order. */ + jpegDest(&cinfo, buffer, image_width*image_height*4); + + /* Step 3: set parameters for compression */ + cinfo.image_width = image_width; /* image width and height, in pixels */ + cinfo.image_height = image_height; + cinfo.input_components = 4; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + + /* Step 4: Start compressor */ + jpeg_start_compress(&cinfo, TRUE); + + /* Step 5: while (scan lines remain to be written) */ + /* jpeg_write_scanlines(...); */ + row_stride = image_width * 4; /* JSAMPLEs per row in image_buffer */ + + while (cinfo.next_scanline < cinfo.image_height) { + /* jpeg_write_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could pass + * more than one scanline at a time if that's more convenient. + */ + row_pointer[0] = & image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + /* Step 6: Finish compression */ + jpeg_finish_compress(&cinfo); + + /* Step 7: release JPEG compression object */ + jpeg_destroy_compress(&cinfo); + + /* And we're done! */ + return hackSize; +} + //=================================================================== /* diff --git a/code/renderer/tr_init.c b/code/renderer/tr_init.c index ffdd997..285ed18 100644 --- a/code/renderer/tr_init.c +++ b/code/renderer/tr_init.c @@ -699,6 +699,51 @@ void R_ScreenShotJPEG_f (void) { //============================================================================ +/* +================== +RB_TakeVideoFrameCmd +================== +*/ +const void *RB_TakeVideoFrameCmd( const void *data ) +{ + const videoFrameCommand_t *cmd; + int frameSize; + int i; + + cmd = (const videoFrameCommand_t *)data; + + qglReadPixels( 0, 0, cmd->width, cmd->height, GL_RGBA, + GL_UNSIGNED_BYTE, cmd->captureBuffer ); + + // gamma correct + if( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) + R_GammaCorrect( cmd->captureBuffer, cmd->width * cmd->height * 4 ); + + if( cmd->motionJpeg ) + { + frameSize = SaveJPGToBuffer( cmd->encodeBuffer, 95, + cmd->width, cmd->height, cmd->captureBuffer ); + } + else + { + frameSize = cmd->width * cmd->height * 4; + + // Vertically flip the image + for( i = 0; i < cmd->height; i++ ) + { + Com_Memcpy( &cmd->encodeBuffer[ i * ( cmd->width * 4 ) ], + &cmd->captureBuffer[ ( cmd->height - i - 1 ) * ( cmd->width * 4 ) ], + cmd->width * 4 ); + } + } + + ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, frameSize ); + + return (const void *)(cmd + 1); +} + +//============================================================================ + /* ** GL_SetDefaultState */ @@ -1201,5 +1246,7 @@ refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { re.GetEntityToken = R_GetEntityToken; re.inPVS = R_inPVS; + re.TakeVideoFrame = RE_TakeVideoFrame; + return &re; } diff --git a/code/renderer/tr_local.h b/code/renderer/tr_local.h index 865128f..1b47f96 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -1215,6 +1215,7 @@ skin_t *R_GetSkinByHandle( qhandle_t hSkin ); int R_ComputeLOD( trRefEntity_t *ent ); +const void *RB_TakeVideoFrameCmd( const void *data ); // // tr_shader.c @@ -1579,6 +1580,15 @@ typedef struct { qboolean jpeg; } screenshotCommand_t; +typedef struct { + int commandId; + int width; + int height; + byte *captureBuffer; + byte *encodeBuffer; + qboolean motionJpeg; +} videoFrameCommand_t; + typedef enum { RC_END_OF_LIST, RC_SET_COLOR, @@ -1586,7 +1596,8 @@ typedef enum { RC_DRAW_SURFS, RC_DRAW_BUFFER, RC_SWAP_BUFFERS, - RC_SCREENSHOT + RC_SCREENSHOT, + RC_VIDEOFRAME } renderCommand_t; @@ -1635,6 +1646,11 @@ void RE_StretchPic ( float x, float y, float w, float h, void RE_BeginFrame( stereoFrame_t stereoFrame ); void RE_EndFrame( int *frontEndMsec, int *backEndMsec ); void SaveJPG(char * filename, int quality, int image_width, int image_height, unsigned char *image_buffer); +int SaveJPGToBuffer( byte *buffer, int quality, + int image_width, int image_height, + byte *image_buffer ); +void RE_TakeVideoFrame( int width, int height, + byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); // font stuff void R_InitFreeType( void ); diff --git a/code/renderer/tr_public.h b/code/renderer/tr_public.h index 9752305..6cefe16 100644 --- a/code/renderer/tr_public.h +++ b/code/renderer/tr_public.h @@ -97,6 +97,8 @@ typedef struct { void (*RemapShader)(const char *oldShader, const char *newShader, const char *offsetTime); qboolean (*GetEntityToken)( char *buffer, int size ); qboolean (*inPVS)( const vec3_t p1, const vec3_t p2 ); + + void (*TakeVideoFrame)( int h, int w, byte* captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); } refexport_t; // @@ -156,6 +158,7 @@ typedef struct { int (*CIN_PlayCinematic)( const char *arg0, int xpos, int ypos, int width, int height, int bits); e_status (*CIN_RunCinematic) (int handle); + void (*CL_WriteAVIVideoFrame)( const byte *buffer, int size ); } refimport_t; -- cgit v1.2.3