hlssink: Add segment target-duration property
This property causes hlssink to push a force key unit event upstream
every target-duration seconds. This is for simple automatic segmenting.
To disable it, set the property to 0 and push the force key unit event
manually.
diff --git a/gst/hls/gsthlssink.c b/gst/hls/gsthlssink.c
index 8a67f40..7c5dbe7 100644
--- a/gst/hls/gsthlssink.c
+++ b/gst/hls/gsthlssink.c
@@ -36,6 +36,7 @@
#define DEFAULT_PLAYLIST_LOCATION "playlist.m3u8"
#define DEFAULT_PLAYLIST_ROOT NULL
#define DEFAULT_MAX_FILES 10
+#define DEFAULT_TARGET_DURATION 15
enum
{
@@ -43,7 +44,8 @@
PROP_LOCATION,
PROP_PLAYLIST_LOCATION,
PROP_PLAYLIST_ROOT,
- PROP_MAX_FILES
+ PROP_MAX_FILES,
+ PROP_TARGET_DURATION
};
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
@@ -140,6 +142,13 @@
"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));
+ g_object_class_install_property (gobject_class, PROP_TARGET_DURATION,
+ g_param_spec_uint ("target-duration", "Target duration",
+ "The target duration in seconds of a segment/file. "
+ "(0 - disabled, useful for management of segment duration by the "
+ "streaming server)",
+ 0, G_MAXUINT, DEFAULT_TARGET_DURATION,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static void
@@ -160,6 +169,9 @@
sink->playlist_root = g_strdup (DEFAULT_PLAYLIST_ROOT);
sink->playlist = gst_m3u8_playlist_new (6, 5, FALSE);
sink->max_files = DEFAULT_MAX_FILES;
+ sink->target_duration = DEFAULT_TARGET_DURATION;
+ sink->count = 0;
+ sink->timeout_id = 0;
}
static gboolean
@@ -250,6 +262,27 @@
GST_BIN_CLASS (parent_class)->handle_message (bin, message);
}
+static gboolean
+send_force_key_unit_event (gpointer user_data)
+{
+ GstHlsSink *sink = GST_HLS_SINK_CAST (user_data);
+ GstPad *sinkpad = gst_element_get_static_pad (GST_ELEMENT (sink), "sink");
+
+ /* FIXME - try to make segments >= target duration in length by setting the
+ * time parameter of the force key unit event to last segment cut time +
+ * target duration */
+ sink->count++;
+ if (!gst_pad_push_event (sinkpad,
+ gst_video_event_new_upstream_force_key_unit (sink->target_duration *
+ sink->count, TRUE, sink->count))) {
+ GST_WARNING_OBJECT (sink, "Failed to push upstream force key unit event");
+ }
+
+ gst_object_unref (sinkpad);
+
+ return TRUE;
+}
+
static GstStateChangeReturn
gst_hls_sink_change_state (GstElement * element, GstStateChange trans)
{
@@ -262,6 +295,13 @@
return GST_STATE_CHANGE_FAILURE;
}
break;
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ if (sink->target_duration) {
+ sink->timeout_id =
+ g_timeout_add_seconds (sink->target_duration,
+ send_force_key_unit_event, sink);
+ }
+ break;
default:
break;
}
@@ -269,6 +309,16 @@
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, trans);
switch (trans) {
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ if (sink->timeout_id && !g_source_remove (sink->timeout_id)) {
+ GST_WARNING_OBJECT (sink, "Failed to remove target-duration timeout");
+ }
+ sink->timeout_id = 0;
+ break;
+ case GST_STATE_CHANGE_PAUSED_TO_READY:
+ /* reset the segment/file count */
+ sink->count = 0;
+ break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
default:
@@ -307,6 +357,9 @@
NULL);
}
break;
+ case PROP_TARGET_DURATION:
+ sink->target_duration = g_value_get_uint (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -332,6 +385,9 @@
case PROP_MAX_FILES:
g_value_set_uint (value, sink->max_files);
break;
+ case PROP_TARGET_DURATION:
+ g_value_set_uint (value, sink->target_duration);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
diff --git a/gst/hls/gsthlssink.h b/gst/hls/gsthlssink.h
index 44c60da..e678e0c 100644
--- a/gst/hls/gsthlssink.h
+++ b/gst/hls/gsthlssink.h
@@ -50,6 +50,9 @@
GstM3U8Playlist *playlist;
guint index;
gint max_files;
+ gint target_duration;
+ gint count;
+ guint timeout_id;
};
struct _GstHlsSinkClass