diff --git a/gst/multifile/gstsplitmuxsink.c b/gst/multifile/gstsplitmuxsink.c index 98f744ff6e..8d352ff02d 100644 --- a/gst/multifile/gstsplitmuxsink.c +++ b/gst/multifile/gstsplitmuxsink.c @@ -55,6 +55,7 @@ #include #include +#include #include "gstsplitmuxsink.h" GST_DEBUG_CATEGORY_STATIC (splitmux_debug); @@ -71,6 +72,7 @@ enum PROP_LOCATION, PROP_MAX_SIZE_TIME, PROP_MAX_SIZE_BYTES, + PROP_SEND_KEYFRAME_REQUESTS, PROP_MAX_FILES, PROP_MUXER_OVERHEAD, PROP_MUXER, @@ -81,6 +83,7 @@ enum #define DEFAULT_MAX_SIZE_BYTES 0 #define DEFAULT_MAX_FILES 0 #define DEFAULT_MUXER_OVERHEAD 0.02 +#define DEFAULT_SEND_KEYFRAME_REQUESTS FALSE #define DEFAULT_MUXER "mp4mux" #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", "Max. amount of data per file (in bytes, 0=disable)", 0, G_MAXUINT64, 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_param_spec_uint ("max-files", "Max files", "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.", - 0, G_MAXUINT, DEFAULT_MAX_FILES, + "old files start to be deleted to make room for new ones.", 0, + G_MAXUINT, DEFAULT_MAX_FILES, 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_bytes = DEFAULT_MAX_SIZE_BYTES; splitmux->max_files = DEFAULT_MAX_FILES; + splitmux->send_keyframe_requests = DEFAULT_SEND_KEYFRAME_REQUESTS; 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); GST_OBJECT_UNLOCK (splitmux); 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: GST_OBJECT_LOCK (splitmux); 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); GST_OBJECT_UNLOCK (splitmux); 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: GST_OBJECT_LOCK (splitmux); g_value_set_uint (value, splitmux->max_files); @@ -601,6 +622,22 @@ complete_or_wait_on_out (GstSplitMuxSink * splitmux, MqStreamCtx * ctx) } 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 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); 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); 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_bytes += buf_info->buf_size; + splitmux->last_frame_duration = buf_info->duration; #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 */ 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; GST_DEBUG_OBJECT (splitmux, @@ -814,6 +857,9 @@ start_next_fragment (GstSplitMuxSink * splitmux) GST_STIME_ARGS (splitmux->max_out_running_time)); 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); } @@ -907,7 +953,6 @@ handle_gathered_gop (GstSplitMuxSink * splitmux) queued_time > splitmux->threshold_time)))) { splitmux->state = SPLITMUX_STATE_ENDING_FILE; - GST_INFO_OBJECT (splitmux, "mq overflowed since last, draining out. max out TS is %" 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->buf_size = gst_buffer_get_size (buf); + buf_info->duration = GST_BUFFER_DURATION (buf); /* Update total input byte counter for overflow detect */ 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->max_in_running_time = GST_CLOCK_STIME_NONE; 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->opening_first_fragment = TRUE; GST_SPLITMUX_UNLOCK (splitmux); diff --git a/gst/multifile/gstsplitmuxsink.h b/gst/multifile/gstsplitmuxsink.h index 8a4389201e..f966bb75f0 100644 --- a/gst/multifile/gstsplitmuxsink.h +++ b/gst/multifile/gstsplitmuxsink.h @@ -50,6 +50,7 @@ typedef struct _MqStreamBuf gboolean keyframe; GstClockTimeDiff run_ts; gsize buf_size; + GstClockTime duration; } MqStreamBuf; typedef struct _MqStreamCtx @@ -95,6 +96,7 @@ struct _GstSplitMuxSink { GstClockTime threshold_time; guint64 threshold_bytes; guint max_files; + gboolean send_keyframe_requests; guint mq_max_buffers; @@ -123,6 +125,7 @@ struct _GstSplitMuxSink { GstClockTimeDiff mux_start_time; gsize mux_start_bytes; + GstClockTime last_frame_duration; gboolean opening_first_fragment; gboolean switching_fragment;