splitmuxsink: Added a split-at-running-time action signal

The video file can now be split at an arbitrary time, given by the user
as an argument to the action signal.

https://bugzilla.gnome.org/show_bug.cgi?id=787922
This commit is contained in:
John Nikolaides 2018-09-26 17:43:05 +03:00 committed by Vivia Nikolaidou
parent 506e080a15
commit 6fe214e7a9
2 changed files with 86 additions and 0 deletions

View file

@ -85,6 +85,8 @@ GST_DEBUG_CATEGORY_STATIC (splitmux_debug);
static void split_now (GstSplitMuxSink * splitmux);
static void split_after (GstSplitMuxSink * splitmux);
static void split_at_running_time (GstSplitMuxSink * splitmux,
GstClockTime split_time);
enum
{
@ -132,6 +134,7 @@ enum
SIGNAL_FORMAT_LOCATION_FULL,
SIGNAL_SPLIT_NOW,
SIGNAL_SPLIT_AFTER,
SIGNAL_SPLIT_AT_RUNNING_TIME,
SIGNAL_MUXER_ADDED,
SIGNAL_SINK_ADDED,
SIGNAL_LAST
@ -432,6 +435,32 @@ gst_splitmux_sink_class_init (GstSplitMuxSinkClass * klass)
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSplitMuxSinkClass,
split_after), NULL, NULL, NULL, G_TYPE_NONE, 0);
/**
* GstSplitMuxSink::split-now:
* @splitmux: the #GstSplitMuxSink
*
* When called by the user, this action signal splits the video file (and
* begins a new one) as soon as the given running time is reached. If this
* action signal is called multiple times, running times are queued up and
* processed in the order they were given.
*
* Note that this is prone to race conditions, where said running time is
* reached and surpassed before we had a chance to split. The file will
* still split immediately, but in order to make sure that the split doesn't
* happen too late, it is recommended to call this action signal from
* something that will prevent further buffers from flowing into
* splitmuxsink before the split is completed, such as a pad probe before
* splitmuxsink.
*
*
* Since: 1.16
*/
signals[SIGNAL_SPLIT_AT_RUNNING_TIME] =
g_signal_new ("split-at-running-time", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSplitMuxSinkClass,
split_at_running_time), NULL, NULL, NULL, G_TYPE_NONE, 1,
G_TYPE_UINT64);
/**
* GstSplitMuxSink::muxer-added:
* @splitmux: the #GstSplitMuxSink
@ -456,6 +485,7 @@ gst_splitmux_sink_class_init (GstSplitMuxSinkClass * klass)
klass->split_now = split_now;
klass->split_after = split_after;
klass->split_at_running_time = split_at_running_time;
}
static void
@ -487,6 +517,7 @@ gst_splitmux_sink_init (GstSplitMuxSink * splitmux)
GST_OBJECT_FLAG_SET (splitmux, GST_ELEMENT_FLAG_SINK);
splitmux->split_requested = FALSE;
splitmux->do_split_next_gop = FALSE;
splitmux->times_to_split = gst_queue_array_new_for_struct (8, 8);
}
static void
@ -544,6 +575,9 @@ gst_splitmux_sink_finalize (GObject * object)
if (splitmux->threshold_timecode_str)
g_free (splitmux->threshold_timecode_str);
if (splitmux->times_to_split)
gst_queue_array_free (splitmux->times_to_split);
g_free (splitmux->location);
/* Make sure to free any un-released contexts. There should not be any,
@ -1835,10 +1869,16 @@ need_new_fragment (GstSplitMuxSink * splitmux,
guint64 thresh_bytes;
GstClockTime thresh_time;
gboolean check_robust_muxing;
GstClockTime time_to_split = GST_CLOCK_TIME_NONE;
GstClockTime *ptr_to_time;
GST_OBJECT_LOCK (splitmux);
thresh_bytes = splitmux->threshold_bytes;
thresh_time = splitmux->threshold_time;
ptr_to_time = (GstClockTime *)
gst_queue_array_peek_head_struct (splitmux->times_to_split);
if (ptr_to_time)
time_to_split = *ptr_to_time;
check_robust_muxing = splitmux->use_robust_muxing
&& splitmux->muxer_has_reserved_props;
GST_OBJECT_UNLOCK (splitmux);
@ -1851,6 +1891,25 @@ need_new_fragment (GstSplitMuxSink * splitmux,
if (g_atomic_int_get (&(splitmux->do_split_next_gop)) == TRUE)
return TRUE;
/* User told us to split at this running time */
if (splitmux->reference_ctx->in_running_time > time_to_split) {
GST_OBJECT_LOCK (splitmux);
/* Dequeue running time */
gst_queue_array_pop_head_struct (splitmux->times_to_split);
/* Empty any running times after this that are past now */
ptr_to_time = gst_queue_array_peek_head_struct (splitmux->times_to_split);
while (ptr_to_time) {
time_to_split = *ptr_to_time;
if (splitmux->reference_ctx->in_running_time <= time_to_split) {
break;
}
gst_queue_array_pop_head_struct (splitmux->times_to_split);
ptr_to_time = gst_queue_array_peek_head_struct (splitmux->times_to_split);
}
GST_OBJECT_UNLOCK (splitmux);
return TRUE;
}
if (thresh_bytes > 0 && queued_bytes > thresh_bytes)
return TRUE; /* Would overrun byte limit */
@ -2984,6 +3043,7 @@ gst_splitmux_sink_change_state (GstElement * element, GstStateChange transition)
g_atomic_int_set (&(splitmux->do_split_next_gop), FALSE);
case GST_STATE_CHANGE_READY_TO_NULL:
GST_SPLITMUX_LOCK (splitmux);
gst_queue_array_clear (splitmux->times_to_split);
splitmux->output_state = SPLITMUX_OUTPUT_STATE_STOPPED;
splitmux->input_state = SPLITMUX_INPUT_STATE_STOPPED;
/* Wake up any blocked threads */
@ -3072,3 +3132,26 @@ split_after (GstSplitMuxSink * splitmux)
{
g_atomic_int_set (&(splitmux->split_requested), TRUE);
}
static void
split_at_running_time (GstSplitMuxSink * splitmux, GstClockTime split_time)
{
gboolean send_keyframe_requests;
GST_SPLITMUX_LOCK (splitmux);
gst_queue_array_push_tail_struct (splitmux->times_to_split, &split_time);
send_keyframe_requests = splitmux->send_keyframe_requests;
GST_SPLITMUX_UNLOCK (splitmux);
if (send_keyframe_requests) {
GstEvent *ev =
gst_video_event_new_upstream_force_key_unit (split_time, TRUE, 0);
GST_INFO_OBJECT (splitmux, "Requesting next keyframe at %" GST_TIME_FORMAT,
GST_TIME_ARGS (split_time));
if (!gst_pad_push_event (splitmux->reference_ctx->sinkpad, ev)) {
GST_WARNING_OBJECT (splitmux,
"Could not request keyframe at %" GST_TIME_FORMAT,
GST_TIME_ARGS (split_time));
}
}
}

View file

@ -22,6 +22,7 @@
#include <gst/gst.h>
#include <gst/pbutils/pbutils.h>
#include <gst/base/base.h>
G_BEGIN_DECLS
#define GST_TYPE_SPLITMUX_SINK (gst_splitmux_sink_get_type())
@ -173,6 +174,7 @@ struct _GstSplitMuxSink
gboolean split_requested;
gboolean do_split_next_gop;
GstQueueArray *times_to_split;
/* Async finalize options */
gboolean async_finalize;
@ -189,6 +191,7 @@ struct _GstSplitMuxSinkClass
/* actions */
void (*split_now) (GstSplitMuxSink * splitmux);
void (*split_after) (GstSplitMuxSink * splitmux);
void (*split_at_running_time) (GstSplitMuxSink * splitmux, GstClockTime split_time);
};
G_END_DECLS