mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-25 16:48:11 +00:00
splitmux: Avoid negative DTS
In order to concatenate fragments, splitmuxsrc offsets the start of each fragment PTS to 0 to align it with the previous file. This means that DTS can go negative for the first fragment, with really bad results. Add a fixed offset to outgoing timestamp ranges to avoid that.
This commit is contained in:
parent
54f68ff36b
commit
f490c38416
4 changed files with 72 additions and 32 deletions
|
@ -149,6 +149,9 @@ handle_buffer_measuring (GstSplitMuxPartReader * reader,
|
||||||
/* Adjust buffer timestamps */
|
/* Adjust buffer timestamps */
|
||||||
offset = reader->start_offset + part_pad->segment.base;
|
offset = reader->start_offset + part_pad->segment.base;
|
||||||
offset -= part_pad->initial_ts_offset;
|
offset -= part_pad->initial_ts_offset;
|
||||||
|
/* We don't add the ts_offset here, because we
|
||||||
|
* want to measure the logical length of the stream,
|
||||||
|
* not to generate output timestamps */
|
||||||
|
|
||||||
/* Update the stored max duration on the pad,
|
/* Update the stored max duration on the pad,
|
||||||
* always preferring making DTS contiguous
|
* always preferring making DTS contiguous
|
||||||
|
@ -159,8 +162,8 @@ handle_buffer_measuring (GstSplitMuxPartReader * reader,
|
||||||
ts = GST_BUFFER_PTS (buf) + offset;
|
ts = GST_BUFFER_PTS (buf) + offset;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (reader, "Pad %" GST_PTR_FORMAT
|
GST_DEBUG_OBJECT (reader, "Pad %" GST_PTR_FORMAT
|
||||||
" incoming PTS %" GST_TIME_FORMAT
|
" incoming DTS %" GST_TIME_FORMAT
|
||||||
" DTS %" GST_TIME_FORMAT " offset by %" GST_STIME_FORMAT
|
" PTS %" GST_TIME_FORMAT " offset by %" GST_STIME_FORMAT
|
||||||
" to %" GST_STIME_FORMAT, part_pad,
|
" to %" GST_STIME_FORMAT, part_pad,
|
||||||
GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
|
GST_TIME_ARGS (GST_BUFFER_DTS (buf)),
|
||||||
GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
|
GST_TIME_ARGS (GST_BUFFER_PTS (buf)),
|
||||||
|
@ -228,6 +231,7 @@ splitmux_part_pad_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
|
||||||
/* Adjust buffer timestamps */
|
/* Adjust buffer timestamps */
|
||||||
offset = reader->start_offset + part_pad->segment.base;
|
offset = reader->start_offset + part_pad->segment.base;
|
||||||
offset -= part_pad->initial_ts_offset;
|
offset -= part_pad->initial_ts_offset;
|
||||||
|
offset += reader->ts_offset;
|
||||||
|
|
||||||
if (GST_BUFFER_PTS_IS_VALID (buf))
|
if (GST_BUFFER_PTS_IS_VALID (buf))
|
||||||
GST_BUFFER_PTS (buf) += offset;
|
GST_BUFFER_PTS (buf) += offset;
|
||||||
|
@ -356,21 +360,22 @@ splitmux_part_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
||||||
goto wrong_segment;
|
goto wrong_segment;
|
||||||
|
|
||||||
/* Adjust segment */
|
/* Adjust segment */
|
||||||
/* Adjust start/stop so the overall file is 0 + start_offset based */
|
/* Adjust start/stop so the overall file is 0 + start_offset based,
|
||||||
|
* adding a fixed offset so that DTS is never negative */
|
||||||
if (seg->stop != -1) {
|
if (seg->stop != -1) {
|
||||||
seg->stop -= seg->start;
|
seg->stop -= seg->start;
|
||||||
seg->stop += seg->time + reader->start_offset;
|
seg->stop += seg->time + reader->start_offset + reader->ts_offset;
|
||||||
}
|
}
|
||||||
seg->start = seg->time + reader->start_offset;
|
seg->start = seg->time + reader->start_offset + reader->ts_offset;
|
||||||
seg->time += reader->start_offset;
|
seg->time += reader->start_offset;
|
||||||
seg->position += reader->start_offset;
|
seg->position += reader->start_offset;
|
||||||
|
|
||||||
GST_LOG_OBJECT (pad, "Adjusted segment now %" GST_PTR_FORMAT, event);
|
|
||||||
|
|
||||||
/* Replace event */
|
/* Replace event */
|
||||||
gst_event_unref (event);
|
gst_event_unref (event);
|
||||||
event = gst_event_new_segment (seg);
|
event = gst_event_new_segment (seg);
|
||||||
|
|
||||||
|
GST_LOG_OBJECT (pad, "Adjusted segment now %" GST_PTR_FORMAT, event);
|
||||||
|
|
||||||
if (reader->prep_state != PART_STATE_PREPARING_COLLECT_STREAMS
|
if (reader->prep_state != PART_STATE_PREPARING_COLLECT_STREAMS
|
||||||
&& reader->prep_state != PART_STATE_PREPARING_MEASURE_STREAMS)
|
&& reader->prep_state != PART_STATE_PREPARING_MEASURE_STREAMS)
|
||||||
break; /* Only do further stuff with segments during initial measuring */
|
break; /* Only do further stuff with segments during initial measuring */
|
||||||
|
@ -1246,12 +1251,13 @@ gst_splitmux_part_reader_get_end_offset (GstSplitMuxPartReader * reader)
|
||||||
|
|
||||||
void
|
void
|
||||||
gst_splitmux_part_reader_set_start_offset (GstSplitMuxPartReader * reader,
|
gst_splitmux_part_reader_set_start_offset (GstSplitMuxPartReader * reader,
|
||||||
GstClockTime offset)
|
GstClockTime time_offset, GstClockTime ts_offset)
|
||||||
{
|
{
|
||||||
SPLITMUX_PART_LOCK (reader);
|
SPLITMUX_PART_LOCK (reader);
|
||||||
reader->start_offset = offset;
|
reader->start_offset = time_offset;
|
||||||
GST_INFO_OBJECT (reader, "TS offset now %" GST_TIME_FORMAT,
|
reader->ts_offset = ts_offset;
|
||||||
GST_TIME_ARGS (offset));
|
GST_INFO_OBJECT (reader, "Time offset now %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (time_offset));
|
||||||
SPLITMUX_PART_UNLOCK (reader);
|
SPLITMUX_PART_UNLOCK (reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ struct _GstSplitMuxPartReader
|
||||||
|
|
||||||
GstClockTime duration;
|
GstClockTime duration;
|
||||||
GstClockTime start_offset;
|
GstClockTime start_offset;
|
||||||
|
GstClockTime ts_offset;
|
||||||
|
|
||||||
GList *pads;
|
GList *pads;
|
||||||
|
|
||||||
|
@ -107,7 +108,7 @@ void gst_splitmux_part_reader_deactivate (GstSplitMuxPartReader *part);
|
||||||
gboolean gst_splitmux_part_reader_is_active (GstSplitMuxPartReader *part);
|
gboolean gst_splitmux_part_reader_is_active (GstSplitMuxPartReader *part);
|
||||||
|
|
||||||
gboolean gst_splitmux_part_reader_src_query (GstSplitMuxPartReader *part, GstPad *src_pad, GstQuery * query);
|
gboolean gst_splitmux_part_reader_src_query (GstSplitMuxPartReader *part, GstPad *src_pad, GstQuery * query);
|
||||||
void gst_splitmux_part_reader_set_start_offset (GstSplitMuxPartReader *part, GstClockTime offset);
|
void gst_splitmux_part_reader_set_start_offset (GstSplitMuxPartReader *part, GstClockTime time_offset, GstClockTime ts_offset);
|
||||||
GstClockTime gst_splitmux_part_reader_get_start_offset (GstSplitMuxPartReader *part);
|
GstClockTime gst_splitmux_part_reader_get_start_offset (GstSplitMuxPartReader *part);
|
||||||
GstClockTime gst_splitmux_part_reader_get_end_offset (GstSplitMuxPartReader *part);
|
GstClockTime gst_splitmux_part_reader_get_end_offset (GstSplitMuxPartReader *part);
|
||||||
GstClockTime gst_splitmux_part_reader_get_duration (GstSplitMuxPartReader * reader);
|
GstClockTime gst_splitmux_part_reader_get_duration (GstSplitMuxPartReader * reader);
|
||||||
|
|
|
@ -55,6 +55,8 @@
|
||||||
GST_DEBUG_CATEGORY (splitmux_debug);
|
GST_DEBUG_CATEGORY (splitmux_debug);
|
||||||
#define GST_CAT_DEFAULT splitmux_debug
|
#define GST_CAT_DEFAULT splitmux_debug
|
||||||
|
|
||||||
|
#define FIXED_TS_OFFSET (1000*GST_SECOND)
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
|
@ -642,14 +644,14 @@ gst_splitmux_handle_event (GstSplitMuxSrc * splitmux,
|
||||||
* seg or play_segment */
|
* seg or play_segment */
|
||||||
if (splitmux->play_segment.rate > 0.0) {
|
if (splitmux->play_segment.rate > 0.0) {
|
||||||
if (splitmux->play_segment.stop != -1)
|
if (splitmux->play_segment.stop != -1)
|
||||||
seg.stop = splitmux->play_segment.stop;
|
seg.stop = splitmux->play_segment.stop + FIXED_TS_OFFSET;
|
||||||
else
|
else
|
||||||
seg.stop = splitpad->segment.stop;
|
seg.stop = splitpad->segment.stop;
|
||||||
} else {
|
} else {
|
||||||
/* Reverse playback from stop time to start time */
|
/* Reverse playback from stop time to start time */
|
||||||
/* See if an end point was requested in the seek */
|
/* See if an end point was requested in the seek */
|
||||||
if (splitmux->play_segment.start != -1) {
|
if (splitmux->play_segment.start != -1) {
|
||||||
seg.start = splitmux->play_segment.start;
|
seg.start = splitmux->play_segment.start + FIXED_TS_OFFSET;
|
||||||
seg.time = splitmux->play_segment.time;
|
seg.time = splitmux->play_segment.time;
|
||||||
} else {
|
} else {
|
||||||
seg.start = splitpad->segment.start;
|
seg.start = splitpad->segment.start;
|
||||||
|
@ -858,7 +860,7 @@ gst_splitmux_src_prepare_next_part (GstSplitMuxSrc * splitmux)
|
||||||
splitmux->parts[idx]->path, idx);
|
splitmux->parts[idx]->path, idx);
|
||||||
|
|
||||||
gst_splitmux_part_reader_set_start_offset (splitmux->parts[idx],
|
gst_splitmux_part_reader_set_start_offset (splitmux->parts[idx],
|
||||||
splitmux->end_offset);
|
splitmux->end_offset, FIXED_TS_OFFSET);
|
||||||
if (!gst_splitmux_part_reader_prepare (splitmux->parts[idx])) {
|
if (!gst_splitmux_part_reader_prepare (splitmux->parts[idx])) {
|
||||||
GST_WARNING_OBJECT (splitmux,
|
GST_WARNING_OBJECT (splitmux,
|
||||||
"Failed to prepare file part %s. Cannot play past there.",
|
"Failed to prepare file part %s. Cannot play past there.",
|
||||||
|
|
|
@ -146,17 +146,40 @@ seek_pipeline (GstElement * pipeline, gdouble rate, GstClockTime start,
|
||||||
current_rate = rate;
|
current_rate = rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static GstFlowReturn
|
||||||
receive_handoff (GstElement * object G_GNUC_UNUSED, GstBuffer * buf,
|
receive_sample (GstAppSink * appsink, gpointer user_data G_GNUC_UNUSED)
|
||||||
GstPad * arg1 G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
|
|
||||||
{
|
{
|
||||||
GstClockTime start = GST_BUFFER_TIMESTAMP (buf);
|
GstSample *sample;
|
||||||
GstClockTime end = start;
|
GstSegment *seg;
|
||||||
|
GstBuffer *buf;
|
||||||
|
GstClockTime start;
|
||||||
|
GstClockTime end;
|
||||||
|
|
||||||
if (GST_BUFFER_DURATION_IS_VALID (buf))
|
g_signal_emit_by_name (appsink, "pull-sample", &sample);
|
||||||
end += GST_BUFFER_DURATION (buf);
|
fail_unless (sample != NULL);
|
||||||
|
|
||||||
GST_LOG ("Got buffer %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
|
seg = gst_sample_get_segment (sample);
|
||||||
|
fail_unless (seg != NULL);
|
||||||
|
|
||||||
|
buf = gst_sample_get_buffer (sample);
|
||||||
|
fail_unless (buf != NULL);
|
||||||
|
|
||||||
|
GST_LOG ("Got buffer %" GST_PTR_FORMAT, buf);
|
||||||
|
|
||||||
|
start = GST_BUFFER_PTS (buf);
|
||||||
|
end = start;
|
||||||
|
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (start))
|
||||||
|
start = gst_segment_to_stream_time (seg, GST_FORMAT_TIME, start);
|
||||||
|
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (end)) {
|
||||||
|
if (GST_BUFFER_DURATION_IS_VALID (buf))
|
||||||
|
end += GST_BUFFER_DURATION (buf);
|
||||||
|
|
||||||
|
end = gst_segment_to_stream_time (seg, GST_FORMAT_TIME, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG ("Got buffer stream time %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
|
||||||
GST_TIME_ARGS (start), GST_TIME_ARGS (end));
|
GST_TIME_ARGS (start), GST_TIME_ARGS (end));
|
||||||
|
|
||||||
/* Check time is moving in the right direction */
|
/* Check time is moving in the right direction */
|
||||||
|
@ -184,6 +207,10 @@ receive_handoff (GstElement * object G_GNUC_UNUSED, GstBuffer * buf,
|
||||||
first_ts = start;
|
first_ts = start;
|
||||||
if (!GST_CLOCK_TIME_IS_VALID (last_ts) || end > last_ts)
|
if (!GST_CLOCK_TIME_IS_VALID (last_ts) || end > last_ts)
|
||||||
last_ts = end;
|
last_ts = end;
|
||||||
|
|
||||||
|
gst_sample_unref (sample);
|
||||||
|
|
||||||
|
return GST_FLOW_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -192,16 +219,19 @@ test_playback (const gchar * in_pattern, GstClockTime exp_first_time,
|
||||||
{
|
{
|
||||||
GstMessage *msg;
|
GstMessage *msg;
|
||||||
GstElement *pipeline;
|
GstElement *pipeline;
|
||||||
GstElement *fakesink;
|
GstElement *appsink;
|
||||||
GstElement *fakesink2;
|
GstElement *fakesink2;
|
||||||
|
GstAppSinkCallbacks callbacks = { NULL };
|
||||||
gchar *uri;
|
gchar *uri;
|
||||||
|
|
||||||
|
GST_DEBUG ("Playing back files matching %s", in_pattern);
|
||||||
|
|
||||||
pipeline = gst_element_factory_make ("playbin", NULL);
|
pipeline = gst_element_factory_make ("playbin", NULL);
|
||||||
fail_if (pipeline == NULL);
|
fail_if (pipeline == NULL);
|
||||||
|
|
||||||
fakesink = gst_element_factory_make ("fakesink", NULL);
|
appsink = gst_element_factory_make ("appsink", NULL);
|
||||||
fail_if (fakesink == NULL);
|
fail_if (appsink == NULL);
|
||||||
g_object_set (G_OBJECT (pipeline), "video-sink", fakesink, NULL);
|
g_object_set (G_OBJECT (pipeline), "video-sink", appsink, NULL);
|
||||||
fakesink2 = gst_element_factory_make ("fakesink", NULL);
|
fakesink2 = gst_element_factory_make ("fakesink", NULL);
|
||||||
fail_if (fakesink2 == NULL);
|
fail_if (fakesink2 == NULL);
|
||||||
g_object_set (G_OBJECT (pipeline), "audio-sink", fakesink2, NULL);
|
g_object_set (G_OBJECT (pipeline), "audio-sink", fakesink2, NULL);
|
||||||
|
@ -211,8 +241,8 @@ test_playback (const gchar * in_pattern, GstClockTime exp_first_time,
|
||||||
g_object_set (G_OBJECT (pipeline), "uri", uri, NULL);
|
g_object_set (G_OBJECT (pipeline), "uri", uri, NULL);
|
||||||
g_free (uri);
|
g_free (uri);
|
||||||
|
|
||||||
g_signal_connect (fakesink, "handoff", (GCallback) receive_handoff, NULL);
|
callbacks.new_sample = receive_sample;
|
||||||
g_object_set (G_OBJECT (fakesink), "signal-handoffs", TRUE, NULL);
|
gst_app_sink_set_callbacks (GST_APP_SINK (appsink), &callbacks, NULL, NULL);
|
||||||
|
|
||||||
/* test forwards */
|
/* test forwards */
|
||||||
seek_pipeline (pipeline, 1.0, 0, -1);
|
seek_pipeline (pipeline, 1.0, 0, -1);
|
||||||
|
@ -223,11 +253,12 @@ test_playback (const gchar * in_pattern, GstClockTime exp_first_time,
|
||||||
|
|
||||||
/* Check we saw the entire range of values */
|
/* Check we saw the entire range of values */
|
||||||
fail_unless (first_ts == exp_first_time,
|
fail_unless (first_ts == exp_first_time,
|
||||||
"Expected start of playback range 0, got %" GST_TIME_FORMAT,
|
"Expected start of playback range %" GST_TIME_FORMAT ", got %"
|
||||||
|
GST_TIME_FORMAT, GST_TIME_ARGS (exp_first_time),
|
||||||
GST_TIME_ARGS (first_ts));
|
GST_TIME_ARGS (first_ts));
|
||||||
fail_unless (last_ts == exp_last_time,
|
fail_unless (last_ts == exp_last_time,
|
||||||
"Expected end of playback range 3s, got %" GST_TIME_FORMAT,
|
"Expected end of playback range %" GST_TIME_FORMAT ", got %"
|
||||||
GST_TIME_ARGS (last_ts));
|
GST_TIME_FORMAT, GST_TIME_ARGS (exp_last_time), GST_TIME_ARGS (last_ts));
|
||||||
|
|
||||||
if (test_reverse) {
|
if (test_reverse) {
|
||||||
/* Test backwards */
|
/* Test backwards */
|
||||||
|
|
Loading…
Reference in a new issue