Added pthread software sync for improved timestamps and pthread queue and sync control to make sure that frames are q...

Original commit message from CVS:
Added pthread software sync for improved timestamps and pthread queue and sync control to make sure that frames are queued if we sync on them
This commit is contained in:
Ronald S. Bultje 2001-12-23 19:14:18 +00:00
parent 6447e09f6b
commit 580121964c
5 changed files with 204 additions and 26 deletions

View file

@ -1,22 +1,30 @@
General idea: General Idea:
=============
/ gstv4lsrc.[ch] _____/ gstv4lsrc.[ch]
/ v4lsrc_calls.[ch] _____/ \ v4lsrc_calls.[ch]
/ /
gstv4lelement.[ch] ------------- gstv4lmjpegsrc.[ch] gstv4lelement.[ch] _/____________/ gstv4lmjpegsrc.[ch]
v4l_calls.[ch] ------------- v4lmjpegsrc_calls.[ch] v4l_calls.[ch] \ \ v4lmjpegsrc_calls.[ch]
\ \_____
\ gstv4lmjpegsink.[ch] \_____/ gstv4lmjpegsink.[ch]
\ v4lmjpegsink_calls.[ch] \ v4lmjpegsink_calls.[ch]
I.e., all the files on the right are child classes of
I.e., all the files on thei right are child classes of the v4lelement 'parent' on the left. mjpegsink is still
the v4lelement 'parent' on the left.mjpegsink is still
todo. todo.
Generic idea:
* v4lelement handles generic v4l stuff (picture settings, * v4lelement handles generic v4l stuff (picture settings,
audio, norm/input setting, open()/close()) audio, norm/input setting, open()/close())
* v4lsrc, v4lmjpegsrc handle the capture specific * v4lsrc, v4lmjpegsrc handle the capture specific
functions. Maybe we'd need a v4lmpegsrc too functions. Maybe we'd need a v4lmpegsrc too
* v4lmjpegsink handles mjpeg hardware playback of video * v4lmjpegsink handles mjpeg hardware playback of video
Useful Documentation:
=====================
MJPEG/V4L API : ./videodev_mjpeg.h
V4L API : /usr/include/linux/videodev.h or
http://roadrunner.swansea.uk.linux.org/v4l.shtml
V4L2 API : http://www.thedirks.org/v4l2/
BSD/Meteor API: /usr/include/machine/ioctl_meteor.h
mjpegtools : http://www.sourceforge.net/projects/mjpeg

View file

@ -1,5 +1,23 @@
TODO list (short term):
=======================
* v4lmjpegsrc: integrate input/norm autodetection * v4lmjpegsrc: integrate input/norm autodetection
* v4lmjpegsink: build * libgstrec: build (a library for video recording)
* v4lsrc: threaded sync() * v4lmjpegsink: build (based on liblavplay (mjpegtools) and MJPEG/V4L API)
* v4lsrc: threaded wait-for-sync()-until-queue() * v4lsrc: threaded sync() (done?)
* BSD-src: build (based on Bt848/Bt878/Meteor API) * v4lsrc: threaded wait-for-sync()-until-queue() (done?)
TODO list (long term):
======================
* v4lmpegsrc: build (*hint* MPEG card needed *hint*)
* v4l2element && v4l2src: build (v4l2 supports far more features than
v4l1 so I really want seperate plugins for it)
* BSD-videosrc: build (based on Meteor API)
Useful Documentation:
=====================
MJPEG/V4L API : ./videodev_mjpeg.h
V4L API : /usr/include/linux/videodev.h or
http://roadrunner.swansea.uk.linux.org/v4l.shtml
V4L2 API : http://www.thedirks.org/v4l2/
BSD/Meteor API: /usr/include/machine/ioctl_meteor.h
mjpegtools : http://www.sourceforge.net/projects/mjpeg

View file

