From b233df3537a67c551d36c69796b05e8d27cf981e Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Thu, 14 Apr 2022 01:19:51 +0900 Subject: [PATCH] splitmuxsink: Don't crash on EOS without buffer Fix a case where upstream pushed EOS without buffers. Part-of: --- .../gst/multifile/gstsplitmuxsink.c | 33 +++++++++++- .../tests/check/elements/splitmuxsink.c | 50 +++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/subprojects/gst-plugins-good/gst/multifile/gstsplitmuxsink.c b/subprojects/gst-plugins-good/gst/multifile/gstsplitmuxsink.c index 8fa6f1b7f2..948ca35ba7 100644 --- a/subprojects/gst-plugins-good/gst/multifile/gstsplitmuxsink.c +++ b/subprojects/gst-plugins-good/gst/multifile/gstsplitmuxsink.c @@ -2805,14 +2805,34 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx) splitmux->input_state = SPLITMUX_INPUT_STATE_WAITING_GOP_COLLECT; /* Wake up other input pads to collect this GOP */ GST_SPLITMUX_BROADCAST_INPUT (splitmux); - check_completed_gop (splitmux, ctx); + if (g_queue_is_empty (&splitmux->pending_input_gops)) { + GST_WARNING_OBJECT (splitmux, + "EOS with no buffers received on the reference pad"); + + /* - child muxer and sink might be still locked state + * (see gst_splitmux_reset_elements()) so should be unlocked + * for state change of splitmuxsink to be applied to child + * - would need to post async done message + * - location on sink element is still null then it will post + * error message on bus (muxer will produce something, header + * data for example) + * + * Calls start_next_fragment() here, the method will address + * everything the above mentioned one */ + ret = start_next_fragment (splitmux, ctx); + if (ret != GST_FLOW_OK) + goto beach; + } else { + check_completed_gop (splitmux, ctx); + } } else if (splitmux->input_state == SPLITMUX_INPUT_STATE_WAITING_GOP_COLLECT) { /* If we are waiting for a GOP to be completed (ie, for aux * pads to catch up), then this pad is complete, so check * if the whole GOP is. */ - check_completed_gop (splitmux, ctx); + if (!g_queue_is_empty (&splitmux->pending_input_gops)) + check_completed_gop (splitmux, ctx); } GST_SPLITMUX_UNLOCK (splitmux); break; @@ -3178,6 +3198,15 @@ handle_mq_input (GstPad * pad, GstPadProbeInfo * info, MqStreamCtx * ctx) GST_LOG_OBJECT (pad, "Collected last packet of GOP. Checking other pads"); + + if (g_queue_is_empty (&splitmux->pending_input_gops)) { + GST_WARNING_OBJECT (pad, + "Reference was closed without GOP, dropping"); + GST_SPLITMUX_UNLOCK (splitmux); + GST_PAD_PROBE_INFO_FLOW_RETURN (info) = GST_FLOW_EOS; + return GST_PAD_PROBE_DROP; + } + check_completed_gop (splitmux, ctx); break; } diff --git a/subprojects/gst-plugins-good/tests/check/elements/splitmuxsink.c b/subprojects/gst-plugins-good/tests/check/elements/splitmuxsink.c index 93c86e7486..dba0c0aeed 100644 --- a/subprojects/gst-plugins-good/tests/check/elements/splitmuxsink.c +++ b/subprojects/gst-plugins-good/tests/check/elements/splitmuxsink.c @@ -663,6 +663,55 @@ GST_START_TEST (test_splitmuxsink_muxer_pad_map) GST_END_TEST; +static void +run_eos_pipeline (guint num_video_buf, guint num_audio_buf, + gboolean configure_audio) +{ + GstMessage *msg; + GstElement *pipeline; + gchar *dest_pattern; + gchar *pipeline_str; + gchar *audio_branch = NULL; + + dest_pattern = g_build_filename (tmpdir, "out%05d.mp4", NULL); + + if (configure_audio) { + audio_branch = g_strdup_printf ("audiotestsrc num-buffers=%d ! " + "splitsink.audio_0", num_audio_buf); + } + + pipeline_str = g_strdup_printf ("splitmuxsink name=splitsink location=%s " + "muxer-factory=qtmux videotestsrc num-buffers=%d ! jpegenc ! splitsink. " + "%s", dest_pattern, num_video_buf, audio_branch ? audio_branch : ""); + pipeline = gst_parse_launch (pipeline_str, NULL); + g_free (dest_pattern); + g_free (audio_branch); + g_free (pipeline_str); + + fail_if (pipeline == NULL); + + 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); +} + +GST_START_TEST (test_splitmuxsink_eos_without_buffer) +{ + /* below pipelines will create non-playable files but at least we should not + * crash */ + run_eos_pipeline (0, 0, FALSE); + run_eos_pipeline (0, 0, TRUE); + run_eos_pipeline (1, 0, TRUE); + run_eos_pipeline (0, 1, TRUE); +} + +GST_END_TEST; + static GstPadProbeReturn count_upstrea_fku (GstPad * pad, GstPadProbeInfo * info, guint * upstream_fku_count) @@ -863,6 +912,7 @@ splitmuxsink_suite (void) tcase_add_checked_fixture (tc_chain_mp4_jpeg, tempdir_setup, tempdir_cleanup); tcase_add_test (tc_chain_mp4_jpeg, test_splitmuxsink_muxer_pad_map); + tcase_add_test (tc_chain_mp4_jpeg, test_splitmuxsink_eos_without_buffer); } else { GST_INFO ("Skipping tests, missing plugins: jpegenc or mp4mux"); }