mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-10 17:35:59 +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 */
|
||||
offset = reader->start_offset + part_pad->segment.base;
|
||||
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,
|
||||
* always preferring making DTS contiguous
|
||||
|
@ -159,8 +162,8 @@ handle_buffer_measuring (GstSplitMuxPartReader * reader,
|
|||
ts = GST_BUFFER_PTS (buf) + offset;
|
||||
|
||||
GST_DEBUG_OBJECT (reader, "Pad %" GST_PTR_FORMAT
|
||||
" incoming PTS %" GST_TIME_FORMAT
|
||||
" DTS %" GST_TIME_FORMAT " offset by %" GST_STIME_FORMAT
|
||||
" incoming DTS %" GST_TIME_FORMAT
|
||||
" PTS %" GST_TIME_FORMAT " offset by %" GST_STIME_FORMAT
|
||||
" to %" GST_STIME_FORMAT, part_pad,
|
||||
GST_TIME_ARGS (GST_BUFFER_DTS (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 */
|
||||
offset = reader->start_offset + part_pad->segment.base;
|
||||
offset -= part_pad->initial_ts_offset;
|
||||
offset += reader->ts_offset;
|
||||
|
||||
if (GST_BUFFER_PTS_IS_VALID (buf))
|
||||
GST_BUFFER_PTS (buf) += offset;
|
||||
|
@ -356,21 +360,22 @@ splitmux_part_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
|
|||
goto wrong_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) {
|
||||
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->position += reader->start_offset;
|
||||
|
||||
GST_LOG_OBJECT (pad, "Adjusted segment now %" GST_PTR_FORMAT, event);
|
||||
|
||||
/* Replace event */
|
||||
gst_event_unref (event);
|
||||
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
|
||||
&& reader->prep_state != PART_STATE_PREPARING_MEASURE_STREAMS)
|
||||
break; /* Only do further stuff with segments during initial measuring */
|
||||
|
@ -1246,12 +1251,13 @@ gst_splitmux_part_reader_get_end_offset (GstSplitMuxPartReader * reader)
|
|||
|
||||
void
|
||||
gst_splitmux_part_reader_set_start_offset (GstSplitMuxPartReader * reader,
|
||||
GstClockTime offset)
|
||||
GstClockTime time_offset, GstClockTime ts_offset)
|
||||
{
|
||||
SPLITMUX_PART_LOCK (reader);
|
||||
reader->start_offset = offset;
|
||||
GST_INFO_OBJECT (reader, "TS offset now %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (offset));
|
||||
reader->start_offset = time_offset;
|
||||
reader->ts_offset = ts_offset;
|
||||
GST_INFO_OBJECT (reader, "Time offset now %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (time_offset));
|
||||
SPLITMUX_PART_UNLOCK (reader);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ struct _GstSplitMuxPartReader
|
|||
|
||||
GstClockTime duration;
|
||||
GstClockTime start_offset;
|
||||
GstClockTime ts_offset;
|
||||
|
||||
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_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_end_offset (GstSplitMuxPartReader *part);
|
||||
GstClockTime gst_splitmux_part_reader_get_duration (GstSplitMuxPartReader * reader);
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
GST_DEBUG_CATEGORY (splitmux_debug);
|
||||
#define GST_CAT_DEFAULT splitmux_debug
|
||||
|
||||
#define FIXED_TS_OFFSET (1000*GST_SECOND)
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
@ -642,14 +644,14 @@ gst_splitmux_handle_event (GstSplitMuxSrc * splitmux,
|
|||
* seg or play_segment */
|
||||
if (splitmux->play_segment.rate > 0.0) {
|
||||
if (splitmux->play_segment.stop != -1)
|
||||
seg.stop = splitmux->play_segment.stop;
|
||||
seg.stop = splitmux->play_segment.stop + FIXED_TS_OFFSET;
|
||||
else
|
||||
seg.stop = splitpad->segment.stop;
|
||||
} else {
|
||||
/* Reverse playback from stop time to start time */
|
||||
/* See if an end point was requested in the seek */
|
||||
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;
|
||||
} else {
|
||||
seg.start = splitpad->segment.start;
|
||||
|
@ -858,7 +860,7 @@ gst_splitmux_src_prepare_next_part (GstSplitMuxSrc * splitmux)
|
|||
splitmux->parts[idx]->path, 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])) {
|
||||
GST_WARNING_OBJECT (splitmux,
|
||||
"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;
|
||||
};
|
||||
|
||||
static void
|
||||
receive_handoff (GstElement * object G_GNUC_UNUSED, GstBuffer * buf,
|
||||
GstPad * arg1 G_GNUC_UNUSED, gpointer user_data G_GNUC_UNUSED)
|
||||
static GstFlowReturn
|
||||
receive_sample (GstAppSink * appsink, gpointer user_data G_GNUC_UNUSED)
|
||||
{
|
||||
GstClockTime start = GST_BUFFER_TIMESTAMP (buf);
|
||||
GstClockTime end = start;
|
||||
GstSample *sample;
|
||||
GstSegment *seg;
|
||||
GstBuffer *buf;
|
||||
GstClockTime start;
|
||||
GstClockTime end;
|
||||
|
||||
if (GST_BUFFER_DURATION_IS_VALID (buf))
|
||||
end += GST_BUFFER_DURATION (buf);
|
||||
g_signal_emit_by_name (appsink, "pull-sample", &sample);
|
||||
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));
|
||||
|
||||
/* Check time is moving in the right direction */
|
||||
|
@ -184,6 +207,10 @@ receive_handoff (GstElement * object G_GNUC_UNUSED, GstBuffer * buf,
|
|||
first_ts = start;
|
||||
if (!GST_CLOCK_TIME_IS_VALID (last_ts) || end > last_ts)
|
||||
last_ts = end;
|
||||
|
||||
gst_sample_unref (sample);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -192,16 +219,19 @@ test_playback (const gchar * in_pattern, GstClockTime exp_first_time,
|
|||
{
|
||||
GstMessage *msg;
|
||||
GstElement *pipeline;
|
||||
GstElement *fakesink;
|
||||
GstElement *appsink;
|
||||
GstElement *fakesink2;
|
||||
GstAppSinkCallbacks callbacks = { NULL };
|
||||
gchar *uri;
|
||||
|
||||
GST_DEBUG ("Playing back files matching %s", in_pattern);
|
||||
|
||||
pipeline = gst_element_factory_make ("playbin", NULL);
|
||||
fail_if (pipeline == NULL);
|
||||
|
||||
fakesink = gst_element_factory_make ("fakesink", NULL);
|
||||
fail_if (fakesink == NULL);
|
||||
g_object_set (G_OBJECT (pipeline), "video-sink", fakesink, NULL);
|
||||
appsink = gst_element_factory_make ("appsink", NULL);
|
||||
fail_if (appsink == NULL);
|
||||
g_object_set (G_OBJECT (pipeline), "video-sink", appsink, NULL);
|
||||
fakesink2 = gst_element_factory_make ("fakesink", NULL);
|
||||
fail_if (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_free (uri);
|
||||
|
||||
g_signal_connect (fakesink, "handoff", (GCallback) receive_handoff, NULL);
|
||||
g_object_set (G_OBJECT (fakesink), "signal-handoffs", TRUE, NULL);
|
||||
callbacks.new_sample = receive_sample;
|
||||
gst_app_sink_set_callbacks (GST_APP_SINK (appsink), &callbacks, NULL, NULL);
|
||||
|
||||
/* test forwards */
|
||||
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 */
|
||||
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));
|
||||
fail_unless (last_ts == exp_last_time,
|
||||
"Expected end of playback range 3s, got %" GST_TIME_FORMAT,
|
||||
GST_TIME_ARGS (last_ts));
|
||||
"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 */
|
||||
|
|
Loading…
Reference in a new issue