mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 04:31:06 +00:00
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:
parent
6447e09f6b
commit
580121964c
5 changed files with 204 additions and 26 deletions
|
@ -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
|
||||||
|
|
26
sys/v4l/TODO
26
sys/v4l/TODO
|
@ -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
|
||||||
|
|
|
@ -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(×tamp, 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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue