Implement SampleEncryption box parsing and generation
Issue #41 Change-Id: Ic57436b6e6c8b58fc264037fd81c98627c525932
This commit is contained in:
parent
c0df6b3239
commit
098e087459
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
bandwidth: 128635
|
||||
bandwidth: 128728
|
||||
audio_info {
|
||||
codec: "mp4a.40.2"
|
||||
sampling_frequency: 44100
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -2,7 +2,7 @@
|
|||
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S">
|
||||
<Period>
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subSegmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="885058" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<Representation id="0" bandwidth="885151" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
|
@ -14,7 +14,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" subSegmentAlignment="true">
|
||||
<Representation id="1" bandwidth="128635" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="128728" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
</ContentProtection>
|
||||
<Representation id="0" bandwidth="885058" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<Representation id="0" bandwidth="885151" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<BaseURL>output_video.mp4</BaseURL>
|
||||
<SegmentBase indexRange="941-1008" timescale="30000">
|
||||
<Initialization range="0-940"/>
|
||||
|
@ -18,7 +18,7 @@
|
|||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
</ContentProtection>
|
||||
<Representation id="1" bandwidth="128635" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="128728" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<BaseURL>output_audio.mp4</BaseURL>
|
||||
<SegmentBase indexRange="817-884" timescale="44100">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="875528" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<Representation id="0" bandwidth="875620" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
|
@ -16,7 +16,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<Representation id="1" bandwidth="124085" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="124195" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
</ContentProtection>
|
||||
<Representation id="0" bandwidth="875528" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<Representation id="0" bandwidth="875620" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<SegmentTemplate timescale="30000" initialization="output_video-init.mp4" media="output_video-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="2002" d="30030" r="1"/>
|
||||
|
@ -20,7 +20,7 @@
|
|||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
</ContentProtection>
|
||||
<Representation id="1" bandwidth="124085" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="124195" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<SegmentTemplate timescale="44100" initialization="output_audio-init.mp4" media="output_audio-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<MPD xmlns="urn:mpeg:DASH:schema:MPD:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<Representation id="0" bandwidth="876377" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<Representation id="0" bandwidth="876470" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"/>
|
||||
<SegmentTemplate timescale="30000" initialization="output_video-init.mp4" media="output_video-$Number$.m4s" startNumber="1">
|
||||
|
@ -14,7 +14,7 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<Representation id="1" bandwidth="125028" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="125138" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"/>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"/>
|
||||
<Representation id="0" bandwidth="876377" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<Representation id="0" bandwidth="876470" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
|
||||
<SegmentTemplate timescale="30000" initialization="output_video-init.mp4" media="output_video-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
<S t="2002" d="30030" r="1"/>
|
||||
|
@ -16,7 +16,7 @@
|
|||
<AdaptationSet id="1" contentType="audio" segmentAlignment="true">
|
||||
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="00000000-0000-0000-0000-000000000000"/>
|
||||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"/>
|
||||
<Representation id="1" bandwidth="125028" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<Representation id="1" bandwidth="125138" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
|
||||
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
|
||||
<SegmentTemplate timescale="44100" initialization="output_audio-init.mp4" media="output_audio-$Number$.m4s" startNumber="1">
|
||||
<SegmentTimeline>
|
||||
|
|
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
bandwidth: 885058
|
||||
bandwidth: 885151
|
||||
video_info {
|
||||
codec: "avc1.64001e"
|
||||
width: 640
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,93 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "packager/media/formats/mp4/cenc.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "packager/media/base/buffer_reader.h"
|
||||
#include "packager/media/base/buffer_writer.h"
|
||||
#include "packager/media/formats/mp4/rcheck.h"
|
||||
|
||||
namespace {
|
||||
// According to ISO/IEC FDIS 23001-7: CENC spec, IV should be either
|
||||
// 64-bit (8-byte) or 128-bit (16-byte).
|
||||
bool IsIvSizeValid(size_t iv_size) { return iv_size == 8 || iv_size == 16; }
|
||||
|
||||
// 16-bit |clear_bytes| and 32-bit |cipher_bytes|.
|
||||
const size_t kSubsampleEntrySize = sizeof(uint16_t) + sizeof(uint32_t);
|
||||
} // namespace
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
namespace mp4 {
|
||||
|
||||
FrameCENCInfo::FrameCENCInfo() {}
|
||||
FrameCENCInfo::FrameCENCInfo(const std::vector<uint8_t>& iv) : iv_(iv) {
|
||||
}
|
||||
FrameCENCInfo::~FrameCENCInfo() {}
|
||||
|
||||
bool FrameCENCInfo::Parse(uint8_t iv_size, BufferReader* reader) {
|
||||
DCHECK(reader);
|
||||
// Mandated by CENC spec.
|
||||
RCHECK(IsIvSizeValid(iv_size));
|
||||
|
||||
iv_.resize(iv_size);
|
||||
RCHECK(reader->ReadToVector(&iv_, iv_size));
|
||||
|
||||
if (!reader->HasBytes(1))
|
||||
return true;
|
||||
|
||||
uint16_t subsample_count;
|
||||
RCHECK(reader->Read2(&subsample_count) &&
|
||||
reader->HasBytes(subsample_count * kSubsampleEntrySize));
|
||||
|
||||
subsamples_.resize(subsample_count);
|
||||
for (uint16_t i = 0; i < subsample_count; ++i) {
|
||||
uint16_t clear_bytes;
|
||||
uint32_t cipher_bytes;
|
||||
RCHECK(reader->Read2(&clear_bytes) &&
|
||||
reader->Read4(&cipher_bytes));
|
||||
subsamples_[i].clear_bytes = clear_bytes;
|
||||
subsamples_[i].cipher_bytes = cipher_bytes;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FrameCENCInfo::Write(BufferWriter* writer) const {
|
||||
DCHECK(writer);
|
||||
DCHECK(IsIvSizeValid(iv_.size()));
|
||||
writer->AppendVector(iv_);
|
||||
|
||||
uint16_t subsample_count = subsamples_.size();
|
||||
if (subsample_count == 0)
|
||||
return;
|
||||
writer->AppendInt(subsample_count);
|
||||
|
||||
for (uint16_t i = 0; i < subsample_count; ++i) {
|
||||
writer->AppendInt(subsamples_[i].clear_bytes);
|
||||
writer->AppendInt(subsamples_[i].cipher_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
size_t FrameCENCInfo::ComputeSize() const {
|
||||
uint16_t subsample_count = subsamples_.size();
|
||||
if (subsample_count == 0)
|
||||
return iv_.size();
|
||||
|
||||
return iv_.size() + sizeof(subsample_count) +
|
||||
subsample_count * kSubsampleEntrySize;
|
||||
}
|
||||
|
||||
size_t FrameCENCInfo::GetTotalSizeOfSubsamples() const {
|
||||
size_t size = 0;
|
||||
for (size_t i = 0; i < subsamples_.size(); ++i) {
|
||||
size += subsamples_[i].clear_bytes + subsamples_[i].cipher_bytes;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
} // namespace mp4
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef MEDIA_FORMATS_MP4_CENC_H_
|
||||
#define MEDIA_FORMATS_MP4_CENC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "packager/media/base/decrypt_config.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
||||
class BufferReader;
|
||||
class BufferWriter;
|
||||
|
||||
namespace mp4 {
|
||||
|
||||
class FrameCENCInfo {
|
||||
public:
|
||||
FrameCENCInfo();
|
||||
explicit FrameCENCInfo(const std::vector<uint8_t>& iv);
|
||||
~FrameCENCInfo();
|
||||
|
||||
bool Parse(uint8_t iv_size, BufferReader* reader);
|
||||
void Write(BufferWriter* writer) const;
|
||||
size_t ComputeSize() const;
|
||||
size_t GetTotalSizeOfSubsamples() const;
|
||||
|
||||
void AddSubsample(const SubsampleEntry& subsample) {
|
||||
subsamples_.push_back(subsample);
|
||||
}
|
||||
|
||||
const std::vector<uint8_t>& iv() const { return iv_; }
|
||||
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
|
||||
|
||||
private:
|
||||
std::vector<uint8_t> iv_;
|
||||
std::vector<SubsampleEntry> subsamples_;
|
||||
|
||||
// Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
|
||||
// generated copy constructor and assignment operator.
|
||||
};
|
||||
|
||||
} // namespace mp4
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
||||
|
||||
#endif // MEDIA_FORMATS_MP4_CENC_H_
|
|
@ -13,7 +13,6 @@
|
|||
#include "packager/media/filters/vp8_parser.h"
|
||||
#include "packager/media/filters/vp9_parser.h"
|
||||
#include "packager/media/formats/mp4/box_definitions.h"
|
||||
#include "packager/media/formats/mp4/cenc.h"
|
||||
|
||||
namespace {
|
||||
// Generate 64bit IV by default.
|
||||
|
@ -67,6 +66,11 @@ Status EncryptingFragmenter::InitializeFragment(int64_t first_sample_dts) {
|
|||
|
||||
traf()->auxiliary_size.sample_info_sizes.clear();
|
||||
traf()->auxiliary_offset.offsets.clear();
|
||||
if (IsSubsampleEncryptionRequired()) {
|
||||
traf()->sample_encryption.flags |=
|
||||
SampleEncryption::kUseSubsampleEncryption;
|
||||
}
|
||||
traf()->sample_encryption.sample_encryption_entries.clear();
|
||||
|
||||
const bool enable_encryption = clear_time_ <= 0;
|
||||
if (!enable_encryption) {
|
||||
|
@ -116,6 +120,7 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() {
|
|||
DCHECK(!IsSubsampleEncryptionRequired());
|
||||
saiz.default_sample_info_size = encryptor_->iv().size();
|
||||
}
|
||||
traf()->sample_encryption.iv_size = encryptor_->iv().size();
|
||||
}
|
||||
|
||||
Status EncryptingFragmenter::CreateEncryptor() {
|
||||
|
@ -141,7 +146,8 @@ void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) {
|
|||
Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
||||
DCHECK(encryptor_);
|
||||
|
||||
FrameCENCInfo cenc_info(encryptor_->iv());
|
||||
SampleEncryptionEntry sample_encryption_entry;
|
||||
sample_encryption_entry.initialization_vector = encryptor_->iv();
|
||||
uint8_t* data = sample->writable_data();
|
||||
if (IsSubsampleEncryptionRequired()) {
|
||||
if (vpx_parser_) {
|
||||
|
@ -155,7 +161,7 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
|||
subsample.clear_bytes = frame.uncompressed_header_size;
|
||||
subsample.cipher_bytes =
|
||||
frame.frame_size - frame.uncompressed_header_size;
|
||||
cenc_info.AddSubsample(subsample);
|
||||
sample_encryption_entry.subsamples.push_back(subsample);
|
||||
if (subsample.cipher_bytes > 0)
|
||||
EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes);
|
||||
data += frame.frame_size;
|
||||
|
@ -167,27 +173,30 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
|
|||
if (!reader.ReadNBytesInto8(&nalu_length, nalu_length_size_))
|
||||
return Status(error::MUXER_FAILURE, "Fail to read nalu_length.");
|
||||
|
||||
SubsampleEntry subsample;
|
||||
subsample.clear_bytes = nalu_length_size_ + 1;
|
||||
subsample.cipher_bytes = nalu_length - 1;
|
||||
if (!reader.SkipBytes(nalu_length)) {
|
||||
return Status(error::MUXER_FAILURE,
|
||||
"Sample size does not match nalu_length.");
|
||||
}
|
||||
|
||||
SubsampleEntry subsample;
|
||||
subsample.clear_bytes = nalu_length_size_ + 1;
|
||||
subsample.cipher_bytes = nalu_length - 1;
|
||||
sample_encryption_entry.subsamples.push_back(subsample);
|
||||
|
||||
EncryptBytes(data + subsample.clear_bytes, subsample.cipher_bytes);
|
||||
cenc_info.AddSubsample(subsample);
|
||||
data += nalu_length_size_ + nalu_length;
|
||||
}
|
||||
}
|
||||
|
||||
// The length of per-sample auxiliary datum, defined in CENC ch. 7.
|
||||
traf()->auxiliary_size.sample_info_sizes.push_back(cenc_info.ComputeSize());
|
||||
traf()->auxiliary_size.sample_info_sizes.push_back(
|
||||
sample_encryption_entry.ComputeSize());
|
||||
} else {
|
||||
EncryptBytes(data, sample->data_size());
|
||||
}
|
||||
|
||||
cenc_info.Write(aux_data());
|
||||
traf()->sample_encryption.sample_encryption_entries.push_back(
|
||||
sample_encryption_entry);
|
||||
encryptor_->UpdateIv();
|
||||
return Status::OK;
|
||||
}
|
||||
|
|
|
@ -84,7 +84,6 @@ Status Fragmenter::InitializeFragment(int64_t first_sample_dts) {
|
|||
earliest_presentation_time_ = kInvalidTime;
|
||||
first_sap_time_ = kInvalidTime;
|
||||
data_.reset(new BufferWriter());
|
||||
aux_data_.reset(new BufferWriter());
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@ class Fragmenter {
|
|||
bool fragment_initialized() const { return fragment_initialized_; }
|
||||
bool fragment_finalized() const { return fragment_finalized_; }
|
||||
BufferWriter* data() { return data_.get(); }
|
||||
BufferWriter* aux_data() { return aux_data_.get(); }
|
||||
|
||||
protected:
|
||||
TrackFragment* traf() { return traf_; }
|
||||
|
@ -82,7 +81,6 @@ class Fragmenter {
|
|||
int64_t earliest_presentation_time_;
|
||||
int64_t first_sap_time_;
|
||||
scoped_ptr<BufferWriter> data_;
|
||||
scoped_ptr<BufferWriter> aux_data_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Fragmenter);
|
||||
};
|
||||
|
|
|
@ -22,8 +22,6 @@
|
|||
'box_definitions.h',
|
||||
'box_reader.cc',
|
||||
'box_reader.h',
|
||||
'cenc.cc',
|
||||
'cenc.h',
|
||||
'chunk_info_iterator.cc',
|
||||
'chunk_info_iterator.h',
|
||||
'composition_offset_iterator.cc',
|
||||
|
|
|
@ -25,6 +25,10 @@ namespace edash_packager {
|
|||
namespace media {
|
||||
|
||||
namespace {
|
||||
const char kKey[] =
|
||||
"\xeb\xdd\x62\xf1\x68\x14\xd2\x7b\x68\xef\x12\x2a\xfc\xe4\xae\x3c";
|
||||
const char kKeyId[] = "0123456789012345";
|
||||
|
||||
class MockKeySource : public KeySource {
|
||||
public:
|
||||
MOCK_METHOD1(FetchKeys, Status(const std::vector<uint8_t>& pssh_data));
|
||||
|
@ -225,7 +229,7 @@ TEST_F(MP4MediaParserTest, NON_FRAGMENTED_MP4) {
|
|||
|
||||
TEST_F(MP4MediaParserTest, CencWithoutDecryptionSource) {
|
||||
// Parsing should fail but it will get the streams successfully.
|
||||
EXPECT_FALSE(ParseMP4File("bear-640x360-v_frag-cenc.mp4", 512));
|
||||
EXPECT_FALSE(ParseMP4File("bear-640x360-v_frag-cenc-aux.mp4", 512));
|
||||
EXPECT_EQ(1u, num_streams_);
|
||||
}
|
||||
|
||||
|
@ -233,20 +237,16 @@ TEST_F(MP4MediaParserTest, CencInitWithoutDecryptionSource) {
|
|||
InitializeParser(NULL);
|
||||
|
||||
std::vector<uint8_t> buffer =
|
||||
ReadTestDataFile("bear-640x360-v_frag-cenc.mp4");
|
||||
ReadTestDataFile("bear-640x360-v_frag-cenc-aux.mp4");
|
||||
const int kFirstMoofOffset = 1646;
|
||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), kFirstMoofOffset, 512));
|
||||
EXPECT_EQ(1u, num_streams_);
|
||||
}
|
||||
|
||||
TEST_F(MP4MediaParserTest, CencWithDecryptionSource) {
|
||||
TEST_F(MP4MediaParserTest, CencWithDecryptionSourceAndAuxInMdat) {
|
||||
MockKeySource mock_key_source;
|
||||
EXPECT_CALL(mock_key_source, FetchKeys(_)).WillOnce(Return(Status::OK));
|
||||
|
||||
const char kKey[] =
|
||||
"\xeb\xdd\x62\xf1\x68\x14\xd2\x7b\x68\xef\x12\x2a\xfc\xe4\xae\x3c";
|
||||
const char kKeyId[] = "0123456789012345";
|
||||
|
||||
EncryptionKey encryption_key;
|
||||
encryption_key.key.assign(kKey, kKey + strlen(kKey));
|
||||
EXPECT_CALL(mock_key_source,
|
||||
|
@ -256,7 +256,26 @@ TEST_F(MP4MediaParserTest, CencWithDecryptionSource) {
|
|||
InitializeParser(&mock_key_source);
|
||||
|
||||
std::vector<uint8_t> buffer =
|
||||
ReadTestDataFile("bear-640x360-v_frag-cenc.mp4");
|
||||
ReadTestDataFile("bear-640x360-v_frag-cenc-aux.mp4");
|
||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), buffer.size(), 512));
|
||||
EXPECT_EQ(1u, num_streams_);
|
||||
EXPECT_EQ(82u, num_samples_);
|
||||
}
|
||||
|
||||
TEST_F(MP4MediaParserTest, CencWithDecryptionSourceAndSenc) {
|
||||
MockKeySource mock_key_source;
|
||||
EXPECT_CALL(mock_key_source, FetchKeys(_)).WillOnce(Return(Status::OK));
|
||||
|
||||
EncryptionKey encryption_key;
|
||||
encryption_key.key.assign(kKey, kKey + strlen(kKey));
|
||||
EXPECT_CALL(mock_key_source,
|
||||
GetKey(std::vector<uint8_t>(kKeyId, kKeyId + strlen(kKeyId)), _))
|
||||
.WillOnce(DoAll(SetArgPointee<1>(encryption_key), Return(Status::OK)));
|
||||
|
||||
InitializeParser(&mock_key_source);
|
||||
|
||||
std::vector<uint8_t> buffer =
|
||||
ReadTestDataFile("bear-640x360-v_frag-cenc-senc.mp4");
|
||||
EXPECT_TRUE(AppendDataInPieces(buffer.data(), buffer.size(), 512));
|
||||
EXPECT_EQ(1u, num_streams_);
|
||||
EXPECT_EQ(82u, num_samples_);
|
||||
|
|
|
@ -385,39 +385,41 @@ Status Segmenter::FinalizeFragment(bool finalize_segment,
|
|||
}
|
||||
|
||||
MediaData mdat;
|
||||
// Fill in data offsets. Data offset base is moof size + mdat box size.
|
||||
// (mdat is still empty, mdat size is the same as mdat box size).
|
||||
uint64_t base = moof_->ComputeSize() + mdat.ComputeSize();
|
||||
// Data offset relative to 'moof': moof size + mdat header size.
|
||||
// The code will also update box sizes for moof_ and its child boxes.
|
||||
uint64_t data_offset = moof_->ComputeSize() + mdat.HeaderSize();
|
||||
// 'traf' should follow 'mfhd' moof header box.
|
||||
uint64_t next_traf_position = moof_->HeaderSize() + moof_->header.box_size();
|
||||
for (size_t i = 0; i < moof_->tracks.size(); ++i) {
|
||||
TrackFragment& traf = moof_->tracks[i];
|
||||
Fragmenter* fragmenter = fragmenters_[i];
|
||||
if (fragmenter->aux_data()->Size() > 0) {
|
||||
traf.auxiliary_offset.offsets[0] += base;
|
||||
base += fragmenter->aux_data()->Size();
|
||||
if (traf.auxiliary_offset.offsets.size() > 0) {
|
||||
DCHECK_EQ(traf.auxiliary_offset.offsets.size(), 1u);
|
||||
DCHECK(!traf.sample_encryption.sample_encryption_entries.empty());
|
||||
|
||||
next_traf_position += traf.box_size();
|
||||
// SampleEncryption 'senc' box should be the last box in 'traf'.
|
||||
// |auxiliary_offset| should point to the data of SampleEncryption.
|
||||
traf.auxiliary_offset.offsets[0] =
|
||||
next_traf_position - traf.sample_encryption.box_size() +
|
||||
traf.sample_encryption.HeaderSize() +
|
||||
sizeof(uint32_t); // for sample count field in 'senc'
|
||||
}
|
||||
traf.runs[0].data_offset += base;
|
||||
base += fragmenter->data()->Size();
|
||||
traf.runs[0].data_offset = data_offset + mdat.data_size;
|
||||
mdat.data_size += fragmenters_[i]->data()->Size();
|
||||
}
|
||||
|
||||
// Generate segment reference.
|
||||
sidx_->references.resize(sidx_->references.size() + 1);
|
||||
fragmenters_[GetReferenceStreamId()]->GenerateSegmentReference(
|
||||
&sidx_->references[sidx_->references.size() - 1]);
|
||||
sidx_->references[sidx_->references.size() - 1].referenced_size = base;
|
||||
sidx_->references[sidx_->references.size() - 1].referenced_size =
|
||||
data_offset + mdat.data_size;
|
||||
|
||||
// Write the fragment to buffer.
|
||||
moof_->Write(fragment_buffer_.get());
|
||||
|
||||
for (size_t i = 0; i < moof_->tracks.size(); ++i) {
|
||||
Fragmenter* fragmenter = fragmenters_[i];
|
||||
mdat.data_size =
|
||||
fragmenter->aux_data()->Size() + fragmenter->data()->Size();
|
||||
mdat.WriteHeader(fragment_buffer_.get());
|
||||
if (fragmenter->aux_data()->Size()) {
|
||||
fragment_buffer_->AppendBuffer(*fragmenter->aux_data());
|
||||
}
|
||||
mdat.WriteHeader(fragment_buffer_.get());
|
||||
for (Fragmenter* fragmenter : fragmenters_)
|
||||
fragment_buffer_->AppendBuffer(*fragmenter->data());
|
||||
}
|
||||
|
||||
// Increase sequence_number for next fragment.
|
||||
++moof_->header.sequence_number;
|
||||
|
|
|
@ -40,6 +40,12 @@ struct TrackRunInfo {
|
|||
const AudioSampleEntry* audio_description;
|
||||
const VideoSampleEntry* video_description;
|
||||
|
||||
// Stores sample encryption entries, which is populated from 'senc' box if it
|
||||
// is available, otherwise will try to load from cenc auxiliary information.
|
||||
std::vector<SampleEncryptionEntry> sample_encryption_entries;
|
||||
|
||||
// These variables are useful to load |sample_encryption_entries| from cenc
|
||||
// auxiliary information when 'senc' box is not available.
|
||||
int64_t aux_info_start_offset; // Only valid if aux_info_total_size > 0.
|
||||
int aux_info_default_size;
|
||||
std::vector<uint8_t> aux_info_sizes; // Populated if default_size == 0.
|
||||
|
@ -300,6 +306,40 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
|
|||
RCHECK(desc_idx > 0); // Descriptions are one-indexed in the file
|
||||
desc_idx -= 1;
|
||||
|
||||
const AudioSampleEntry* audio_sample_entry = NULL;
|
||||
const VideoSampleEntry* video_sample_entry = NULL;
|
||||
switch (stsd.type) {
|
||||
case kAudio:
|
||||
RCHECK(!stsd.audio_entries.empty());
|
||||
if (desc_idx > stsd.audio_entries.size())
|
||||
desc_idx = 0;
|
||||
audio_sample_entry = &stsd.audio_entries[desc_idx];
|
||||
break;
|
||||
case kVideo:
|
||||
RCHECK(!stsd.video_entries.empty());
|
||||
if (desc_idx > stsd.video_entries.size())
|
||||
desc_idx = 0;
|
||||
video_sample_entry = &stsd.video_entries[desc_idx];
|
||||
break;
|
||||
default:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
|
||||
// SampleEncryptionEntries should not have been parsed, without having
|
||||
// iv_size. Parse the box now.
|
||||
DCHECK(traf.sample_encryption.sample_encryption_entries.empty());
|
||||
std::vector<SampleEncryptionEntry> sample_encryption_entries;
|
||||
if (!traf.sample_encryption.sample_encryption_data.empty()) {
|
||||
RCHECK(audio_sample_entry || video_sample_entry);
|
||||
const uint8_t default_iv_size =
|
||||
audio_sample_entry
|
||||
? audio_sample_entry->sinf.info.track_encryption.default_iv_size
|
||||
: video_sample_entry->sinf.info.track_encryption.default_iv_size;
|
||||
RCHECK(traf.sample_encryption.ParseFromSampleEncryptionData(
|
||||
default_iv_size, &sample_encryption_entries));
|
||||
}
|
||||
|
||||
int64_t run_start_dts = traf.decode_time_absent
|
||||
? next_fragment_start_dts_[i]
|
||||
: traf.decode_time.decode_time;
|
||||
|
@ -314,27 +354,30 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
|
|||
tri.sample_start_offset = trun.data_offset;
|
||||
|
||||
tri.track_type = stsd.type;
|
||||
if (tri.track_type == kAudio) {
|
||||
RCHECK(!stsd.audio_entries.empty());
|
||||
if (desc_idx > stsd.audio_entries.size())
|
||||
desc_idx = 0;
|
||||
tri.audio_description = &stsd.audio_entries[desc_idx];
|
||||
} else if (tri.track_type == kVideo) {
|
||||
RCHECK(!stsd.video_entries.empty());
|
||||
if (desc_idx > stsd.video_entries.size())
|
||||
desc_idx = 0;
|
||||
tri.video_description = &stsd.video_entries[desc_idx];
|
||||
}
|
||||
tri.audio_description = audio_sample_entry;
|
||||
tri.video_description = video_sample_entry;
|
||||
|
||||
// Collect information from the auxiliary_offset entry with the same index
|
||||
// in the 'saiz' container as the current run's index in the 'trun'
|
||||
// container, if it is present.
|
||||
if (traf.auxiliary_offset.offsets.size() > j) {
|
||||
tri.aux_info_start_offset = -1;
|
||||
tri.aux_info_total_size = 0;
|
||||
// Populate sample encryption entries from SampleEncryption 'senc' box if
|
||||
// it is available; otherwise initialize aux_info variables, which will
|
||||
// be used to populate sample encryption entries later in CacheAuxInfo.
|
||||
if (!sample_encryption_entries.empty()) {
|
||||
RCHECK(sample_encryption_entries.size() >=
|
||||
sample_count_sum + trun.sample_count);
|
||||
for (size_t k = 0; k < trun.sample_count; ++k) {
|
||||
tri.sample_encryption_entries.push_back(
|
||||
sample_encryption_entries[sample_count_sum + k]);
|
||||
}
|
||||
} else if (traf.auxiliary_offset.offsets.size() > j) {
|
||||
// Collect information from the auxiliary_offset entry with the same
|
||||
// index in the 'saiz' container as the current run's index in the
|
||||
// 'trun' container, if it is present.
|
||||
tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j];
|
||||
// There should be an auxiliary info entry corresponding to each sample
|
||||
// in the auxiliary offset entry's corresponding track run.
|
||||
RCHECK(traf.auxiliary_size.sample_count >=
|
||||
sample_count_sum + trun.sample_count);
|
||||
tri.aux_info_start_offset = traf.auxiliary_offset.offsets[j];
|
||||
tri.aux_info_default_size =
|
||||
traf.auxiliary_size.default_sample_info_size;
|
||||
if (tri.aux_info_default_size == 0) {
|
||||
|
@ -358,9 +401,6 @@ bool TrackRunIterator::Init(const MovieFragment& moof) {
|
|||
tri.aux_info_total_size += tri.aux_info_sizes[k];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tri.aux_info_start_offset = -1;
|
||||
tri.aux_info_total_size = 0;
|
||||
}
|
||||
|
||||
tri.samples.resize(trun.sample_count);
|
||||
|
@ -391,7 +431,6 @@ void TrackRunIterator::ResetRun() {
|
|||
sample_dts_ = run_itr_->start_dts;
|
||||
sample_offset_ = run_itr_->sample_start_offset;
|
||||
sample_itr_ = run_itr_->samples.begin();
|
||||
cenc_info_.clear();
|
||||
}
|
||||
|
||||
void TrackRunIterator::AdvanceSample() {
|
||||
|
@ -405,14 +444,17 @@ 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 is_encrypted() && aux_info_size() > 0 &&
|
||||
run_itr_->sample_encryption_entries.size() == 0;
|
||||
}
|
||||
|
||||
// This implementation currently only caches CENC auxiliary info.
|
||||
bool TrackRunIterator::CacheAuxInfo(const uint8_t* buf, int buf_size) {
|
||||
RCHECK(AuxInfoNeedsToBeCached() && buf_size >= aux_info_size());
|
||||
|
||||
cenc_info_.resize(run_itr_->samples.size());
|
||||
std::vector<SampleEncryptionEntry>& sample_encryption_entries =
|
||||
runs_[run_itr_ - runs_.begin()].sample_encryption_entries;
|
||||
sample_encryption_entries.resize(run_itr_->samples.size());
|
||||
int64_t pos = 0;
|
||||
for (size_t i = 0; i < run_itr_->samples.size(); i++) {
|
||||
int info_size = run_itr_->aux_info_default_size;
|
||||
|
@ -420,7 +462,9 @@ bool TrackRunIterator::CacheAuxInfo(const uint8_t* buf, int buf_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));
|
||||
const bool has_subsamples = info_size > track_encryption().default_iv_size;
|
||||
RCHECK(sample_encryption_entries[i].ParseFromBuffer(
|
||||
track_encryption().default_iv_size, has_subsamples, &reader));
|
||||
pos += info_size;
|
||||
}
|
||||
|
||||
|
@ -539,12 +583,14 @@ const TrackEncryption& TrackRunIterator::track_encryption() const {
|
|||
|
||||
scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
|
||||
size_t sample_idx = sample_itr_ - run_itr_->samples.begin();
|
||||
DCHECK_LT(sample_idx, cenc_info_.size());
|
||||
const FrameCENCInfo& cenc_info = cenc_info_[sample_idx];
|
||||
DCHECK_LT(sample_idx, run_itr_->sample_encryption_entries.size());
|
||||
const SampleEncryptionEntry& sample_encryption_entry =
|
||||
run_itr_->sample_encryption_entries[sample_idx];
|
||||
DCHECK(is_encrypted());
|
||||
DCHECK(!AuxInfoNeedsToBeCached());
|
||||
|
||||
const size_t total_size_of_subsamples = cenc_info.GetTotalSizeOfSubsamples();
|
||||
const size_t total_size_of_subsamples =
|
||||
sample_encryption_entry.GetTotalSizeOfSubsamples();
|
||||
if (total_size_of_subsamples != 0 &&
|
||||
total_size_of_subsamples != static_cast<size_t>(sample_size())) {
|
||||
LOG(ERROR) << "Incorrect CENC subsample size.";
|
||||
|
@ -553,9 +599,9 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
|
|||
|
||||
return scoped_ptr<DecryptConfig>(new DecryptConfig(
|
||||
track_encryption().default_kid,
|
||||
cenc_info.iv(),
|
||||
sample_encryption_entry.initialization_vector,
|
||||
0, // No offset to start of media data in MP4 using CENC.
|
||||
cenc_info.subsamples()));
|
||||
sample_encryption_entry.subsamples));
|
||||
}
|
||||
|
||||
} // namespace mp4
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "packager/base/memory/scoped_ptr.h"
|
||||
#include "packager/media/formats/mp4/box_definitions.h"
|
||||
#include "packager/media/formats/mp4/cenc.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
@ -111,7 +110,6 @@ class TrackRunIterator {
|
|||
std::vector<TrackRunInfo>::const_iterator run_itr_;
|
||||
std::vector<SampleInfo>::const_iterator sample_itr_;
|
||||
|
||||
std::vector<FrameCENCInfo> cenc_info_;
|
||||
// Track the start dts of the next segment, only useful if decode_time box is
|
||||
// absent.
|
||||
std::vector<int64_t> next_fragment_start_dts_;
|
||||
|
|
|
@ -11,14 +11,16 @@
|
|||
#include "packager/media/formats/mp4/rcheck.h"
|
||||
#include "packager/media/formats/mp4/track_run_iterator.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// The sum of the elements in a vector initialized with SumAscending,
|
||||
// less the value of the last element.
|
||||
static const int kSumAscending1 = 45;
|
||||
const int kSumAscending1 = 45;
|
||||
|
||||
static const int kAudioScale = 48000;
|
||||
static const int kVideoScale = 25;
|
||||
const int kAudioScale = 48000;
|
||||
const int kVideoScale = 25;
|
||||
|
||||
static const uint8_t kAuxInfo[] = {
|
||||
const uint8_t kAuxInfo[] = {
|
||||
// Sample 1: IV (no subsumples).
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||
// Sample 2: IV.
|
||||
|
@ -30,11 +32,39 @@ static const uint8_t kAuxInfo[] = {
|
|||
// Sample 2: Subsample 2.
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04};
|
||||
|
||||
static const char kIv1[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31, };
|
||||
const uint8_t kSampleEncryptionDataWithSubsamples[] = {
|
||||
// Sample count.
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 1: IV.
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||
// Sample 1: Subsample count.
|
||||
0x00, 0x01,
|
||||
// Sample 1: Subsample 1.
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 2: IV.
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
|
||||
// Sample 2: Subsample count.
|
||||
0x00, 0x02,
|
||||
// Sample 2: Subsample 1.
|
||||
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 2: Subsample 2.
|
||||
0x00, 0x03, 0x00, 0x00, 0x00, 0x04};
|
||||
|
||||
static const uint8_t kKeyId[] = {0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c,
|
||||
0x65, 0x54, 0x65, 0x73, 0x74, 0x4b,
|
||||
0x65, 0x79, 0x49, 0x44};
|
||||
const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
|
||||
// Sample count.
|
||||
0x00, 0x00, 0x00, 0x02,
|
||||
// Sample 1: IV.
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
|
||||
// Sample 2: IV.
|
||||
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32};
|
||||
|
||||
const char kIv1[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31};
|
||||
const char kIv2[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32};
|
||||
|
||||
const uint8_t kKeyId[] = {0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
|
||||
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
@ -143,6 +173,38 @@ class TrackRunIteratorTest : public testing::Test {
|
|||
frag->runs[0].sample_sizes[1] = 10;
|
||||
}
|
||||
|
||||
void AddSampleEncryption(uint8_t use_subsample_flag, TrackFragment* frag) {
|
||||
frag->sample_encryption.iv_size = 8;
|
||||
frag->sample_encryption.flags = use_subsample_flag;
|
||||
if (use_subsample_flag) {
|
||||
frag->sample_encryption.sample_encryption_data.assign(
|
||||
kSampleEncryptionDataWithSubsamples,
|
||||
kSampleEncryptionDataWithSubsamples +
|
||||
arraysize(kSampleEncryptionDataWithSubsamples));
|
||||
} else {
|
||||
frag->sample_encryption.sample_encryption_data.assign(
|
||||
kSampleEncryptionDataWithoutSubsamples,
|
||||
kSampleEncryptionDataWithoutSubsamples +
|
||||
arraysize(kSampleEncryptionDataWithoutSubsamples));
|
||||
}
|
||||
|
||||
// Update sample sizes and aux info header.
|
||||
frag->runs.resize(1);
|
||||
frag->runs[0].sample_count = 2;
|
||||
frag->auxiliary_offset.offsets.push_back(0);
|
||||
frag->auxiliary_size.sample_count = 2;
|
||||
if (use_subsample_flag) {
|
||||
// Update sample sizes to match with subsample entries above.
|
||||
frag->runs[0].sample_sizes[0] = 3;
|
||||
frag->runs[0].sample_sizes[1] = 10;
|
||||
// Set aux info header.
|
||||
frag->auxiliary_size.sample_info_sizes.push_back(16);
|
||||
frag->auxiliary_size.sample_info_sizes.push_back(22);
|
||||
} else {
|
||||
frag->auxiliary_size.default_sample_info_size = 8;
|
||||
}
|
||||
}
|
||||
|
||||
void SetAscending(std::vector<uint32_t>* vec) {
|
||||
vec->resize(10);
|
||||
for (size_t i = 0; i < vec->size(); i++)
|
||||
|
@ -303,7 +365,77 @@ TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) {
|
|||
EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
|
||||
}
|
||||
|
||||
TEST_F(TrackRunIteratorTest, DecryptConfigTest) {
|
||||
TEST_F(TrackRunIteratorTest,
|
||||
DecryptConfigTestWithSampleEncryptionAndSubsample) {
|
||||
AddEncryption(&moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
MovieFragment moof = CreateFragment();
|
||||
AddSampleEncryption(SampleEncryption::kUseSubsampleEncryption,
|
||||
&moof.tracks[1]);
|
||||
|
||||
ASSERT_TRUE(iter_->Init(moof));
|
||||
// The run for track 2 will be the second, which is parsed according to
|
||||
// data_offset.
|
||||
iter_->AdvanceRun();
|
||||
EXPECT_EQ(iter_->track_id(), 2u);
|
||||
|
||||
EXPECT_TRUE(iter_->is_encrypted());
|
||||
// No need to cache aux info as it is already available in SampleEncryption.
|
||||
EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
|
||||
EXPECT_EQ(iter_->aux_info_size(), 0);
|
||||
EXPECT_EQ(iter_->sample_offset(), 200);
|
||||
EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[1].runs[0].data_offset);
|
||||
scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
|
||||
EXPECT_EQ(std::vector<uint8_t>(kKeyId, kKeyId + arraysize(kKeyId)),
|
||||
config->key_id());
|
||||
EXPECT_EQ(std::vector<uint8_t>(kIv1, kIv1 + arraysize(kIv1)), config->iv());
|
||||
EXPECT_EQ(config->subsamples().size(), 1u);
|
||||
EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
|
||||
EXPECT_EQ(config->subsamples()[0].cipher_bytes, 2u);
|
||||
iter_->AdvanceSample();
|
||||
config = iter_->GetDecryptConfig();
|
||||
EXPECT_EQ(std::vector<uint8_t>(kIv2, kIv2 + arraysize(kIv2)), config->iv());
|
||||
EXPECT_EQ(config->subsamples().size(), 2u);
|
||||
EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
|
||||
EXPECT_EQ(config->subsamples()[0].cipher_bytes, 2u);
|
||||
EXPECT_EQ(config->subsamples()[1].clear_bytes, 3u);
|
||||
EXPECT_EQ(config->subsamples()[1].cipher_bytes, 4u);
|
||||
}
|
||||
|
||||
TEST_F(TrackRunIteratorTest,
|
||||
DecryptConfigTestWithSampleEncryptionAndNoSubsample) {
|
||||
AddEncryption(&moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
MovieFragment moof = CreateFragment();
|
||||
AddSampleEncryption(!SampleEncryption::kUseSubsampleEncryption,
|
||||
&moof.tracks[1]);
|
||||
|
||||
ASSERT_TRUE(iter_->Init(moof));
|
||||
// The run for track 2 will be the second, which is parsed according to
|
||||
// data_offset.
|
||||
iter_->AdvanceRun();
|
||||
EXPECT_EQ(iter_->track_id(), 2u);
|
||||
|
||||
EXPECT_TRUE(iter_->is_encrypted());
|
||||
// No need to cache aux info as it is already available in SampleEncryption.
|
||||
EXPECT_FALSE(iter_->AuxInfoNeedsToBeCached());
|
||||
EXPECT_EQ(iter_->aux_info_size(), 0);
|
||||
EXPECT_EQ(iter_->sample_offset(), 200);
|
||||
EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[1].runs[0].data_offset);
|
||||
scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
|
||||
EXPECT_EQ(std::vector<uint8_t>(kKeyId, kKeyId + arraysize(kKeyId)),
|
||||
config->key_id());
|
||||
EXPECT_EQ(std::vector<uint8_t>(kIv1, kIv1 + arraysize(kIv1)), config->iv());
|
||||
EXPECT_EQ(config->subsamples().size(), 0u);
|
||||
iter_->AdvanceSample();
|
||||
config = iter_->GetDecryptConfig();
|
||||
EXPECT_EQ(std::vector<uint8_t>(kIv2, kIv2 + arraysize(kIv2)), config->iv());
|
||||
EXPECT_EQ(config->subsamples().size(), 0u);
|
||||
}
|
||||
|
||||
TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) {
|
||||
AddEncryption(&moov_.tracks[1]);
|
||||
iter_.reset(new TrackRunIterator(&moov_));
|
||||
|
||||
|
@ -316,7 +448,7 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTest) {
|
|||
// element in the file.
|
||||
EXPECT_EQ(iter_->track_id(), 2u);
|
||||
EXPECT_TRUE(iter_->is_encrypted());
|
||||
EXPECT_TRUE(iter_->AuxInfoNeedsToBeCached());
|
||||
ASSERT_TRUE(iter_->AuxInfoNeedsToBeCached());
|
||||
EXPECT_EQ(static_cast<uint32_t>(iter_->aux_info_size()), arraysize(kAuxInfo));
|
||||
EXPECT_EQ(iter_->aux_info_offset(), 50);
|
||||
EXPECT_EQ(iter_->GetMaxClearOffset(), 50);
|
||||
|
@ -328,11 +460,9 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTest) {
|
|||
EXPECT_EQ(iter_->sample_offset(), 200);
|
||||
EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[0].runs[0].data_offset);
|
||||
scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
|
||||
ASSERT_EQ(arraysize(kKeyId), config->key_id().size());
|
||||
EXPECT_TRUE(
|
||||
!memcmp(kKeyId, config->key_id().data(), config->key_id().size()));
|
||||
ASSERT_EQ(arraysize(kIv1), config->iv().size());
|
||||
EXPECT_TRUE(!memcmp(kIv1, config->iv().data(), config->iv().size()));
|
||||
EXPECT_EQ(std::vector<uint8_t>(kKeyId, kKeyId + arraysize(kKeyId)),
|
||||
config->key_id());
|
||||
EXPECT_EQ(std::vector<uint8_t>(kIv1, kIv1 + arraysize(kIv1)), config->iv());
|
||||
EXPECT_TRUE(config->subsamples().empty());
|
||||
iter_->AdvanceSample();
|
||||
config = iter_->GetDecryptConfig();
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
// found in the LICENSE file.
|
||||
|
||||
bear-320x240.webm - WebM encode of bear.1280x720.mp4 resized to 320x240.
|
||||
no_streams.webm - Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it looks
|
||||
no_streams.webm - Header, Info, & Tracks element from bear-320x240.webm slightly corrupted so it looks
|
||||
like there are no tracks.
|
||||
nonzero-start-time.webm - Has the same headers as bear-320x240.webm but the first cluster of this file
|
||||
nonzero-start-time.webm - Has the same headers as bear-320x240.webm but the first cluster of this file
|
||||
is the second cluster of bear-320x240.webm. This creates the situation where
|
||||
the media data doesn't start at time 0.
|
||||
bear-320x240-live.webm - bear-320x240.webm remuxed w/o a duration and using clusters with unknown sizes.
|
||||
|
@ -41,7 +41,11 @@ bear-640x360-non_square_pixel-with_pasp.mp4 - A non-square pixel version of the
|
|||
bear-640x360-non_square_pixel-without_pasp.mp4 - A non-square pixel version of the video track of bear-640x360.mp4 without PixelAspectRatio box.
|
||||
|
||||
// Encrypted Files.
|
||||
bear-640x360-v_frag-cenc.mp4 - A fragmented MP4 version of the video track of bear-640x360.mp4 encrypted (ISO CENC) using key ID [1] and key [2].
|
||||
bear-640x360-v_frag-cenc-aux.mp4 - A fragmented MP4 version of the video track of bear-640x360.mp4
|
||||
encrypted (ISO CENC) using key ID [1] and key [2] and with sample
|
||||
encryption auxiliary information in the beginning of mdat box.
|
||||
bear-640x360-v_frag-cenc-senc.mp4 - Same as above, but with sample encryption information stored in
|
||||
senc box.
|
||||
|
||||
[1] 30313233343536373839303132333435
|
||||
[2] ebdd62f16814d27b68ef122afce4ae3c
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue