videodecoder: don't take STREAM_LOCK on upstream events
authorTim-Philipp Müller <tim.muller@collabora.co.uk>
Mon, 24 Sep 2012 09:16:09 +0000 (10:16 +0100)
committerTim-Philipp Müller <tim.muller@collabora.co.uk>
Mon, 24 Sep 2012 09:56:35 +0000 (10:56 +0100)
Don't try to take STREAM_LOCK on upstream events such as QOS.
Protect qos-related variables with object lock instead. Fixes
possible deadlock when shutting down in certain situations.

https://bugzilla.gnome.org/show_bug.cgi?id=684658

gst-libs/gst/video/gstvideodecoder.c

index 9765add..6585c22 100644 (file)
@@ -382,8 +382,9 @@ struct _GstVideoDecoderPrivate
   gboolean output_state_changed;
 
   /* QoS properties */
-  gdouble proportion;
-  GstClockTime earliest_time;
+  gdouble proportion;           /* OBJECT_LOCK */
+  GstClockTime earliest_time;   /* OBJECT_LOCK */
+  GstClockTime qos_frame_duration;      /* OBJECT_LOCK */
   gboolean discont;
   /* qos messages: frames dropped/processed */
   guint dropped;
@@ -1261,7 +1262,6 @@ gst_video_decoder_src_event_default (GstVideoDecoder * decoder,
       gdouble proportion;
       GstClockTimeDiff diff;
       GstClockTime timestamp;
-      GstClockTime duration;
 
       gst_event_parse_qos (event, &type, &proportion, &diff, &timestamp);
 
@@ -1269,15 +1269,7 @@ gst_video_decoder_src_event_default (GstVideoDecoder * decoder,
       priv->proportion = proportion;
       if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (timestamp))) {
         if (G_UNLIKELY (diff > 0)) {
-          GST_VIDEO_DECODER_STREAM_LOCK (decoder);
-          if (priv->output_state != NULL && priv->output_state->info.fps_n > 0)
-            duration =
-                gst_util_uint64_scale (GST_SECOND,
-                priv->output_state->info.fps_d, priv->output_state->info.fps_n);
-          else
-            duration = 0;
-          GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
-          priv->earliest_time = timestamp + 2 * diff + duration;
+          priv->earliest_time = timestamp + 2 * diff + priv->qos_frame_duration;
         } else {
           priv->earliest_time = timestamp + diff;
         }
@@ -1614,6 +1606,11 @@ gst_video_decoder_reset (GstVideoDecoder * decoder, gboolean full)
     if (priv->output_state)
       gst_video_codec_state_unref (priv->output_state);
     priv->output_state = NULL;
+
+    GST_OBJECT_LOCK (decoder);
+    priv->qos_frame_duration = 0;
+    GST_OBJECT_UNLOCK (decoder);
+
     priv->min_latency = 0;
     priv->max_latency = 0;
 
@@ -1652,8 +1649,10 @@ gst_video_decoder_reset (GstVideoDecoder * decoder, gboolean full)
   priv->bytes_out = 0;
   priv->time = 0;
 
+  GST_OBJECT_LOCK (decoder);
   priv->earliest_time = GST_CLOCK_TIME_NONE;
   priv->proportion = 0.5;
+  GST_OBJECT_UNLOCK (decoder);
 
   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
 }
@@ -2226,13 +2225,16 @@ gst_video_decoder_drop_frame (GstVideoDecoder * dec, GstVideoCodecFrame * frame)
   dec->priv->dropped++;
 
   /* post QoS message */
-  timestamp = frame->pts;
+  GST_OBJECT_LOCK (dec);
   proportion = dec->priv->proportion;
+  earliest_time = dec->priv->earliest_time;
+  GST_OBJECT_UNLOCK (dec);
+
+  timestamp = frame->pts;
   segment = &dec->output_segment;
   stream_time =
       gst_segment_to_stream_time (segment, GST_FORMAT_TIME, timestamp);
   qostime = gst_segment_to_running_time (segment, GST_FORMAT_TIME, timestamp);
-  earliest_time = dec->priv->earliest_time;
   jitter = GST_CLOCK_DIFF (qostime, earliest_time);
   qos_msg =
       gst_message_new_qos (GST_OBJECT_CAST (dec), FALSE, qostime, stream_time,
@@ -2629,6 +2631,7 @@ gst_video_decoder_set_output_state (GstVideoDecoder * decoder,
 {
   GstVideoDecoderPrivate *priv = decoder->priv;
   GstVideoCodecState *state;
+  GstClockTime qos_frame_duration;
 
   GST_DEBUG_OBJECT (decoder, "fmt:%d, width:%d, height:%d, reference:%p",
       fmt, width, height, reference);
@@ -2642,9 +2645,20 @@ gst_video_decoder_set_output_state (GstVideoDecoder * decoder,
     gst_video_codec_state_unref (priv->output_state);
   priv->output_state = gst_video_codec_state_ref (state);
 
+  if (priv->output_state != NULL && priv->output_state->info.fps_n > 0) {
+    qos_frame_duration =
+        gst_util_uint64_scale (GST_SECOND, priv->output_state->info.fps_d,
+        priv->output_state->info.fps_n);
+  } else {
+    qos_frame_duration = 0;
+  }
   priv->output_state_changed = TRUE;
   GST_VIDEO_DECODER_STREAM_UNLOCK (decoder);
 
+  GST_OBJECT_LOCK (decoder);
+  priv->qos_frame_duration = qos_frame_duration;
+  GST_OBJECT_UNLOCK (decoder);
+
   return state;
 }