@ -340,7 +340,6 @@ gst_v4lsrc_get (GstPad *pad)
GstV4lSrc *v4lsrc; GstV4lSrc *v4lsrc;
GstBuffer *buf; GstBuffer *buf;
gint num; gint num;
struct timeval timestamp;
g_return_val_if_fail (pad != NULL, NULL); g_return_val_if_fail (pad != NULL, NULL);
@ -368,10 +367,10 @@ gst_v4lsrc_get (GstPad *pad)
/* grab a frame from the device */ /* grab a frame from the device */
if (!gst_v4lsrc_grab_frame(v4lsrc, &num)) if (!gst_v4lsrc_grab_frame(v4lsrc, &num))
return NULL; return NULL;
gettimeofday(&timestamp, 0); /* TODO: threaded sync() */
GST_BUFFER_DATA(buf) = gst_v4lsrc_get_buffer(v4lsrc, num); GST_BUFFER_DATA(buf) = gst_v4lsrc_get_buffer(v4lsrc, num);
GST_BUFFER_SIZE(buf) = v4lsrc->buffer_size; GST_BUFFER_SIZE(buf) = v4lsrc->buffer_size;
buf->timestamp = timestamp.tv_sec * 1000000000 + timestamp.tv_usec * 1000; buf->timestamp = v4lsrc->timestamp_soft_sync[num].tv_sec * 1000000000 +
v4lsrc->timestamp_soft_sync[num].tv_usec * 1000;
return buf; return buf;
} }

View file

