diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py
index 9ad469dae1..77aecde6cd 100755
--- a/packager/app/test/packager_test.py
+++ b/packager/app/test/packager_test.py
@@ -721,6 +721,11 @@ class PackagerFunctionalTest(PackagerAppTest):
self._GetFlags(output_dash=True))
self._CheckTestResults('video-audio-text')
+ def testVideoNoEditList(self):
+ stream = self._GetStream('video', test_file='bear-640x360-no_edit_list.mp4')
+ self.assertPackageSuccess([stream], self._GetFlags(output_dash=True))
+ self._CheckTestResults('video-no-edit-list')
+
def testAvcAacTs(self):
# Currently we only support live packaging for ts.
self.assertPackageSuccess(
diff --git a/packager/app/test/testdata/video-no-edit-list/bear-640x360-no_edit_list-video.mp4 b/packager/app/test/testdata/video-no-edit-list/bear-640x360-no_edit_list-video.mp4
new file mode 100644
index 0000000000..37528cb505
Binary files /dev/null and b/packager/app/test/testdata/video-no-edit-list/bear-640x360-no_edit_list-video.mp4 differ
diff --git a/packager/app/test/testdata/video-no-edit-list/output.mpd b/packager/app/test/testdata/video-no-edit-list/output.mpd
new file mode 100644
index 0000000000..61df73f51c
--- /dev/null
+++ b/packager/app/test/testdata/video-no-edit-list/output.mpd
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ bear-640x360-no_edit_list-video.mp4
+
+
+
+
+
+
+
diff --git a/packager/media/formats/mp4/mp4.gyp b/packager/media/formats/mp4/mp4.gyp
index 9ee4c6638e..0707fd5aff 100644
--- a/packager/media/formats/mp4/mp4.gyp
+++ b/packager/media/formats/mp4/mp4.gyp
@@ -46,6 +46,7 @@
],
'dependencies': [
'../../../third_party/boringssl/boringssl.gyp:boringssl',
+ '../../../third_party/gflags/gflags.gyp:gflags',
'../../base/media_base.gyp:media_base',
'../../codecs/codecs.gyp:codecs',
'../../event/media_event.gyp:media_event',
@@ -68,6 +69,7 @@
'../../../file/file.gyp:file',
'../../../testing/gtest.gyp:gtest',
'../../../testing/gmock.gyp:gmock',
+ '../../../third_party/gflags/gflags.gyp:gflags',
'../../test/media_test.gyp:media_test_support',
'mp4',
]
diff --git a/packager/media/formats/mp4/track_run_iterator.cc b/packager/media/formats/mp4/track_run_iterator.cc
index dde7ebdb33..2a0dbd1eaa 100644
--- a/packager/media/formats/mp4/track_run_iterator.cc
+++ b/packager/media/formats/mp4/track_run_iterator.cc
@@ -4,6 +4,13 @@
#include "packager/media/formats/mp4/track_run_iterator.h"
+#include
+
+DEFINE_bool(mp4_reset_initial_composition_offset_to_zero,
+ true,
+ "MP4 only. If it is true, reset the initial composition offset to "
+ "zero, i.e. by assuming that there is a missing EditList.");
+
#include
#include
@@ -180,7 +187,7 @@ bool TrackRunIterator::Init() {
// dts is directly adjusted, which then propagates to pts as pts is encoded
// as difference (composition offset) to dts in mp4.
- int64_t run_start_dts = GetTimestampAdjustment(*moov_, *trak);
+ int64_t run_start_dts = GetTimestampAdjustment(*moov_, *trak, nullptr);
uint32_t num_samples = sample_size.sample_count;
uint32_t num_chunks = static_cast(chunk_offset_vector.size());
@@ -349,7 +356,7 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
// dts is directly adjusted, which then propagates to pts as pts is encoded
// as difference (composition offset) to dts in mp4.
- run_start_dts += GetTimestampAdjustment(*moov_, *trak);
+ run_start_dts += GetTimestampAdjustment(*moov_, *trak, &traf);
int sample_count_sum = 0;
@@ -634,8 +641,14 @@ std::unique_ptr TrackRunIterator::GetDecryptConfig() {
}
int64_t TrackRunIterator::GetTimestampAdjustment(const Movie& movie,
- const Track& track) {
- int64_t edit_list_offset = 0;
+ const Track& track,
+ const TrackFragment* traf) {
+ const uint32_t track_id = track.header.track_id;
+ const auto iter = timestamp_adjustment_map_.find(track_id);
+ if (iter != timestamp_adjustment_map_.end())
+ return iter->second;
+
+ int64_t timestamp_adjustment = 0;
const std::vector& edits = track.edit.list.edits;
if (!edits.empty()) {
// ISO/IEC 14496-12:2015 8.6.6 Edit List Box.
@@ -651,13 +664,48 @@ int64_t TrackRunIterator::GetTimestampAdjustment(const Movie& movie,
const int64_t scaled_time =
Rescale(edit.segment_duration, movie.header.timescale,
track.media.header.timescale);
- edit_list_offset += scaled_time;
+ timestamp_adjustment += scaled_time;
} else {
- edit_list_offset -= edit.media_time;
+ timestamp_adjustment -= edit.media_time;
}
}
}
- return edit_list_offset;
+
+ if (timestamp_adjustment == 0) {
+ int64_t composition_offset = 0;
+ if (traf && !traf->runs.empty()) {
+ const auto& cts_offsets =
+ traf->runs.front().sample_composition_time_offsets;
+ if (!cts_offsets.empty())
+ composition_offset = cts_offsets.front();
+ } else {
+ CompositionOffsetIterator composition_offset_iter(
+ track.media.information.sample_table.composition_time_to_sample);
+ if (composition_offset_iter.IsValid())
+ composition_offset = composition_offset_iter.sample_offset();
+ }
+
+ int64_t decode_time = 0;
+ if (traf)
+ decode_time = traf->decode_time.decode_time;
+ if (composition_offset != 0 && decode_time == 0) {
+ LOG(WARNING) << "Seeing non-zero composition offset "
+ << composition_offset
+ << ". An EditList is probably missing.";
+ if (FLAGS_mp4_reset_initial_composition_offset_to_zero) {
+ LOG(WARNING)
+ << "Adjusting timestamps by " << -composition_offset
+ << ". Please file a bug to "
+ "https://github.com/google/shaka-packager/issues if you "
+ "do not think it is right or if you are seeing any problems.";
+ timestamp_adjustment = -composition_offset;
+ }
+ }
+ }
+
+ timestamp_adjustment_map_.insert(
+ std::make_pair(track_id, timestamp_adjustment));
+ return timestamp_adjustment;
}
} // namespace mp4
diff --git a/packager/media/formats/mp4/track_run_iterator.h b/packager/media/formats/mp4/track_run_iterator.h
index 15cdf84080..e5b920c486 100644
--- a/packager/media/formats/mp4/track_run_iterator.h
+++ b/packager/media/formats/mp4/track_run_iterator.h
@@ -5,6 +5,7 @@
#ifndef PACKAGER_MEDIA_FORMATS_MP4_TRACK_RUN_ITERATOR_H_
#define PACKAGER_MEDIA_FORMATS_MP4_TRACK_RUN_ITERATOR_H_
+#include