splitmuxsink: Fix occasional deadlock when ending file with subtitle

Deadlock occurs when splitting files if one stream received no buffer during
the first GOP of the next file. That can happen in that scenario for example:
 1) The first GOP of video is collected, it has a duration of 10s.
    max_in_running_time is set to 10s.
 2) Other streams catchup and we receive the first subtitle buffer at ts=0 and
    has a duration of 1min.
 3) We receive the 2nd subtitle buffer with a ts=1min. in_running_time is set to
    1min. That buffer is blocked in handle_mq_input() because
    max_in_running_time is still 10s.
 4) Since all in_running_time are now > 10s, max_out_running_time is now set to
    10s. That first GOP gets recorded into the file. The muxer pop buffers out
    of the mq, when it tries to pop a 2nd subtitle buffer it blocks because the
    GstDataQueue is empty.
 5) A 2nd GOP of video is collected and has a duration of 10s as well.
    max_in_running_time is now 20s. Since subtitle's in_running_time is already
    1min, that GOP is already complete.
 6) But let's say we overran the max file size, we thus set state to
    SPLITMUX_STATE_ENDING_FILE now. As soon as a buffer with ts > 10s (end of
    previous GOP) arrives in handle_mq_output(), EOS event is sent downstream
    instead. But since the subtitle queue is empty, that's never going to
    happen. Pipeline is now deadlocked.

To fix this situation we have to:
 - Send a dummy event through the queue to wakeup output thread.
 - Update out_running_time to at least max_out_running_time so it sends EOS.
 - Respect time order, so we set out_running_tim=max_in_running_time because
   that's bigger than previous buffer and smaller than next.

https://bugzilla.gnome.org/show_bug.cgi?id=763711
This commit is contained in:
Xavier Claessens 2016-03-18 15:45:01 -04:00 committed by Jan Schmidt
parent 4c0e509328
commit fb835c100a

View file

@ -627,6 +627,25 @@ handle_mq_output (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
GST_SPLITMUX_UNLOCK (splitmux);
break;
}
case GST_EVENT_CUSTOM_DOWNSTREAM:{
const GstStructure *s;
GstClockTime ts = 0;
s = gst_event_get_structure (event);
if (!gst_structure_has_name (s, "splitmuxsink-unblock"))
break;
gst_structure_get_uint64 (s, "timestamp", &ts);
GST_SPLITMUX_LOCK (splitmux);
if (splitmux->state == SPLITMUX_STATE_STOPPED)
goto beach;
ctx->out_running_time = ts;
complete_or_wait_on_out (splitmux, ctx);
GST_SPLITMUX_UNLOCK (splitmux);
return GST_PAD_PROBE_DROP;
}
default:
break;
}
@ -1125,7 +1144,23 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
"Collected last packet of GOP. Checking other pads");
check_completed_gop (splitmux, ctx);
break;
case SPLITMUX_STATE_ENDING_FILE:
case SPLITMUX_STATE_ENDING_FILE:{
GstEvent *event;
/* If somes streams received no buffer during the last GOP that overran,
* because its next buffer has a timestamp bigger than
* ctx->max_in_running_time, its queue is empty. In that case the only
* way to wakeup the output thread is by injecting an event in the
* queue. This usually happen with subtitle streams.
* See https://bugzilla.gnome.org/show_bug.cgi?id=763711. */
GST_LOG_OBJECT (pad, "Sending splitmuxsink-unblock event");
event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM |
GST_EVENT_TYPE_SERIALIZED,
gst_structure_new ("splitmuxsink-unblock", "timestamp",
G_TYPE_UINT64, splitmux->max_in_running_time, NULL));
gst_pad_send_event (ctx->sinkpad, event);
/* fallthrough */
}
case SPLITMUX_STATE_START_NEXT_FRAGMENT:
/* A fragment is ending, wait until that's done before continuing */
GST_DEBUG_OBJECT (pad, "Sleeping for fragment restart");