summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am2
-rw-r--r--src/rmd_get_frame.c606
-rw-r--r--src/rmd_initialize_data.c1
-rw-r--r--src/rmd_threads.c13
-rw-r--r--src/rmd_timer.c145
-rw-r--r--src/rmd_timer.h44
-rw-r--r--src/rmd_types.h7
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;
© All Rights Reserved