|
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.
|