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
This commit is contained in:
Kongqun Yang 2015-08-04 14:11:15 -07:00 committed by Gerrit Code Review
parent 3d4872ec77
commit 0ac14327c2
8 changed files with 72 additions and 24 deletions

View File

@ -484,25 +484,49 @@ FourCC CompositionTimeToSample::BoxType() const { return FOURCC_CTTS; }
bool CompositionTimeToSample::ReadWrite(BoxBuffer* buffer) { bool CompositionTimeToSample::ReadWrite(BoxBuffer* buffer) {
uint32_t count = composition_offset.size(); 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) && RCHECK(FullBox::ReadWrite(buffer) &&
buffer->ReadWriteUInt32(&count)); buffer->ReadWriteUInt32(&count));
composition_offset.resize(count); composition_offset.resize(count);
for (uint32_t i = 0; i < count; ++i) { for (uint32_t i = 0; i < count; ++i) {
RCHECK(buffer->ReadWriteUInt32(&composition_offset[i].sample_count) && RCHECK(buffer->ReadWriteUInt32(&composition_offset[i].sample_count));
buffer->ReadWriteInt32(&composition_offset[i].sample_offset));
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; return true;
} }
uint32_t CompositionTimeToSample::ComputeSize() { uint32_t CompositionTimeToSample::ComputeSize() {
// Version 1 to support signed offset.
version = 1;
// This box is optional. Skip it if it is empty. // This box is optional. Skip it if it is empty.
atom_size = 0; atom_size = 0;
if (!composition_offset.empty()) { 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) + atom_size = kFullBoxSize + sizeof(uint32_t) +
sizeof(CompositionOffset) * composition_offset.size(); kCompositionOffsetSize * composition_offset.size();
} }
return atom_size; return atom_size;
} }
@ -1537,6 +1561,21 @@ TrackFragmentRun::~TrackFragmentRun() {}
FourCC TrackFragmentRun::BoxType() const { return FOURCC_TRUN; } FourCC TrackFragmentRun::BoxType() const { return FOURCC_TRUN; }
bool TrackFragmentRun::ReadWrite(BoxBuffer* buffer) { 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) && RCHECK(FullBox::ReadWrite(buffer) &&
buffer->ReadWriteUInt32(&sample_count)); buffer->ReadWriteUInt32(&sample_count));
@ -1598,8 +1637,18 @@ bool TrackFragmentRun::ReadWrite(BoxBuffer* buffer) {
RCHECK(buffer->ReadWriteUInt32(&sample_sizes[i])); RCHECK(buffer->ReadWriteUInt32(&sample_sizes[i]));
if (sample_flags_present) if (sample_flags_present)
RCHECK(buffer->ReadWriteUInt32(&sample_flags[i])); 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()) { if (buffer->Reading()) {
@ -1615,7 +1664,6 @@ bool TrackFragmentRun::ReadWrite(BoxBuffer* buffer) {
} }
uint32_t TrackFragmentRun::ComputeSize() { uint32_t TrackFragmentRun::ComputeSize() {
version = 1; // Version 1 to support signed offset.
atom_size = kFullBoxSize + sizeof(sample_count); atom_size = kFullBoxSize + sizeof(sample_count);
if (flags & kDataOffsetPresentMask) if (flags & kDataOffsetPresentMask)
atom_size += sizeof(data_offset); atom_size += sizeof(data_offset);

View File

@ -252,9 +252,8 @@ struct CompositionOffset {
uint32_t sample_count; uint32_t sample_count;
// If version == 0, sample_offset is uint32_t; // If version == 0, sample_offset is uint32_t;
// If version == 1, sample_offset is int32_t. // If version == 1, sample_offset is int32_t.
// Always use signed version, which should work unless the offset // Use int64_t so both can be supported properly.
// exceeds 31 bits, which shouldn't happen. int64_t sample_offset;
int32_t sample_offset;
}; };
// ctts. Optional. // ctts. Optional.
@ -490,7 +489,7 @@ struct TrackFragmentRun : FullBox {
std::vector<uint32_t> sample_flags; std::vector<uint32_t> sample_flags;
std::vector<uint32_t> sample_sizes; std::vector<uint32_t> sample_sizes;
std::vector<uint32_t> sample_durations; std::vector<uint32_t> sample_durations;
std::vector<int32_t> sample_composition_time_offsets; std::vector<int64_t> sample_composition_time_offsets;
}; };
struct SampleToGroupEntry { struct SampleToGroupEntry {

View File

@ -363,13 +363,13 @@ class BoxDefinitionsTestGeneral : public testing::Test {
ctts->composition_offset[0].sample_offset = 5; ctts->composition_offset[0].sample_offset = 5;
ctts->composition_offset[1].sample_count = 2; ctts->composition_offset[1].sample_count = 2;
ctts->composition_offset[1].sample_offset = 9; ctts->composition_offset[1].sample_offset = 9;
ctts->version = 1; ctts->version = 0;
} }
void Modify(CompositionTimeToSample* ctts) { void Modify(CompositionTimeToSample* ctts) {
ctts->composition_offset.resize(1); ctts->composition_offset.resize(1);
ctts->composition_offset[0].sample_count = 6; ctts->composition_offset[0].sample_count = 6;
ctts->composition_offset[0].sample_offset = 1; ctts->composition_offset[0].sample_offset = -9;
ctts->version = 1; ctts->version = 1;
} }
@ -617,7 +617,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
trun->sample_durations[1] += 2343; trun->sample_durations[1] += 2343;
trun->sample_composition_time_offsets.assign(kData32, trun->sample_composition_time_offsets.assign(kData32,
kData32 + arraysize(kData32)); kData32 + arraysize(kData32));
trun->sample_composition_time_offsets[2] -= 89782; trun->sample_composition_time_offsets[2] = -89782;
trun->version = 1; trun->version = 1;
} }
@ -625,7 +625,8 @@ class BoxDefinitionsTestGeneral : public testing::Test {
trun->flags |= TrackFragmentRun::kFirstSampleFlagsPresentMask; trun->flags |= TrackFragmentRun::kFirstSampleFlagsPresentMask;
trun->flags &= ~TrackFragmentRun::kSampleFlagsPresentMask; trun->flags &= ~TrackFragmentRun::kSampleFlagsPresentMask;
trun->sample_flags.resize(1); trun->sample_flags.resize(1);
trun->version = 1; trun->sample_composition_time_offsets[2] = 9;
trun->version = 0;
} }
void Fill(SampleToGroup* sbgp) { void Fill(SampleToGroup* sbgp) {

View File

@ -35,7 +35,7 @@ bool CompositionOffsetIterator::IsValid() const {
sample_index_ < iterator_->sample_count; 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; uint32_t current_sample = 0;
std::vector<CompositionOffset>::const_iterator it = std::vector<CompositionOffset>::const_iterator it =
composition_offset_table_.begin(); composition_offset_table_.begin();

View File

@ -37,10 +37,10 @@ class CompositionOffsetIterator {
bool IsValid() const; bool IsValid() const;
/// @return Sample offset for current sample. /// @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. /// @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. /// @return Total number of samples.
uint32_t NumSamples() const; uint32_t NumSamples() const;

View File

@ -35,7 +35,7 @@ class CompositionOffsetIteratorTest : public testing::Test {
} }
protected: protected:
std::vector<int32_t> composition_offset_table_; std::vector<int64_t> composition_offset_table_;
CompositionTimeToSample composition_time_to_sample_; CompositionTimeToSample composition_time_to_sample_;
scoped_ptr<CompositionOffsetIterator> composition_offset_iterator_; scoped_ptr<CompositionOffsetIterator> composition_offset_iterator_;

View File

@ -23,9 +23,9 @@ namespace media {
namespace mp4 { namespace mp4 {
struct SampleInfo { struct SampleInfo {
int size; int64_t size;
int duration; int64_t duration;
int cts_offset; int64_t cts_offset;
bool is_keyframe; bool is_keyframe;
}; };

View File

@ -268,7 +268,7 @@ TEST_F(TrackRunIteratorTest, ReorderingTest) {
// would simply be [0, 3, -2]. Since CTS offsets should be non-negative for // 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]. // maximum compatibility, these values are biased up to [2, 5, 0].
MovieFragment moof = CreateFragment(); MovieFragment moof = CreateFragment();
std::vector<int32_t>& cts_offsets = std::vector<int64_t>& cts_offsets =
moof.tracks[1].runs[0].sample_composition_time_offsets; moof.tracks[1].runs[0].sample_composition_time_offsets;
cts_offsets.resize(10); cts_offsets.resize(10);
cts_offsets[0] = 2; cts_offsets[0] = 2;