splitmuxsink: Add option to split at exactly max-size-time

Will try to request a keyframe from the encoder to be sent at the target
running time.

https://bugzilla.gnome.org/show_bug.cgi?id=769664
This commit is contained in:
Vivia Nikolaidou 2016-08-09 12:55:59 +03:00 committed by Jan Schmidt
parent 369d37d227
commit b9a8188704
2 changed files with 53 additions and 4 deletions

View file

@ -55,6 +55,7 @@
#include <string.h> #include <string.h>
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include <gst/video/video.h>
#include "gstsplitmuxsink.h" #include "gstsplitmuxsink.h"
GST_DEBUG_CATEGORY_STATIC (splitmux_debug); GST_DEBUG_CATEGORY_STATIC (splitmux_debug);
@ -71,6 +72,7 @@ enum
PROP_LOCATION, PROP_LOCATION,
PROP_MAX_SIZE_TIME, PROP_MAX_SIZE_TIME,
PROP_MAX_SIZE_BYTES, PROP_MAX_SIZE_BYTES,
PROP_SEND_KEYFRAME_REQUESTS,
PROP_MAX_FILES, PROP_MAX_FILES,
PROP_MUXER_OVERHEAD, PROP_MUXER_OVERHEAD,
PROP_MUXER, PROP_MUXER,
@ -81,6 +83,7 @@ enum
#define DEFAULT_MAX_SIZE_BYTES 0 #define DEFAULT_MAX_SIZE_BYTES 0
#define DEFAULT_MAX_FILES 0 #define DEFAULT_MAX_FILES 0
#define DEFAULT_MUXER_OVERHEAD 0.02 #define DEFAULT_MUXER_OVERHEAD 0.02
#define DEFAULT_SEND_KEYFRAME_REQUESTS FALSE
#define DEFAULT_MUXER "mp4mux" #define DEFAULT_MUXER "mp4mux"
#define DEFAULT_SINK "filesink" #define DEFAULT_SINK "filesink"
@ -207,11 +210,18 @@ gst_splitmux_sink_class_init (GstSplitMuxSinkClass * klass)
g_param_spec_uint64 ("max-size-bytes", "Max. size bytes", g_param_spec_uint64 ("max-size-bytes", "Max. size bytes",
"Max. amount of data per file (in bytes, 0=disable)", 0, G_MAXUINT64, "Max. amount of data per file (in bytes, 0=disable)", 0, G_MAXUINT64,
DEFAULT_MAX_SIZE_BYTES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); DEFAULT_MAX_SIZE_BYTES, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_SEND_KEYFRAME_REQUESTS,
g_param_spec_boolean ("send-keyframe-requests",
"Request keyframes at max-size-time",
"Request a keyframe every max-size-time ns to try splitting at that point. "
"Needs max-size-bytes to be 0 in order to be effective.",
DEFAULT_SEND_KEYFRAME_REQUESTS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_MAX_FILES, g_object_class_install_property (gobject_class, PROP_MAX_FILES,
g_param_spec_uint ("max-files", "Max files", g_param_spec_uint ("max-files", "Max files",
"Maximum number of files to keep on disk. Once the maximum is reached," "Maximum number of files to keep on disk. Once the maximum is reached,"
"old files start to be deleted to make room for new ones.", "old files start to be deleted to make room for new ones.", 0,
0, G_MAXUINT, DEFAULT_MAX_FILES, G_MAXUINT, DEFAULT_MAX_FILES,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
@ -246,6 +256,7 @@ gst_splitmux_sink_init (GstSplitMuxSink * splitmux)
splitmux->threshold_time = DEFAULT_MAX_SIZE_TIME; splitmux->threshold_time = DEFAULT_MAX_SIZE_TIME;
splitmux->threshold_bytes = DEFAULT_MAX_SIZE_BYTES; splitmux->threshold_bytes = DEFAULT_MAX_SIZE_BYTES;
splitmux->max_files = DEFAULT_MAX_FILES; splitmux->max_files = DEFAULT_MAX_FILES;
splitmux->send_keyframe_requests = DEFAULT_SEND_KEYFRAME_REQUESTS;
GST_OBJECT_FLAG_SET (splitmux, GST_ELEMENT_FLAG_SINK); GST_OBJECT_FLAG_SET (splitmux, GST_ELEMENT_FLAG_SINK);
} }
@ -320,6 +331,11 @@ gst_splitmux_sink_set_property (GObject * object, guint prop_id,
splitmux->threshold_time = g_value_get_uint64 (value); splitmux->threshold_time = g_value_get_uint64 (value);
GST_OBJECT_UNLOCK (splitmux); GST_OBJECT_UNLOCK (splitmux);
break; break;
case PROP_SEND_KEYFRAME_REQUESTS:
GST_OBJECT_LOCK (splitmux);
splitmux->send_keyframe_requests = g_value_get_boolean (value);
GST_OBJECT_UNLOCK (splitmux);
break;
case PROP_MAX_FILES: case PROP_MAX_FILES:
GST_OBJECT_LOCK (splitmux); GST_OBJECT_LOCK (splitmux);
splitmux->max_files = g_value_get_uint (value); splitmux->max_files = g_value_get_uint (value);
@ -372,6 +388,11 @@ gst_splitmux_sink_get_property (GObject * object, guint prop_id,
g_value_set_uint64 (value, splitmux->threshold_time); g_value_set_uint64 (value, splitmux->threshold_time);
GST_OBJECT_UNLOCK (splitmux); GST_OBJECT_UNLOCK (splitmux);
break; break;
case PROP_SEND_KEYFRAME_REQUESTS:
GST_OBJECT_LOCK (splitmux);
g_value_set_boolean (value, splitmux->send_keyframe_requests);
GST_OBJECT_UNLOCK (splitmux);
break;
case PROP_MAX_FILES: case PROP_MAX_FILES:
GST_OBJECT_LOCK (splitmux); GST_OBJECT_LOCK (splitmux);
g_value_set_uint (value, splitmux->max_files); g_value_set_uint (value, splitmux->max_files);
@ -601,6 +622,22 @@ complete_or_wait_on_out (GstSplitMuxSink * splitmux, MqStreamCtx * ctx)
} while (1); } while (1);
} }
static gboolean
request_next_keyframe (GstSplitMuxSink * splitmux)
{
GstEvent *ev;
if (splitmux->send_keyframe_requests == FALSE || splitmux->threshold_time == 0
|| splitmux->threshold_bytes != 0)
return TRUE;
ev = gst_video_event_new_upstream_force_key_unit (splitmux->fragment_id *
splitmux->threshold_time, TRUE, 0);
GST_DEBUG_OBJECT (splitmux, "Requesting next keyframe at %" GST_TIME_FORMAT,
GST_TIME_ARGS (splitmux->fragment_id * splitmux->threshold_time));
return gst_pad_push_event (splitmux->reference_ctx->sinkpad, ev);
}
static GstPadProbeReturn static GstPadProbeReturn
handle_mq_output (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx) handle_mq_output (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
{ {
@ -715,6 +752,9 @@ handle_mq_output (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
pad, GST_STIME_ARGS (ctx->out_running_time), buf_info->buf_size); pad, GST_STIME_ARGS (ctx->out_running_time), buf_info->buf_size);
if (splitmux->opening_first_fragment) { if (splitmux->opening_first_fragment) {
if (request_next_keyframe (splitmux) == FALSE)
GST_WARNING_OBJECT (splitmux,
"Could not request a keyframe. Files may not split at the exact location they should");
send_fragment_opened_closed_msg (splitmux, TRUE); send_fragment_opened_closed_msg (splitmux, TRUE);
splitmux->opening_first_fragment = FALSE; splitmux->opening_first_fragment = FALSE;
} }
@ -726,6 +766,7 @@ handle_mq_output (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
splitmux->muxed_out_time = buf_info->run_ts; splitmux->muxed_out_time = buf_info->run_ts;
splitmux->muxed_out_bytes += buf_info->buf_size; splitmux->muxed_out_bytes += buf_info->buf_size;
splitmux->last_frame_duration = buf_info->duration;
#ifndef GST_DISABLE_GST_DEBUG #ifndef GST_DISABLE_GST_DEBUG
{ {
@ -807,6 +848,8 @@ start_next_fragment (GstSplitMuxSink * splitmux)
/* 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;
if (splitmux->last_frame_duration != GST_CLOCK_STIME_NONE)
splitmux->mux_start_time += splitmux->last_frame_duration;
splitmux->mux_start_bytes = splitmux->muxed_out_bytes; splitmux->mux_start_bytes = splitmux->muxed_out_bytes;
GST_DEBUG_OBJECT (splitmux, GST_DEBUG_OBJECT (splitmux,
@ -814,6 +857,9 @@ start_next_fragment (GstSplitMuxSink * splitmux)
GST_STIME_ARGS (splitmux->max_out_running_time)); GST_STIME_ARGS (splitmux->max_out_running_time));
send_fragment_opened_closed_msg (splitmux, TRUE); send_fragment_opened_closed_msg (splitmux, TRUE);
if (request_next_keyframe (splitmux) == FALSE)
GST_WARNING_OBJECT (splitmux,
"Could not request a keyframe. Files may not split at the exact location they should");
GST_SPLITMUX_BROADCAST (splitmux); GST_SPLITMUX_BROADCAST (splitmux);
} }
@ -907,7 +953,6 @@ handle_gathered_gop (GstSplitMuxSink * splitmux)
queued_time > splitmux->threshold_time)))) { queued_time > splitmux->threshold_time)))) {
splitmux->state = SPLITMUX_STATE_ENDING_FILE; splitmux->state = SPLITMUX_STATE_ENDING_FILE;
GST_INFO_OBJECT (splitmux, GST_INFO_OBJECT (splitmux,
"mq overflowed since last, draining out. max out TS is %" "mq overflowed since last, draining out. max out TS is %"
GST_STIME_FORMAT, GST_STIME_ARGS (splitmux->max_out_running_time)); GST_STIME_FORMAT, GST_STIME_ARGS (splitmux->max_out_running_time));
@ -1160,6 +1205,7 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx)
buf_info->run_ts = ctx->in_running_time; buf_info->run_ts = ctx->in_running_time;
buf_info->buf_size = gst_buffer_get_size (buf); buf_info->buf_size = gst_buffer_get_size (buf);
buf_info->duration = GST_BUFFER_DURATION (buf);
/* Update total input byte counter for overflow detect */ /* Update total input byte counter for overflow detect */
ctx->in_bytes += buf_info->buf_size; ctx->in_bytes += buf_info->buf_size;
@ -1659,7 +1705,7 @@ gst_splitmux_sink_change_state (GstElement * element, GstStateChange transition)
splitmux->state = SPLITMUX_STATE_COLLECTING_GOP_START; splitmux->state = SPLITMUX_STATE_COLLECTING_GOP_START;
splitmux->max_in_running_time = GST_CLOCK_STIME_NONE; splitmux->max_in_running_time = GST_CLOCK_STIME_NONE;
splitmux->muxed_out_time = splitmux->mux_start_time = splitmux->muxed_out_time = splitmux->mux_start_time =
GST_CLOCK_STIME_NONE; splitmux->last_frame_duration = GST_CLOCK_STIME_NONE;
splitmux->muxed_out_bytes = splitmux->mux_start_bytes = 0; splitmux->muxed_out_bytes = splitmux->mux_start_bytes = 0;
splitmux->opening_first_fragment = TRUE; splitmux->opening_first_fragment = TRUE;
GST_SPLITMUX_UNLOCK (splitmux); GST_SPLITMUX_UNLOCK (splitmux);

View file

@ -50,6 +50,7 @@ typedef struct _MqStreamBuf
gboolean keyframe; gboolean keyframe;
GstClockTimeDiff run_ts; GstClockTimeDiff run_ts;
gsize buf_size; gsize buf_size;
GstClockTime duration;
} MqStreamBuf; } MqStreamBuf;
typedef struct _MqStreamCtx typedef struct _MqStreamCtx
@ -95,6 +96,7 @@ struct _GstSplitMuxSink {
GstClockTime threshold_time; GstClockTime threshold_time;
guint64 threshold_bytes; guint64 threshold_bytes;
guint max_files; guint max_files;
gboolean send_keyframe_requests;
guint mq_max_buffers; guint mq_max_buffers;
@ -123,6 +125,7 @@ struct _GstSplitMuxSink {
GstClockTimeDiff mux_start_time; GstClockTimeDiff mux_start_time;
gsize mux_start_bytes; gsize mux_start_bytes;
GstClockTime last_frame_duration;
gboolean opening_first_fragment; gboolean opening_first_fragment;
gboolean switching_fragment; gboolean switching_fragment;