diff options
Diffstat (limited to 'src/renderer')
-rw-r--r-- | src/renderer/tr_backend.c | 3 | ||||
-rw-r--r-- | src/renderer/tr_cmds.c | 27 | ||||
-rw-r--r-- | src/renderer/tr_image.c | 58 | ||||
-rw-r--r-- | src/renderer/tr_init.c | 47 | ||||
-rw-r--r-- | src/renderer/tr_local.h | 18 | ||||
-rw-r--r-- | src/renderer/tr_public.h | 3 |
6 files changed, 155 insertions, 1 deletions
diff --git a/src/renderer/tr_backend.c b/src/renderer/tr_backend.c index 73449586..9c9b841a 100644 --- a/src/renderer/tr_backend.c +++ b/src/renderer/tr_backend.c @@ -1082,6 +1082,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/src/renderer/tr_cmds.c b/src/renderer/tr_cmds.c index bbcf6385..d637aec0 100644 --- a/src/renderer/tr_cmds.c +++ b/src/renderer/tr_cmds.c @@ -446,3 +446,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/src/renderer/tr_image.c b/src/renderer/tr_image.c index a3efa2f1..692f00c6 100644 --- a/src/renderer/tr_image.c +++ b/src/renderer/tr_image.c @@ -1853,6 +1853,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/src/renderer/tr_init.c b/src/renderer/tr_init.c index a3fd2ca6..79c910d1 100644 --- a/src/renderer/tr_init.c +++ b/src/renderer/tr_init.c @@ -701,6 +701,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 */ void GL_SetDefaultState( void ) @@ -1202,5 +1247,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/src/renderer/tr_local.h b/src/renderer/tr_local.h index 4119efac..7efed35d 100644 --- a/src/renderer/tr_local.h +++ b/src/renderer/tr_local.h @@ -1216,6 +1216,7 @@ skin_t *R_GetSkinByHandle( qhandle_t hSkin ); int R_ComputeLOD( trRefEntity_t *ent ); +const void *RB_TakeVideoFrameCmd( const void *data ); // // tr_shader.c @@ -1580,6 +1581,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, @@ -1587,7 +1597,8 @@ typedef enum { RC_DRAW_SURFS, RC_DRAW_BUFFER, RC_SWAP_BUFFERS, - RC_SCREENSHOT + RC_SCREENSHOT, + RC_VIDEOFRAME } renderCommand_t; @@ -1636,6 +1647,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/src/renderer/tr_public.h b/src/renderer/tr_public.h index 92801ac4..e4e4d047 100644 --- a/src/renderer/tr_public.h +++ b/src/renderer/tr_public.h @@ -98,6 +98,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; // @@ -157,6 +159,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; |