From 0ac14327c28ed51df590200792b5b7f6f6fffa8c Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Tue, 4 Aug 2015 14:11:15 -0700 Subject: [PATCH] Handle large composition offset greater than (1<<31) properly This fixes Issue #34: Sample composition offset greater than (1<<31) is not supported. Change-Id: I76b7bf7ceeaa9d9a7555f61842d4972a80ee48ca --- packager/media/formats/mp4/box_definitions.cc | 64 ++++++++++++++++--- packager/media/formats/mp4/box_definitions.h | 7 +- .../formats/mp4/box_definitions_unittest.cc | 9 +-- .../mp4/composition_offset_iterator.cc | 2 +- .../formats/mp4/composition_offset_iterator.h | 4 +- .../composition_offset_iterator_unittest.cc | 2 +- .../media/formats/mp4/track_run_iterator.cc | 6 +- .../mp4/track_run_iterator_unittest.cc | 2 +- 8 files changed, 72 insertions(+), 24 deletions(-) diff --git a/packager/media/formats/mp4/box_definitions.cc b/packager/media/formats/mp4/box_definitions.cc index e484441c52..2358c36428 100644 --- a/packager/media/formats/mp4/box_definitions.cc +++ b/packager/media/formats/mp4/box_definitions.cc @@ -484,25 +484,49 @@ FourCC CompositionTimeToSample::BoxType() const { return FOURCC_CTTS; } bool CompositionTimeToSample::ReadWrite(BoxBuffer* buffer) { uint32_t count = composition_offset.size(); + if (!buffer->Reading()) { + // Determine whether version 0 or version 1 should be used. + // Use version 0 if possible, use version 1 if there is a negative + // sample_offset value. + version = 0; + for (uint32_t i = 0; i < count; ++i) { + if (composition_offset[i].sample_offset < 0) { + version = 1; + break; + } + } + } + RCHECK(FullBox::ReadWrite(buffer) && buffer->ReadWriteUInt32(&count)); composition_offset.resize(count); for (uint32_t i = 0; i < count; ++i) { - RCHECK(buffer->ReadWriteUInt32(&composition_offset[i].sample_count) && - buffer->ReadWriteInt32(&composition_offset[i].sample_offset)); + RCHECK(buffer->ReadWriteUInt32(&composition_offset[i].sample_count)); + + if (version == 0) { + uint32_t sample_offset = composition_offset[i].sample_offset; + RCHECK(buffer->ReadWriteUInt32(&sample_offset)); + composition_offset[i].sample_offset = sample_offset; + } else { + int32_t sample_offset = composition_offset[i].sample_offset; + RCHECK(buffer->ReadWriteInt32(&sample_offset)); + composition_offset[i].sample_offset = sample_offset; + } } return true; } uint32_t CompositionTimeToSample::ComputeSize() { - // Version 1 to support signed offset. - version = 1; // This box is optional. Skip it if it is empty. atom_size = 0; if (!composition_offset.empty()) { + // Structure CompositionOffset contains |sample_offset| (uint32_t) and + // |sample_offset| (int64_t). The actual size of |sample_offset| is + // 4 bytes (uint32_t for version 0 and int32_t for version 1). + const uint32_t kCompositionOffsetSize = sizeof(uint32_t) * 2; atom_size = kFullBoxSize + sizeof(uint32_t) + - sizeof(CompositionOffset) * composition_offset.size(); + kCompositionOffsetSize * composition_offset.size(); } return atom_size; } @@ -1537,6 +1561,21 @@ TrackFragmentRun::~TrackFragmentRun() {} FourCC TrackFragmentRun::BoxType() const { return FOURCC_TRUN; } bool TrackFragmentRun::ReadWrite(BoxBuffer* buffer) { + if (!buffer->Reading()) { + // Determine whether version 0 or version 1 should be used. + // Use version 0 if possible, use version 1 if there is a negative + // sample_offset value. + version = 0; + if (flags & kSampleCompTimeOffsetsPresentMask) { + for (uint32_t i = 0; i < sample_count; ++i) { + if (sample_composition_time_offsets[i] < 0) { + version = 1; + break; + } + } + } + } + RCHECK(FullBox::ReadWrite(buffer) && buffer->ReadWriteUInt32(&sample_count)); @@ -1598,8 +1637,18 @@ bool TrackFragmentRun::ReadWrite(BoxBuffer* buffer) { RCHECK(buffer->ReadWriteUInt32(&sample_sizes[i])); if (sample_flags_present) RCHECK(buffer->ReadWriteUInt32(&sample_flags[i])); - if (sample_composition_time_offsets_present) - RCHECK(buffer->ReadWriteInt32(&sample_composition_time_offsets[i])); + + if (sample_composition_time_offsets_present) { + if (version == 0) { + uint32_t sample_offset = sample_composition_time_offsets[i]; + RCHECK(buffer->ReadWriteUInt32(&sample_offset)); + sample_composition_time_offsets[i] = sample_offset; + } else { + int32_t sample_offset = sample_composition_time_offsets[i]; + RCHECK(buffer->ReadWriteInt32(&sample_offset)); + sample_composition_time_offsets[i] = sample_offset; + } + } } if (buffer->Reading()) { @@ -1615,7 +1664,6 @@ bool TrackFragmentRun::ReadWrite(BoxBuffer* buffer) { } uint32_t TrackFragmentRun::ComputeSize() { - version = 1; // Version 1 to support signed offset. atom_size = kFullBoxSize + sizeof(sample_count); if (flags & kDataOffsetPresentMask) atom_size += sizeof(data_offset); diff --git a/packager/media/formats/mp4/box_definitions.h b/packager/media/formats/mp4/box_definitions.h index 9d69ac77c7..6f2280fd8e 100644 --- a/packager/media/formats/mp4/box_definitions.h +++ b/packager/media/formats/mp4/box_definitions.h @@ -252,9 +252,8 @@ struct CompositionOffset { uint32_t sample_count; // If version == 0, sample_offset is uint32_t; // If version == 1, sample_offset is int32_t. - // Always use signed version, which should work unless the offset - // exceeds 31 bits, which shouldn't happen. - int32_t sample_offset; + // Use int64_t so both can be supported properly. + int64_t sample_offset; }; // ctts. Optional. @@ -490,7 +489,7 @@ struct TrackFragmentRun : FullBox { std::vector sample_flags; std::vector sample_sizes; std::vector sample_durations; - std::vector sample_composition_time_offsets; + std::vector sample_composition_time_offsets; }; struct SampleToGroupEntry { diff --git a/packager/media/formats/mp4/box_definitions_unittest.cc b/packager/media/formats/mp4/box_definitions_unittest.cc index e277a893a9..2c8ce28c63 100644 --- a/packager/media/formats/mp4/box_definitions_unittest.cc +++ b/packager/media/formats/mp4/box_definitions_unittest.cc @@ -363,13 +363,13 @@ class BoxDefinitionsTestGeneral : public testing::Test { ctts->composition_offset[0].sample_offset = 5; ctts->composition_offset[1].sample_count = 2; ctts->composition_offset[1].sample_offset = 9; - ctts->version = 1; + ctts->version = 0; } void Modify(CompositionTimeToSample* ctts) { ctts->composition_offset.resize(1); ctts->composition_offset[0].sample_count = 6; - ctts->composition_offset[0].sample_offset = 1; + ctts->composition_offset[0].sample_offset = -9; ctts->version = 1; } @@ -617,7 +617,7 @@ class BoxDefinitionsTestGeneral : public testing::Test { trun->sample_durations[1] += 2343; trun->sample_composition_time_offsets.assign(kData32, kData32 + arraysize(kData32)); - trun->sample_composition_time_offsets[2] -= 89782; + trun->sample_composition_time_offsets[2] = -89782; trun->version = 1; } @@ -625,7 +625,8 @@ class BoxDefinitionsTestGeneral : public testing::Test { trun->flags |= TrackFragmentRun::kFirstSampleFlagsPresentMask; trun->flags &= ~TrackFragmentRun::kSampleFlagsPresentMask; trun->sample_flags.resize(1); - trun->version = 1; + trun->sample_composition_time_offsets[2] = 9; + trun->version = 0; } void Fill(SampleToGroup* sbgp) { diff --git a/packager/media/formats/mp4/composition_offset_iterator.cc b/packager/media/formats/mp4/composition_offset_iterator.cc index ca7f69a66b..c9b5582a16 100644 --- a/packager/media/formats/mp4/composition_offset_iterator.cc +++ b/packager/media/formats/mp4/composition_offset_iterator.cc @@ -35,7 +35,7 @@ bool CompositionOffsetIterator::IsValid() const { sample_index_ < iterator_->sample_count; } -int32_t CompositionOffsetIterator::SampleOffset(uint32_t sample) const { +int64_t CompositionOffsetIterator::SampleOffset(uint32_t sample) const { uint32_t current_sample = 0; std::vector::const_iterator it = composition_offset_table_.begin(); diff --git a/packager/media/formats/mp4/composition_offset_iterator.h b/packager/media/formats/mp4/composition_offset_iterator.h index 5a35c8489d..0e9453399f 100644 --- a/packager/media/formats/mp4/composition_offset_iterator.h +++ b/packager/media/formats/mp4/composition_offset_iterator.h @@ -37,10 +37,10 @@ class CompositionOffsetIterator { bool IsValid() const; /// @return Sample offset for current sample. - int32_t sample_offset() const { return iterator_->sample_offset; } + int64_t sample_offset() const { return iterator_->sample_offset; } /// @return Sample offset @a sample, 1-based. - int32_t SampleOffset(uint32_t sample) const; + int64_t SampleOffset(uint32_t sample) const; /// @return Total number of samples. uint32_t NumSamples() const; diff --git a/packager/media/formats/mp4/composition_offset_iterator_unittest.cc b/packager/media/formats/mp4/composition_offset_iterator_unittest.cc index bcec61ad61..08de2db94f 100644 --- a/packager/media/formats/mp4/composition_offset_iterator_unittest.cc +++ b/packager/media/formats/mp4/composition_offset_iterator_unittest.cc @@ -35,7 +35,7 @@ class CompositionOffsetIteratorTest : public testing::Test { } protected: - std::vector composition_offset_table_; + std::vector composition_offset_table_; CompositionTimeToSample composition_time_to_sample_; scoped_ptr composition_offset_iterator_; diff --git a/packager/media/formats/mp4/track_run_iterator.cc b/packager/media/formats/mp4/track_run_iterator.cc index d7e788d3cc..a75cc14904 100644 --- a/packager/media/formats/mp4/track_run_iterator.cc +++ b/packager/media/formats/mp4/track_run_iterator.cc @@ -23,9 +23,9 @@ namespace media { namespace mp4 { struct SampleInfo { - int size; - int duration; - int cts_offset; + int64_t size; + int64_t duration; + int64_t cts_offset; bool is_keyframe; }; diff --git a/packager/media/formats/mp4/track_run_iterator_unittest.cc b/packager/media/formats/mp4/track_run_iterator_unittest.cc index 68d9468c1b..fbc8ebaef9 100644 --- a/packager/media/formats/mp4/track_run_iterator_unittest.cc +++ b/packager/media/formats/mp4/track_run_iterator_unittest.cc @@ -268,7 +268,7 @@ TEST_F(TrackRunIteratorTest, ReorderingTest) { // would simply be [0, 3, -2]. Since CTS offsets should be non-negative for // maximum compatibility, these values are biased up to [2, 5, 0]. MovieFragment moof = CreateFragment(); - std::vector& cts_offsets = + std::vector& cts_offsets = moof.tracks[1].runs[0].sample_composition_time_offsets; cts_offsets.resize(10); cts_offsets[0] = 2;