From 80db1c7bbf9a99fe93798b0d78520eeaa175d936 Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Fri, 30 Jan 2015 17:03:38 -0800 Subject: [PATCH] Handle non-MSE compliant fmp4 properly MSE requires 'base-data-offset-present' not to be set, but FFmpeg may generate fmp4 files with this flag though it actually uses moof as base. Log a warning instead of failing directly in this case. FFmpeg may also generates fmp4 files without tfdt box (TrackFragmentDecodeTime) box, which is again non-MSE compliant. Handle this scenario properly. Bug: 18613712 Also fixes a test failure in SegmentTemplateTest introduced in an earlier change. Bug: 19235748 Change-Id: I4bcbb675b22a832a88cd33ee64c3e99a1c6e3a63 --- packager/media/formats/mp4/box_definitions.cc | 30 ++++++++++--------- packager/media/formats/mp4/box_definitions.h | 3 +- .../media/formats/mp4/track_run_iterator.cc | 6 +++- .../media/formats/mp4/track_run_iterator.h | 3 ++ packager/mpd/base/mpd_builder_unittest.cc | 6 ++-- 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index 345f12a65f..e484441c52 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -1485,16 +1485,15 @@ bool TrackFragmentHeader::ReadWrite(BoxBuffer* buffer) { RCHECK(FullBox::ReadWrite(buffer) && buffer->ReadWriteUInt32(&track_id)); - // Media Source specific: reject tracks that set 'base-data-offset-present'. - // Although the Media Source requires that 'default-base-is-moof' (14496-12 - // Amendment 2) be set, we omit this check as many otherwise-valid files in - // the wild don't set it. - // - // RCHECK((flags & kDefaultBaseIsMoofMask) && - // !(flags & kBaseDataOffsetPresentMask)); - if (flags & kDataOffsetPresentMask) { - NOTIMPLEMENTED() << " base-data-offset-present is not supported."; - return false; + if (flags & kBaseDataOffsetPresentMask) { + // MSE requires 'default-base-is-moof' to be set and + // 'base-data-offset-present' not to be set. We omit these checks as some + // valid files in the wild don't follow these rules, though they use moof as + // base. + uint64_t base_data_offset; + RCHECK(buffer->ReadWriteUInt64(&base_data_offset)); + DLOG(WARNING) << "base-data-offset-present is not expected. Assumes " + "default-base-is-moof."; } if (flags & kSampleDescriptionIndexPresentMask) { @@ -1751,18 +1750,19 @@ uint32_t SampleGroupDescription::ComputeSize() { return atom_size; } -TrackFragment::TrackFragment() {} +TrackFragment::TrackFragment() : decode_time_absent(false) {} TrackFragment::~TrackFragment() {} FourCC TrackFragment::BoxType() const { return FOURCC_TRAF; } bool TrackFragment::ReadWrite(BoxBuffer* buffer) { RCHECK(Box::ReadWrite(buffer) && buffer->PrepareChildren() && - buffer->ReadWriteChild(&header) && - // Media Source specific: 'tfdt' required - buffer->ReadWriteChild(&decode_time)); + buffer->ReadWriteChild(&header)); if (buffer->Reading()) { DCHECK(buffer->reader()); + decode_time_absent = !buffer->reader()->ChildExist(&decode_time); + if (!decode_time_absent) + RCHECK(buffer->ReadWriteChild(&decode_time)); RCHECK(buffer->reader()->TryReadChildren(&runs)); // There could be multiple SampleGroupDescription and SampleToGroup boxes @@ -1778,6 +1778,8 @@ bool TrackFragment::ReadWrite(BoxBuffer* buffer) { RCHECK(buffer->reader()->ReadChild(&sample_group_description)); } } else { + if (!decode_time_absent) + RCHECK(buffer->ReadWriteChild(&decode_time)); for (uint32_t i = 0; i < runs.size(); ++i) RCHECK(runs[i].ReadWrite(buffer)); RCHECK(buffer->TryReadWriteChild(&sample_to_group) && diff --git a/packager/media/formats/mp4/box_definitions.h b/packager/media/formats/mp4/box_definitions.h index ee03d9f96e..9d69ac77c7 100644 --- a/packager/media/formats/mp4/box_definitions.h +++ b/packager/media/formats/mp4/box_definitions.h @@ -445,7 +445,7 @@ struct MovieFragmentHeader : FullBox { struct TrackFragmentHeader : FullBox { enum TrackFragmentFlagsMasks { - kDataOffsetPresentMask = 0x000001, + kBaseDataOffsetPresentMask = 0x000001, kSampleDescriptionIndexPresentMask = 0x000002, kDefaultSampleDurationPresentMask = 0x000008, kDefaultSampleSizePresentMask = 0x000010, @@ -532,6 +532,7 @@ struct TrackFragment : Box { TrackFragmentHeader header; std::vector runs; + bool decode_time_absent; TrackFragmentDecodeTime decode_time; SampleToGroup sample_to_group; SampleGroupDescription sample_group_description; diff --git a/packager/media/formats/mp4/track_run_iterator.cc b/packager/media/formats/mp4/track_run_iterator.cc index 30fd875394..d7e788d3cc 100644 --- a/packager/media/formats/mp4/track_run_iterator.cc +++ b/packager/media/formats/mp4/track_run_iterator.cc @@ -270,6 +270,7 @@ bool TrackRunIterator::Init() { bool TrackRunIterator::Init(const MovieFragment& moof) { runs_.clear(); + next_fragment_start_dts_.resize(moof.tracks.size(), 0); for (size_t i = 0; i < moof.tracks.size(); i++) { const TrackFragment& traf = moof.tracks[i]; @@ -299,7 +300,9 @@ bool TrackRunIterator::Init(const MovieFragment& moof) { RCHECK(desc_idx > 0); // Descriptions are one-indexed in the file desc_idx -= 1; - int64_t run_start_dts = traf.decode_time.decode_time; + int64_t run_start_dts = traf.decode_time_absent + ? next_fragment_start_dts_[i] + : traf.decode_time.decode_time; int sample_count_sum = 0; for (size_t j = 0; j < traf.runs.size(); j++) { @@ -368,6 +371,7 @@ bool TrackRunIterator::Init(const MovieFragment& moof) { runs_.push_back(tri); sample_count_sum += trun.sample_count; } + next_fragment_start_dts_[i] = run_start_dts; } std::sort(runs_.begin(), runs_.end(), CompareMinTrackRunDataOffset()); diff --git a/packager/media/formats/mp4/track_run_iterator.h b/packager/media/formats/mp4/track_run_iterator.h index 64383a5fbf..fb3a3f65a1 100644 --- a/packager/media/formats/mp4/track_run_iterator.h +++ b/packager/media/formats/mp4/track_run_iterator.h @@ -112,6 +112,9 @@ class TrackRunIterator { std::vector::const_iterator sample_itr_; std::vector cenc_info_; + // Track the start dts of the next segment, only useful if decode_time box is + // absent. + std::vector next_fragment_start_dts_; int64_t sample_dts_; int64_t sample_offset_; diff --git a/packager/mpd/base/mpd_builder_unittest.cc b/packager/mpd/base/mpd_builder_unittest.cc index 797d900ab5..d722edc10a 100644 --- a/packager/mpd/base/mpd_builder_unittest.cc +++ b/packager/mpd/base/mpd_builder_unittest.cc @@ -496,8 +496,7 @@ TEST_F(SegmentTemplateTest, OutOfOrder) { const uint64_t kRepeat = 0; AddSegments(kLaterStartTime, kDuration, kSize, kRepeat); - EXPECT_DEBUG_DEATH(AddSegments(kEarlierStartTime, kDuration, kSize, kRepeat), - ""); + AddSegments(kEarlierStartTime, kDuration, kSize, kRepeat); ASSERT_NO_FATAL_FAILURE(CheckMpdAgainstExpectedResult()); } @@ -513,8 +512,7 @@ TEST_F(SegmentTemplateTest, OverlappingSegments) { CHECK_GT(kDuration, kOverlappingSegmentStartTime); AddSegments(kEarlierStartTime, kDuration, kSize, kRepeat); - EXPECT_DEBUG_DEATH( - AddSegments(kOverlappingSegmentStartTime, kDuration, kSize, kRepeat), ""); + AddSegments(kOverlappingSegmentStartTime, kDuration, kSize, kRepeat); ASSERT_NO_FATAL_FAILURE(CheckMpdAgainstExpectedResult()); }