splitmuxsink: allow non-video streams to serve as reference

In the absence of a video stream, the first stream will be used as
reference.

https://bugzilla.gnome.org/show_bug.cgi?id=753617
This commit is contained in:
Ramiro Polla 2015-04-29 18:23:28 +01:00 committed by Jan Schmidt
parent 20754db26f
commit f0a47d0b60
2 changed files with 27 additions and 21 deletions

View file

@ -24,7 +24,7 @@
* This element wraps a muxer and a sink, and starts a new file when the mux * This element wraps a muxer and a sink, and starts a new file when the mux
* contents are about to cross a threshold of maximum size of maximum time, * contents are about to cross a threshold of maximum size of maximum time,
* splitting at video keyframe boundaries. Exactly one input video stream * splitting at video keyframe boundaries. Exactly one input video stream
* is required, with as many accompanying audio and subtitle streams as * can be muxed, with as many accompanying audio and subtitle streams as
* desired. * desired.
* *
* By default, it uses mp4mux and filesink, but they can be changed via * By default, it uses mp4mux and filesink, but they can be changed via
@ -33,9 +33,10 @@
* The minimum file size is 1 GOP, however - so limits may be overrun if the * The minimum file size is 1 GOP, however - so limits may be overrun if the
* distance between any 2 keyframes is larger than the limits. * distance between any 2 keyframes is larger than the limits.
* *
* The splitting process is driven by the video stream contents, and * If a video stream is available, the splitting process is driven by the video
* the video stream must contain closed GOPs for the output file parts * stream contents, and the video stream must contain closed GOPs for the output
* to be played individually correctly. * file parts to be played individually correctly. In the absence of a video
* stream, the first available stream is used as reference for synchronization.
* *
* <refsect2> * <refsect2>
* <title>Example pipelines</title> * <title>Example pipelines</title>
@ -700,14 +701,14 @@ start_next_fragment (GstSplitMuxSink * splitmux)
/* Switch state and go back to processing */ /* Switch state and go back to processing */
splitmux->state = SPLITMUX_STATE_COLLECTING_GOP_START; splitmux->state = SPLITMUX_STATE_COLLECTING_GOP_START;
if (!splitmux->video_ctx->in_eos) { if (!splitmux->reference_ctx->in_eos) {
splitmux->max_out_running_time = splitmux->video_ctx->in_running_time; splitmux->max_out_running_time = splitmux->reference_ctx->in_running_time;
} else { } else {
splitmux->max_out_running_time = GST_CLOCK_TIME_NONE; splitmux->max_out_running_time = GST_CLOCK_TIME_NONE;
splitmux->have_muxed_something = FALSE; splitmux->have_muxed_something = FALSE;
} }
splitmux->have_muxed_something = splitmux->have_muxed_something =
(splitmux->video_ctx->in_running_time > splitmux->muxed_out_time); (splitmux->reference_ctx->in_running_time > splitmux->muxed_out_time);
/* Store the overflow parameters as the basis for the next fragment */ /* Store the overflow parameters as the basis for the next fragment */
splitmux->mux_start_time = splitmux->muxed_out_time; splitmux->mux_start_time = splitmux->muxed_out_time;
@ -810,8 +811,8 @@ handle_gathered_gop (GstSplitMuxSink * splitmux)
splitmux->state = SPLITMUX_STATE_COLLECTING_GOP_START; splitmux->state = SPLITMUX_STATE_COLLECTING_GOP_START;
splitmux->have_muxed_something = TRUE; splitmux->have_muxed_something = TRUE;
if (!splitmux->video_ctx->in_eos) if (!splitmux->reference_ctx->in_eos)
splitmux->max_out_running_time = splitmux->video_ctx->in_running_time; splitmux->max_out_running_time = splitmux->reference_ctx->in_running_time;
else else
splitmux->max_out_running_time = GST_CLOCK_TIME_NONE; splitmux->max_out_running_time = GST_CLOCK_TIME_NONE;
@ -824,7 +825,7 @@ handle_gathered_gop (GstSplitMuxSink * splitmux)
/* Called with splitmux lock held */ /* Called with splitmux lock held */
/* Called from each input pad when it is has all the pieces /* Called from each input pad when it is has all the pieces
* for a GOP or EOS, starting with the video pad which has set the * for a GOP or EOS, starting with the reference pad which has set the
* splitmux->max_in_running_time * splitmux->max_in_running_time
*/ */
static void static void
@ -836,7 +837,7 @@ check_completed_gop (GstSplitMuxSink * splitmux, MqStreamCtx * ctx)
if (splitmux->state == SPLITMUX_STATE_WAITING_GOP_COMPLETE) { if (splitmux->state == SPLITMUX_STATE_WAITING_GOP_COMPLETE) {
/* Iterate each pad, and check that the input running time is at least /* Iterate each pad, and check that the input running time is at least
* up to the video runnning time, and if so handle the collected GOP */ * up to the reference running time, and if so handle the collected GOP */
GST_LOG_OBJECT (splitmux, "Checking GOP collected, ctx %p", ctx); GST_LOG_OBJECT (splitmux, "Checking GOP collected, ctx %p", ctx);
for (cur = g_list_first (splitmux->contexts); for (cur = g_list_first (splitmux->contexts);
cur != NULL; cur = g_list_next (cur)) { cur != NULL; cur = g_list_next (cur)) {
@ -903,7 +904,7 @@ check_queue_length (GstSplitMuxSink * splitmux, MqStreamCtx * ctx)
splitmux->queued_gops <= 1) { splitmux->queued_gops <= 1) {
allow_grow = TRUE; allow_grow = TRUE;
} else if (splitmux->state == SPLITMUX_STATE_COLLECTING_GOP_START && } else if (splitmux->state == SPLITMUX_STATE_COLLECTING_GOP_START &&
ctx->is_video && splitmux->queued_gops <= 1) { ctx->is_reference && splitmux->queued_gops <= 1) {
allow_grow = TRUE; allow_grow = TRUE;
} }
@ -972,8 +973,8 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
if (splitmux->state == SPLITMUX_STATE_STOPPED) if (splitmux->state == SPLITMUX_STATE_STOPPED)
goto beach; goto beach;
if (ctx->is_video) { if (ctx->is_reference) {
GST_INFO_OBJECT (splitmux, "Got Video EOS. Finishing up"); GST_INFO_OBJECT (splitmux, "Got Reference EOS. Finishing up");
/* Act as if this is a new keyframe with infinite timestamp */ /* Act as if this is a new keyframe with infinite timestamp */
splitmux->max_in_running_time = GST_CLOCK_TIME_NONE; splitmux->max_in_running_time = GST_CLOCK_TIME_NONE;
splitmux->state = SPLITMUX_STATE_WAITING_GOP_COMPLETE; splitmux->state = SPLITMUX_STATE_WAITING_GOP_COMPLETE;
@ -1051,7 +1052,7 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
switch (splitmux->state) { switch (splitmux->state) {
case SPLITMUX_STATE_COLLECTING_GOP_START: case SPLITMUX_STATE_COLLECTING_GOP_START:
if (ctx->is_video) { if (ctx->is_reference) {
/* If a keyframe, we have a complete GOP */ /* If a keyframe, we have a complete GOP */
if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) || if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) ||
!GST_CLOCK_TIME_IS_VALID (ctx->in_running_time) || !GST_CLOCK_TIME_IS_VALID (ctx->in_running_time) ||
@ -1070,7 +1071,7 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
GST_SPLITMUX_BROADCAST (splitmux); GST_SPLITMUX_BROADCAST (splitmux);
check_completed_gop (splitmux, ctx); check_completed_gop (splitmux, ctx);
} else { } else {
/* We're still waiting for a keyframe on the video pad, sleep */ /* We're still waiting for a keyframe on the reference pad, sleep */
GST_LOG_OBJECT (pad, "Sleeping for GOP start"); GST_LOG_OBJECT (pad, "Sleeping for GOP start");
GST_SPLITMUX_WAIT (splitmux); GST_SPLITMUX_WAIT (splitmux);
GST_LOG_OBJECT (pad, "Done sleeping for GOP start state now %d", GST_LOG_OBJECT (pad, "Done sleeping for GOP start state now %d",
@ -1201,7 +1202,6 @@ gst_splitmux_sink_request_new_pad (GstElement * element,
gst_object_unref (GST_OBJECT (res)); gst_object_unref (GST_OBJECT (res));
ctx = mq_stream_ctx_new (splitmux); ctx = mq_stream_ctx_new (splitmux);
ctx->is_video = is_video;
ctx->srcpad = mq_src; ctx->srcpad = mq_src;
ctx->sinkpad = mq_sink; ctx->sinkpad = mq_sink;
@ -1210,8 +1210,14 @@ gst_splitmux_sink_request_new_pad (GstElement * element,
gst_pad_add_probe (mq_src, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, gst_pad_add_probe (mq_src, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
(GstPadProbeCallback) handle_mq_output, ctx, (GDestroyNotify) (GstPadProbeCallback) handle_mq_output, ctx, (GDestroyNotify)
_pad_block_destroy_src_notify); _pad_block_destroy_src_notify);
if (is_video) if (is_video && splitmux->reference_ctx != NULL) {
splitmux->video_ctx = ctx; splitmux->reference_ctx->is_reference = FALSE;
splitmux->reference_ctx = NULL;
}
if (splitmux->reference_ctx == NULL) {
splitmux->reference_ctx = ctx;
ctx->is_reference = TRUE;
}
res = gst_ghost_pad_new (gname, mq_sink); res = gst_ghost_pad_new (gname, mq_sink);
g_object_set_qdata ((GObject *) (res), PAD_CONTEXT, ctx); g_object_set_qdata ((GObject *) (res), PAD_CONTEXT, ctx);

View file

@ -61,7 +61,7 @@ typedef struct _MqStreamCtx
guint sink_pad_block_id; guint sink_pad_block_id;
guint src_pad_block_id; guint src_pad_block_id;
gboolean is_video; gboolean is_reference;
gboolean flushing; gboolean flushing;
gboolean in_eos; gboolean in_eos;
@ -111,7 +111,7 @@ struct _GstSplitMuxSink {
GList *contexts; GList *contexts;
MqStreamCtx *video_ctx; MqStreamCtx *reference_ctx;
guint queued_gops; guint queued_gops;
GstClockTime max_in_running_time; GstClockTime max_in_running_time;
GstClockTime max_out_running_time; GstClockTime max_out_running_time;