summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorkqyang@chromium.org <kqyang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-09 11:59:28 +0000
committerkqyang@chromium.org <kqyang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-09 11:59:28 +0000
commit32181c608fdd05eb6f6aab668607d71e7b890fcc (patch)
treef254e698f56db81ca3deabef701a3342cfa55a6f /media
parent7499b25162978b51acb0a98899977936d6deaa2d (diff)
downloadchromium_src-32181c608fdd05eb6f6aab668607d71e7b890fcc.zip
chromium_src-32181c608fdd05eb6f6aab668607d71e7b890fcc.tar.gz
chromium_src-32181c608fdd05eb6f6aab668607d71e7b890fcc.tar.bz2
Support CENC Sample Groups in MP4 Parser
Required for key rotation support. BUG=367366 Review URL: https://codereview.chromium.org/263773005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269241 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/formats/mp4/mp4_stream_parser.cc6
-rw-r--r--media/formats/mp4/mp4_stream_parser_unittest.cc6
-rw-r--r--media/formats/mp4/track_run_iterator.cc99
-rw-r--r--media/formats/mp4/track_run_iterator.h9
-rw-r--r--media/formats/mp4/track_run_iterator_unittest.cc115
5 files changed, 217 insertions, 18 deletions
diff --git a/media/formats/mp4/mp4_stream_parser.cc b/media/formats/mp4/mp4_stream_parser.cc
index 76fdeaf..a46178b 100644
--- a/media/formats/mp4/mp4_stream_parser.cc
+++ b/media/formats/mp4/mp4_stream_parser.cc
@@ -428,12 +428,6 @@ bool MP4StreamParser::EnqueueSample(BufferQueue* audio_buffers,
if (!audio && !video)
runs_->AdvanceRun();
- // AuxInfo is required for encrypted samples.
- // See ISO Common Encryption spec: ISO/IEC FDIS 23001-7:2011(E);
- // Section 7: Common Encryption Sample Auxiliary Information.
- if (runs_->is_encrypted() && !runs_->aux_info_size())
- return false;
-
// Attempt to cache the auxiliary information first. Aux info is usually
// placed in a contiguous block before the sample data, rather than being
// interleaved. If we didn't cache it, this would require that we retain the
diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc
index d19e47c..ef0bd44 100644
--- a/media/formats/mp4/mp4_stream_parser_unittest.cc
+++ b/media/formats/mp4/mp4_stream_parser_unittest.cc
@@ -209,7 +209,11 @@ TEST_F(MP4StreamParserTest, NoMoovAfterFlush) {
// SampleAuxiliaryInformation{Sizes|Offsets}Box (saiz|saio) are missing.
// The parser should fail instead of crash. See http://crbug.com/361347
TEST_F(MP4StreamParserTest, MissingSampleAuxInfo) {
- ParseMP4File("bear-1280x720-a_frag-cenc_missing-saiz-saio.mp4", 512);
+ InitializeParser();
+
+ scoped_refptr<DecoderBuffer> buffer =
+ ReadTestDataFile("bear-1280x720-a_frag-cenc_missing-saiz-saio.mp4");
+ EXPECT_FALSE(AppendDataInPieces(buffer->data(), buffer->data_size(), 512));
}
// Test a file where all video samples start with an Access Unit
diff --git a/media/formats/mp4/track_run_iterator.cc b/media/formats/mp4/track_run_iterator.cc
index bc38352..2fff4b3 100644
--- a/media/formats/mp4/track_run_iterator.cc
+++ b/media/formats/mp4/track_run_iterator.cc
@@ -9,6 +9,7 @@
#include "media/base/buffers.h"
#include "media/base/stream_parser_buffer.h"
#include "media/formats/mp4/rcheck.h"
+#include "media/formats/mp4/sample_to_group_iterator.h"
namespace {
static const uint32 kSampleIsDifferenceSampleFlagMask = 0x10000;
@@ -22,6 +23,7 @@ struct SampleInfo {
int duration;
int cts_offset;
bool is_keyframe;
+ uint32 cenc_group_description_index;
};
struct TrackRunInfo {
@@ -40,6 +42,8 @@ struct TrackRunInfo {
std::vector<uint8> aux_info_sizes; // Populated if default_size == 0.
int aux_info_total_size;
+ std::vector<CencSampleEncryptionInfoEntry> sample_encryption_info;
+
TrackRunInfo();
~TrackRunInfo();
};
@@ -215,6 +219,9 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
}
}
+ SampleToGroupIterator sample_to_group_itr(traf.sample_to_group);
+ bool is_sample_to_group_valid = sample_to_group_itr.IsValid();
+
int64 run_start_dts = traf.decode_time.decode_time;
int sample_count_sum = 0;
bool is_sync_sample_box_present =
@@ -226,6 +233,7 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
tri.timescale = trak->media.header.timescale;
tri.start_dts = run_start_dts;
tri.sample_start_offset = trun.data_offset;
+ tri.sample_encryption_info = traf.sample_group_description.entries;
tri.is_audio = (stsd.type == kAudio);
if (tri.is_audio) {
@@ -289,10 +297,41 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
// and downstream code's "is keyframe" concept.
if (!is_sync_sample_box_present)
tri.samples[k].is_keyframe = true;
+
+ if (!is_sample_to_group_valid) {
+ // Set group description index to 0 to read encryption information
+ // from TrackEncryption Box.
+ tri.samples[k].cenc_group_description_index = 0;
+ continue;
+ }
+
+ // ISO-14496-12 Section 8.9.2.3 and 8.9.4 : group description index
+ // (1) ranges from 1 to the number of sample group entries in the track
+ // level SampleGroupDescription Box, or (2) takes the value 0 to
+ // indicate that this sample is a member of no group, in this case, the
+ // sample is associated with the default values specified in
+ // TrackEncryption Box, or (3) starts at 0x10001, i.e. the index value
+ // 1, with the value 1 in the top 16 bits, to reference fragment-local
+ // SampleGroupDescription Box.
+ // Case (1) is not supported currently. We might not need it either as
+ // the same functionality can be better achieved using (2).
+ uint32 index = sample_to_group_itr.group_description_index();
+ if (index >= SampleToGroupEntry::kFragmentGroupDescriptionIndexBase) {
+ index -= SampleToGroupEntry::kFragmentGroupDescriptionIndexBase;
+ RCHECK(index != 0 && index <= tri.sample_encryption_info.size());
+ } else if (index != 0) {
+ NOTIMPLEMENTED() << "'sgpd' box in 'moov' is not supported.";
+ return false;
+ }
+ tri.samples[k].cenc_group_description_index = index;
+ is_sample_to_group_valid = sample_to_group_itr.Advance();
}
runs_.push_back(tri);
sample_count_sum += trun.sample_count;
}
+
+ // We should have iterated through all samples in SampleToGroup Box.
+ RCHECK(!sample_to_group_itr.IsValid());
}
std::sort(runs_.begin(), runs_.end(), CompareMinTrackRunDataOffset());
@@ -325,7 +364,7 @@ void TrackRunIterator::AdvanceSample() {
// info is available in the stream.
bool TrackRunIterator::AuxInfoNeedsToBeCached() {
DCHECK(IsRunValid());
- return is_encrypted() && aux_info_size() > 0 && cenc_info_.size() == 0;
+ return aux_info_size() > 0 && cenc_info_.size() == 0;
}
// This implementation currently only caches CENC auxiliary info.
@@ -339,8 +378,10 @@ bool TrackRunIterator::CacheAuxInfo(const uint8* buf, int buf_size) {
if (!info_size)
info_size = run_itr_->aux_info_sizes[i];
- BufferReader reader(buf + pos, info_size);
- RCHECK(cenc_info_[i].Parse(track_encryption().default_iv_size, &reader));
+ if (IsSampleEncrypted(i)) {
+ BufferReader reader(buf + pos, info_size);
+ RCHECK(cenc_info_[i].Parse(GetIvSize(i), &reader));
+ }
pos += info_size;
}
@@ -387,8 +428,8 @@ uint32 TrackRunIterator::track_id() const {
}
bool TrackRunIterator::is_encrypted() const {
- DCHECK(IsRunValid());
- return track_encryption().is_encrypted;
+ DCHECK(IsSampleValid());
+ return IsSampleEncrypted(sample_itr_ - run_itr_->samples.begin());
}
int64 TrackRunIterator::aux_info_offset() const {
@@ -454,10 +495,17 @@ const TrackEncryption& TrackRunIterator::track_encryption() const {
}
scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
+ DCHECK(is_encrypted());
+
+ if (cenc_info_.empty()) {
+ DCHECK_EQ(0, aux_info_size());
+ MEDIA_LOG(log_cb_) << "Aux Info is not available.";
+ return scoped_ptr<DecryptConfig>();
+ }
+
size_t sample_idx = sample_itr_ - run_itr_->samples.begin();
- DCHECK(sample_idx < cenc_info_.size());
+ DCHECK_LT(sample_idx, cenc_info_.size());
const FrameCENCInfo& cenc_info = cenc_info_[sample_idx];
- DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached());
size_t total_size = 0;
if (!cenc_info.subsamples.empty() &&
@@ -467,7 +515,7 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
return scoped_ptr<DecryptConfig>();
}
- const std::vector<uint8>& kid = track_encryption().default_kid;
+ const std::vector<uint8>& kid = GetKeyId(sample_idx);
return scoped_ptr<DecryptConfig>(new DecryptConfig(
std::string(reinterpret_cast<const char*>(&kid[0]), kid.size()),
std::string(reinterpret_cast<const char*>(cenc_info.iv),
@@ -475,5 +523,40 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
cenc_info.subsamples));
}
+uint32 TrackRunIterator::GetGroupDescriptionIndex(uint32 sample_index) const {
+ DCHECK(IsRunValid());
+ DCHECK_LT(sample_index, run_itr_->samples.size());
+ return run_itr_->samples[sample_index].cenc_group_description_index;
+}
+
+const CencSampleEncryptionInfoEntry&
+TrackRunIterator::GetSampleEncryptionInfoEntry(
+ uint32 group_description_index) const {
+ DCHECK(IsRunValid());
+ DCHECK_NE(group_description_index, 0u);
+ DCHECK_LE(group_description_index, run_itr_->sample_encryption_info.size());
+ // |group_description_index| is 1-based. Subtract by 1 to index the vector.
+ return run_itr_->sample_encryption_info[group_description_index - 1];
+}
+
+bool TrackRunIterator::IsSampleEncrypted(size_t sample_index) const {
+ uint32 index = GetGroupDescriptionIndex(sample_index);
+ return (index == 0) ? track_encryption().is_encrypted
+ : GetSampleEncryptionInfoEntry(index).is_encrypted;
+}
+
+const std::vector<uint8>& TrackRunIterator::GetKeyId(
+ size_t sample_index) const {
+ uint32 index = GetGroupDescriptionIndex(sample_index);
+ return (index == 0) ? track_encryption().default_kid
+ : GetSampleEncryptionInfoEntry(index).key_id;
+}
+
+uint8 TrackRunIterator::GetIvSize(size_t sample_index) const {
+ uint32 index = GetGroupDescriptionIndex(sample_index);
+ return (index == 0) ? track_encryption().default_iv_size
+ : GetSampleEncryptionInfoEntry(index).iv_size;
+}
+
} // namespace mp4
} // namespace media
diff --git a/media/formats/mp4/track_run_iterator.h b/media/formats/mp4/track_run_iterator.h
index 829dd11..8167478 100644
--- a/media/formats/mp4/track_run_iterator.h
+++ b/media/formats/mp4/track_run_iterator.h
@@ -87,6 +87,15 @@ class MEDIA_EXPORT TrackRunIterator {
void ResetRun();
const TrackEncryption& track_encryption() const;
+ uint32 GetGroupDescriptionIndex(uint32 sample_index) const;
+ const CencSampleEncryptionInfoEntry& GetSampleEncryptionInfoEntry(
+ uint32 group_description_index) const;
+
+ // Sample encryption information.
+ bool IsSampleEncrypted(size_t sample_index) const;
+ uint8 GetIvSize(size_t sample_index) const;
+ const std::vector<uint8>& GetKeyId(size_t sample_index) const;
+
const Movie* moov_;
LogCB log_cb_;
diff --git a/media/formats/mp4/track_run_iterator_unittest.cc b/media/formats/mp4/track_run_iterator_unittest.cc
index ea37bab..2ef3f83 100644
--- a/media/formats/mp4/track_run_iterator_unittest.cc
+++ b/media/formats/mp4/track_run_iterator_unittest.cc
@@ -37,6 +37,11 @@ static const uint8 kKeyId[] = {
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44
};
+static const uint8 kCencSampleGroupKeyId[] = {
+ 0x46, 0x72, 0x61, 0x67, 0x53, 0x61, 0x6d, 0x70,
+ 0x6c, 0x65, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b
+};
+
namespace media {
namespace mp4 {
@@ -133,9 +138,28 @@ class TrackRunIteratorTest : public testing::Test {
sinf->type.type = FOURCC_CENC;
sinf->info.track_encryption.is_encrypted = true;
sinf->info.track_encryption.default_iv_size = 8;
- sinf->info.track_encryption.default_kid.insert(
- sinf->info.track_encryption.default_kid.begin(),
- kKeyId, kKeyId + arraysize(kKeyId));
+ sinf->info.track_encryption.default_kid.assign(kKeyId,
+ kKeyId + arraysize(kKeyId));
+ }
+
+ // Add SampleGroupDescription Box with two entries (an unencrypted entry and
+ // an encrypted entry). Populate SampleToGroup Box from input array.
+ void AddCencSampleGroup(TrackFragment* frag,
+ const SampleToGroupEntry* sample_to_group_entries,
+ size_t num_entries) {
+ frag->sample_group_description.grouping_type = FOURCC_SEIG;
+ frag->sample_group_description.entries.resize(2);
+ frag->sample_group_description.entries[0].is_encrypted = false;
+ frag->sample_group_description.entries[0].iv_size = 0;
+ frag->sample_group_description.entries[1].is_encrypted = true;
+ frag->sample_group_description.entries[1].iv_size = 8;
+ frag->sample_group_description.entries[1].key_id.assign(
+ kCencSampleGroupKeyId,
+ kCencSampleGroupKeyId + arraysize(kCencSampleGroupKeyId));
+
+ frag->sample_to_group.grouping_type = FOURCC_SEIG;
+ frag->sample_to_group.entries.assign(sample_to_group_entries,
+ sample_to_group_entries + num_entries);
}
// Add aux info covering the first track run to a TrackFragment, and update
@@ -149,6 +173,20 @@ class TrackRunIteratorTest : public testing::Test {
frag->runs[0].sample_sizes[1] = 10;
}
+ bool InitMoofWithArbitraryAuxInfo(MovieFragment* moof) {
+ // Add aux info header (equal sized aux info for every sample).
+ for (uint32 i = 0; i < moof->tracks.size(); ++i) {
+ moof->tracks[i].auxiliary_offset.offsets.push_back(50);
+ moof->tracks[i].auxiliary_size.sample_count = 10;
+ moof->tracks[i].auxiliary_size.default_sample_info_size = 8;
+ }
+
+ // We don't care about the actual data in aux.
+ std::vector<uint8> aux_info(1000);
+ return iter_->Init(*moof) &&
+ iter_->CacheAuxInfo(&aux_info[0], aux_info.size());
+ }
+
void SetAscending(std::vector<uint32>* vec) {
vec->resize(10);
for (size_t i = 0; i < vec->size(); i++)
@@ -356,6 +394,77 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTest) {
EXPECT_EQ(config->subsamples()[1].cypher_bytes, 4u);
}
+TEST_F(TrackRunIteratorTest, CencSampleGroupTest) {
+ MovieFragment moof = CreateFragment();
+
+ const SampleToGroupEntry kSampleToGroupTable[] = {
+ // Associated with the second entry in SampleGroupDescription Box.
+ {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 2},
+ // Associated with the first entry in SampleGroupDescription Box.
+ {1, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 1}};
+ AddCencSampleGroup(
+ &moof.tracks[0], kSampleToGroupTable, arraysize(kSampleToGroupTable));
+
+ iter_.reset(new TrackRunIterator(&moov_, log_cb_));
+ ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof));
+
+ std::string cenc_sample_group_key_id(
+ kCencSampleGroupKeyId,
+ kCencSampleGroupKeyId + arraysize(kCencSampleGroupKeyId));
+ // The first sample is encrypted and the second sample is unencrypted.
+ EXPECT_TRUE(iter_->is_encrypted());
+ EXPECT_EQ(cenc_sample_group_key_id, iter_->GetDecryptConfig()->key_id());
+ iter_->AdvanceSample();
+ EXPECT_FALSE(iter_->is_encrypted());
+}
+
+TEST_F(TrackRunIteratorTest, CencSampleGroupWithTrackEncryptionBoxTest) {
+ // Add TrackEncryption Box.
+ AddEncryption(&moov_.tracks[0]);
+
+ MovieFragment moof = CreateFragment();
+
+ const SampleToGroupEntry kSampleToGroupTable[] = {
+ // Associated with the second entry in SampleGroupDescription Box.
+ {2, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 2},
+ // Associated with the default values specified in TrackEncryption Box.
+ {4, 0},
+ // Associated with the first entry in SampleGroupDescription Box.
+ {3, SampleToGroupEntry::kFragmentGroupDescriptionIndexBase + 1}};
+ AddCencSampleGroup(
+ &moof.tracks[0], kSampleToGroupTable, arraysize(kSampleToGroupTable));
+
+ iter_.reset(new TrackRunIterator(&moov_, log_cb_));
+ ASSERT_TRUE(InitMoofWithArbitraryAuxInfo(&moof));
+
+ std::string track_encryption_key_id(kKeyId, kKeyId + arraysize(kKeyId));
+ std::string cenc_sample_group_key_id(
+ kCencSampleGroupKeyId,
+ kCencSampleGroupKeyId + arraysize(kCencSampleGroupKeyId));
+
+ for (size_t i = 0; i < kSampleToGroupTable[0].sample_count; ++i) {
+ EXPECT_TRUE(iter_->is_encrypted());
+ EXPECT_EQ(cenc_sample_group_key_id, iter_->GetDecryptConfig()->key_id());
+ iter_->AdvanceSample();
+ }
+
+ for (size_t i = 0; i < kSampleToGroupTable[1].sample_count; ++i) {
+ EXPECT_TRUE(iter_->is_encrypted());
+ EXPECT_EQ(track_encryption_key_id, iter_->GetDecryptConfig()->key_id());
+ iter_->AdvanceSample();
+ }
+
+ for (size_t i = 0; i < kSampleToGroupTable[2].sample_count; ++i) {
+ EXPECT_FALSE(iter_->is_encrypted());
+ iter_->AdvanceSample();
+ }
+
+ // The remaining samples should be associated with the default values
+ // specified in TrackEncryption Box.
+ EXPECT_TRUE(iter_->is_encrypted());
+ EXPECT_EQ(track_encryption_key_id, iter_->GetDecryptConfig()->key_id());
+}
+
// It is legal for aux info blocks to be shared among multiple formats.
TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
AddEncryption(&moov_.tracks[0]);