diff options
author | Vito Caputo <vcaputo@pengaru.com> | 2020-11-11 23:44:15 -0800 |
---|---|---|
committer | Vito Caputo <vcaputo@pengaru.com> | 2020-11-12 23:14:05 -0800 |
commit | c265f13978fbd68f295d99b2fe4720fe4866d64c (patch) | |
tree | a08822aa66dd16bb3ef79d7e46706386db02ddcc | |
parent | 7281dc6fdc4f345c9c1c7bcda20e3bef3003c140 (diff) |
timer: remove unnecessary timer thread
This removes the timer thread entirely in favor of a coordinated
delay loop directly within rmdGetFrame.
When there's an audio stream to synchronize with, avd is
maintained by the pcm buffer updates, and the fps-derived
frametimes synchronize with the audio that way.
When there's no audio stream (--no-sound), avd is now maintained
synthetically via clock_gettime(CLOCK_MONOTONIC) coordinating
with the clock instead.
There's been some reworking of frame sampling/reusing and cloning
logic, which may need some refinement. But for now the tests
seem to show promise.
The old timer approach just increased the non-determinism by
adding more scheduler latency and influence unnecessarily.
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/rmd_get_frame.c | 606 | ||||
-rw-r--r-- | src/rmd_initialize_data.c | 1 | ||||
-rw-r--r-- | src/rmd_threads.c | 13 | ||||
-rw-r--r-- | src/rmd_timer.c | 145 | ||||
-rw-r--r-- | src/rmd_timer.h | 44 | ||||
-rw-r--r-- | src/rmd_types.h | 7 |
7 files changed, 366 insertions, 452 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index ba429cb..78b8d7e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -61,8 +61,6 @@ recordmydesktop_SOURCES = \ rmd_specsfile.h \ rmd_threads.c \ rmd_threads.h \ - rmd_timer.c \ - rmd_timer.h \ rmd_types.h \ rmd_update_image.c \ rmd_update_image.h \ diff --git a/src/rmd_get_frame.c b/src/rmd_get_frame.c index 1a5f4d0..cd49258 100644 --- a/src/rmd_get_frame.c +++ b/src/rmd_get_frame.c @@ -120,7 +120,7 @@ static void mark_buffer_area( unsigned char *data, } } -//besides taking the first screenshot, this functions primary purpose is to +//besides taking the first screenshot, this functions primary purpose is to //initialize the structures and memory. static int rmdFirstFrame(ProgData *pdata, Image *image) { @@ -262,9 +262,140 @@ static void rmdBlocksFromList( RectArea **root, } } + +static struct timespec us_to_timespec(unsigned us) +{ + return (struct timespec){ + .tv_sec = us / 1000000, + .tv_nsec = (us % 1000000) * 1000, + }; +} + + +/* subtract t2 from t1, return difference in microseconds */ +static unsigned timespec_delta_in_us(struct timespec *t1, struct timespec *t2) +{ + unsigned us = (t1->tv_sec - t2->tv_sec) * 1000000; + + us += t1->tv_nsec / 1000; + us -= t2->tv_nsec / 1000; + + return us; +} + + +/* Synchronize the next frame with either just time, or if there's + * an audio stream, with the audio stream. + * + * Returns wether a new frame should be acquired, or if the current + * yuv contents should be reused as-is (when too far behind). + */ +static boolean sync_next_frame(ProgData *pdata) +{ + int avd, delay_us; + + assert(pdata); + + if (pdata->args.nosound) { + struct timespec now; + + /* when there's no audio timeline to synchronize with, + * drive avd entirely from CLOCK_MONOTONIC + */ + clock_gettime(CLOCK_MONOTONIC, &now); + + if (pdata->last_frame_ts.tv_sec) { + pthread_mutex_lock(&pdata->avd_mutex); + pdata->avd -= timespec_delta_in_us(&now, &pdata->last_frame_ts); + avd = pdata->avd; + pthread_mutex_unlock(&pdata->avd_mutex); + } + + pdata->last_frame_ts = now; + } + + pthread_mutex_lock(&pdata->avd_mutex); + avd = pdata->avd; + pthread_mutex_unlock(&pdata->avd_mutex); + + delay_us = (int)pdata->frametime_us + avd; + if (delay_us > 1000 /* only bother sleeping when > 1ms */) { + struct timespec delay; + + delay = us_to_timespec(delay_us); + nanosleep(&delay, NULL); + + /* update avd post-sleep */ + pthread_mutex_lock(&pdata->avd_mutex); + avd = pdata->avd; + pthread_mutex_unlock(&pdata->avd_mutex); + } + + + /* refresh the frame unless we're way too far behind */ + return (avd > 2 * -(int)MAX(pdata->frametime_us, pdata->periodtime_us)); +} + + +/* Advances *frameno according to avd, returns wether frameno was advanced. */ +static boolean advance_frameno(ProgData *pdata, unsigned *frameno) +{ + int increment, avd; + + assert(pdata); + + pthread_mutex_lock(&pdata->avd_mutex); + avd = pdata->avd; + + if (avd > (int)MAX(pdata->frametime_us, pdata->periodtime_us)) { + /* if we're far ahead, don't produce a frame */ + increment = 0; + } else if (avd > 0) { + /* if we're just a little ahead, produce a frame */ + increment = 1; + } else if (avd <= 2 * -(int)MAX(pdata->frametime_us, pdata->periodtime_us)) { + /* if we're far behind, clone as many frames as fit to ~avd=0 */ + increment = -avd / (int)pdata->frametime_us; + } else { + /* if we're just a little behind produce a frame */ + increment = 1; + } + + pdata->avd += increment * (int)pdata->frametime_us; + pthread_mutex_unlock(&pdata->avd_mutex); + + (*frameno) += increment; +#if 0 + printf("avd=%i increment=%u periodtime_us=%u frametime_us=%u\n", avd, increment, pdata->periodtime_us, pdata->frametime_us); +#endif + return (increment > 0); +} + + +static boolean paused(ProgData *pdata) +{ + pthread_mutex_lock(&pdata->pause_mutex); + if (pdata->pause_state_changed) { + pdata->pause_state_changed = FALSE; + + if (!pdata->paused) { + pdata->paused = TRUE; + fprintf(stderr, "STATE:PAUSED\n"); + } else { + pdata->paused = FALSE; + fprintf(stderr, "STATE:RECORDING\n"); + pthread_cond_broadcast(&pdata->pause_cond); + } + } + pthread_mutex_unlock(&pdata->pause_mutex); + + return pdata->paused; +} + + /* This thread just samples the recorded window in response to rmdTimer's * triggers, updating the yuv buffer hopefully at the desired frame rate. - * Following every update, the time_frameno is propogated to capture_frameno + * Following every update, the frameno is propogated to capture_frameno * and image_buffer_ready is signaled for the cache/encode side to consume the * yuv buffer. */ @@ -280,6 +411,7 @@ void *rmdGetFrame(ProgData *pdata) Image image = {}, image_back = {}; //the image that holds //the current full screenshot int init_img1 = 0, init_img2 = 0, img_sel, d_buff; + unsigned frameno = 0; rmdThreadsSetName("rmdGetFrame"); @@ -335,284 +467,272 @@ void *rmdGetFrame(ProgData *pdata) mouse_pos_abs.height = mouse_pos_temp.height = pdata->dummy_p_size; //This is the the place where we call XSelectInput - //and arrange so that we listen for damage on all + //and arrange so that we listen for damage on all //windows rmdInitEventsPolling(pdata); while (pdata->running) { - unsigned time_frameno; - - pthread_mutex_lock(&pdata->time_mutex); - while (pdata->capture_frameno >= pdata->time_frameno) { - pthread_cond_wait(&pdata->time_cond, &pdata->time_mutex); - - if (pdata->capture_frameno >= pdata->time_frameno) { - /* *Likely* paused, but could be a spurious wakeup. - * either way, it's harmless to service the event loop - * before waiting again. This is how the timer keeps - * the event loop serviced by signaling us without - * advancing time_frameno. - */ - pthread_mutex_unlock(&pdata->time_mutex); - rmdEventLoop(pdata); - pthread_mutex_lock(&pdata->time_mutex); - } + + if (paused(pdata)) { + /* just pump events while paused, so shortcuts can work for unpausing */ + nanosleep(&(struct timespec) { .tv_nsec = 100000000 }, NULL); + rmdEventLoop(pdata); + continue; } - time_frameno = pdata->time_frameno; - pthread_mutex_unlock(&pdata->time_mutex); + if (sync_next_frame(pdata)) { + //read all events and construct list with damage + //events (if not full_shots) + rmdEventLoop(pdata); - //read all events and construct list with damage - //events (if not full_shots) - rmdEventLoop(pdata); + /* TODO: refactor this inherited spaghetti into functions */ - //switch back and front buffers (full_shots only) - if (d_buff) - img_sel = img_sel ? 0 : 1; + //switch back and front buffers (full_shots only) + if (d_buff) + img_sel = img_sel ? 0 : 1; - rmdBRWinCpy(&temp_brwin, &pdata->brwin); + rmdBRWinCpy(&temp_brwin, &pdata->brwin); - if ( pdata->args.xfixes_cursor || - pdata->args.have_dummy_cursor || - pdata->args.follow_mouse) { + if ( pdata->args.xfixes_cursor || + pdata->args.have_dummy_cursor || + pdata->args.follow_mouse) { - // Pointer sequence: - // * Mark previous position as dirty with rmdRectInsert() - // * Update to new position - // * Mark new position as dirty with rmdRectInsert() - if ( !pdata->args.full_shots && - mouse_pos_temp.x >= 0 && - mouse_pos_temp.y >= 0 && - mouse_pos_temp.width > 0 && - mouse_pos_temp.height > 0) { - rmdRectInsert(&pdata->rect_root, &mouse_pos_temp); - } - - if (pdata->args.xfixes_cursor) { - xcim = XFixesGetCursorImage(pdata->dpy); - mouse_pos_abs.x = xcim->x - xcim->xhot; - mouse_pos_abs.y = xcim->y - xcim->yhot; - mouse_pos_abs.width = xcim->width; - mouse_pos_abs.height = xcim->height; - } else { - XQueryPointer( pdata->dpy, - pdata->specs.root, - &root_ret, &child_ret, - (int *)&mouse_pos_abs.x, - (int *)&mouse_pos_abs.y, - (int *)&mouse_pos_rel.x, - (int *)&mouse_pos_rel.y, - &msk_ret); - } - - clip_dummy_pointer_area(&mouse_pos_abs, &temp_brwin.rrect, &mouse_pos_temp); - if ( mouse_pos_temp.x >= 0 && - mouse_pos_temp.y >= 0 && - mouse_pos_temp.width > 0 && - mouse_pos_temp.height > 0) { - - //there are 3 capture scenarios: - // * Xdamage - // * full-shots with double buffering - // * full-shots on a single buffer - //The last one cannot be reached through - //this code (see above how the d_buf variable is set), but - //even if it could, it would not be of interest regarding the - //marking of the cursor area. Single buffer means full repaint - //on every frame so there is no need for marking at all. - - if (!pdata->args.full_shots) { + // Pointer sequence: + // * Mark previous position as dirty with rmdRectInsert() + // * Update to new position + // * Mark new position as dirty with rmdRectInsert() + if ( !pdata->args.full_shots && + mouse_pos_temp.x >= 0 && + mouse_pos_temp.y >= 0 && + mouse_pos_temp.width > 0 && + mouse_pos_temp.height > 0) { rmdRectInsert(&pdata->rect_root, &mouse_pos_temp); - } else if (d_buff) { - unsigned char *back_buff= img_sel ? - ((unsigned char*)image.ximage->data) : - ((unsigned char*)image_back.ximage->data); - - mark_buffer_area( - back_buff, - mouse_pos_temp.x - temp_brwin.rrect.x, - mouse_pos_temp.y - temp_brwin.rrect.y, - mouse_pos_temp.width, mouse_pos_temp.height, temp_brwin.rrect.width, - pdata->specs.depth - ); } - } - } - if (pdata->args.follow_mouse) { - rmdMoveCaptureArea( &pdata->brwin.rrect, - mouse_pos_abs.x + pdata->args.xfixes_cursor ? xcim->xhot : 0, - mouse_pos_abs.y + pdata->args.xfixes_cursor ? xcim->yhot : 0, - pdata->specs.width, - pdata->specs.height); - - if (!pdata->args.noframe) - rmdMoveFrame( pdata->dpy, - pdata->shaped_w, - temp_brwin.rrect.x, - temp_brwin.rrect.y); - } + if (pdata->args.xfixes_cursor) { + xcim = XFixesGetCursorImage(pdata->dpy); + mouse_pos_abs.x = xcim->x - xcim->xhot; + mouse_pos_abs.y = xcim->y - xcim->yhot; + mouse_pos_abs.width = xcim->width; + mouse_pos_abs.height = xcim->height; + } else { + XQueryPointer( pdata->dpy, + pdata->specs.root, + &root_ret, &child_ret, + (int *)&mouse_pos_abs.x, + (int *)&mouse_pos_abs.y, + (int *)&mouse_pos_rel.x, + (int *)&mouse_pos_rel.y, + &msk_ret); + } - if (!pdata->args.full_shots) { - pthread_mutex_lock(&pdata->yuv_mutex); - rmdUpdateImage( pdata->dpy, - &pdata->enc_data->yuv, - &pdata->specs, - &pdata->rect_root, - &temp_brwin, - pdata->enc_data, - &image, - pdata->args.noshared, - pdata->shm_opcode, - pdata->args.no_quick_subsample); - - rmdBlocksFromList( &pdata->rect_root, - temp_brwin.rrect.x, - temp_brwin.rrect.y, - blocks_w, - blocks_h); - - pthread_mutex_unlock(&pdata->yuv_mutex); - } else { - unsigned char *front_buff = !img_sel ? ((unsigned char*)image.ximage->data): - ((unsigned char*)image_back.ximage->data); - unsigned char *back_buff = !d_buff ? NULL : (img_sel ? - ((unsigned char*)image.ximage->data): - ((unsigned char*)image_back.ximage->data)); - - if (pdata->args.noshared) { - rmdGetZPixmap( pdata->dpy, - pdata->specs.root, - image.ximage->data, - temp_brwin.rrect.x, - temp_brwin.rrect.y, - temp_brwin.rrect.width, - temp_brwin.rrect.height); - } else { - XShmGetImage( pdata->dpy, - pdata->specs.root, - ((!img_sel) ? image.ximage : image_back.ximage), - temp_brwin.rrect.x, - temp_brwin.rrect.y, - AllPlanes); + clip_dummy_pointer_area(&mouse_pos_abs, &temp_brwin.rrect, &mouse_pos_temp); + if ( mouse_pos_temp.x >= 0 && + mouse_pos_temp.y >= 0 && + mouse_pos_temp.width > 0 && + mouse_pos_temp.height > 0) { + + //there are 3 capture scenarios: + // * Xdamage + // * full-shots with double buffering + // * full-shots on a single buffer + //The last one cannot be reached through + //this code (see above how the d_buf variable is set), but + //even if it could, it would not be of interest regarding the + //marking of the cursor area. Single buffer means full repaint + //on every frame so there is no need for marking at all. + + if (!pdata->args.full_shots) { + rmdRectInsert(&pdata->rect_root, &mouse_pos_temp); + } else if (d_buff) { + unsigned char *back_buff= img_sel ? + ((unsigned char*)image.ximage->data) : + ((unsigned char*)image_back.ximage->data); + + mark_buffer_area( + back_buff, + mouse_pos_temp.x - temp_brwin.rrect.x, + mouse_pos_temp.y - temp_brwin.rrect.y, + mouse_pos_temp.width, mouse_pos_temp.height, temp_brwin.rrect.width, + pdata->specs.depth + ); + } + } } - pthread_mutex_lock(&pdata->yuv_mutex); - rmdBlocksReset(blocks_w, blocks_h); - rmdUpdateYuvBuffer( &pdata->enc_data->yuv, - front_buff, - back_buff, - 0, - 0, - temp_brwin.rrect.width, - temp_brwin.rrect.height, - pdata->args.no_quick_subsample, - pdata->specs.depth); - - pthread_mutex_unlock(&pdata->yuv_mutex); - } - - if (pdata->args.xfixes_cursor || pdata->args.have_dummy_cursor) { - int mouse_xoffset, mouse_yoffset; - - //avoid segfaults - clip_dummy_pointer_area(&mouse_pos_abs, &temp_brwin.rrect, &mouse_pos_temp); - mouse_xoffset = mouse_pos_temp.x - mouse_pos_abs.x; - mouse_yoffset = mouse_pos_temp.y - mouse_pos_abs.y; - - if ((mouse_xoffset < 0) || (mouse_xoffset > mouse_pos_abs.width)) - mouse_xoffset = 0; + if (pdata->args.follow_mouse) { + rmdMoveCaptureArea( &pdata->brwin.rrect, + mouse_pos_abs.x + pdata->args.xfixes_cursor ? xcim->xhot : 0, + mouse_pos_abs.y + pdata->args.xfixes_cursor ? xcim->yhot : 0, + pdata->specs.width, + pdata->specs.height); + + if (!pdata->args.noframe) + rmdMoveFrame( pdata->dpy, + pdata->shaped_w, + temp_brwin.rrect.x, + temp_brwin.rrect.y); + } - if ((mouse_yoffset < 0) || (mouse_yoffset > mouse_pos_abs.height)) - mouse_yoffset = 0; + if (!pdata->args.full_shots) { + pthread_mutex_lock(&pdata->yuv_mutex); + rmdUpdateImage( pdata->dpy, + &pdata->enc_data->yuv, + &pdata->specs, + &pdata->rect_root, + &temp_brwin, + pdata->enc_data, + &image, + pdata->args.noshared, + pdata->shm_opcode, + pdata->args.no_quick_subsample); + + rmdBlocksFromList( &pdata->rect_root, + temp_brwin.rrect.x, + temp_brwin.rrect.y, + blocks_w, + blocks_h); + + pthread_mutex_unlock(&pdata->yuv_mutex); + } else { + unsigned char *front_buff = !img_sel ? ((unsigned char*)image.ximage->data): + ((unsigned char*)image_back.ximage->data); + unsigned char *back_buff = !d_buff ? NULL : (img_sel ? + ((unsigned char*)image.ximage->data): + ((unsigned char*)image_back.ximage->data)); + + if (pdata->args.noshared) { + rmdGetZPixmap( pdata->dpy, + pdata->specs.root, + image.ximage->data, + temp_brwin.rrect.x, + temp_brwin.rrect.y, + temp_brwin.rrect.width, + temp_brwin.rrect.height); + } else { + XShmGetImage( pdata->dpy, + pdata->specs.root, + ((!img_sel) ? image.ximage : image_back.ximage), + temp_brwin.rrect.x, + temp_brwin.rrect.y, + AllPlanes); + } - //draw the cursor - if ( (mouse_pos_temp.x >= 0) && - (mouse_pos_temp.y >= 0) && - (mouse_pos_temp.width > 0) && - (mouse_pos_temp.height > 0)) { + pthread_mutex_lock(&pdata->yuv_mutex); + rmdBlocksReset(blocks_w, blocks_h); + rmdUpdateYuvBuffer( &pdata->enc_data->yuv, + front_buff, + back_buff, + 0, + 0, + temp_brwin.rrect.width, + temp_brwin.rrect.height, + pdata->args.no_quick_subsample, + pdata->specs.depth); + + pthread_mutex_unlock(&pdata->yuv_mutex); + } - if (pdata->args.xfixes_cursor) { - rmdXFixesPointerToYuv( - &pdata->enc_data->yuv, - ((unsigned char*)xcim->pixels), - mouse_pos_temp.x - temp_brwin.rrect.x, - mouse_pos_temp.y - temp_brwin.rrect.y, - mouse_pos_temp.width, - mouse_pos_temp.height, - mouse_xoffset, - mouse_yoffset, - xcim->width-mouse_pos_temp.width - ); - } else { - rmdDummyPointerToYuv( - &pdata->enc_data->yuv, - pdata->dummy_pointer, + if (pdata->args.xfixes_cursor || pdata->args.have_dummy_cursor) { + int mouse_xoffset, mouse_yoffset; + + //avoid segfaults + clip_dummy_pointer_area(&mouse_pos_abs, &temp_brwin.rrect, &mouse_pos_temp); + mouse_xoffset = mouse_pos_temp.x - mouse_pos_abs.x; + mouse_yoffset = mouse_pos_temp.y - mouse_pos_abs.y; + + if ((mouse_xoffset < 0) || (mouse_xoffset > mouse_pos_abs.width)) + mouse_xoffset = 0; + + if ((mouse_yoffset < 0) || (mouse_yoffset > mouse_pos_abs.height)) + mouse_yoffset = 0; + + //draw the cursor + if ( (mouse_pos_temp.x >= 0) && + (mouse_pos_temp.y >= 0) && + (mouse_pos_temp.width > 0) && + (mouse_pos_temp.height > 0)) { + + if (pdata->args.xfixes_cursor) { + rmdXFixesPointerToYuv( + &pdata->enc_data->yuv, + ((unsigned char*)xcim->pixels), + mouse_pos_temp.x - temp_brwin.rrect.x, + mouse_pos_temp.y - temp_brwin.rrect.y, + mouse_pos_temp.width, + mouse_pos_temp.height, + mouse_xoffset, + mouse_yoffset, + xcim->width-mouse_pos_temp.width + ); + } else { + rmdDummyPointerToYuv( + &pdata->enc_data->yuv, + pdata->dummy_pointer, + mouse_pos_temp.x - temp_brwin.rrect.x, + mouse_pos_temp.y - temp_brwin.rrect.y, + mouse_pos_temp.width, + mouse_pos_temp.height, + mouse_xoffset, + mouse_yoffset, + pdata->npxl + ); + } + + if (d_buff) { + //make previous cursor position dirty + //on the currently front buffer (which + //will be the back buffer next time it's + //used) + unsigned char *front_buff = !img_sel ? + ((unsigned char*)image.ximage->data) : + ((unsigned char*)image_back.ximage->data); + + mark_buffer_area( + front_buff, mouse_pos_temp.x - temp_brwin.rrect.x, mouse_pos_temp.y - temp_brwin.rrect.y, mouse_pos_temp.width, mouse_pos_temp.height, - mouse_xoffset, - mouse_yoffset, - pdata->npxl + temp_brwin.rrect.width, + pdata->specs.depth ); } - if (d_buff) { - //make previous cursor position dirty - //on the currently front buffer (which - //will be the back buffer next time it's - //used) - unsigned char *front_buff = !img_sel ? - ((unsigned char*)image.ximage->data) : - ((unsigned char*)image_back.ximage->data); - - mark_buffer_area( - front_buff, - mouse_pos_temp.x - temp_brwin.rrect.x, - mouse_pos_temp.y - temp_brwin.rrect.y, - mouse_pos_temp.width, - mouse_pos_temp.height, - temp_brwin.rrect.width, - pdata->specs.depth - ); } + if (pdata->args.xfixes_cursor) { + XFree(xcim); + xcim = NULL; + } } - if (pdata->args.xfixes_cursor) { - XFree(xcim); - xcim = NULL; - } + if (!pdata->args.full_shots) + rmdClearList(&pdata->rect_root); } - if (!pdata->args.full_shots) - rmdClearList(&pdata->rect_root); - - /* Since frame acquisition may take a long and variable time, update - * frameno again just in case we've missed some so they can be included - * as this frame by the encoder. - */ - pthread_mutex_lock(&pdata->time_mutex); - time_frameno = pdata->time_frameno; - pthread_mutex_unlock(&pdata->time_mutex); - - /* notify the encoder of the new frame */ - pthread_mutex_lock(&pdata->img_buff_ready_mutex); - pdata->capture_frameno = time_frameno; - pthread_cond_signal(&pdata->image_buffer_ready); - pthread_mutex_unlock(&pdata->img_buff_ready_mutex); - - /* XXX: note if the encoder thread doesn't manage to wake up and grab the yuv_mutex - * before this thread does in response to another tick, then the frame just - * submitted is lost and the encoder thread will wait again for another frame. - * In practice if this never happens it's irrelevant, but if it proves to be an - * issue there are options. - */ + if (advance_frameno(pdata, &frameno)) { + /* notify the encoder of additional frames */ + pthread_mutex_lock(&pdata->img_buff_ready_mutex); + pdata->capture_frameno = frameno; + pthread_cond_signal(&pdata->image_buffer_ready); + pthread_mutex_unlock(&pdata->img_buff_ready_mutex); + } } + /* Make sure the encoder/cacher doen't get lost in pthread_cond_wait indefinitely, + * now that !pdata->running they will exit their wait loops. + */ + pthread_cond_signal(&pdata->image_buffer_ready); + + /* Same for waiters on pause_cond in case we quit from being paused */ + pthread_mutex_lock(&pdata->pause_mutex); + pdata->paused = FALSE; + pthread_cond_broadcast(&pdata->pause_cond); + pthread_mutex_unlock(&pdata->pause_mutex); + if (!pdata->args.noframe) XDestroyWindow(pdata->dpy, pdata->shaped_w); diff --git a/src/rmd_initialize_data.c b/src/rmd_initialize_data.c index 19a91ee..6e1a294 100644 --- a/src/rmd_initialize_data.c +++ b/src/rmd_initialize_data.c @@ -101,7 +101,6 @@ int rmdInitializeData(ProgData *pdata, EncData *enc_data, CacheData *cache_data) pdata->aborted = FALSE; pdata->pause_state_changed = FALSE; pdata->capture_frameno = 0; - pdata->time_frameno = 0; if (!pdata->args.nosound) { if (!pdata->args.use_jack) { diff --git a/src/rmd_threads.c b/src/rmd_threads.c index 40fe94a..8514649 100644 --- a/src/rmd_threads.c +++ b/src/rmd_threads.c @@ -36,7 +36,6 @@ #include "rmd_get_frame.h" #include "rmd_jack.h" #include "rmd_register_callbacks.h" -#include "rmd_timer.h" #include "rmd_types.h" #include <pthread.h> @@ -55,8 +54,7 @@ void rmdThreads(ProgData *pdata) sound_capture_t, sound_encode_t, sound_cache_t, - flush_to_ogg_t, - timer_t; + flush_to_ogg_t; if (pdata->args.delay > 0) { fprintf(stderr, "Will sleep for %d seconds now.\n", pdata->args.delay); @@ -106,11 +104,6 @@ void rmdThreads(ProgData *pdata) (void *)pdata); rmdRegisterCallbacks(pdata); - pdata->timer_alive = 1; - pthread_create( &timer_t, - NULL, - (void *)rmdTimer, - (void *)pdata); fprintf(stderr,"Capturing!\n"); #ifdef HAVE_LIBJACK @@ -165,10 +158,6 @@ void rmdThreads(ProgData *pdata) fprintf(stderr,".."); fprintf(stderr,"."); - - //Now that we are done with recording we cancel the timer - pdata->timer_alive = 0; - pthread_join(timer_t,NULL); } void rmdThreadsSetName(const char *name) diff --git a/src/rmd_timer.c b/src/rmd_timer.c deleted file mode 100644 index 8b2e076..0000000 --- a/src/rmd_timer.c +++ /dev/null @@ -1,145 +0,0 @@ -/****************************************************************************** -* recordMyDesktop * -******************************************************************************* -* * -* Copyright (C) 2006,2007,2008 John Varouhakis * -* * -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU General Public License for more details. * -* * -* You should have received a copy of the GNU General Public License * -* along with this program; if not, write to the Free Software * -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * -* * -* * -* * -* For further information contact me at johnvarouhakis@gmail.com * -******************************************************************************/ - -#include "config.h" -#include "rmd_timer.h" - -#include "rmd_threads.h" -#include "rmd_types.h" - -#include <pthread.h> - -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <time.h> -#include <unistd.h> - - -static struct timespec us_to_timespec(unsigned int us) -{ - return (struct timespec){ - .tv_sec = us / 1000000, - .tv_nsec = (us % 1000000) * 1000, - }; -} - -static void sync_streams(ProgData *pdata, unsigned int *frame_step, struct timespec *delay) -{ - int avd; - - pthread_mutex_lock(&pdata->avd_mutex); - avd = pdata->avd + pdata->frametime_us; - - /* There are two knobs available for keeping the video synchronized with the audio: - * 1. frame_step; how many frames to encode from this frame (aka dropping frames if > 1) - * 2. delay; how long to delay the next get_frame - * - * When avd is negative, we need more video relative to audio. That can be achieved - * by either sleeping less between frames, or dropping them by having the encoder - * encode a given frame multiple times. - * - * When avd is positive, we need more audio relative to video, so less video. This - * can be achieved by sleeping more between frames. - */ - - if (avd < 0) { - int frames_behind = -avd / pdata->frametime_us; - - if (frames_behind > 0) { - /* more than a whole frame behind, drop frames to catch up */ - *frame_step += frames_behind; - avd += frames_behind * pdata->frametime_us; - } else { - /* less than a whole frame behind, just sleep less */ - *delay = us_to_timespec(pdata->frametime_us + avd); - } - - } else if (avd > 0) { - /* sleep longer */ - *delay = us_to_timespec(pdata->frametime_us + avd); - } - - pdata->avd = avd; - pthread_mutex_unlock(&pdata->avd_mutex); - -#if 0 - printf("avd: %i frame_step: %u delay: %lu,%lu\n", - avd, *frame_step, (*delay).tv_sec, (*delay).tv_nsec); -#endif -} - -void *rmdTimer(ProgData *pdata) -{ - rmdThreadsSetName("rmdTimer"); - - while (pdata->timer_alive) { - struct timespec delay; - unsigned int frame_step = 1; - - delay.tv_sec = 1.f / pdata->args.fps; - delay.tv_nsec = 1000000000.f / pdata->args.fps - delay.tv_sec * 1000000000.f; - - pthread_mutex_lock(&pdata->pause_mutex); - if (pdata->pause_state_changed) { - pdata->pause_state_changed = FALSE; - - if (!pdata->paused) { - pdata->paused = TRUE; - fprintf(stderr, "STATE:PAUSED\n"); - } else { - pdata->paused = FALSE; - fprintf(stderr, "STATE:RECORDING\n"); - pthread_cond_broadcast(&pdata->pause_cond); - } - } - - /* When paused, stop advancing time_frameno, get_frame only progresses - * when time_frameno > capture_frameno. But get_frame needs to service - * the event loop even when paused, so still signal time_cond just with - * pdata->time_frameno frozen. get_frame will then handle time_cond - * wakeups where capture_frameno >= time_frameno as "poll events". - */ - if (pdata->paused) - frame_step = 0; - pthread_mutex_unlock(&pdata->pause_mutex); - - if (frame_step) { - if (!pdata->args.nosound) - sync_streams(pdata, &frame_step, &delay); - - pthread_mutex_lock(&pdata->time_mutex); - pdata->time_frameno += frame_step; - pthread_mutex_unlock(&pdata->time_mutex); - } - - pthread_cond_signal(&pdata->time_cond); - - nanosleep(&delay, NULL); - } - - pthread_exit(&errno); -} diff --git a/src/rmd_timer.h b/src/rmd_timer.h deleted file mode 100644 index d9292d9..0000000 --- a/src/rmd_timer.h +++ /dev/null @@ -1,44 +0,0 @@ -/****************************************************************************** -* recordMyDesktop * -******************************************************************************* -* * -* Copyright (C) 2006,2007,2008 John Varouhakis * -* * -* * -* This program is free software; you can redistribute it and/or modify * -* it under the terms of the GNU General Public License as published by * -* the Free Software Foundation; either version 2 of the License, or * -* (at your option) any later version. * -* * -* This program is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -* GNU General Public License for more details. * -* * -* You should have received a copy of the GNU General Public License * -* along with this program; if not, write to the Free Software * -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * -* * -* * -* * -* For further information contact me at johnvarouhakis@gmail.com * -******************************************************************************/ - -#ifndef RMD_TIMER_H -#define RMD_TIMER_H 1 - -#include "rmd_types.h" - - -/** -* Loop ,signal timer cond var,sleep-\ -* ^ | -* |________________________________/ -* -* -* \param pdata ProgData struct containing all program data -*/ -void *rmdTimer(ProgData *pdata); - - -#endif diff --git a/src/rmd_types.h b/src/rmd_types.h index 6d1f1ba..0c859da 100644 --- a/src/rmd_types.h +++ b/src/rmd_types.h @@ -323,7 +323,6 @@ struct _ProgData { dummy_p_size, //dummy pointer size,initially 16x16,always square th_encoding_clean, //thread exit inidcator v_encoding_clean, // >> >> - timer_alive, //determines loop of timer thread hard_pause, //if sound device doesn't support pause //we have to close and reopen avd, //syncronization among audio and video @@ -335,10 +334,8 @@ struct _ProgData { boolean aborted; //1 if we should abort boolean pause_state_changed; //1 if pause state changed - /* timer advances time_frameno, getframe copies time_frameno to capture_frameno - * access to both is serialized by time_{mutex,cond} - */ - unsigned int time_frameno, capture_frameno; + unsigned int capture_frameno; // frame counter advanced by getframe for encoder/cacher + struct timespec last_frame_ts; // time of last frame when args.nosound=1 for avd maintenance pthread_mutex_t pause_mutex; pthread_mutex_t time_mutex; |