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) {
|
||||
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);
|
||||
|
|
|
@ -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<uint32_t> sample_flags;
|
||||
std::vector<uint32_t> sample_sizes;
|
||||
std::vector<uint32_t> sample_durations;
|
||||
std::vector<int32_t> sample_composition_time_offsets;
|
||||
std::vector<int64_t> sample_composition_time_offsets;
|
||||
};
|
||||
|
||||
struct SampleToGroupEntry {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<CompositionOffset>::const_iterator it =
|
||||
composition_offset_table_.begin();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -35,7 +35,7 @@ class CompositionOffsetIteratorTest : public testing::Test {
|
|||
}
|
||||
|
||||
protected:
|
||||
std::vector<int32_t> composition_offset_table_;
|
||||
std::vector<int64_t> composition_offset_table_;
|
||||
CompositionTimeToSample composition_time_to_sample_;
|
||||
scoped_ptr<CompositionOffsetIterator> composition_offset_iterator_;
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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<int32_t>& cts_offsets =
|
||||
std::vector<int64_t>& cts_offsets =
|
||||
moof.tracks[1].runs[0].sample_composition_time_offsets;
|
||||
cts_offsets.resize(10);
|
||||
cts_offsets[0] = 2;
|
||||
|
|
Loading…
Reference in New Issue