diff options
-rw-r--r-- | gst/multifile/gstsplitmuxsink.c | 26 | ||||
-rw-r--r-- | gst/multifile/gstsplitmuxsink.h | 2 | ||||
-rw-r--r-- | tests/check/elements/splitmux.c | 107 |
3 files changed, 104 insertions, 31 deletions
diff --git a/gst/multifile/gstsplitmuxsink.c b/gst/multifile/gstsplitmuxsink.c index ab9fa00eb..1db6a45f9 100644 --- a/gst/multifile/gstsplitmuxsink.c +++ b/gst/multifile/gstsplitmuxsink.c @@ -1,5 +1,5 @@ /* GStreamer Muxer bin that splits output stream by size/time - * Copyright (C) <2014> Jan Schmidt <jan@centricular.com> + * Copyright (C) <2014-2019> Jan Schmidt <jan@centricular.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -146,6 +146,11 @@ GST_STATIC_PAD_TEMPLATE ("video", GST_PAD_SINK, GST_PAD_REQUEST, GST_STATIC_CAPS_ANY); +static GstStaticPadTemplate video_aux_sink_template = +GST_STATIC_PAD_TEMPLATE ("video_aux_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS_ANY); static GstStaticPadTemplate audio_sink_template = GST_STATIC_PAD_TEMPLATE ("audio_%u", GST_PAD_SINK, @@ -265,6 +270,8 @@ gst_splitmux_sink_class_init (GstSplitMuxSinkClass * klass) gst_element_class_add_static_pad_template (gstelement_class, &video_sink_template); gst_element_class_add_static_pad_template (gstelement_class, + &video_aux_sink_template); + gst_element_class_add_static_pad_template (gstelement_class, &audio_sink_template); gst_element_class_add_static_pad_template (gstelement_class, &subtitle_sink_template); @@ -2580,7 +2587,7 @@ gst_splitmux_sink_request_new_pad (GstElement * element, GstElement *q; GstPad *q_sink = NULL, *q_src = NULL; gchar *gname, *qname; - gboolean is_video = FALSE; + gboolean is_primary_video = FALSE; MqStreamCtx *ctx; GST_DEBUG_OBJECT (element, "templ:%s, name:%s", templ->name_template, name); @@ -2591,8 +2598,10 @@ gst_splitmux_sink_request_new_pad (GstElement * element, g_signal_emit (splitmux, signals[SIGNAL_MUXER_ADDED], 0, splitmux->muxer); if (templ->name_template) { - if (g_str_equal (templ->name_template, "video")) { - if (splitmux->have_video) + if (g_str_equal (templ->name_template, "video") || + g_str_has_prefix (templ->name_template, "video_aux_")) { + is_primary_video = g_str_equal (templ->name_template, "video"); + if (is_primary_video && splitmux->have_video) goto already_have_video; /* FIXME: Look for a pad template with matching caps, rather than by name */ @@ -2610,7 +2619,6 @@ gst_splitmux_sink_request_new_pad (GstElement * element, gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (splitmux->muxer), "video"); } - is_video = TRUE; name = NULL; } else { GST_DEBUG_OBJECT (element, "searching for pad-template with name '%s'", @@ -2620,7 +2628,7 @@ gst_splitmux_sink_request_new_pad (GstElement * element, (splitmux->muxer), templ->name_template); /* Fallback to find sink pad templates named 'audio' (flvmux) */ - if (!mux_template) { + if (!mux_template && g_str_has_prefix (templ->name_template, "audio_")) { GST_DEBUG_OBJECT (element, "searching for pad-template with name 'audio'"); mux_template = @@ -2677,7 +2685,7 @@ gst_splitmux_sink_request_new_pad (GstElement * element, goto fail; } - if (is_video) + if (is_primary_video) gname = g_strdup ("video"); else if (name == NULL) gname = gst_pad_get_name (res); @@ -2720,7 +2728,7 @@ gst_splitmux_sink_request_new_pad (GstElement * element, gst_pad_add_probe (q_src, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_FLUSH, (GstPadProbeCallback) handle_mq_output, ctx, NULL); - if (is_video && splitmux->reference_ctx != NULL) { + if (is_primary_video && splitmux->reference_ctx != NULL) { splitmux->reference_ctx->is_reference = FALSE; splitmux->reference_ctx = NULL; } @@ -2745,7 +2753,7 @@ gst_splitmux_sink_request_new_pad (GstElement * element, g_free (gname); - if (is_video) + if (is_primary_video) splitmux->have_video = TRUE; gst_pad_set_active (res, TRUE); diff --git a/gst/multifile/gstsplitmuxsink.h b/gst/multifile/gstsplitmuxsink.h index a5a148e94..8e78bf1a5 100644 --- a/gst/multifile/gstsplitmuxsink.h +++ b/gst/multifile/gstsplitmuxsink.h @@ -1,5 +1,5 @@ /* GStreamer split muxer bin - * Copyright (C) 2014 Jan Schmidt <jan@centricular.com> + * Copyright (C) 2014-2019 Jan Schmidt <jan@centricular.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/tests/check/elements/splitmux.c b/tests/check/elements/splitmux.c index 25925e97f..9dfd14e26 100644 --- a/tests/check/elements/splitmux.c +++ b/tests/check/elements/splitmux.c @@ -188,7 +188,7 @@ receive_handoff (GstElement * object G_GNUC_UNUSED, GstBuffer * buf, static void test_playback (const gchar * in_pattern, GstClockTime exp_first_time, - GstClockTime exp_last_time) + GstClockTime exp_last_time, gboolean test_reverse) { GstMessage *msg; GstElement *pipeline; @@ -229,20 +229,22 @@ test_playback (const gchar * in_pattern, GstClockTime exp_first_time, "Expected end of playback range 3s, got %" GST_TIME_FORMAT, GST_TIME_ARGS (last_ts)); - /* Test backwards */ - seek_pipeline (pipeline, -1.0, 0, -1); - msg = run_pipeline (pipeline); - fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS); - gst_message_unref (msg); - /* Check we saw the entire range of values */ - fail_unless (first_ts == exp_first_time, - "Expected start of playback range %" GST_TIME_FORMAT - ", got %" GST_TIME_FORMAT, GST_TIME_ARGS (exp_first_time), - GST_TIME_ARGS (first_ts)); - fail_unless (last_ts == exp_last_time, - "Expected end of playback range %" GST_TIME_FORMAT - ", got %" GST_TIME_FORMAT, GST_TIME_ARGS (exp_last_time), - GST_TIME_ARGS (last_ts)); + if (test_reverse) { + /* Test backwards */ + seek_pipeline (pipeline, -1.0, 0, -1); + msg = run_pipeline (pipeline); + fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS); + gst_message_unref (msg); + /* Check we saw the entire range of values */ + fail_unless (first_ts == exp_first_time, + "Expected start of playback range %" GST_TIME_FORMAT + ", got %" GST_TIME_FORMAT, GST_TIME_ARGS (exp_first_time), + GST_TIME_ARGS (first_ts)); + fail_unless (last_ts == exp_last_time, + "Expected end of playback range %" GST_TIME_FORMAT + ", got %" GST_TIME_FORMAT, GST_TIME_ARGS (exp_last_time), + GST_TIME_ARGS (last_ts)); + } gst_object_unref (pipeline); } @@ -251,7 +253,7 @@ GST_START_TEST (test_splitmuxsrc) { gchar *in_pattern = g_build_filename (GST_TEST_FILES_PATH, "splitvideo*.ogg", NULL); - test_playback (in_pattern, 0, 3 * GST_SECOND); + test_playback (in_pattern, 0, 3 * GST_SECOND, TRUE); g_free (in_pattern); } @@ -367,7 +369,62 @@ GST_START_TEST (test_splitmuxsink) fail_unless (count == 3, "Expected 3 output files, got %d", count); in_pattern = g_build_filename (tmpdir, "out*.ogg", NULL); - test_playback (in_pattern, 0, 3 * GST_SECOND); + test_playback (in_pattern, 0, 3 * GST_SECOND, TRUE); + g_free (in_pattern); +} + +GST_END_TEST; + +GST_START_TEST (test_splitmuxsink_multivid) +{ + GstMessage *msg; + GstElement *pipeline; + GstElement *sink; + gchar *dest_pattern; + guint count; + gchar *in_pattern; + + /* This pipeline should start a new file every GOP, ie 1 second, + * driven by the primary video stream and with 2 auxilliary video streams */ + pipeline = + gst_parse_launch + ("splitmuxsink name=splitsink " + " max-size-time=1000000 max-size-bytes=1000000 muxer=qtmux " + "videotestsrc num-buffers=15 ! video/x-raw,width=80,height=64,framerate=5/1 ! videoconvert !" + " queue ! vp8enc keyframe-max-dist=5 ! splitsink.video " + "videotestsrc num-buffers=15 pattern=snow ! video/x-raw,width=80,height=64,framerate=5/1 ! videoconvert !" + " queue ! vp8enc keyframe-max-dist=6 ! splitsink.video_aux_0 " + "videotestsrc num-buffers=15 pattern=ball ! video/x-raw,width=80,height=64,framerate=5/1 ! videoconvert !" + " queue ! vp8enc keyframe-max-dist=8 ! splitsink.video_aux_1 ", NULL); + fail_if (pipeline == NULL); + sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink"); + fail_if (sink == NULL); + g_signal_connect (sink, "format-location-full", + (GCallback) check_format_location, NULL); + dest_pattern = g_build_filename (tmpdir, "out%05d.m4v", NULL); + g_object_set (G_OBJECT (sink), "location", dest_pattern, NULL); + g_free (dest_pattern); + g_object_unref (sink); + + msg = run_pipeline (pipeline); + + if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) + dump_error (msg); + fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS); + gst_message_unref (msg); + + gst_object_unref (pipeline); + + count = count_files (tmpdir); + fail_unless (count == 3, "Expected 3 output files, got %d", 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 + * written, and causes test failures like buffers being output + * multiple times by qtdemux as it loops through GOPs. Disable that + * for now */ + test_playback (in_pattern, 0, 3 * GST_SECOND, FALSE); g_free (in_pattern); } @@ -431,7 +488,7 @@ GST_START_TEST (test_splitmuxsink_async) fail_unless (count == 3, "Expected 3 output files, got %d", count); in_pattern = g_build_filename (tmpdir, "matroska*.mkv", NULL); - test_playback (in_pattern, 0, 3 * GST_SECOND); + test_playback (in_pattern, 0, 3 * GST_SECOND, TRUE); g_free (in_pattern); } @@ -713,7 +770,7 @@ GST_START_TEST (test_splitmuxsrc_caps_change) fail_unless (count == 2, "Expected 2 output files, got %d", count); in_pattern = g_build_filename (tmpdir, "out*.mp4", NULL); - test_playback (in_pattern, 0, GST_SECOND); + test_playback (in_pattern, 0, GST_SECOND, TRUE); g_free (in_pattern); } @@ -760,7 +817,7 @@ GST_START_TEST (test_splitmuxsrc_robust_mux) * reserved duration property. All we care about is that the muxing didn't fail because space ran out */ in_pattern = g_build_filename (tmpdir, "out*.mp4", NULL); - test_playback (in_pattern, 0, GST_SECOND); + test_playback (in_pattern, 0, GST_SECOND, TRUE); g_free (in_pattern); } @@ -802,7 +859,7 @@ splitmux_suite (void) TCase *tc_chain_complex = tcase_create ("complex"); TCase *tc_chain_mp4_jpeg = tcase_create ("caps_change"); gboolean have_theora, have_ogg, have_vorbis, have_matroska, have_qtmux, - have_jpeg; + have_jpeg, have_vp8; /* we assume that if encoder/muxer are there, decoder/demuxer will be a well */ have_theora = gst_registry_check_feature_version (gst_registry_get (), @@ -817,6 +874,8 @@ splitmux_suite (void) "qtmux", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0); have_jpeg = gst_registry_check_feature_version (gst_registry_get (), "jpegenc", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0); + have_vp8 = gst_registry_check_feature_version (gst_registry_get (), + "vp8enc", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0); suite_add_tcase (s, tc_chain); suite_add_tcase (s, tc_chain_basic); @@ -854,6 +913,12 @@ splitmux_suite (void) } else { GST_INFO ("Skipping tests, missing plugins: jpegenc or mp4mux"); } + + if (have_qtmux && have_vp8) { + tcase_add_test (tc_chain, test_splitmuxsink_multivid); + } else { + GST_INFO ("Skipping tests, missing plugins: vp8enc or mp4mux"); + } return s; } |