celvideosrc: fix nasty deadlock

We cannot call any CMBufferQueue functions while holding the lock that
our callback also depends on. So now we make use of CMBufferQueue's
trigger API in order to get notified when the queue has data.
This commit is contained in:
Ole André Vadla Ravnås 2010-11-12 00:40:33 +01:00
parent de76e9fdb6
commit abdb30c567
2 changed files with 51 additions and 35 deletions

View file

@ -26,11 +26,11 @@
#define DEFAULT_DEVICE_INDEX -1 #define DEFAULT_DEVICE_INDEX -1
#define DEFAULT_DO_STATS FALSE #define DEFAULT_DO_STATS FALSE
#define BUFQUEUE_LOCK(instance) GST_OBJECT_LOCK (instance) #define QUEUE_READY_LOCK(instance) GST_OBJECT_LOCK (instance)
#define BUFQUEUE_UNLOCK(instance) GST_OBJECT_UNLOCK (instance) #define QUEUE_READY_UNLOCK(instance) GST_OBJECT_UNLOCK (instance)
#define BUFQUEUE_WAIT(instance) \ #define QUEUE_READY_WAIT(instance) \
g_cond_wait (instance->cond, GST_OBJECT_GET_LOCK (instance)) g_cond_wait (instance->ready_cond, GST_OBJECT_GET_LOCK (instance))
#define BUFQUEUE_NOTIFY(instance) g_cond_signal (instance->cond) #define QUEUE_READY_NOTIFY(instance) g_cond_signal (instance->ready_cond)
GST_DEBUG_CATEGORY (gst_cel_video_src_debug); GST_DEBUG_CATEGORY (gst_cel_video_src_debug);
#define GST_CAT_DEFAULT gst_cel_video_src_debug #define GST_CAT_DEFAULT gst_cel_video_src_debug
@ -92,7 +92,7 @@ gst_cel_video_src_init (GstCelVideoSrc * self, GstCelVideoSrcClass * gclass)
gst_base_src_set_live (base_src, TRUE); gst_base_src_set_live (base_src, TRUE);
gst_base_src_set_format (base_src, GST_FORMAT_TIME); gst_base_src_set_format (base_src, GST_FORMAT_TIME);
self->cond = g_cond_new (); self->ready_cond = g_cond_new ();
} }
static void static void
@ -106,7 +106,7 @@ gst_cel_video_src_finalize (GObject * object)
{ {
GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (object); GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (object);
g_cond_free (self->cond); g_cond_free (self->ready_cond);
G_OBJECT_CLASS (parent_class)->finalize (object); G_OBJECT_CLASS (parent_class)->finalize (object);
} }
@ -282,7 +282,7 @@ gst_cel_video_src_start (GstBaseSrc * basesrc)
{ {
GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (basesrc); GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (basesrc);
self->running = TRUE; g_atomic_int_set (&self->is_running, TRUE);
self->offset = 0; self->offset = 0;
self->last_sampling = GST_CLOCK_TIME_NONE; self->last_sampling = GST_CLOCK_TIME_NONE;
@ -335,10 +335,11 @@ gst_cel_video_src_unlock (GstBaseSrc * basesrc)
{ {
GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (basesrc); GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (basesrc);
BUFQUEUE_LOCK (self); g_atomic_int_set (&self->is_running, FALSE);
self->running = FALSE;
BUFQUEUE_NOTIFY (self); QUEUE_READY_LOCK (self);
BUFQUEUE_UNLOCK (self); QUEUE_READY_NOTIFY (self);
QUEUE_READY_UNLOCK (self);
return TRUE; return TRUE;
} }
@ -349,18 +350,16 @@ gst_cel_video_src_unlock_stop (GstBaseSrc * basesrc)
return TRUE; return TRUE;
} }
static Boolean static void
gst_cel_video_src_validate (CMBufferQueueRef queue, CMSampleBufferRef buf, gst_cel_video_src_on_queue_ready (void *triggerRefcon,
void *refCon) CMBufferQueueTriggerToken triggerToken)
{ {
GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (refCon); GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (triggerRefcon);
BUFQUEUE_LOCK (self); QUEUE_READY_LOCK (self);
self->has_pending = TRUE; self->queue_is_ready = TRUE;
BUFQUEUE_NOTIFY (self); QUEUE_READY_NOTIFY (self);
BUFQUEUE_UNLOCK (self); QUEUE_READY_UNLOCK (self);
return FALSE;
} }
static void static void
@ -437,16 +436,24 @@ gst_cel_video_src_create (GstPushSrc * pushsrc, GstBuffer ** buf)
{ {
GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (pushsrc); GstCelVideoSrc *self = GST_CEL_VIDEO_SRC_CAST (pushsrc);
GstCMApi *cm = self->ctx->cm; GstCMApi *cm = self->ctx->cm;
CMSampleBufferRef sbuf = NULL; CMSampleBufferRef sbuf;
BUFQUEUE_LOCK (self);
while (self->running && !self->has_pending)
BUFQUEUE_WAIT (self);
sbuf = cm->CMBufferQueueDequeueAndRetain (self->queue); sbuf = cm->CMBufferQueueDequeueAndRetain (self->queue);
self->has_pending = !cm->CMBufferQueueIsEmpty (self->queue);
BUFQUEUE_UNLOCK (self);
if (G_UNLIKELY (!self->running)) while (sbuf == NULL) {
QUEUE_READY_LOCK (self);
while (!self->queue_is_ready && g_atomic_int_get (&self->is_running))
QUEUE_READY_WAIT (self);
self->queue_is_ready = FALSE;
QUEUE_READY_UNLOCK (self);
if (G_UNLIKELY (!g_atomic_int_get (&self->is_running)))
goto shutting_down;
sbuf = cm->CMBufferQueueDequeueAndRetain (self->queue);
}
if (G_UNLIKELY (!g_atomic_int_get (&self->is_running)))
goto shutting_down; goto shutting_down;
*buf = gst_core_media_buffer_new (self->ctx, sbuf); *buf = gst_core_media_buffer_new (self->ctx, sbuf);
@ -485,6 +492,7 @@ gst_cel_video_src_open_device (GstCelVideoSrc * self)
FigBaseObjectRef stream_base; FigBaseObjectRef stream_base;
FigBaseVTable *stream_vt; FigBaseVTable *stream_vt;
CMBufferQueueRef queue = NULL; CMBufferQueueRef queue = NULL;
CMTime ignored_time;
ctx = gst_core_media_ctx_new (GST_API_CORE_VIDEO | GST_API_CORE_MEDIA ctx = gst_core_media_ctx_new (GST_API_CORE_VIDEO | GST_API_CORE_MEDIA
| GST_API_MEDIA_TOOLBOX | GST_API_CELESTIAL, &error); | GST_API_MEDIA_TOOLBOX | GST_API_CELESTIAL, &error);
@ -532,10 +540,15 @@ gst_cel_video_src_open_device (GstCelVideoSrc * self)
if (status != noErr) if (status != noErr)
goto unexpected_error; goto unexpected_error;
self->has_pending = FALSE; self->queue_is_ready = FALSE;
cm->CMBufferQueueSetValidationCallback (queue, ignored_time = cm->CMTimeMake (1, 1);
gst_cel_video_src_validate, self); status = cm->CMBufferQueueInstallTrigger (queue,
gst_cel_video_src_on_queue_ready, self,
kCMBufferQueueTrigger_WhenDataBecomesReady, ignored_time,
&self->ready_trigger);
if (status != noErr)
goto unexpected_error;
self->ctx = ctx; self->ctx = ctx;
@ -619,7 +632,9 @@ gst_cel_video_src_close_device (GstCelVideoSrc * self)
self->device_base = NULL; self->device_base = NULL;
self->device_base_iface = NULL; self->device_base_iface = NULL;
self->ctx->cm->CMBufferQueueRemoveTrigger (self->queue, self->ready_trigger);
self->ctx->cm->FigBufferQueueRelease (self->queue); self->ctx->cm->FigBufferQueueRelease (self->queue);
self->ready_trigger = NULL;
self->queue = NULL; self->queue = NULL;
g_object_unref (self->ctx); g_object_unref (self->ctx);

View file

@ -61,15 +61,16 @@ struct _GstCelVideoSrc
FigBaseIface *stream_base_iface; FigBaseIface *stream_base_iface;
CMBufferQueueRef queue; CMBufferQueueRef queue;
CMBufferQueueTriggerToken ready_trigger;
GstCaps *device_caps; GstCaps *device_caps;
GArray *device_formats; GArray *device_formats;
GstClockTime duration; GstClockTime duration;
volatile gboolean running; volatile gint is_running;
guint64 offset; guint64 offset;
GCond *cond; GCond *ready_cond;
volatile gboolean has_pending; volatile gboolean queue_is_ready;
GstClockTime last_sampling; GstClockTime last_sampling;
guint count; guint count;