diff --git a/sys/v4l/README b/sys/v4l/README index 2df87faa98..59016db921 100644 --- a/sys/v4l/README +++ b/sys/v4l/README @@ -1,22 +1,30 @@ -General idea: +General Idea: +============= - / gstv4lsrc.[ch] - / v4lsrc_calls.[ch] - / -gstv4lelement.[ch] ------------- gstv4lmjpegsrc.[ch] -v4l_calls.[ch] ------------- v4lmjpegsrc_calls.[ch] - \ - \ gstv4lmjpegsink.[ch] - \ v4lmjpegsink_calls.[ch] + _____/ gstv4lsrc.[ch] + _____/ \ v4lsrc_calls.[ch] + / +gstv4lelement.[ch] _/____________/ gstv4lmjpegsrc.[ch] +v4l_calls.[ch] \ \ v4lmjpegsrc_calls.[ch] + \_____ + \_____/ gstv4lmjpegsink.[ch] + \ v4lmjpegsink_calls.[ch] - -I.e., all the files on thei right are child classes of -the v4lelement 'parent' on the left.mjpegsink is still +I.e., all the files on the right are child classes of +the v4lelement 'parent' on the left. mjpegsink is still todo. -Generic idea: * v4lelement handles generic v4l stuff (picture settings, audio, norm/input setting, open()/close()) * v4lsrc, v4lmjpegsrc handle the capture specific functions. Maybe we'd need a v4lmpegsrc too * 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 diff --git a/sys/v4l/TODO b/sys/v4l/TODO index efda739966..c6a24f1711 100644 --- a/sys/v4l/TODO +++ b/sys/v4l/TODO @@ -1,5 +1,23 @@ +TODO list (short term): +======================= * v4lmjpegsrc: integrate input/norm autodetection -* v4lmjpegsink: build -* v4lsrc: threaded sync() -* v4lsrc: threaded wait-for-sync()-until-queue() -* BSD-src: build (based on Bt848/Bt878/Meteor API) +* libgstrec: build (a library for video recording) +* v4lmjpegsink: build (based on liblavplay (mjpegtools) and MJPEG/V4L API) +* v4lsrc: threaded sync() (done?) +* 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 diff --git a/sys/v4l/gstv4lsrc.c b/sys/v4l/gstv4lsrc.c index d12afdcc21..2cc1435a1d 100644 --- a/sys/v4l/gstv4lsrc.c +++ b/sys/v4l/gstv4lsrc.c @@ -340,7 +340,6 @@ gst_v4lsrc_get (GstPad *pad) GstV4lSrc *v4lsrc; GstBuffer *buf; gint num; - struct timeval timestamp; g_return_val_if_fail (pad != NULL, NULL); @@ -368,10 +367,10 @@ gst_v4lsrc_get (GstPad *pad) /* grab a frame from the device */ if (!gst_v4lsrc_grab_frame(v4lsrc, &num)) return NULL; - gettimeofday(×tamp, 0); /* TODO: threaded sync() */ GST_BUFFER_DATA(buf) = gst_v4lsrc_get_buffer(v4lsrc, num); 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; } diff --git a/sys/v4l/gstv4lsrc.h b/sys/v4l/gstv4lsrc.h index ab68742d1c..e45ab6f0bc 100644 --- a/sys/v4l/gstv4lsrc.h +++ b/sys/v4l/gstv4lsrc.h @@ -59,6 +59,18 @@ struct _GstV4lSrc { gboolean *frame_queued; 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 */ gint width; gint height; diff --git a/sys/v4l/v4lsrc_calls.c b/sys/v4l/v4lsrc_calls.c index e0e53211c7..de7d1b98a8 100644 --- a/sys/v4l/v4lsrc_calls.c +++ b/sys/v4l/v4lsrc_calls.c @@ -62,10 +62,91 @@ gst_v4lsrc_queue_frame (GstV4lSrc *v4lsrc, 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; } +/****************************************************** + * 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(): * 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; - 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), - "Error syncing on a buffer (%d): %s", - *num, sys_errlist[errno]); - return FALSE; + pthread_cond_wait(&(v4lsrc->cond_soft_sync[*num]), + &(v4lsrc->mutex_soft_sync)); } + 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; @@ -168,6 +255,42 @@ gst_v4lsrc_capture_init (GstV4lSrc *v4lsrc) for (n=0;nmbuf.frames;n++) 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;nmbuf.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;nmbuf.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 */ GST_V4LELEMENT(v4lsrc)->buffer = mmap(0, v4lsrc->mbuf.size, 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_ACTIVE(GST_V4LELEMENT(v4lsrc)); + v4lsrc->num_queued_frames = 0; + /* queue all buffers, this starts streaming capture */ for (n=0;nmbuf.frames;n++) if (!gst_v4lsrc_queue_frame(v4lsrc, n)) @@ -209,6 +334,16 @@ gst_v4lsrc_capture_start (GstV4lSrc *v4lsrc) 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; } @@ -240,7 +375,7 @@ gst_v4lsrc_grab_frame (GstV4lSrc *v4lsrc, gint *num) /****************************************************** * gst_v4lsrc_get_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 * @@ -307,6 +442,9 @@ gst_v4lsrc_capture_stop (GstV4lSrc *v4lsrc) if (!gst_v4lsrc_sync_next_frame(v4lsrc, &num)) return FALSE; + pthread_cancel(v4lsrc->thread_soft_sync); + pthread_join(v4lsrc->thread_soft_sync, NULL); + return TRUE; } @@ -329,6 +467,9 @@ gst_v4lsrc_capture_deinit (GstV4lSrc *v4lsrc) /* free buffer tracker */ free(v4lsrc->frame_queued); + free(v4lsrc->cond_soft_sync); + free(v4lsrc->isready_soft_sync); + free(v4lsrc->timestamp_soft_sync); /* unmap the buffer */ munmap(GST_V4LELEMENT(v4lsrc)->buffer, v4lsrc->mbuf.size);