@ -59,6 +59,18 @@ struct _GstV4lSrc {
gboolean *frame_queued; gboolean *frame_queued;
guint buffer_size; guint buffer_size;
/* a seperate pthread for the sync() thread (improves correctness of timestamps) */
gint8 *isready_soft_sync; /* 1 = ok, 0 = waiting, -1 = error */
struct timeval *timestamp_soft_sync;
pthread_t thread_soft_sync;
pthread_mutex_t mutex_soft_sync;
pthread_cond_t *cond_soft_sync;
/* num of queued frames and some pthread stuff to wait if there's not enough */
guint16 num_queued_frames;
pthread_mutex_t mutex_queued_frames;
pthread_cond_t cond_queued_frames;
/* caching values */ /* caching values */
gint width; gint width;
gint height; gint height;

View file

@ -62,10 +62,91 @@ gst_v4lsrc_queue_frame (GstV4lSrc *v4lsrc,
v4lsrc->frame_queued[num] = TRUE; v4lsrc->frame_queued[num] = TRUE;
pthread_mutex_lock(&(v4lsrc->mutex_queued_frames));
v4lsrc->num_queued_frames++;
pthread_cond_broadcast(&(v4lsrc->cond_queued_frames));
pthread_mutex_unlock(&(v4lsrc->mutex_queued_frames));
return TRUE; return TRUE;
} }
/******************************************************
* gst_v4lsrc_soft_sync_thread()
* syncs on frames and signals the main thread
* purpose: actually get the correct frame timestamps
******************************************************/
static void *
gst_v4lsrc_soft_sync_thread (void *arg)
{
GstV4lSrc *v4lsrc = GST_V4LSRC(arg);
guint16 frame = 0;
#ifdef DEBUG
fprintf(stderr, "gst_v4lsrc_soft_sync_thread()\n");
#endif
/* Allow easy shutting down by other processes... */
pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );
while (1)
{
/* are there queued frames left? */
pthread_mutex_lock(&(v4lsrc->mutex_queued_frames));
if (v4lsrc->num_queued_frames < 1)
{
pthread_cond_wait(&(v4lsrc->cond_queued_frames),
&(v4lsrc->mutex_queued_frames));
}
pthread_mutex_unlock(&(v4lsrc->mutex_queued_frames));
/* if still wrong, we got interrupted and we should exit */
if (v4lsrc->num_queued_frames < 1)
{
goto end;
}
/* sync on the frame */
retry:
if (ioctl(GST_V4LELEMENT(v4lsrc)->video_fd, VIDIOCSYNC, &frame) < 0)
{
/* if the sync() got interrupted, we can retry */
if (errno == EINTR)
goto retry;
gst_element_error(GST_ELEMENT(v4lsrc),
"Error syncing on a buffer (%d): %s",
frame, sys_errlist[errno]);
pthread_mutex_lock(&(v4lsrc->mutex_soft_sync));
v4lsrc->isready_soft_sync[frame] = -1;
pthread_cond_broadcast(&(v4lsrc->cond_soft_sync[frame]));
pthread_mutex_unlock(&(v4lsrc->mutex_soft_sync));
goto end;
}
else
{
pthread_mutex_lock(&(v4lsrc->mutex_soft_sync));
gettimeofday(&(v4lsrc->timestamp_soft_sync[frame]), NULL);
v4lsrc->isready_soft_sync[frame] = 1;
pthread_cond_broadcast(&(v4lsrc->cond_soft_sync[frame]));
pthread_mutex_unlock(&(v4lsrc->mutex_soft_sync));
}
frame = (frame+1)%v4lsrc->mbuf.frames;
pthread_mutex_lock(&(v4lsrc->mutex_queued_frames));
v4lsrc->num_queued_frames--;
pthread_mutex_unlock(&(v4lsrc->mutex_queued_frames));
}
end:
gst_element_info(GST_ELEMENT(v4lsrc),
"Software sync thread got signalled to exit");
pthread_exit(NULL);
}
/****************************************************** /******************************************************
* gst_v4lsrc_sync_frame(): * gst_v4lsrc_sync_frame():
* sync on a frame for capturing * sync on a frame for capturing
@ -83,13 +164,19 @@ gst_v4lsrc_sync_next_frame (GstV4lSrc *v4lsrc,
*num = (v4lsrc->sync_frame + 1)%v4lsrc->mbuf.frames; *num = (v4lsrc->sync_frame + 1)%v4lsrc->mbuf.frames;
if (ioctl(GST_V4LELEMENT(v4lsrc)->video_fd, VIDIOCSYNC, num) < 0) /* "software sync()" on the frame */
pthread_mutex_lock(&(v4lsrc->mutex_soft_sync));
if (v4lsrc->isready_soft_sync[*num])
{ {
gst_element_error(GST_ELEMENT(v4lsrc), pthread_cond_wait(&(v4lsrc->cond_soft_sync[*num]),
"Error syncing on a buffer (%d): %s", &(v4lsrc->mutex_soft_sync));
*num, sys_errlist[errno]);
return FALSE;
} }
pthread_mutex_unlock(&(v4lsrc->mutex_soft_sync));
if (v4lsrc->isready_soft_sync[*num] < 0)
return FALSE;
v4lsrc->isready_soft_sync[*num] = 0;
v4lsrc->frame_queued[*num] = FALSE; v4lsrc->frame_queued[*num] = FALSE;
@ -168,6 +255,42 @@ gst_v4lsrc_capture_init (GstV4lSrc *v4lsrc)
for (n=0;n<v4lsrc->mbuf.frames;n++) for (n=0;n<v4lsrc->mbuf.frames;n++)
v4lsrc->frame_queued[n] = FALSE; v4lsrc->frame_queued[n] = FALSE;
/* init the pthread stuff */
pthread_mutex_init(&(v4lsrc->mutex_soft_sync), NULL);
v4lsrc->isready_soft_sync = (gint8 *) malloc(sizeof(gint8) * v4lsrc->mbuf.frames);
if (!v4lsrc->isready_soft_sync)
{
gst_element_error(GST_ELEMENT(v4lsrc),
"Error creating software-sync buffer tracker: %s",
sys_errlist[errno]);
return FALSE;
}
for (n=0;n<v4lsrc->mbuf.frames;n++)
v4lsrc->isready_soft_sync[n] = 0;
v4lsrc->timestamp_soft_sync = (struct timeval *)
malloc(sizeof(struct timeval) * v4lsrc->mbuf.frames);
if (!v4lsrc->timestamp_soft_sync)
{
gst_element_error(GST_ELEMENT(v4lsrc),
"Error creating software-sync timestamp tracker: %s",
sys_errlist[errno]);
return FALSE;
}
v4lsrc->cond_soft_sync = (pthread_cond_t *)
malloc(sizeof(pthread_cond_t) * v4lsrc->mbuf.frames);
if (!v4lsrc->cond_soft_sync)
{
gst_element_error(GST_ELEMENT(v4lsrc),
"Error creating software-sync condition tracker: %s",
sys_errlist[errno]);
return FALSE;
}
for (n=0;n<v4lsrc->mbuf.frames;n++)
pthread_cond_init(&(v4lsrc->cond_soft_sync[n]), NULL);
pthread_mutex_init(&(v4lsrc->mutex_queued_frames), NULL);
pthread_cond_init(&(v4lsrc->cond_queued_frames), NULL);
/* Map the buffers */ /* Map the buffers */
GST_V4LELEMENT(v4lsrc)->buffer = mmap(0, v4lsrc->mbuf.size, GST_V4LELEMENT(v4lsrc)->buffer = mmap(0, v4lsrc->mbuf.size,
PROT_READ, MAP_SHARED, GST_V4LELEMENT(v4lsrc)->video_fd, 0); PROT_READ, MAP_SHARED, GST_V4LELEMENT(v4lsrc)->video_fd, 0);
@ -202,6 +325,8 @@ gst_v4lsrc_capture_start (GstV4lSrc *v4lsrc)
GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc)); GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc));
GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lsrc)); GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lsrc));
v4lsrc->num_queued_frames = 0;
/* queue all buffers, this starts streaming capture */ /* queue all buffers, this starts streaming capture */
for (n=0;n<v4lsrc->mbuf.frames;n++) for (n=0;n<v4lsrc->mbuf.frames;n++)
if (!gst_v4lsrc_queue_frame(v4lsrc, n)) if (!gst_v4lsrc_queue_frame(v4lsrc, n))
@ -209,6 +334,16 @@ gst_v4lsrc_capture_start (GstV4lSrc *v4lsrc)
v4lsrc->sync_frame = -1; v4lsrc->sync_frame = -1;
/* start the sync() thread (correct timestamps) */
if ( pthread_create( &(v4lsrc->thread_soft_sync), NULL,
gst_v4lsrc_soft_sync_thread, (void *) v4lsrc ) )
{
gst_element_error(GST_ELEMENT(v4lsrc),
"Failed to create software sync thread: %s",
sys_errlist[errno]);
return FALSE;
}
return TRUE; return TRUE;
} }
@ -240,7 +375,7 @@ gst_v4lsrc_grab_frame (GstV4lSrc *v4lsrc, gint *num)
/****************************************************** /******************************************************
* gst_v4lsrc_get_buffer(): * gst_v4lsrc_get_buffer():
* get the address of the just-capture buffer * get the address of the just-capture buffer
* return value: TRUE on success, FALSE on error * return value: the buffer's address or NULL
******************************************************/ ******************************************************/
guint8 * guint8 *
@ -307,6 +442,9 @@ gst_v4lsrc_capture_stop (GstV4lSrc *v4lsrc)
if (!gst_v4lsrc_sync_next_frame(v4lsrc, &num)) if (!gst_v4lsrc_sync_next_frame(v4lsrc, &num))
return FALSE; return FALSE;
pthread_cancel(v4lsrc->thread_soft_sync);
pthread_join(v4lsrc->thread_soft_sync, NULL);
return TRUE; return TRUE;
} }
@ -329,6 +467,9 @@ gst_v4lsrc_capture_deinit (GstV4lSrc *v4lsrc)
/* free buffer tracker */ /* free buffer tracker */
free(v4lsrc->frame_queued); free(v4lsrc->frame_queued);
free(v4lsrc->cond_soft_sync);
free(v4lsrc->isready_soft_sync);
free(v4lsrc->timestamp_soft_sync);
/* unmap the buffer */ /* unmap the buffer */
munmap(GST_V4LELEMENT(v4lsrc)->buffer, v4lsrc->mbuf.size); munmap(GST_V4LELEMENT(v4lsrc)->buffer, v4lsrc->mbuf.size);