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
This commit is contained in:
KongQun Yang 2015-01-30 17:03:38 -08:00
parent 5462b350ae
commit 80db1c7bbf
5 changed files with 28 additions and 20 deletions

View File

@ -1485,16 +1485,15 @@ bool TrackFragmentHeader::ReadWrite(BoxBuffer* buffer) {
RCHECK(FullBox::ReadWrite(buffer) && RCHECK(FullBox::ReadWrite(buffer) &&
buffer->ReadWriteUInt32(&track_id)); buffer->ReadWriteUInt32(&track_id));
// Media Source specific: reject tracks that set 'base-data-offset-present'. if (flags & kBaseDataOffsetPresentMask) {
// Although the Media Source requires that 'default-base-is-moof' (14496-12 // MSE requires 'default-base-is-moof' to be set and
// Amendment 2) be set, we omit this check as many otherwise-valid files in // 'base-data-offset-present' not to be set. We omit these checks as some
// the wild don't set it. // valid files in the wild don't follow these rules, though they use moof as
// // base.
// RCHECK((flags & kDefaultBaseIsMoofMask) && uint64_t base_data_offset;
// !(flags & kBaseDataOffsetPresentMask)); RCHECK(buffer->ReadWriteUInt64(&base_data_offset));
if (flags & kDataOffsetPresentMask) { DLOG(WARNING) << "base-data-offset-present is not expected. Assumes "
NOTIMPLEMENTED() << " base-data-offset-present is not supported."; "default-base-is-moof.";
return false;
} }
if (flags & kSampleDescriptionIndexPresentMask) { if (flags & kSampleDescriptionIndexPresentMask) {
@ -1751,18 +1750,19 @@ uint32_t SampleGroupDescription::ComputeSize() {
return atom_size; return atom_size;
} }
TrackFragment::TrackFragment() {} TrackFragment::TrackFragment() : decode_time_absent(false) {}
TrackFragment::~TrackFragment() {} TrackFragment::~TrackFragment() {}
FourCC TrackFragment::BoxType() const { return FOURCC_TRAF; } FourCC TrackFragment::BoxType() const { return FOURCC_TRAF; }
bool TrackFragment::ReadWrite(BoxBuffer* buffer) { bool TrackFragment::ReadWrite(BoxBuffer* buffer) {
RCHECK(Box::ReadWrite(buffer) && RCHECK(Box::ReadWrite(buffer) &&
buffer->PrepareChildren() && buffer->PrepareChildren() &&
buffer->ReadWriteChild(&header) && buffer->ReadWriteChild(&header));
// Media Source specific: 'tfdt' required
buffer->ReadWriteChild(&decode_time));
if (buffer->Reading()) { if (buffer->Reading()) {
DCHECK(buffer->reader()); 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)); RCHECK(buffer->reader()->TryReadChildren(&runs));
// There could be multiple SampleGroupDescription and SampleToGroup boxes // There could be multiple SampleGroupDescription and SampleToGroup boxes
@ -1778,6 +1778,8 @@ bool TrackFragment::ReadWrite(BoxBuffer* buffer) {
RCHECK(buffer->reader()->ReadChild(&sample_group_description)); RCHECK(buffer->reader()->ReadChild(&sample_group_description));
} }
} else { } else {
if (!decode_time_absent)
RCHECK(buffer->ReadWriteChild(&decode_time));
for (uint32_t i = 0; i < runs.size(); ++i) for (uint32_t i = 0; i < runs.size(); ++i)
RCHECK(runs[i].ReadWrite(buffer)); RCHECK(runs[i].ReadWrite(buffer));
RCHECK(buffer->TryReadWriteChild(&sample_to_group) && RCHECK(buffer->TryReadWriteChild(&sample_to_group) &&

View File

@ -445,7 +445,7 @@ struct MovieFragmentHeader : FullBox {
struct TrackFragmentHeader : FullBox { struct TrackFragmentHeader : FullBox {
enum TrackFragmentFlagsMasks { enum TrackFragmentFlagsMasks {
kDataOffsetPresentMask = 0x000001, kBaseDataOffsetPresentMask = 0x000001,
kSampleDescriptionIndexPresentMask = 0x000002, kSampleDescriptionIndexPresentMask = 0x000002,
kDefaultSampleDurationPresentMask = 0x000008, kDefaultSampleDurationPresentMask = 0x000008,
kDefaultSampleSizePresentMask = 0x000010, kDefaultSampleSizePresentMask = 0x000010,
@ -532,6 +532,7 @@ struct TrackFragment : Box {
TrackFragmentHeader header; TrackFragmentHeader header;
std::vector<TrackFragmentRun> runs; std::vector<TrackFragmentRun> runs;
bool decode_time_absent;
TrackFragmentDecodeTime decode_time; TrackFragmentDecodeTime decode_time;
SampleToGroup sample_to_group; SampleToGroup sample_to_group;
SampleGroupDescription sample_group_description; SampleGroupDescription sample_group_description;

View File

@ -270,6 +270,7 @@ bool TrackRunIterator::Init() {
bool TrackRunIterator::Init(const MovieFragment& moof) { bool TrackRunIterator::Init(const MovieFragment& moof) {
runs_.clear(); runs_.clear();
next_fragment_start_dts_.resize(moof.tracks.size(), 0);
for (size_t i = 0; i < moof.tracks.size(); i++) { for (size_t i = 0; i < moof.tracks.size(); i++) {
const TrackFragment& traf = moof.tracks[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 RCHECK(desc_idx > 0); // Descriptions are one-indexed in the file
desc_idx -= 1; 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; int sample_count_sum = 0;
for (size_t j = 0; j < traf.runs.size(); j++) { for (size_t j = 0; j < traf.runs.size(); j++) {
@ -368,6 +371,7 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
runs_.push_back(tri); runs_.push_back(tri);
sample_count_sum += trun.sample_count; sample_count_sum += trun.sample_count;
} }
next_fragment_start_dts_[i] = run_start_dts;
} }
std::sort(runs_.begin(), runs_.end(), CompareMinTrackRunDataOffset()); std::sort(runs_.begin(), runs_.end(), CompareMinTrackRunDataOffset());

View File

@ -112,6 +112,9 @@ class TrackRunIterator {
std::vector<SampleInfo>::const_iterator sample_itr_; std::vector<SampleInfo>::const_iterator sample_itr_;
std::vector<FrameCENCInfo> cenc_info_; std::vector<FrameCENCInfo> cenc_info_;
// Track the start dts of the next segment, only useful if decode_time box is
// absent.
std::vector<int64_t> next_fragment_start_dts_;
int64_t sample_dts_; int64_t sample_dts_;
int64_t sample_offset_; int64_t sample_offset_;

View File

@ -496,8 +496,7 @@ TEST_F(SegmentTemplateTest, OutOfOrder) {
const uint64_t kRepeat = 0; const uint64_t kRepeat = 0;
AddSegments(kLaterStartTime, kDuration, kSize, kRepeat); AddSegments(kLaterStartTime, kDuration, kSize, kRepeat);
EXPECT_DEBUG_DEATH(AddSegments(kEarlierStartTime, kDuration, kSize, kRepeat), AddSegments(kEarlierStartTime, kDuration, kSize, kRepeat);
"");
ASSERT_NO_FATAL_FAILURE(CheckMpdAgainstExpectedResult()); ASSERT_NO_FATAL_FAILURE(CheckMpdAgainstExpectedResult());
} }
@ -513,8 +512,7 @@ TEST_F(SegmentTemplateTest, OverlappingSegments) {
CHECK_GT(kDuration, kOverlappingSegmentStartTime); CHECK_GT(kDuration, kOverlappingSegmentStartTime);
AddSegments(kEarlierStartTime, kDuration, kSize, kRepeat); AddSegments(kEarlierStartTime, kDuration, kSize, kRepeat);
EXPECT_DEBUG_DEATH( AddSegments(kOverlappingSegmentStartTime, kDuration, kSize, kRepeat);
AddSegments(kOverlappingSegmentStartTime, kDuration, kSize, kRepeat), "");
ASSERT_NO_FATAL_FAILURE(CheckMpdAgainstExpectedResult()); ASSERT_NO_FATAL_FAILURE(CheckMpdAgainstExpectedResult());
} }