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:
parent
3d4872ec77
commit
0ac14327c2
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue