From 2101b1f76e5e7eab4b4ea632db226fb76d99eeda Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Tue, 14 Jul 2020 18:01:32 -0700 Subject: *: rework avd/frameno handling for cached mode This brings the !--on-the-fly-encoding mode up to speed. The cached file header loses the total_frames counter, as the capture_frameno already represents this. Dropped frames are detected by simply looking at the difference between the previous capture_frameno and the current one. This simply gets passed to the encoder as a n_frames count so theora can duplicate the frames as needed. This was being done manually before by looking at the frameno and total frames in each header and maintaining separate counts for "extra frames" "missed frames" etc, and resubmitting entire frames multiply for encoding dropped frames. So a chunk of code has been thrown out from rmd_load_cache.c, and some general cleanups have occurred there as well. I also needed to add more locking around pdata->avd accesses. --- src/rmd_cache_audio.c | 3 ++ src/rmd_cache_frame.c | 14 ++++-- src/rmd_encode_image_buffer.c | 19 ++++++-- src/rmd_encode_image_buffer.h | 3 +- src/rmd_flush_to_ogg.c | 2 +- src/rmd_load_cache.c | 111 ++++++++++++++++++++---------------------- src/rmd_types.h | 15 ++---- 7 files changed, 86 insertions(+), 81 deletions(-) diff --git a/src/rmd_cache_audio.c b/src/rmd_cache_audio.c index 0ac011f..0a3be39 100644 --- a/src/rmd_cache_audio.c +++ b/src/rmd_cache_audio.c @@ -99,7 +99,10 @@ void *rmdCacheSoundBuffer(ProgData *pdata) { fwrite(jackbuf, 1, write_size, pdata->cache_data->afp); #endif } + + pthread_mutex_lock(&pdata->avd_mutex); pdata->avd -= pdata->periodtime; + pthread_mutex_unlock(&pdata->avd_mutex); } fclose(pdata->cache_data->afp); diff --git a/src/rmd_cache_frame.c b/src/rmd_cache_frame.c index c2e37bf..5c0ea5b 100644 --- a/src/rmd_cache_frame.c +++ b/src/rmd_cache_frame.c @@ -105,7 +105,7 @@ void *rmdCacheImageBuffer(ProgData *pdata) { v_short_blocks[blocknum_x * blocknum_y]; unsigned long long int total_bytes = 0; unsigned long long int total_received_bytes = 0; - unsigned int capture_frameno = 0; + unsigned int capture_frameno = 0, last_capture_frameno = 0; rmdThreadsSetName("rmdCacheImages"); @@ -181,8 +181,8 @@ void *rmdCacheImageBuffer(ProgData *pdata) { } strncpy(fheader.frame_prefix, "FRAM", 4); - fheader.frameno = ++frameno; - fheader.current_total = pdata->frames_total; + fheader.capture_frameno = capture_frameno; + frameno++; fheader.Ynum = ynum; fheader.Unum = unum; @@ -255,7 +255,9 @@ void *rmdCacheImageBuffer(ProgData *pdata) { nbytes += rmdFlushBlock(NULL, 0, 0, 0, 0, fp, ucfp, 1); /**@________________@**/ - pdata->avd += pdata->frametime; + pthread_mutex_lock(&pdata->avd_mutex); + pdata->avd += pdata->frametime * (capture_frameno - last_capture_frameno); + pthread_mutex_unlock(&pdata->avd_mutex); if (nbytes > CACHE_FILE_SIZE_LIMIT) { if (rmdSwapCacheFilesWrite(pdata->cache_data->imgdata, nth_cache, &fp, &ucfp)) { @@ -279,6 +281,8 @@ void *rmdCacheImageBuffer(ProgData *pdata) { nth_cache++; nbytes = 0; } + + last_capture_frameno = capture_frameno; } total_bytes += nbytes; @@ -307,7 +311,7 @@ void *rmdCacheImageBuffer(ProgData *pdata) { fprintf(stderr, "Saved %d frames in a total of %d requests\n", frameno, - pdata->frames_total); + capture_frameno); fflush(stderr); if (!pdata->args.zerocompression) { diff --git a/src/rmd_encode_image_buffer.c b/src/rmd_encode_image_buffer.c index d83c694..5b08620 100644 --- a/src/rmd_encode_image_buffer.c +++ b/src/rmd_encode_image_buffer.c @@ -95,7 +95,7 @@ void *rmdEncodeImageBuffer(ProgData *pdata) { //this function is meant to be called normally //not through a thread of it's own -void rmdSyncEncodeImageBuffer(ProgData *pdata) { +void rmdSyncEncodeImageBuffer(ProgData *pdata, unsigned int n_frames) { EncData *enc_data = pdata->enc_data; if (theora_encode_YUVin(&enc_data->m_th_st, &enc_data->yuv)) { @@ -103,14 +103,23 @@ void rmdSyncEncodeImageBuffer(ProgData *pdata) { return; } - if (theora_encode_packetout(&enc_data->m_th_st, !pdata->running, &enc_data->m_ogg_pckt1) == 1) { + if (n_frames > 1) + theora_control( &enc_data->m_th_st, + TH_ENCCTL_SET_DUP_COUNT, + (void *)&(int){n_frames - 1}, + sizeof(int)); + + while (theora_encode_packetout(&enc_data->m_th_st, 0, &enc_data->m_ogg_pckt1) > 0) { pthread_mutex_lock(&pdata->libogg_mutex); ogg_stream_packetin(&enc_data->m_ogg_ts, &enc_data->m_ogg_pckt1); - + /* TODO: this is the only real difference from what's open-coded above, + * investigate why this is done this way and make the thread just call + * this function instead of duplicating it. + */ if (!pdata->running) enc_data->m_ogg_ts.e_o_s = 1; - - pdata->avd += pdata->frametime; pthread_mutex_unlock(&pdata->libogg_mutex); } + + pdata->avd += pdata->frametime * n_frames; } diff --git a/src/rmd_encode_image_buffer.h b/src/rmd_encode_image_buffer.h index e5ca3db..d011e26 100644 --- a/src/rmd_encode_image_buffer.h +++ b/src/rmd_encode_image_buffer.h @@ -34,6 +34,7 @@ * feed a yuv buffer to the theora encoder and submit outcome to * the ogg stream. * \param pdata ProgData struct containing all program data +* \param n_frames number of frames to encode from image buffer */ void *rmdEncodeImageBuffer(ProgData *pdata); @@ -44,7 +45,7 @@ void *rmdEncodeImageBuffer(ProgData *pdata); * \param pdata ProgData struct containing all program data * */ -void rmdSyncEncodeImageBuffer(ProgData *pdata); +void rmdSyncEncodeImageBuffer(ProgData *pdata, unsigned int n_frames); #endif diff --git a/src/rmd_flush_to_ogg.c b/src/rmd_flush_to_ogg.c index b4ca6fb..6c5a0d8 100644 --- a/src/rmd_flush_to_ogg.c +++ b/src/rmd_flush_to_ogg.c @@ -127,7 +127,7 @@ void *rmdFlushToOgg(ProgData *pdata) { while (!pdata->th_encoding_clean) pthread_cond_wait(&pdata->theora_lib_clean, &pdata->theora_lib_mutex); pthread_mutex_unlock(&pdata->theora_lib_mutex); - rmdSyncEncodeImageBuffer(pdata); + rmdSyncEncodeImageBuffer(pdata, 1); } } diff --git a/src/rmd_load_cache.c b/src/rmd_load_cache.c index 417b3dc..0d94a37 100644 --- a/src/rmd_load_cache.c +++ b/src/rmd_load_cache.c @@ -161,6 +161,14 @@ static int rmdReadFrame(CachedFrame *frame, FILE *ucfp, gzFile ifp) { return 0; } +static int read_header(ProgData *pdata, gzFile ifp, FILE *ucfp, FrameHeader *fheader) { + if (!pdata->args.zerocompression) { + return gzread(ifp, fheader, sizeof(FrameHeader)) == sizeof(FrameHeader); + } else { + return fread(fheader, sizeof(FrameHeader), 1, ucfp) == 1; + } +} + void *rmdLoadCache(ProgData *pdata) { yuv_buffer *yuv = &pdata->enc_data->yuv; @@ -171,9 +179,6 @@ void *rmdLoadCache(ProgData *pdata) { CachedFrame frame; int nth_cache = 1, audio_end = 0, - extra_frames = 0, //total number of duplicated frames - missing_frames = 0, //if this is found >0 current run will not load - //a frame but it will proccess the previous thread_exit = 0, //0 success, -1 couldn't find files,1 couldn't remove blocknum_x = pdata->enc_data->yuv.y_width / Y_UNIT_WIDTH, blocknum_y = pdata->enc_data->yuv.y_height / Y_UNIT_WIDTH, @@ -184,6 +189,7 @@ void *rmdLoadCache(ProgData *pdata) { u_int32_t YBlocks[(yuv->y_width * yuv->y_height) / Y_UNIT_BYTES], UBlocks[(yuv->uv_width * yuv->uv_height) / UV_UNIT_BYTES], VBlocks[(yuv->uv_width * yuv->uv_height) / UV_UNIT_BYTES]; + unsigned int last_capture_frameno = 0; rmdThreadsSetName("rmdEncodeCache"); @@ -225,70 +231,60 @@ void *rmdLoadCache(ProgData *pdata) { //If sound finishes first,we go on with the video. //If video ends we will do one more run to flush audio in the ogg file while (pdata->running) { + //video load and encoding if (pdata->avd <= 0 || pdata->args.nosound || audio_end) { - if (missing_frames > 0) { - extra_frames++; - missing_frames--; - rmdSyncEncodeImageBuffer(pdata); - } else if (((!pdata->args.zerocompression) && - (gzread(ifp, frame.header, sizeof(FrameHeader)) == - sizeof(FrameHeader) )) || - ((pdata->args.zerocompression) && - (fread(frame.header, sizeof(FrameHeader), 1, ucfp) == 1))) { - //sync - missing_frames += frame.header->current_total - - (extra_frames + frame.header->frameno); + + if (read_header(pdata, ifp, ucfp, frame.header)) { + if (pdata->frames_total) { fprintf(stdout, "\r[%d%%] ", - ((frame.header->frameno + extra_frames) * 100) / pdata->frames_total); + ((frame.header->capture_frameno) * 100) / pdata->capture_frameno); } else fprintf(stdout, "\r[%d frames rendered] ", - (frame.header->frameno + extra_frames)); + (frame.header->capture_frameno)); fflush(stdout); - if ( (frame.header->Ynum <= blocknum_x * blocknum_y) && - (frame.header->Unum <= blocknum_x * blocknum_y) && - (frame.header->Vnum <= blocknum_x * blocknum_y) && - !rmdReadFrame( &frame, - (pdata->args.zerocompression ? ucfp : NULL), - (pdata->args.zerocompression ? NULL : ifp)) - ) { - - //load the blocks for each buffer - if (frame.header->Ynum) - for (int j = 0; j < frame.header->Ynum; j++) - rmdLoadBlock( yuv->y, - &frame.YData[j * blockszy], - frame.YBlocks[j], - yuv->y_width, - yuv->y_height, - Y_UNIT_WIDTH); - if (frame.header->Unum) - for (int j = 0; j < frame.header->Unum; j++) - rmdLoadBlock( yuv->u, - &frame.UData[j * blockszuv], - frame.UBlocks[j], - yuv->uv_width, - yuv->uv_height, - UV_UNIT_WIDTH); - if (frame.header->Vnum) - for (int j = 0; j < frame.header->Vnum; j++) - rmdLoadBlock( yuv->v, - &frame.VData[j * blockszuv], - frame.VBlocks[j], - yuv->uv_width, - yuv->uv_height, - UV_UNIT_WIDTH); - - //encode. This is not made in a thread since - //now blocking is not a problem - //and this way sync problems - //can be avoided more easily. - rmdSyncEncodeImageBuffer(pdata); - } else { + + if ( (frame.header->Ynum > blocknum_x * blocknum_y) || + (frame.header->Unum > blocknum_x * blocknum_y) || + (frame.header->Vnum > blocknum_x * blocknum_y) || + rmdReadFrame( &frame, + pdata->args.zerocompression ? ucfp : NULL, + pdata->args.zerocompression ? NULL : ifp) < 0) { + raise(SIGINT); continue; } + + //load the blocks for each buffer + if (frame.header->Ynum) + for (int j = 0; j < frame.header->Ynum; j++) + rmdLoadBlock( yuv->y, + &frame.YData[j * blockszy], + frame.YBlocks[j], + yuv->y_width, + yuv->y_height, + Y_UNIT_WIDTH); + if (frame.header->Unum) + for (int j = 0; j < frame.header->Unum; j++) + rmdLoadBlock( yuv->u, + &frame.UData[j * blockszuv], + frame.UBlocks[j], + yuv->uv_width, + yuv->uv_height, + UV_UNIT_WIDTH); + if (frame.header->Vnum) + for (int j = 0; j < frame.header->Vnum; j++) + rmdLoadBlock( yuv->v, + &frame.VData[j * blockszuv], + frame.VBlocks[j], + yuv->uv_width, + yuv->uv_height, + UV_UNIT_WIDTH); + + rmdSyncEncodeImageBuffer(pdata, fheader.capture_frameno - last_capture_frameno); + + last_capture_frameno = fheader.capture_frameno; } else { if (rmdSwapCacheFilesRead( pdata->cache_data->imgdata, nth_cache, @@ -299,7 +295,6 @@ void *rmdLoadCache(ProgData *pdata) { fprintf(stderr, "\t[Cache File %d]", nth_cache); nth_cache++; } - continue; } //audio load and encoding } else { diff --git a/src/rmd_types.h b/src/rmd_types.h index f01083c..b7c27a8 100644 --- a/src/rmd_types.h +++ b/src/rmd_types.h @@ -349,19 +349,12 @@ struct _ProgData { //We need the total number of blocks //for each plane. -//The number of the frame compared to the -//number of time expirations at the time of -//caching, will enable us to make up for lost frames. - - typedef struct _FrameHeader{ char frame_prefix[4]; //always FRAM - u_int32_t frameno, //number of frame(cached frames) - current_total; //number of frames that should have been - //taken at time of caching this one - u_int32_t Ynum, //number of changed blocks in the Y plane - Unum, //number of changed blocks in the U plane - Vnum; //number of changed blocks in the V plane + u_int32_t capture_frameno; //number of frame + u_int32_t Ynum, //number of changed blocks in the Y plane + Unum, //number of changed blocks in the U plane + Vnum; //number of changed blocks in the V plane }FrameHeader; #endif -- cgit v1.2.1