Added pthread software sync for improved timestamps and pthread queue and sync contro...
authorRonald S. Bultje <rbultje@ronald.bitfreak.net>
Sun, 23 Dec 2001 19:14:18 +0000 (19:14 +0000)
committerRonald S. Bultje <rbultje@ronald.bitfreak.net>
Sun, 23 Dec 2001 19:14:18 +0000 (19:14 +0000)
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

sys/v4l/README
sys/v4l/TODO
sys/v4l/gstv4lsrc.c
sys/v4l/gstv4lsrc.h
sys/v4l/v4lsrc_calls.c

index 2df87fa..59016db 100644 (file)
@@ -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
index efda739..c6a24f1 100644 (file)
@@ -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
index d12afdc..2cc1435 100644 (file)
@@ -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(&timestamp, 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;
 }
index ab68742..e45ab6f 100644 (file)
@@ -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;
index e0e5321..de7d1b9 100644 (file)
@@ -62,11 +62,92 @@ 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
  * return value: TRUE on success, FALSE on error
@@ -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;n<v4lsrc->mbuf.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;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 */
   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;n<v4lsrc->mbuf.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);