|
From: libvidcap c. <lib...@li...> - 2007-11-26 17:59:41
|
Revision: 65
http://libvidcap.svn.sourceforge.net/libvidcap/?rev=65&view=rev
Author: bcholew
Date: 2007-11-26 09:59:33 -0800 (Mon, 26 Nov 2007)
Log Message:
-----------
Add a capture timer thread to strictly enforce framerate (but preserve code for use without this thread). Add a double buffer to pass frames between threads. Abstract threading and mutexes for linux, mac and windows. Add new double_buffer and os_funcs files to vcproj and Makefiles. Simplify and rearrange some includes.
Modified Paths:
--------------
trunk/contrib/win/vs2005/libvidcap.vcproj
trunk/src/Makefile.am
trunk/src/directshow/DShowSrcManager.cpp
trunk/src/directshow/DevMonitor.cpp
trunk/src/directshow/DevMonitor.h
trunk/src/directshow/DirectShowSource.cpp
trunk/src/directshow/SourceStateMachine.cpp
trunk/src/sapi.c
trunk/src/sapi_context.h
trunk/src/sapi_dshow.cpp
trunk/src/vidcap.c
Added Paths:
-----------
trunk/src/double_buffer.c
trunk/src/double_buffer.h
trunk/src/os_funcs.h
Modified: trunk/contrib/win/vs2005/libvidcap.vcproj
===================================================================
--- trunk/contrib/win/vs2005/libvidcap.vcproj 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/contrib/win/vs2005/libvidcap.vcproj 2007-11-26 17:59:33 UTC (rev 65)
@@ -228,6 +228,10 @@
</FileConfiguration>
</File>
<File
+ RelativePath="..\..\..\src\double_buffer.c"
+ >
+ </File>
+ <File
RelativePath="..\..\..\src\directshow\DShowSrcManager.cpp"
>
<FileConfiguration
@@ -372,6 +376,10 @@
>
</File>
<File
+ RelativePath="..\..\..\src\double_buffer.h"
+ >
+ </File>
+ <File
RelativePath="..\..\..\src\directshow\DShowSrcManager.h"
>
</File>
@@ -388,6 +396,10 @@
>
</File>
<File
+ RelativePath="..\..\..\src\os_funcs.h"
+ >
+ </File>
+ <File
RelativePath="..\..\..\src\sapi.h"
>
</File>
Modified: trunk/src/Makefile.am
===================================================================
--- trunk/src/Makefile.am 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/Makefile.am 2007-11-26 17:59:33 UTC (rev 65)
@@ -19,10 +19,13 @@
conv_to_rgb.c \
conv_to_i420.c \
conv_to_yuy2.c \
+ double_buffer.c \
+ double_buffer.h \
hotlist.c \
hotlist.h \
logging.c \
logging.h \
+ os_funcs.h \
sapi.c \
sapi.h \
sapi_context.h \
Modified: trunk/src/directshow/DShowSrcManager.cpp
===================================================================
--- trunk/src/directshow/DShowSrcManager.cpp 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/directshow/DShowSrcManager.cpp 2007-11-26 17:59:33 UTC (rev 65)
@@ -22,7 +22,7 @@
// <http://www.gnu.org/licenses/>.
//
-#include <windows.h>
+#include "os_funcs.h"
#include <cstring>
#include <cstdio>
#include <cwchar>
Modified: trunk/src/directshow/DevMonitor.cpp
===================================================================
--- trunk/src/directshow/DevMonitor.cpp 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/directshow/DevMonitor.cpp 2007-11-26 17:59:33 UTC (rev 65)
@@ -22,11 +22,12 @@
// <http://www.gnu.org/licenses/>.
//
-#include "DevMonitor.h"
+#include "os_funcs.h"
#include <dbt.h>
#include <process.h>
#include <string>
#include "sapi_context.h"
+#include "DevMonitor.h"
#include "logging.h"
DevMonitor::DevMonitor()
Modified: trunk/src/directshow/DevMonitor.h
===================================================================
--- trunk/src/directshow/DevMonitor.h 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/directshow/DevMonitor.h 2007-11-26 17:59:33 UTC (rev 65)
@@ -25,21 +25,8 @@
#ifndef _DEVMONITOR_H_
#define _DEVMONITOR_H_
-#include <windows.h>
#include <vidcap/vidcap.h>
-// Allow use of features specific to Windows XP or later.
-#ifndef _WIN32_WINNT
-
-// Change this to the appropriate value to target other versions of Windows.
-// see: http://msdn2.microsoft.com/en-us/library/aa383745.aspx
-#define _WIN32_WINNT 0x0501
-
-#endif
-
-// Exclude rarely-used stuff from Windows headers
-#define WIN32_LEAN_AND_MEAN
-
class DevMonitor
{
Modified: trunk/src/directshow/DirectShowSource.cpp
===================================================================
--- trunk/src/directshow/DirectShowSource.cpp 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/directshow/DirectShowSource.cpp 2007-11-26 17:59:33 UTC (rev 65)
@@ -22,7 +22,7 @@
// <http://www.gnu.org/licenses/>.
//
-#include <windows.h>
+#include "os_funcs.h"
#include <cstring>
#include <cstdio>
#include <cwchar>
@@ -686,6 +686,9 @@
static_cast<double>(fmtNominal->fps_denominator);
// check framerate
+ // TODO: a timer thread could enforce this framerate,
+ // but would need to pass src_ctx->use_timer_thread here
+ // to know that
if ( fps > fpsMax )
{
freeMediaType(*pMediaType);
Modified: trunk/src/directshow/SourceStateMachine.cpp
===================================================================
--- trunk/src/directshow/SourceStateMachine.cpp 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/directshow/SourceStateMachine.cpp 2007-11-26 17:59:33 UTC (rev 65)
@@ -22,11 +22,9 @@
// <http://www.gnu.org/licenses/>.
//
-#include <windows.h>
-
+#include "sapi.h"
#include "hotlist.h"
#include "logging.h"
-#include "sapi.h"
#include "SourceStateMachine.h"
SourceStateMachine::SourceStateMachine(struct sapi_src_context *src,
Added: trunk/src/double_buffer.c
===================================================================
--- trunk/src/double_buffer.c (rev 0)
+++ trunk/src/double_buffer.c 2007-11-26 17:59:33 UTC (rev 65)
@@ -0,0 +1,200 @@
+/*
+ * libvidcap - a cross-platform video capture library
+ *
+ * Copyright 2007 Wimba, Inc.
+ *
+ * Contributors:
+ * Peter Grayson <jpg...@gm...>
+ * Bill Cholewka <bc...@gm...>
+ *
+ * libvidcap is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * libvidcap 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "double_buffer.h"
+
+#include "logging.h"
+
+/* The write thread inserts (and frees) objects.
+ * The read thread reads (copies) objects
+ *
+ * It is the responsibility of the read thread's 'application' to
+ * free the copied objects.
+ *
+ * If the read thread 'application' blocks for too long (doesn't
+ * read often enough), the write thread will free a buffered object
+ * to make room for an incoming object
+ */
+
+/* NOTE: This function is passed function pointers to call when
+ * necessary to free or copy an object
+ */
+struct double_buffer *
+double_buffer_create( void (*free_me)(void *), void * (*copy_me)(void *) )
+{
+
+ struct double_buffer * db_buff;
+
+ if ( !free_me || !copy_me )
+ return 0;
+
+ db_buff = calloc(1, sizeof(*db_buff));
+
+ if ( !db_buff )
+ {
+ log_oom(__FILE__, __LINE__);
+ return 0;
+ }
+
+ db_buff->read_count = 0;
+ 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->free_object = free_me;
+ db_buff->copy_object = copy_me;
+
+ vc_mutex_init(&db_buff->locks[0]);
+ vc_mutex_init(&db_buff->locks[1]);
+
+ /* TODO: remove this (debug) counter */
+ db_buff->num_insert_too_far_failures = 0;
+
+ return db_buff;
+}
+
+void
+double_buffer_destroy(struct double_buffer * db_buff)
+{
+ 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]);
+
+ 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)
+{
+ 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*/
+ if ( db_buff->write_count > ( db_buff->read_count + 2 ) )
+ {
+ db_buff->num_insert_too_far_failures++;
+ /* return; */
+ }
+
+ /* get exclusive access to the correct buffer */
+ if ( vc_mutex_trylock(&db_buff->locks[insertion_index] ))
+ {
+ /* failed to obtain lock */
+ /* drop incoming object */
+ db_buff->free_object(new_object);
+ 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]);
+
+ /* 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;
+
+ /* advance the write count */
+ ++db_buff->write_count;
+
+ /* unlock the correct lock */
+ vc_mutex_unlock(&db_buff->locks[insertion_index]);
+}
+
+void *
+double_buffer_read(struct double_buffer * db_buff)
+{
+ int copy_index = 1;
+ void * buff;
+
+ if ( db_buff->write_count < 1 )
+ return 0;
+
+ /* NOTE: Try the newest buffer first
+ * IF that lock fails THEN try other
+ */
+ if ( db_buff->count[0] > db_buff->count[1] )
+ copy_index = 0;
+
+ if ( vc_mutex_trylock(&db_buff->locks[copy_index] ) )
+ {
+ /* failed to obtain lock */
+ /* try the other (older) slot? */
+ if ( db_buff->write_count < 2 )
+ return 0;
+
+ copy_index = 1 - copy_index;
+
+ /* Failed to obtain other lock? */
+ /* This should be pretty rare */
+ if ( vc_mutex_trylock(&db_buff->locks[copy_index] ) )
+ return 0;
+ }
+
+ /* Is this buffer older than the last-read?
+ * TODO: could this happen?
+ */
+ if ( db_buff->count[copy_index] < db_buff->read_count )
+ {
+ vc_mutex_unlock(&db_buff->locks[copy_index]);
+ return 0;
+ }
+
+ buff = db_buff->copy_object(db_buff->objects[copy_index]);
+
+ db_buff->read_count = db_buff->count[copy_index];
+
+ vc_mutex_unlock(&db_buff->locks[copy_index]);
+
+ return buff;
+}
+
+int
+double_buffer_count(struct double_buffer * db_buff)
+{
+ return db_buff->write_count;
+}
Property changes on: trunk/src/double_buffer.c
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/src/double_buffer.h
===================================================================
--- trunk/src/double_buffer.h (rev 0)
+++ trunk/src/double_buffer.h 2007-11-26 17:59:33 UTC (rev 65)
@@ -0,0 +1,65 @@
+/*
+ * libvidcap - a cross-platform video capture library
+ *
+ * Copyright 2007 Wimba, Inc.
+ *
+ * Contributors:
+ * Peter Grayson <jpg...@gm...>
+ * Bill Cholewka <bc...@gm...>
+ *
+ * libvidcap is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * libvidcap 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _DOUBLE_BUFFER_H
+#define _DOUBLE_BUFFER_H
+
+#include "os_funcs.h"
+
+#define _DOUBLE_MEANS_TWO_ 2
+
+struct double_buffer
+{
+ int read_count;
+ int write_count;
+ void * objects[_DOUBLE_MEANS_TWO_];
+ vc_mutex locks[_DOUBLE_MEANS_TWO_];
+
+ int count[_DOUBLE_MEANS_TWO_];
+
+ void * (*copy_object)(void *);
+ void (*free_object)(void *);
+
+ /* TODO: remove */
+ int num_insert_too_far_failures;
+};
+
+struct double_buffer *
+double_buffer_create( void (*free_obj_func)(void *),
+ void * (*copy_object)(void *));
+
+void
+double_buffer_destroy(struct double_buffer * db_buff);
+
+void
+double_buffer_insert(struct double_buffer * db_buff, void * object);
+
+void *
+double_buffer_read(struct double_buffer * db_buff);
+
+int
+double_buffer_count(struct double_buffer * db_buff);
+
+#endif
Property changes on: trunk/src/double_buffer.h
___________________________________________________________________
Name: svn:executable
+ *
Added: trunk/src/os_funcs.h
===================================================================
--- trunk/src/os_funcs.h (rev 0)
+++ trunk/src/os_funcs.h 2007-11-26 17:59:33 UTC (rev 65)
@@ -0,0 +1,219 @@
+/*
+ * libvidcap - a cross-platform video capture library
+ *
+ * Copyright 2007 Wimba, Inc.
+ *
+ * Contributors:
+ * Peter Grayson <jpg...@gm...>
+ * Bill Cholewka <bc...@gm...>
+ *
+ * libvidcap is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * libvidcap 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _OS_FUNCS_H
+#define _OS_FUNCS_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if defined(HAVE_NANOSLEEP) || defined(HAVE_GETTIMEOFDAY)
+#include <sys/time.h>
+#include <time.h>
+#endif
+
+/* os-dependent macros */
+#if defined(WIN32) || defined(_WIN32_WCE)
+
+#ifndef _WIN32_WINNT
+// Change this to the appropriate value to target other versions of Windows.
+// see: http://msdn2.microsoft.com/en-us/library/aa383745.aspx
+#define _WIN32_WINNT 0x0500
+#endif
+
+#include <windows.h>
+
+#if !defined(_WIN32_WCE)
+#include <process.h>
+#endif
+
+#define STDCALL __stdcall
+typedef CRITICAL_SECTION vc_mutex;
+typedef uintptr_t vc_thread;
+
+#else
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+#define STDCALL
+typedef pthread_mutex_t vc_mutex;
+typedef pthread_t vc_thread;
+
+#endif
+
+#ifdef MACOSX
+#include <mach/mach_init.h>
+#include <mach/thread_policy.h>
+#include <sched.h>
+#include <sys/sysctl.h>
+#endif
+
+#ifndef HAVE_GETTIMEOFDAY
+static __inline int
+gettimeofday(struct timeval * tv, struct timezone * tz)
+#ifdef 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;
+#endif
+}
+#endif /* !defined(HAVE_GETTIMEOFDAY) */
+
+
+static __inline int
+vc_create_thread(vc_thread *thread,
+ unsigned int (STDCALL * thread_func)(void *),
+ void *args, unsigned int *thread_id)
+{
+#ifdef WIN32
+ *thread = (uintptr_t)_beginthreadex(NULL, 0, thread_func,
+ (void *)args, 0, thread_id);
+
+ /* FIXME: log the error */
+ if ( thread == 0 )
+ return -1;
+#else
+ int ret;
+ void * (*func)(void *) = (void * (*)(void *))thread_func;
+
+ ret = pthread_create(thread, NULL, func, args);
+
+ /* FIXME: log the error */
+ if ( ret )
+ return -1;
+#endif
+
+ return 0;
+}
+
+static __inline void
+vc_thread_join(vc_thread *thread)
+{
+#ifdef WIN32
+ HANDLE _thread = (HANDLE)thread;
+
+ WaitForSingleObject(_thread, INFINITE);
+#else
+ pthread_t *_thread = (pthread_t *)thread;
+
+ pthread_join(*_thread, 0);
+#endif
+}
+
+static __inline void
+vc_mutex_init(vc_mutex *m)
+{
+#ifdef WIN32
+ InitializeCriticalSection(m);
+#else
+ pthread_mutex_init(m, NULL);
+#endif
+}
+
+static __inline void
+vc_mutex_lock(vc_mutex *m)
+{
+#ifdef WIN32
+ EnterCriticalSection(m);
+#else
+ pthread_mutex_lock(m);
+#endif
+}
+
+static __inline int
+vc_mutex_trylock(vc_mutex *m)
+{
+#ifdef WIN32
+ return !TryEnterCriticalSection(m);
+#else
+ return pthread_mutex_trylock(m);
+#endif
+}
+
+static __inline void
+vc_mutex_unlock(vc_mutex *m)
+{
+#ifdef WIN32
+ LeaveCriticalSection(m);
+#else
+ pthread_mutex_unlock(m);
+#endif
+}
+
+static __inline void
+vc_mutex_destroy(vc_mutex *m)
+{
+#ifdef WIN32
+ DeleteCriticalSection(m);
+#else
+ pthread_mutex_destroy(m);
+#endif
+}
+
+static __inline void
+vc_millisleep(long ms)
+{
+#ifdef WIN32
+
+ Sleep(ms);
+
+#else
+
+ struct timespec req;
+
+ req.tv_nsec = (ms % 1000) * 1000000;
+ req.tv_sec = ms / 1000;
+
+ nanosleep(&req, NULL);
+
+#endif
+}
+
+#endif
Property changes on: trunk/src/os_funcs.h
___________________________________________________________________
Name: svn:executable
+ *
Modified: trunk/src/sapi.c
===================================================================
--- trunk/src/sapi.c 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/sapi.c 2007-11-26 17:59:33 UTC (rev 65)
@@ -30,36 +30,9 @@
#include "logging.h"
#include "sapi.h"
-#ifndef HAVE_GETTIMEOFDAY
-static __inline int gettimeofday(struct timeval * tv, struct timezone * tz)
-#ifdef 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);
- }
+static int
+deliver_frame(struct sapi_src_context * src_ctx);
- return 0;
-
-#else /* !defined(_WINDOWS) */
- errno = ENOSYS;
- return -1;
-#endif
-}
-#endif
-
static __inline int
tv_greater_or_equal(struct timeval * t1, struct timeval * t0)
{
@@ -76,7 +49,7 @@
}
static __inline void
-tv_add_usecs(struct timeval *t1, struct timeval *t0, int usecs)
+tv_add_usecs(struct timeval *t1, struct timeval *t0, long usecs)
{
int secs_carried = 0;
@@ -216,6 +189,19 @@
return 0;
}
+static void
+wait_for_error_ack(struct sapi_src_context * src_ctx)
+{
+ while ( !src_ctx->capture_error_ack )
+ vc_millisleep(10);
+}
+
+static void
+acknowledge_error(struct sapi_src_context * src_ctx)
+{
+ 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,
@@ -223,25 +209,121 @@
int stride,
int error_status)
{
- struct vidcap_capture_info cap_info;
-
/* 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;
+
+ frame->cap_info = malloc(sizeof(struct vidcap_capture_info));
+ if ( !frame->cap_info )
+ {
+ free(frame);
+ return -1;
+ }
+
+ frame->cap_info->video_data_size = video_data_size;
+ frame->cap_info->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->cap_info->video_data = malloc(video_data_size);
+ if ( !frame->cap_info->video_data )
+ {
+ free(frame->cap_info);
+ free(frame);
+ return -1;
+ }
+
+ memcpy(frame->cap_info->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->cap_info->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)
+{
vidcap_src_capture_callback cap_callback = src_ctx->capture_callback;
void * cap_data = src_ctx->capture_data;
void * buf = 0;
int buf_data_size = 0;
-
int send_frame = 0;
- if ( !error_status )
- send_frame = enforce_framerate(src_ctx);
+ struct vidcap_capture_info cap_info;
- if ( send_frame < 0 )
- error_status = -1000;
+ struct frame_info * frame;
+ const char * video_data;
+ int video_data_size;
+ int stride;
+ int error_status;
+ if ( src_ctx->use_timer_thread )
+ {
+ frame = (struct frame_info *)
+ double_buffer_read(src_ctx->double_buff);
+
+ if ( !frame )
+ return -1;
+ }
+ else
+ {
+ frame = src_ctx->no_timer_thread_frame;
+ }
+
+ video_data = frame->cap_info->video_data;
+ video_data_size = frame->cap_info->video_data_size;
+ stride = frame->stride;
+ error_status = frame->cap_info->error_status;
+
+ /* FIXME: right now enforce_framerate() and the capture timer
+ * thread's main loop BOTH use the frame_time_next field
+ */
+ if ( src_ctx->use_timer_thread )
+ {
+ send_frame = 1;
+ }
+ else
+ {
+ if ( !error_status )
+ send_frame = enforce_framerate(src_ctx);
+
+ if ( send_frame < 0 )
+ error_status = -1000;
+ }
+
cap_info.error_status = error_status;
if ( !cap_info.error_status && stride &&
@@ -290,22 +372,143 @@
if ( ( send_frame || error_status ) && cap_callback &&
cap_data != VIDCAP_INVALID_USER_DATA )
{
- /* FIXME: Need to check return code.
- * Application may want capture to stop
+ /* FIXME: Need to check return code (and pass it back).
+ * Application may want capture to stop.
+ * Ensure we don't perform any more callbacks.
*/
cap_callback(src_ctx, cap_data, &cap_info);
- if ( cap_info.error_status )
+ if ( src_ctx->use_timer_thread )
{
- src_ctx->src_state = src_bound;
- src_ctx->capture_callback = 0;
- src_ctx->capture_data = VIDCAP_INVALID_USER_DATA;
+ /* delete frame */
+ free(frame->cap_info->video_data);
+ free(frame->cap_info);
+ free(frame);
+
+ if ( error_status )
+ {
+ /* Let capture thread know that the app
+ * Has received the error status.
+ * Ensure we don't deliver any more frames.
+ */
+ acknowledge_error(src_ctx);
+
+ /* inform calling function of error */
+ return 1;
+ }
}
}
return 0;
}
+unsigned int
+STDCALL sapi_src_timer_thread_func(void *args)
+{
+ struct sapi_src_context * src_ctx = args;
+ struct timeval tv_now;
+
+ /* change to faster rate at capture time, and reduce when capture stopped */
+ const long idle_state_sleep_period_ms = 100;
+ long sleep_ms = idle_state_sleep_period_ms;
+ int first_time = 1;
+ const int sleeps_per_capture = 1;
+ int got_frame = 0;
+ int ret;
+ int capture_error = 0; /* FIXME: perhaps should exit on error */
+
+ 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;
+ }
+
+ src_ctx->frame_time_next.tv_sec = tv_now.tv_sec;
+ src_ctx->frame_time_next.tv_usec = tv_now.tv_usec;
+
+ log_info("capture timer thread now running\n");
+ src_ctx->capture_timer_thread_started = 1;
+
+ while ( !src_ctx->kill_timer_thread )
+ {
+ gettimeofday(&tv_now, 0);
+
+ /* time to attempt to read a frame? */
+ if ( capture_error || src_ctx->src_state != src_capturing ||
+ !tv_greater_or_equal(&tv_now, &src_ctx->frame_time_next) )
+ {
+ if ( src_ctx->src_state != src_capturing )
+ {
+ sleep_ms = idle_state_sleep_period_ms;
+ first_time = 1;
+ }
+
+ vc_millisleep(sleep_ms);
+ }
+ else
+ {
+ src_ctx->timer_thread_idle = 0;
+ /* FIXME: memory barrier needed? */
+
+ /* attempt to read and deliver a frame */
+ ret = deliver_frame(src_ctx);
+
+ got_frame = !ret;
+ capture_error = ret > 0;
+
+ /* Is this the first frame? */
+ if ( got_frame && first_time )
+ {
+ first_time = 0;
+
+ /* re-initialize when next to check for a frame */
+ src_ctx->frame_time_next.tv_sec = tv_now.tv_sec;
+ src_ctx->frame_time_next.tv_usec = tv_now.tv_usec;
+
+ sleep_ms = (1000 / sleeps_per_capture) *
+ src_ctx->fmt_nominal.fps_denominator /
+ src_ctx->fmt_nominal.fps_numerator;
+ }
+
+ if ( !first_time )
+ {
+ /* update when next to check for a frame */
+ /* FIXME: reduce round-off */
+ tv_add_usecs(&src_ctx->frame_time_next, &src_ctx->frame_time_next,
+ 1000000 *
+ src_ctx->fmt_nominal.fps_denominator /
+ src_ctx->fmt_nominal.fps_numerator);
+ }
+ else
+ {
+ /* still no first frame */
+ /* update when next to check for a frame */
+ tv_add_usecs(&src_ctx->frame_time_next, &tv_now,
+ 1000000 *
+ src_ctx->fmt_nominal.fps_denominator /
+ src_ctx->fmt_nominal.fps_numerator);
+ }
+ }
+
+ /* FIXME: memory barrier needed? */
+ src_ctx->timer_thread_idle = 1;
+ }
+
+ log_info("capture timer thread now exiting...\n");
+
+ return 0;
+}
+
+void
+sapi_src_timer_thread_idled(struct sapi_src_context * src_ctx)
+{
+ while ( !src_ctx->timer_thread_idle )
+ vc_millisleep(10);
+}
+
int
sapi_can_convert_native_to_nominal(const struct vidcap_fmt_info * fmt_native,
const struct vidcap_fmt_info * fmt_nominal)
@@ -333,3 +536,4 @@
return 0;
}
+
Modified: trunk/src/sapi_context.h
===================================================================
--- trunk/src/sapi_context.h 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/sapi_context.h 2007-11-26 17:59:33 UTC (rev 65)
@@ -26,26 +26,20 @@
#ifndef _SAPI_CONTEXT_H
#define _SAPI_CONTEXT_H
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#ifdef WIN32
-#include <windows.h>
-#endif
-
-#if defined(HAVE_NANOSLEEP) || defined(HAVE_GETTIMEOFDAY)
-#include <sys/time.h>
-#include <time.h>
-#endif
-
#include <vidcap/vidcap.h>
+#include "double_buffer.h"
#include "sliding_window.h"
#include "conv.h"
struct sapi_context;
+struct frame_info
+{
+ struct vidcap_capture_info *cap_info;
+ int stride;
+};
+
struct sapi_src_context
{
int (*release)(struct sapi_src_context *);
@@ -64,6 +58,7 @@
struct vidcap_src_info src_info;
struct sliding_window * frame_times;
struct timeval frame_time_next;
+ struct double_buffer * double_buff;
struct vidcap_fmt_info fmt_nominal;
struct vidcap_fmt_info fmt_native;
@@ -82,6 +77,15 @@
vidcap_src_capture_callback capture_callback;
void * capture_data;
+ int use_timer_thread;
+ struct frame_info * no_timer_thread_frame;
+ vc_thread capture_timer_thread;
+ unsigned int capture_timer_thread_id;
+ int capture_timer_thread_started;
+ int timer_thread_idle;
+ int kill_timer_thread;
+ int capture_error_ack;
+
void * priv;
};
@@ -124,4 +128,10 @@
void
sapi_src_fps_info_clean(struct sapi_src_context *);
+void
+sapi_src_timer_thread_idled(struct sapi_src_context * src_ctx);
+
+unsigned int
+STDCALL sapi_src_timer_thread_func(void *);
+
#endif
Modified: trunk/src/sapi_dshow.cpp
===================================================================
--- trunk/src/sapi_dshow.cpp 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/sapi_dshow.cpp 2007-11-26 17:59:33 UTC (rev 65)
@@ -22,14 +22,12 @@
// <http://www.gnu.org/licenses/>.
//
-#include <windows.h>
#include <stdio.h>
+#include "sapi.h"
#include "SourceStateMachine.h"
#include "DShowSrcManager.h"
-
#include "logging.h"
-#include "sapi.h"
static const char * identifier = "DirectShow";
static const char * description = "DirectShow video capture API";
@@ -177,7 +175,7 @@
return -1;
}
- if ( src_ctx->priv == 0 )
+ if ( src_ctx->priv == (void *)0 )
{
log_warn("failed constructing source state machine for '%s'\n",
src_info->identifier);
Modified: trunk/src/vidcap.c
===================================================================
--- trunk/src/vidcap.c 2007-11-20 17:51:57 UTC (rev 64)
+++ trunk/src/vidcap.c 2007-11-26 17:59:33 UTC (rev 65)
@@ -26,12 +26,6 @@
#include <stdlib.h>
#include <string.h>
-#include <vidcap/vidcap.h>
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
#include "logging.h"
#include "sapi_context.h"
#include "sapi.h"
@@ -312,6 +306,41 @@
return 0;
}
+ /* TODO: right now we always use the timer thread to enforce
+ * a very accurate framerate. Alternatively, we could
+ * allow an application to choose to forego this feature
+ * and the associated overhead
+ */
+ src_ctx->use_timer_thread = 1;
+
+ if ( src_ctx->use_timer_thread )
+ {
+ src_ctx->kill_timer_thread = 0;
+ src_ctx->capture_timer_thread_started = 0;
+ src_ctx->capture_timer_thread = 0;
+ /* FIXME: memory barrier needed? */
+
+ if ( vc_create_thread(&src_ctx->capture_timer_thread,
+ sapi_src_timer_thread_func, src_ctx,
+ &src_ctx->capture_timer_thread_id) )
+ {
+ src_ctx->capture_timer_thread = 0;
+ goto bail;
+ }
+
+ /* ensure thread has started before proceeding */
+ while ( !src_ctx->capture_timer_thread_started )
+ vc_millisleep(10);
+
+ if ( src_ctx->capture_timer_thread_started < 0 )
+ {
+ log_error("source timer thread failed to start.\n");
+ goto bail;
+ }
+
+ log_info("using a capture timer thread for this source\n");
+ }
+
if ( sapi_ctx->acquire_source(sapi_ctx, src_ctx, src_info) )
{
log_error("failed to acquire %s\n", src_info ?
@@ -330,6 +359,15 @@
return src_ctx;
bail:
+ /* wait for it to exit (if it exists) */
+ if ( src_ctx->use_timer_thread && src_ctx->capture_timer_thread )
+ {
+ /* signal to timer thread to stop */
+ src_ctx->kill_timer_thread = 1;
+
+ vc_thread_join(&src_ctx->capture_timer_thread);
+ }
+
free(src_ctx);
return 0;
}
@@ -352,6 +390,15 @@
if ( src_ctx->src_state == src_capturing )
return -1;
+ if ( src_ctx->use_timer_thread && src_ctx->capture_timer_thread )
+ {
+ /* signal timer thread to exit */
+ src_ctx->kill_timer_thread = 1;
+
+ /* wait for it to exit */
+ vc_thread_join(&src_ctx->capture_timer_thread);
+ }
+
ret = src_ctx->release(src_ctx);
if ( src_ctx->fmt_list_len )
@@ -499,6 +546,53 @@
return 0;
}
+void
+free_frame_info(void * fr)
+{
+ struct frame_info *frame = (struct frame_info *)fr;
+
+ /* FIXME: unconstify video_data */
+ free(frame->cap_info->video_data);
+ free(frame->cap_info);
+ free(frame);
+}
+
+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->cap_info = calloc(1, sizeof(*dup_frame->cap_info));
+ if ( !dup_frame->cap_info )
+ {
+ free(dup_frame);
+ return 0;
+ }
+
+ dup_frame->stride = frame->stride;
+ dup_frame->cap_info->error_status = frame->cap_info->error_status;
+ dup_frame->cap_info->video_data_size = frame->cap_info->video_data_size;
+ dup_frame->cap_info->video_data = malloc(frame->cap_info->video_data_size);
+ if ( !dup_frame->cap_info->video_data )
+ {
+ free(dup_frame->cap_info);
+ free(dup_frame);
+ return 0;
+ }
+
+ /* FIXME: unconstify cap_info->video_data */
+ memcpy(dup_frame->cap_info->video_data,
+ frame->cap_info->video_data,
+ frame->cap_info->video_data_size);
+
+ return dup_frame;
+}
+
int
vidcap_src_capture_start(vidcap_src * src,
vidcap_src_capture_callback callback,
@@ -524,6 +618,16 @@
if ( !src_ctx->frame_times )
return -2;
+ src_ctx->double_buff = double_buffer_create(&free_frame_info,
+ ©_frame_info);
+
+ if ( !src_ctx->double_buff )
+ {
+ sliding_window_destroy(src_ctx->frame_times);
+ src_ctx->frame_times = 0;
+ return -5;
+ }
+
src_ctx->capture_callback = callback;
src_ctx->capture_data = user_data;
@@ -531,6 +635,10 @@
{
src_ctx->capture_callback = 0;
src_ctx->capture_data = VIDCAP_INVALID_USER_DATA;
+
+ double_buffer_destroy(src_ctx->double_buff);
+ src_ctx->double_buff = 0;
+
sliding_window_destroy(src_ctx->frame_times);
src_ctx->frame_times = 0;
return ret;
@@ -551,12 +659,23 @@
ret = src_ctx->stop_capture(src_ctx);
+ /* signal to timer thread that capture has stopped */
+ src_ctx->src_state = src_bound;
+
+ if ( src_ctx->use_timer_thread )
+ {
+ /* FIXME: memory barrier needed? */
+ sapi_src_timer_thread_idled(src_ctx);
+ }
+
+ double_buffer_destroy(src_ctx->double_buff);
+ src_ctx->double_buff = 0;
+
sliding_window_destroy(src_ctx->frame_times);
+ src_ctx->frame_times = 0;
- src_ctx->frame_times = 0;
src_ctx->capture_callback = 0;
src_ctx->capture_data = VIDCAP_INVALID_USER_DATA;
- src_ctx->src_state = src_bound;
return ret;
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|