From: libvidcap c. <lib...@li...> - 2007-12-12 20:55:27
|
Revision: 84 http://libvidcap.svn.sourceforge.net/libvidcap/?rev=84&view=rev Author: bcholew Date: 2007-12-12 12:55:11 -0800 (Wed, 12 Dec 2007) Log Message: ----------- A bunch of changes - including some from Pete Grayson. Add capture time to capture callback data. Wrap gettimeofday() with new vc_now() function. Log malloc errors with log_oom(). Make some functions static. Replace double-buffer malloc()s with memcpy()s. Renamed double_buffer_insert() to double_buffer_write(). Change some info messages to debug. Some cleanup. Modified Paths: -------------- trunk/include/vidcap/vidcap.h trunk/src/double_buffer.c trunk/src/double_buffer.h trunk/src/os_funcs.h trunk/src/sapi.c trunk/src/sapi_context.h trunk/src/vidcap.c Modified: trunk/include/vidcap/vidcap.h =================================================================== --- trunk/include/vidcap/vidcap.h 2007-12-12 20:46:06 UTC (rev 83) +++ trunk/include/vidcap/vidcap.h 2007-12-12 20:55:11 UTC (rev 84) @@ -76,6 +76,8 @@ const char * video_data; int video_data_size; int error_status; + long capture_time_sec; + long capture_time_usec; }; typedef int (*vidcap_src_capture_callback) (vidcap_src *, Modified: trunk/src/double_buffer.c =================================================================== --- trunk/src/double_buffer.c 2007-12-12 20:46:06 UTC (rev 83) +++ trunk/src/double_buffer.c 2007-12-12 20:55:11 UTC (rev 84) @@ -41,16 +41,15 @@ * to make room for an incoming object */ -/* NOTE: This function is passed function pointers to call when - * necessary to free or copy an object +/* NOTE: This function is passed a function pointer to + * call when necessary to copy an object */ struct double_buffer * -double_buffer_create( void (*free_me)(void *), void * (*copy_me)(void *) ) +double_buffer_create( void (*copy_func)(void *, const void *), void *object1, void *object2 ) { - struct double_buffer * db_buff; - if ( !free_me || !copy_me ) + if ( !copy_func ) return 0; db_buff = calloc(1, sizeof(*db_buff)); @@ -65,11 +64,10 @@ db_buff->write_count = 0; db_buff->count[0] = -1; db_buff->count[1] = -1; - db_buff->objects[0] = 0; - db_buff->objects[1] = 0; + db_buff->objects[0] = object1; + db_buff->objects[1] = object2; - db_buff->free_object = free_me; - db_buff->copy_object = copy_me; + db_buff->copy_object = copy_func; vc_mutex_init(&db_buff->locks[0]); vc_mutex_init(&db_buff->locks[1]); @@ -86,33 +84,18 @@ vc_mutex_destroy(&db_buff->locks[1]); vc_mutex_destroy(&db_buff->locks[0]); - if ( db_buff->write_count > 0 ) - db_buff->free_object(db_buff->objects[0]); + log_debug("Double buffer had counter reading of %d\n", + db_buff->num_insert_too_far_failures); - if ( db_buff->write_count > 1 ) - db_buff->free_object(db_buff->objects[1]); - free(db_buff); } void -double_buffer_insert(struct double_buffer * db_buff, void * new_object) +double_buffer_write(struct double_buffer * db_buff, const void * new_object) { const int insertion_index = db_buff->write_count % 2; - /* TODO: we could eliminate a copy by having the reader free - * all buffered objects. This would require that a failed - * check below result in the object being freed. This - * would ensure that the reader sees all BUFFERED objects. - * - * The reader would then need to take care to free objects - * as they become outdated. - * - * The tradeoff is the occasional dropped object when - * the writer gets ahead a little. The counter can help - * to evaluate the cost of this tradeoff - */ - /* don't get far ahead of the reader*/ + /* don't get far ahead of the reader */ if ( db_buff->write_count > ( db_buff->read_count + 2 ) ) { db_buff->num_insert_too_far_failures++; @@ -124,18 +107,13 @@ { /* failed to obtain lock */ /* drop incoming object */ - log_info("vidcap callback failed to write a frame\n"); - db_buff->free_object(new_object); + log_info("callback is skipping a frame\n"); return; } - /* free the slot object if something is already there */ - if ( db_buff->write_count > 1 ) - db_buff->free_object(db_buff->objects[insertion_index]); + /* copy object */ + db_buff->copy_object(db_buff->objects[insertion_index], new_object); - /* insert object */ - db_buff->objects[insertion_index] = new_object; - /* stamp the buffer with the write count */ db_buff->count[insertion_index] = db_buff->write_count; @@ -146,14 +124,13 @@ vc_mutex_unlock(&db_buff->locks[insertion_index]); } -void * -double_buffer_read(struct double_buffer * db_buff) +int +double_buffer_read(struct double_buffer * db_buff, void *dest_buffer) { int copy_index = db_buff->read_count % 2; - void * buff; if ( db_buff->write_count < 1 ) - return 0; + return -1; /* try the next buffer */ if ( db_buff->count[copy_index] < db_buff->read_count ) @@ -162,14 +139,13 @@ copy_index = 1 - copy_index; } - if ( vc_mutex_trylock(&db_buff->locks[copy_index] ) ) { - /* Failed to obtain lock. - * Try the other slot? + /* Failed to obtain buffer's lock. + * Try the other buffer? */ if ( db_buff->write_count < 2 ) - return 0; + return -1; copy_index = 1 - copy_index; @@ -177,14 +153,15 @@ { /* Too old. Don't read this buffer */ vc_mutex_unlock(&db_buff->locks[copy_index]); - return 0; + log_info("Capture timer thread failed to obtain lock\n"); + return -1; } /* Try other lock. Failure should be rare */ if ( vc_mutex_trylock(&db_buff->locks[copy_index] ) ) { log_info("Capture timer thread failed to obtain 2nd lock\n"); - return 0; + return -1; } } @@ -195,16 +172,17 @@ * This needs to be rare. */ vc_mutex_unlock(&db_buff->locks[copy_index]); - return 0; + log_info("Capture timer thread won't read stale buffer\n"); + return -1; } - buff = db_buff->copy_object(db_buff->objects[copy_index]); + db_buff->copy_object(dest_buffer, db_buff->objects[copy_index]); db_buff->read_count = db_buff->count[copy_index] + 1; vc_mutex_unlock(&db_buff->locks[copy_index]); - return buff; + return 0; } int Modified: trunk/src/double_buffer.h =================================================================== --- trunk/src/double_buffer.h 2007-12-12 20:46:06 UTC (rev 83) +++ trunk/src/double_buffer.h 2007-12-12 20:55:11 UTC (rev 84) @@ -39,25 +39,23 @@ int count[_DOUBLE_MEANS_TWO_]; - void * (*copy_object)(void *); - void (*free_object)(void *); + void (*copy_object)(void *, const void *); /* TODO: remove */ int num_insert_too_far_failures; }; struct double_buffer * -double_buffer_create( void (*free_obj_func)(void *), - void * (*copy_object)(void *)); +double_buffer_create(void (*copy_object)(void *, const void *), void *, void *); void double_buffer_destroy(struct double_buffer * db_buff); void -double_buffer_insert(struct double_buffer * db_buff, void * object); +double_buffer_write(struct double_buffer * db_buff, const void * object); -void * -double_buffer_read(struct double_buffer * db_buff); +int +double_buffer_read(struct double_buffer * db_buff, void * object); int double_buffer_count(struct double_buffer * db_buff); Modified: trunk/src/os_funcs.h =================================================================== --- trunk/src/os_funcs.h 2007-12-12 20:46:06 UTC (rev 83) +++ trunk/src/os_funcs.h 2007-12-12 20:55:11 UTC (rev 84) @@ -79,38 +79,33 @@ #include <sys/sysctl.h> #endif -#ifndef HAVE_GETTIMEOFDAY -static __inline int -gettimeofday(struct timeval * tv, struct timezone * tz) -#ifdef WIN32 +static __inline struct timeval +vc_now(void) { + struct timeval tv; +#ifdef HAVE_GETTIMEOFDAY + gettimeofday(&tv, 0); +#elif defined(WIN32) FILETIME ft; LARGE_INTEGER li; __int64 t; static int tzflag; const __int64 EPOCHFILETIME = 116444736000000000i64; - if ( tv ) - { - GetSystemTimeAsFileTime(&ft); - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - t = li.QuadPart; /* In 100-nanosecond intervals */ - t -= EPOCHFILETIME; /* Offset to the Epoch time */ - t /= 10; /* In microseconds */ - tv->tv_sec = (long)(t / 1000000); - tv->tv_usec = (long)(t % 1000000); - } - return 0; - -#else /* !defined(_WINDOWS) */ - errno = ENOSYS; - return -1; + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + t = li.QuadPart; /* In 100-nanosecond intervals */ + t -= EPOCHFILETIME; /* Offset to the Epoch time */ + t /= 10; /* In microseconds */ + tv.tv_sec = (long)(t / 1000000); + tv.tv_usec = (long)(t % 1000000); +#else +#error no gettimeofday or equivalent available #endif + return tv; } -#endif /* !defined(HAVE_GETTIMEOFDAY) */ - static __inline int vc_create_thread(vc_thread *thread, unsigned int (STDCALL * thread_func)(void *), Modified: trunk/src/sapi.c =================================================================== --- trunk/src/sapi.c 2007-12-12 20:46:06 UTC (rev 83) +++ trunk/src/sapi.c 2007-12-12 20:55:11 UTC (rev 84) @@ -30,9 +30,6 @@ #include "logging.h" #include "sapi.h" -static int -deliver_frame(struct sapi_src_context * src_ctx); - static __inline int tv_greater_or_equal(struct timeval * t1, struct timeval * t0) { @@ -73,8 +70,7 @@ first_time = !sliding_window_count(src_ctx->frame_times); - if ( gettimeofday(&tv_now, 0) ) - return -1; + tv_now = vc_now(); if ( !first_time && !tv_greater_or_equal( &tv_now, &src_ctx->frame_time_next) ) @@ -202,76 +198,6 @@ src_ctx->capture_error_ack = 1; } -/* NOTE: stride-ignorant sapis should pass a stride of zero */ -int -sapi_src_capture_notify(struct sapi_src_context * src_ctx, - char * video_data, int video_data_size, - int stride, - int error_status) -{ - /* NOTE: We may be called here by the capture thread while the - * main thread is clearing capture_data and capture_callback - * from within vidcap_src_capture_stop(). - */ - - /* Package the video information */ - struct frame_info * frame = malloc(sizeof(*frame)); - if ( !frame ) - return -1; - - /* Screen-out useless callbacks */ - if ( video_data_size < 1 && !error_status ) - { - log_info("callback with no data?\n"); - return 0; - } - - frame->video_data_size = video_data_size; - frame->error_status = error_status; - frame->stride = stride; - - if ( src_ctx->use_timer_thread ) - { - /* Copy this buffer. It might not exist long enough for - * read thread (capture timer thread) to copy it - */ - frame->video_data = malloc(video_data_size); - if ( !frame->video_data ) - { - free(frame); - return -1; - } - - memcpy(frame->video_data, video_data, video_data_size); - - /* buffer the frame - for processing by the timer thread */ - double_buffer_insert(src_ctx->double_buff, frame); - - /* If there's an error, wait here until it's acknowledged - * by the timer thread (after having delivered it to the app). - */ - if ( error_status ) - wait_for_error_ack(src_ctx); - } - else - { - frame->video_data = video_data; - - /* process the frame now */ - src_ctx->no_timer_thread_frame = frame; - deliver_frame(src_ctx); - } - - if ( error_status ) - { - src_ctx->src_state = src_bound; - src_ctx->capture_callback = 0; - src_ctx->capture_data = VIDCAP_INVALID_USER_DATA; - } - - return 0; -} - static int deliver_frame(struct sapi_src_context * src_ctx) { @@ -283,7 +209,7 @@ struct vidcap_capture_info cap_info; - struct frame_info * frame; + const struct frame_info * frame; const char * video_data; int video_data_size; int stride; @@ -291,21 +217,23 @@ if ( src_ctx->use_timer_thread ) { - frame = (struct frame_info *) - double_buffer_read(src_ctx->double_buff); - - if ( !frame ) + if ( double_buffer_read(src_ctx->double_buff, + &src_ctx->timer_thread_frame) ) return -1; + + frame = &src_ctx->timer_thread_frame; } else { - frame = src_ctx->no_timer_thread_frame; + frame = &src_ctx->callback_frame; } video_data = frame->video_data; video_data_size = frame->video_data_size; stride = frame->stride; error_status = frame->error_status; + cap_info.capture_time_sec = frame->capture_time.tv_sec; + cap_info.capture_time_usec = frame->capture_time.tv_usec; /* FIXME: right now enforce_framerate() and the capture timer * thread's main loop BOTH use the frame_time_next field @@ -379,10 +307,6 @@ if ( src_ctx->use_timer_thread ) { - /* delete frame */ - free(frame->video_data); - free(frame); - if ( error_status ) { /* Let capture thread know that the app @@ -400,11 +324,66 @@ return 0; } +/* NOTE: stride-ignorant sapis should pass a stride of zero */ +int +sapi_src_capture_notify(struct sapi_src_context * src_ctx, + char * video_data, int video_data_size, + int stride, + int error_status) +{ + /* NOTE: We may be called here by the capture thread while the + * main thread is clearing capture_data and capture_callback + * from within vidcap_src_capture_stop(). + */ + + struct frame_info *frame = &src_ctx->callback_frame; + + /* Screen-out useless callbacks */ + if ( video_data_size < 1 && !error_status ) + { + log_info("callback with no data?\n"); + return 0; + } + + /* Package the video information */ + frame->video_data_size = video_data_size; + frame->error_status = error_status; + frame->stride = stride; + frame->capture_time = vc_now(); + frame->video_data = video_data; + + if ( src_ctx->use_timer_thread ) + { + /* buffer the frame - for processing by the timer thread */ + double_buffer_write(src_ctx->double_buff, frame); + + /* If there's an error, wait here until it's acknowledged + * by the timer thread (after having delivered it to the app). + */ + if ( error_status ) + wait_for_error_ack(src_ctx); + } + else + { + /* process the frame now */ + deliver_frame(src_ctx); + } + + if ( error_status ) + { + src_ctx->src_state = src_bound; + src_ctx->capture_callback = 0; + src_ctx->capture_data = VIDCAP_INVALID_USER_DATA; + } + + return 0; +} + unsigned int STDCALL sapi_src_timer_thread_func(void *args) { struct sapi_src_context * src_ctx = args; - struct timeval tv_now; + struct timeval tv_now; const long idle_state_sleep_period_ms = 100; long sleep_ms = idle_state_sleep_period_ms; int first_time = 1; @@ -414,12 +393,7 @@ src_ctx->timer_thread_idle = 1; - if ( gettimeofday(&tv_now, 0) ) - { - src_ctx->capture_timer_thread_started = -1; - log_error("gettimeofday not supported\n"); - return -1; - } + tv_now = vc_now(); src_ctx->frame_time_next.tv_sec = tv_now.tv_sec; src_ctx->frame_time_next.tv_usec = tv_now.tv_usec; @@ -428,7 +402,7 @@ while ( !src_ctx->kill_timer_thread ) { - gettimeofday(&tv_now, 0); + tv_now = vc_now(); /* sleep or read? */ if ( capture_error || src_ctx->src_state != src_capturing || Modified: trunk/src/sapi_context.h =================================================================== --- trunk/src/sapi_context.h 2007-12-12 20:46:06 UTC (rev 83) +++ trunk/src/sapi_context.h 2007-12-12 20:55:11 UTC (rev 84) @@ -40,6 +40,7 @@ int video_data_size; int error_status; int stride; + struct timeval capture_time; }; struct sapi_src_context @@ -79,8 +80,11 @@ vidcap_src_capture_callback capture_callback; void * capture_data; + struct frame_info buffered_frames[2]; + struct frame_info timer_thread_frame; + int use_timer_thread; - struct frame_info * no_timer_thread_frame; + struct frame_info callback_frame; vc_thread capture_timer_thread; unsigned int capture_timer_thread_id; int capture_timer_thread_started; Modified: trunk/src/vidcap.c =================================================================== --- trunk/src/vidcap.c 2007-12-12 20:46:06 UTC (rev 83) +++ trunk/src/vidcap.c 2007-12-12 20:55:11 UTC (rev 84) @@ -338,7 +338,7 @@ goto bail; } - log_info("using a capture timer thread for this source\n"); + log_debug("using a capture timer thread for this source\n"); } if ( sapi_ctx->acquire_source(sapi_ctx, src_ctx, src_info) ) @@ -528,7 +528,7 @@ return -1; } - log_info("format bind requires conversion: %s\n", + log_debug("format bind requires conversion: %s\n", conv_conversion_name_get(src_ctx->fmt_conv_func)); } @@ -546,40 +546,20 @@ return 0; } -void -free_frame_info(void * fr) +static __inline void +copy_frame_info(void * fr2, const void *fr1) { - struct frame_info *frame = (struct frame_info *)fr; + const struct frame_info *frame_orig = (struct frame_info *)fr1; + struct frame_info *frame_dup = (struct frame_info *)fr2; - free(frame->video_data); - free(frame); -} + frame_dup->capture_time = frame_orig->capture_time; + frame_dup->error_status = frame_orig->error_status; + frame_dup->stride = frame_orig->stride; + frame_dup->video_data_size = frame_orig->video_data_size; -void * -copy_frame_info(void * fr) -{ - struct frame_info *frame = (struct frame_info *)fr; - - struct frame_info *dup_frame = malloc(sizeof(*dup_frame)); - - if ( !dup_frame ) - return 0; - - dup_frame->stride = frame->stride; - dup_frame->error_status = frame->error_status; - dup_frame->video_data_size = frame->video_data_size; - dup_frame->video_data = malloc(frame->video_data_size); - if ( !dup_frame->video_data ) - { - free(dup_frame); - return 0; - } - - memcpy(dup_frame->video_data, - frame->video_data, - frame->video_data_size); - - return dup_frame; + memcpy(frame_dup->video_data, + frame_orig->video_data, + frame_orig->video_data_size); } int @@ -587,54 +567,108 @@ vidcap_src_capture_callback callback, void * user_data) { - int ret; + int ret = 0; struct sapi_src_context * src_ctx = (struct sapi_src_context *)src; const int sliding_window_seconds = 4; + /* Assume worst-case video data with stride is 50% + * bigger than without a stride. + */ + /* FIXME: Defer measurement of stride-full + * buffer until first capture callback + */ + const int stride_full_buf_size = src_ctx->stride_free_buf_size * 3 / 2; + if ( user_data == VIDCAP_INVALID_USER_DATA ) return -3; if ( src_ctx->src_state != src_bound ) return -4; - src_ctx->frame_time_next.tv_sec = 0; - src_ctx->frame_time_next.tv_usec = 0; - src_ctx->frame_times = sliding_window_create(sliding_window_seconds * - src_ctx->fmt_nominal.fps_numerator / - src_ctx->fmt_nominal.fps_denominator, - sizeof(struct timeval)); + memset(src_ctx->buffered_frames, 0, sizeof(src_ctx->buffered_frames)); + src_ctx->timer_thread_frame.video_data = 0; + src_ctx->frame_times = 0; + src_ctx->double_buff = 0; - if ( !src_ctx->frame_times ) - return -2; + if ( src_ctx->use_timer_thread ) + { + /* allocate space for double-buffering video buffers */ + src_ctx->buffered_frames[0].video_data = + malloc(2 * stride_full_buf_size); + if ( !src_ctx->buffered_frames[0].video_data ) + { + log_oom(__FILE__, __LINE__); + ret = -6; + goto capture_start_bail; + } - src_ctx->double_buff = double_buffer_create(&free_frame_info, - ©_frame_info); + src_ctx->buffered_frames[1].video_data = + src_ctx->buffered_frames[0].video_data + + stride_full_buf_size; - if ( !src_ctx->double_buff ) + src_ctx->timer_thread_frame.video_data = + malloc(stride_full_buf_size); + if ( !src_ctx->timer_thread_frame.video_data ) + { + log_oom(__FILE__, __LINE__); + ret = -6; + goto capture_start_bail; + } + + src_ctx->double_buff = double_buffer_create(©_frame_info, + &src_ctx->buffered_frames[0], &src_ctx->buffered_frames[1]); + if ( !src_ctx->double_buff ) + { + ret = -5; + goto capture_start_bail; + } + } + else { - sliding_window_destroy(src_ctx->frame_times); - src_ctx->frame_times = 0; - return -5; + src_ctx->frame_time_next.tv_sec = 0; + src_ctx->frame_time_next.tv_usec = 0; + src_ctx->frame_times = sliding_window_create(sliding_window_seconds * + src_ctx->fmt_nominal.fps_numerator / + src_ctx->fmt_nominal.fps_denominator, + sizeof(struct timeval)); + + if ( !src_ctx->frame_times ) + return -2; } src_ctx->capture_callback = callback; src_ctx->capture_data = user_data; if ( (ret = src_ctx->start_capture(src_ctx)) ) - { - src_ctx->capture_callback = 0; - src_ctx->capture_data = VIDCAP_INVALID_USER_DATA; + goto capture_start_bail; + src_ctx->src_state = src_capturing; + return 0; + +capture_start_bail: + + src_ctx->capture_callback = 0; + src_ctx->capture_data = VIDCAP_INVALID_USER_DATA; + + if ( src_ctx->double_buff ) double_buffer_destroy(src_ctx->double_buff); - src_ctx->double_buff = 0; + if ( src_ctx->buffered_frames[0].video_data ) + free(src_ctx->buffered_frames[0].video_data); + + if ( src_ctx->buffered_frames[1].video_data ) + free(src_ctx->buffered_frames[1].video_data); + + if ( src_ctx->timer_thread_frame.video_data ) + free(src_ctx->timer_thread_frame.video_data); + + if ( src_ctx->frame_times ) sliding_window_destroy(src_ctx->frame_times); - src_ctx->frame_times = 0; - return ret; - } - src_ctx->src_state = src_capturing; - return 0; + src_ctx->double_buff = 0; + src_ctx->frame_times = 0; + + return ret; } int @@ -657,10 +691,21 @@ sapi_src_timer_thread_idled(src_ctx); } - double_buffer_destroy(src_ctx->double_buff); + if ( src_ctx->double_buff ) + double_buffer_destroy(src_ctx->double_buff); src_ctx->double_buff = 0; - sliding_window_destroy(src_ctx->frame_times); + if ( src_ctx->buffered_frames[0].video_data ) + free(src_ctx->buffered_frames[0].video_data); + + if ( src_ctx->buffered_frames[1].video_data ) + free(src_ctx->buffered_frames[1].video_data); + + if ( src_ctx->timer_thread_frame.video_data ) + free(src_ctx->timer_thread_frame.video_data); + + if ( src_ctx->frame_times ) + sliding_window_destroy(src_ctx->frame_times); src_ctx->frame_times = 0; src_ctx->capture_callback = 0; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |