* 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
This commit is contained in:
Tim Angus 2006-01-04 03:12:12 +00:00
parent 92ad3e99dc
commit a21eb2bbcb
15 changed files with 910 additions and 11 deletions

View file

@ -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:

View file

@ -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;
}

View file

@ -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;
}
//===================================================================
/*

View file

@ -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;
}

View file

@ -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 );

View file

@ -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;