From b2bc40de23857c762b317d4b02230246e142ff48 Mon Sep 17 00:00:00 2001 From: divverent Date: Thu, 19 Feb 2009 05:33:26 +0000 Subject: [PATCH] libogg sucks. Really does. Work around one of its shortcomings by storing the page to be interleaved in our OWN buffer, as libogg appears to overwrite it when calling some OTHER function in it. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@8740 d7cf8633-e32d-0410-b094-e92efae38249 --- cap_avi.c | 3 +- cap_ogg.c | 129 +++++++++++++++++++++------------------------------- cl_screen.c | 2 +- client.h | 14 ++++-- 4 files changed, 64 insertions(+), 84 deletions(-) diff --git a/cap_avi.c b/cap_avi.c index df75c7d3..e9b0d286 100644 --- a/cap_avi.c +++ b/cap_avi.c @@ -493,7 +493,8 @@ void SCR_CaptureVideo_Avi_BeginVideo() aspect = vid.width / (vid.height * vid_pixelheight.value); cls.capturevideo.format = CAPTUREVIDEOFORMAT_AVI_I420; - cls.capturevideo.videofile = FS_OpenRealFile(va("%s.avi", cls.capturevideo.basename), "wb", false); + cls.capturevideo.formatextension = "avi"; + cls.capturevideo.videofile = FS_OpenRealFile(va("%s.%s", cls.capturevideo.basename, cls.capturevideo.formatextension), "wb", false); cls.capturevideo.endvideo = SCR_CaptureVideo_Avi_EndVideo; cls.capturevideo.videoframes = SCR_CaptureVideo_Avi_VideoFrames; cls.capturevideo.soundframe = SCR_CaptureVideo_Avi_SoundFrame; diff --git a/cap_ogg.c b/cap_ogg.c index 00dc3cbb..c2f34ffd 100644 --- a/cap_ogg.c +++ b/cap_ogg.c @@ -617,6 +617,21 @@ void SCR_CaptureVideo_Ogg_CloseDLL() Sys_UnloadLibrary (&og_dll); } +// this struct should not be needed +// however, libogg appears to pull the ogg_page's data element away from our +// feet before we get to write the data due to interleaving +// so this struct is used to keep the page data around until it actually gets +// written +typedef struct allocatedoggpage_s +{ + size_t len; + double time; + unsigned char data[65307]; + // this number is from RFC 3533. In case libogg writes more, we'll have to increase this + // but we'll get a Host_Error in this case so we can track it down +} +allocatedoggpage_t; + typedef struct capturevideostate_ogg_formatspecific_s { ogg_stream_state to, vo; @@ -630,38 +645,22 @@ typedef struct capturevideostate_ogg_formatspecific_s int lastnum; int channels; - // for interleaving - ogg_page videopage; - ogg_page audiopage; - qboolean have_videopage; - qboolean have_audiopage; + allocatedoggpage_t videopage, audiopage; } capturevideostate_ogg_formatspecific_t; #define LOAD_FORMATSPECIFIC_OGG() capturevideostate_ogg_formatspecific_t *format = (capturevideostate_ogg_formatspecific_t *) cls.capturevideo.formatspecific -#define INTERLEAVING_NOT_WORKING static void SCR_CaptureVideo_Ogg_Interleave() { LOAD_FORMATSPECIFIC_OGG(); - - //fprintf(stderr, "<"); + ogg_page pg; if(!cls.capturevideo.soundrate) { - for(;;) + while(qogg_stream_pageout(&format->to, &pg) > 0) { - // first: make sure we have a page of both types - if(!format->have_videopage) - if(qogg_stream_pageout(&format->to, &format->videopage) > 0) - format->have_videopage = true; - if(format->have_videopage) - { - FS_Write(cls.capturevideo.videofile, format->videopage.header, format->videopage.header_len); - FS_Write(cls.capturevideo.videofile, format->videopage.body, format->videopage.body_len); - format->have_videopage = false; - } - else - break; + FS_Write(cls.capturevideo.videofile, pg.header, pg.header_len); + FS_Write(cls.capturevideo.videofile, pg.body, pg.body_len); } return; } @@ -669,69 +668,44 @@ static void SCR_CaptureVideo_Ogg_Interleave() for(;;) { // first: make sure we have a page of both types - if(!format->have_videopage) - if(qogg_stream_pageout(&format->to, &format->videopage) > 0) + if(!format->videopage.len) + if(qogg_stream_pageout(&format->to, &pg) > 0) { - //fprintf(stderr, "V"); - format->have_videopage = true; - -#ifdef INTERLEAVING_NOT_WORKING - // why do I have to do this? the code should work without the - // following three lines, which turn this attempt at correct - // interleaving back into the old stupid one that oggz-validate - // hates - FS_Write(cls.capturevideo.videofile, format->videopage.header, format->videopage.header_len); - FS_Write(cls.capturevideo.videofile, format->videopage.body, format->videopage.body_len); - format->have_videopage = false; - continue; -#endif + format->videopage.len = pg.header_len + pg.body_len; + format->videopage.time = qtheora_granule_time(&format->ts, qogg_page_granulepos(&pg)); + if(format->videopage.len > sizeof(format->videopage.data)) + Host_Error("video page too long"); + memcpy(format->videopage.data, pg.header, pg.header_len); + memcpy(format->videopage.data + pg.header_len, pg.body, pg.body_len); } - if(!format->have_audiopage) - if(qogg_stream_pageout(&format->vo, &format->audiopage) > 0) + if(!format->audiopage.len) + if(qogg_stream_pageout(&format->vo, &pg) > 0) { - //fprintf(stderr, "A"); - format->have_audiopage = true; - -#ifdef INTERLEAVING_NOT_WORKING - // why do I have to do this? the code should work without the - // following three lines, which turn this attempt at correct - // interleaving back into the old stupid one that oggz-validate - // hates - FS_Write(cls.capturevideo.videofile, format->audiopage.header, format->audiopage.header_len); - FS_Write(cls.capturevideo.videofile, format->audiopage.body, format->audiopage.body_len); - format->have_audiopage = false; - continue; -#endif + format->audiopage.len = pg.header_len + pg.body_len; + format->audiopage.time = qvorbis_granule_time(&format->vd, qogg_page_granulepos(&pg)); + if(format->audiopage.len > sizeof(format->audiopage.data)) + Host_Error("audio page too long"); + memcpy(format->audiopage.data, pg.header, pg.header_len); + memcpy(format->audiopage.data + pg.header_len, pg.body, pg.body_len); } - if(format->have_videopage && format->have_audiopage) + if(format->videopage.len && format->audiopage.len) { // output the page that ends first - double audiotime = qvorbis_granule_time(&format->vd, qogg_page_granulepos(&format->audiopage)); - double videotime = qtheora_granule_time(&format->ts, qogg_page_granulepos(&format->videopage)); - //fprintf(stderr, "(A=%f V=%f)\n", audiotime, videotime); - if(audiotime < videotime) + if(format->videopage.time < format->audiopage.time) { - FS_Write(cls.capturevideo.videofile, format->audiopage.header, format->audiopage.header_len); - FS_Write(cls.capturevideo.videofile, format->audiopage.body, format->audiopage.body_len); - format->have_audiopage = false; - - //fprintf(stderr, "a"); + FS_Write(cls.capturevideo.videofile, format->videopage.data, format->videopage.len); + format->videopage.len = 0; } else { - FS_Write(cls.capturevideo.videofile, format->videopage.header, format->videopage.header_len); - FS_Write(cls.capturevideo.videofile, format->videopage.body, format->videopage.body_len); - format->have_videopage = false; - - //fprintf(stderr, "v"); + FS_Write(cls.capturevideo.videofile, format->audiopage.data, format->audiopage.len); + format->audiopage.len = 0; } } else break; } - - //fprintf(stderr, ">"); } static void SCR_CaptureVideo_Ogg_FlushInterleaving() @@ -739,18 +713,16 @@ static void SCR_CaptureVideo_Ogg_FlushInterleaving() LOAD_FORMATSPECIFIC_OGG(); if(cls.capturevideo.soundrate) - if(format->have_audiopage) + if(format->audiopage.len) { - FS_Write(cls.capturevideo.videofile, format->audiopage.header, format->audiopage.header_len); - FS_Write(cls.capturevideo.videofile, format->audiopage.body, format->audiopage.body_len); - format->have_audiopage = false; + FS_Write(cls.capturevideo.videofile, format->audiopage.data, format->audiopage.len); + format->audiopage.len = 0; } - if(format->have_videopage) + if(format->videopage.len) { - FS_Write(cls.capturevideo.videofile, format->videopage.header, format->videopage.header_len); - FS_Write(cls.capturevideo.videofile, format->videopage.body, format->videopage.body_len); - format->have_videopage = false; + FS_Write(cls.capturevideo.videofile, format->videopage.data, format->videopage.len); + format->videopage.len = 0; } } @@ -948,7 +920,8 @@ static void SCR_CaptureVideo_Ogg_SoundFrame(const portable_sampleframe_t *paintb void SCR_CaptureVideo_Ogg_BeginVideo() { cls.capturevideo.format = CAPTUREVIDEOFORMAT_OGG_VORBIS_THEORA; - cls.capturevideo.videofile = FS_OpenRealFile(va("%s.ogv", cls.capturevideo.basename), "wb", false); + cls.capturevideo.formatextension = "ogv"; + cls.capturevideo.videofile = FS_OpenRealFile(va("%s.%s", cls.capturevideo.basename, cls.capturevideo.formatextension), "wb", false); cls.capturevideo.endvideo = SCR_CaptureVideo_Ogg_EndVideo; cls.capturevideo.videoframes = SCR_CaptureVideo_Ogg_VideoFrames; cls.capturevideo.soundframe = SCR_CaptureVideo_Ogg_SoundFrame; @@ -975,7 +948,7 @@ void SCR_CaptureVideo_Ogg_BeginVideo() qogg_stream_init(&format->vo, format->serial2); } - format->have_videopage = format->have_audiopage = false; + format->videopage.len = format->audiopage.len = 0; qtheora_info_init(&ti); ti.frame_width = cls.capturevideo.width; diff --git a/cl_screen.c b/cl_screen.c index b3eb2a67..d301ca65 100644 --- a/cl_screen.c +++ b/cl_screen.c @@ -1035,7 +1035,7 @@ void SCR_CaptureVideo_EndVideo(void) return; cls.capturevideo.active = false; - Con_DPrintf("Finishing capture (%d frames, %d audio frames)\n", cls.capturevideo.frame, cls.capturevideo.soundsampleframe); + Con_Printf("Finishing capture of %s.%s (%d frames, %d audio frames)\n", cls.capturevideo.basename, cls.capturevideo.formatextension, cls.capturevideo.frame, cls.capturevideo.soundsampleframe); if (cls.capturevideo.videofile) { diff --git a/client.h b/client.h index 2372df94..29c8e349 100644 --- a/client.h +++ b/client.h @@ -458,11 +458,9 @@ typedef struct capturevideostate_s { double startrealtime; double framerate; - qfile_t *videofile; qboolean active; qboolean realtime; qboolean error; - capturevideoformat_t format; int soundrate; int soundchannels; int frame; @@ -472,12 +470,20 @@ typedef struct capturevideostate_s int soundsampleframe; unsigned char *screenbuffer; unsigned char *outbuffer; - char basename[64]; + char basename[MAX_QPATH]; int width, height; + + // precomputed RGB to YUV tables + // if a capturevideo module uses these, it doesn't need to care for scr_screenshot_gammaboost.value short rgbtoyuvscaletable[3][3][256]; unsigned char yuvnormalizetable[3][256]; - // format specific functions + // stuff to be filled in by the video format module + capturevideoformat_t format; + const char *formatextension; + qfile_t *videofile; + // always use this: + // cls.capturevideo.videofile = FS_OpenRealFile(va("%s.%s", cls.capturevideo.basename, cls.capturevideo.formatextension), "wb", false); void (*endvideo) (); void (*videoframes) (int num); void (*soundframe) (const portable_sampleframe_t *paintbuffer, size_t length); -- 2.39.2