splitmux->do_split_next_gop = FALSE;
splitmux->times_to_split = gst_queue_array_new_for_struct (8, 8);
splitmux->last_fku_time = GST_CLOCK_TIME_NONE;
+ splitmux->next_fku_time = GST_CLOCK_TIME_NONE;
}
static void
GstClockTime target_time;
gboolean timecode_based = FALSE;
GstClockTime next_max_tc_time = GST_CLOCK_TIME_NONE;
+ GstClockTime next_fku_time = GST_CLOCK_TIME_NONE;
if (splitmux->tc_interval) {
GstVideoTimeCodeMeta *tc_meta;
}
}
+ /* even if we don't send keyframe request, this should be done here in order
+ * to calculate the threshold timecode */
+ if (timecode_based && !GST_CLOCK_TIME_IS_VALID (splitmux->threshold_timecode)) {
+ splitmux->threshold_timecode = next_max_tc_time - running_time;
+ GST_DEBUG_OBJECT (splitmux, "Calculated threshold timecode duration %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (splitmux->threshold_timecode));
+ }
+
if (splitmux->send_keyframe_requests == FALSE
|| (splitmux->threshold_time == 0 && !timecode_based)
|| splitmux->threshold_bytes != 0)
target_time = running_time + splitmux->threshold_time;
}
- if (GST_CLOCK_TIME_IS_VALID (splitmux->last_fku_time) &&
- splitmux->last_fku_time > target_time) {
+ if (GST_CLOCK_TIME_IS_VALID (splitmux->next_fku_time) &&
+ target_time < splitmux->next_fku_time) {
GST_DEBUG_OBJECT (splitmux, "Target time %" GST_TIME_FORMAT
- " is smaller than last requested keyframe time %" GST_TIME_FORMAT,
- GST_TIME_ARGS (target_time), GST_TIME_ARGS (splitmux->last_fku_time));
+ " is smaller than expected next keyframe time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (target_time), GST_TIME_ARGS (splitmux->next_fku_time));
return TRUE;
}
- splitmux->last_fku_time = target_time;
- if (timecode_based && !GST_CLOCK_TIME_IS_VALID (splitmux->threshold_timecode)) {
- splitmux->threshold_timecode = next_max_tc_time - running_time;
- GST_DEBUG_OBJECT (splitmux, "Calculated threshold timecode duration %"
- GST_TIME_FORMAT, GST_TIME_ARGS (splitmux->threshold_timecode));
+ if (timecode_based) {
+ next_fku_time = target_time + splitmux->threshold_timecode;
+ } else {
+ next_fku_time = target_time + splitmux->threshold_time;
}
+ splitmux->last_fku_time = target_time;
+ splitmux->next_fku_time = next_fku_time;
+
ev = gst_video_event_new_upstream_force_key_unit (target_time, TRUE, 0);
- GST_INFO_OBJECT (splitmux, "Requesting next keyframe at %" GST_TIME_FORMAT,
- GST_TIME_ARGS (target_time));
+ GST_INFO_OBJECT (splitmux, "Requesting keyframe at %" GST_TIME_FORMAT
+ ", the next expected keyframe is %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (target_time), GST_TIME_ARGS (next_fku_time));
return gst_pad_push_event (splitmux->reference_ctx->sinkpad, ev);
}
g_atomic_int_set (&(splitmux->do_split_next_gop), FALSE);
splitmux->last_fku_time = GST_CLOCK_TIME_NONE;
+ splitmux->next_fku_time = GST_CLOCK_TIME_NONE;
gst_queue_array_clear (splitmux->times_to_split);
g_list_foreach (splitmux->contexts, (GFunc) mq_stream_ctx_reset, NULL);
#include <gst/check/gstcheck.h>
#include <gst/app/app.h>
+#include <gst/video/video.h>
#include <stdlib.h>
gchar *tmpdir = NULL;
GST_END_TEST;
+static GstPadProbeReturn
+count_upstrea_fku (GstPad * pad, GstPadProbeInfo * info,
+ guint * upstream_fku_count)
+{
+ GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_CUSTOM_UPSTREAM:
+ if (gst_video_event_is_force_key_unit (event))
+ *upstream_fku_count += 1;
+ break;
+ default:
+ break;
+ }
+
+ return GST_PAD_PROBE_OK;
+}
+
static void
splitmuxsink_split_by_keyframe (gboolean send_keyframe_request,
guint max_size_time_sec, guint encoder_key_interval_sec)
GstMessage *msg;
GstElement *pipeline;
GstElement *sink;
+ GstElement *enc;
+ GstPad *srcpad;
gchar *pipeline_str;
gchar *dest_pattern;
guint count;
guint expected_count;
gchar *in_pattern;
+ guint upstream_fku_count = 0;
+ guint expected_fku_count;
pipeline_str = g_strdup_printf ("splitmuxsink name=splitsink "
"max-size-time=%" G_GUINT64_FORMAT
" send-keyframe-requests=%s muxer=qtmux "
"videotestsrc num-buffers=30 ! video/x-raw,width=80,height=64,framerate=5/1 "
- "! videoconvert ! queue ! vp8enc keyframe-max-dist=%d ! splitsink.video ",
+ "! videoconvert ! queue ! vp8enc name=enc keyframe-max-dist=%d ! splitsink.video ",
max_size_time_sec * GST_SECOND, send_keyframe_request ? "true" : "false",
encoder_key_interval_sec * 5);
g_free (dest_pattern);
g_object_unref (sink);
+ enc = gst_bin_get_by_name (GST_BIN (pipeline), "enc");
+ fail_if (enc == NULL);
+ srcpad = gst_element_get_static_pad (enc, "src");
+ fail_if (srcpad == NULL);
+
+ gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
+ (GstPadProbeCallback) count_upstrea_fku, &upstream_fku_count, NULL);
+ gst_object_unref (srcpad);
+ gst_object_unref (enc);
+
msg = run_pipeline (pipeline);
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
fail_unless (count == expected_count,
"Expected %d output files, got %d", expected_count, count);
+ if (!send_keyframe_request) {
+ expected_fku_count = 0;
+ } else {
+ expected_fku_count = count;
+ }
+
+ GST_INFO ("Upstream force keyunit event count %d", upstream_fku_count);
+
+ fail_unless (upstream_fku_count == expected_fku_count,
+ "Expected upstream force keyunit event count %d, got %d",
+ expected_fku_count, upstream_fku_count);
+
in_pattern = g_build_filename (tmpdir, "out*.m4v", NULL);
/* FIXME: Reverse playback works poorly with multiple video streams
* in qtdemux (at least, maybe other demuxers) at the time this was
GstMessage *msg;
GstElement *pipeline;
GstElement *sink;
+ GstElement *enc;
+ GstPad *srcpad;
gchar *pipeline_str;
gchar *dest_pattern;
guint count;
guint expected_count;
gchar *in_pattern;
+ guint upstream_fku_count = 0;
+ guint expected_fku_count;
pipeline_str = g_strdup_printf ("splitmuxsink name=splitsink "
"max-size-timecode=%s"
" send-keyframe-requests=%s muxer=qtmux "
"videotestsrc num-buffers=30 ! video/x-raw,width=80,height=64,framerate=5/1 "
- "! videoconvert ! timecodestamper ! queue ! vp8enc keyframe-max-dist=%d ! splitsink.video ",
+ "! videoconvert ! timecodestamper ! queue ! vp8enc name=enc keyframe-max-dist=%d ! splitsink.video ",
maxsize_timecode_string, send_keyframe_request ? "true" : "false",
encoder_key_interval_sec ? encoder_key_interval_sec * 5 : 1);
g_free (dest_pattern);
g_object_unref (sink);
+ enc = gst_bin_get_by_name (GST_BIN (pipeline), "enc");
+ fail_if (enc == NULL);
+ srcpad = gst_element_get_static_pad (enc, "src");
+ fail_if (srcpad == NULL);
+
+ gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_UPSTREAM,
+ (GstPadProbeCallback) count_upstrea_fku, &upstream_fku_count, NULL);
+ gst_object_unref (srcpad);
+ gst_object_unref (enc);
+
msg = run_pipeline (pipeline);
if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
fail_unless (count == expected_count,
"Expected %d output files, got %d", expected_count, count);
+ if (!send_keyframe_request) {
+ expected_fku_count = 0;
+ } else {
+ expected_fku_count = count;
+ }
+
+ GST_INFO ("Upstream force keyunit event count %d", upstream_fku_count);
+
+ fail_unless (upstream_fku_count == expected_fku_count,
+ "Expected upstream force keyunit event count %d, got %d",
+ expected_fku_count, upstream_fku_count);
+
in_pattern = g_build_filename (tmpdir, "out*.m4v", NULL);
/* FIXME: Reverse playback works poorly with multiple video streams
* in qtdemux (at least, maybe other demuxers) at the time this was
g_free (in_pattern);
}
+GST_START_TEST (test_splitmuxsink_without_keyframe_request_timecode)
+{
+ /* This encoding option is intended to produce keyframe per 1 second
+ * but splitmuxsink will split file per 2 second without keyframe request */
+ splitmuxsink_split_by_keyframe_timecode (FALSE, "00:00:02:00", 2, 1);
+}
+
+GST_END_TEST;
+
GST_START_TEST (test_splitmuxsink_keyframe_request_timecode)
{
/* This encoding option is intended to produce keyframe per 1 second
}
if (have_qtmux && have_vp8 && have_timecodestamper) {
+ tcase_add_test (tc_chain,
+ test_splitmuxsink_without_keyframe_request_timecode);
tcase_add_test (tc_chain, test_splitmuxsink_keyframe_request_timecode);
tcase_add_test (tc_chain,
test_splitmuxsink_keyframe_request_timecode_trailing_small_segment);