Commit 9746f913 authored by strobe@google.com's avatar strobe@google.com

Add Common Encryption support to BMFF, including subsample decryption.

BUG=132351
TEST=AesDecryptorTest, plus manual playback in browser


Review URL: https://chromiumcodereview.appspot.com/10651006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@148453 0039d316-1c4b-4281-b951-d872f2087c98
parent 7db8893a
......@@ -8,25 +8,19 @@
namespace media {
DecryptConfig::DecryptConfig(const uint8* key_id, int key_id_size,
const uint8* iv, int iv_size,
const uint8* checksum, int checksum_size,
int encrypted_frame_offset)
: key_id_(new uint8[key_id_size]),
key_id_size_(key_id_size),
iv_(new uint8[iv_size]),
iv_size_(iv_size),
checksum_(checksum_size > 0 ? new uint8[checksum_size] : NULL),
checksum_size_(checksum_size),
encrypted_frame_offset_(encrypted_frame_offset) {
CHECK_GT(key_id_size, 0);
CHECK_EQ(iv_size, DecryptConfig::kDecryptionKeySize);
CHECK_GE(checksum_size, 0);
CHECK_GE(encrypted_frame_offset, 0);
memcpy(key_id_.get(), key_id, key_id_size);
memcpy(iv_.get(), iv, iv_size);
if (checksum_size > 0)
memcpy(checksum_.get(), checksum, checksum_size);
DecryptConfig::DecryptConfig(const std::string& key_id,
const std::string& iv,
const std::string& checksum,
const int data_offset,
const std::vector<SubsampleEntry>& subsamples)
: key_id_(key_id),
iv_(iv),
checksum_(checksum),
data_offset_(data_offset),
subsamples_(subsamples) {
CHECK_GT(key_id.size(), 0u);
CHECK_EQ(iv.size(), static_cast<size_t>(DecryptConfig::kDecryptionKeySize));
CHECK_GE(data_offset, 0);
}
DecryptConfig::~DecryptConfig() {}
......
......@@ -5,55 +5,77 @@
#ifndef MEDIA_BASE_DECRYPT_CONFIG_H_
#define MEDIA_BASE_DECRYPT_CONFIG_H_
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
namespace media {
// Contains all information that a decryptor needs to decrypt a frame.
// The Common Encryption spec provides for subsample encryption, where portions
// of a sample are set in cleartext. A SubsampleEntry specifies the number of
// clear and encrypted bytes in each subsample. For decryption, all of the
// encrypted bytes in a sample should be considered a single logical stream,
// regardless of how they are divided into subsamples, and the clear bytes
// should not be considered as part of decryption. This is logically equivalent
// to concatenating all 'cypher_bytes' portions of subsamples, decrypting that
// result, and then copying each byte from the decrypted block over the
// position of the corresponding encrypted byte.
struct SubsampleEntry {
uint32 clear_bytes;
uint32 cypher_bytes;
};
// Contains all information that a decryptor needs to decrypt a media sample.
class MEDIA_EXPORT DecryptConfig {
public:
// Keys are always 128 bits.
static const int kDecryptionKeySize = 16;
// |key_id| is the ID that references the decryption key for this frame. |iv|
// is the initialization vector defined by the encrypted format. Currently
// |iv_size| must be 16 bytes as defined by WebM and ISO. |checksum| is the
// hash value of the encrypted buffer. |checksum| is defined by the
// encrypted format and may be NULL. |encrypted_frame_offset| is the offset
// into the encrypted buffer that the encrypted frame starts. The class
// will copy the data from |key_id|, |iv|, and |checksum|.
DecryptConfig(const uint8* key_id, int key_id_size,
const uint8* iv, int iv_size,
const uint8* checksum, int checksum_size,
int encrypted_frame_offset);
// |key_id| is the ID that references the decryption key for this sample.
// |iv| is the initialization vector defined by the encrypted format.
// Currently |iv_size| must be 16 bytes as defined by WebM and ISO.
// |checksum| is the hash value of the encrypted buffer. |checksum| is
// defined by the encrypted format and may be NULL.
// |data_offset| is the amount of data that should be discarded from the
// head of the sample buffer before applying subsample information. A
// decrypted buffer will be shorter than an encrypted buffer by this amount.
// |subsamples| defines the clear and encrypted portions of the sample as
// described above. A decrypted buffer will be equal in size to the sum
// of the subsample sizes.
//
// |data_offset| is applied after |checksum|, but before |subsamples|.
DecryptConfig(const std::string& key_id,
const std::string& iv,
const std::string& checksum,
const int data_offset,
const std::vector<SubsampleEntry>& subsamples);
~DecryptConfig();
const uint8* key_id() const { return key_id_.get(); }
int key_id_size() const { return key_id_size_; }
const uint8* iv() const { return iv_.get(); }
int iv_size() const { return iv_size_; }
const uint8* checksum() const { return checksum_.get(); }
int checksum_size() const { return checksum_size_; }
int encrypted_frame_offset() const { return encrypted_frame_offset_; }
const std::string& key_id() const { return key_id_; }
const std::string& iv() const { return iv_; }
const std::string& checksum() const { return checksum_; }
int data_offset() const { return data_offset_; }
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
private:
const scoped_array<uint8> key_id_;
const int key_id_size_;
const std::string key_id_;
// Initialization vector.
const scoped_array<uint8> iv_;
const int iv_size_;
const std::string iv_;
// Checksum of the data to be verified before decrypting the data. This may
// be NULL for some formats.
const scoped_array<uint8> checksum_;
const int checksum_size_;
// be empty for some formats.
const std::string checksum_;
// Amount of data to be discarded before applying subsample information.
const int data_offset_;
// This is the offset in bytes to where the encrypted data starts within
// the input buffer.
const int encrypted_frame_offset_;
// Subsample information. May be empty for some formats, meaning entire frame
// (less data ignored by data_offset_) is encrypted.
const std::vector<SubsampleEntry> subsamples_;
DISALLOW_COPY_AND_ASSIGN(DecryptConfig);
};
......
This diff is collapsed.
......@@ -61,12 +61,12 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor {
explicit DecryptionKey(const std::string& secret);
~DecryptionKey();
// Creates the encryption key and HMAC. If |derive_webm_keys| is true then
// the object will derive the decryption key and the HMAC key from
// |secret_|.
bool Init(bool derive_webm_keys);
// Creates the encryption key, and derives the WebM decryption key and HMAC.
bool Init();
crypto::SymmetricKey* decryption_key() { return decryption_key_.get(); }
crypto::SymmetricKey* webm_decryption_key()
{ return webm_decryption_key_.get(); }
base::StringPiece hmac_key() { return base::StringPiece(hmac_key_); }
private:
......@@ -77,6 +77,9 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor {
// The key used to decrypt the data.
scoped_ptr<crypto::SymmetricKey> decryption_key_;
// The key used for decryption of WebM media, derived from the secret.
scoped_ptr<crypto::SymmetricKey> webm_decryption_key_;
// The key used to perform the integrity check. Currently the HMAC key is
// defined by the WebM encrypted specification. Current encrypted WebM
// request for comments specification is here
......
This diff is collapsed.
......@@ -4,6 +4,7 @@
#include <deque>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/message_loop.h"
......@@ -47,10 +48,14 @@ static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() {
const int encrypted_frame_offset = 1; // This should be non-zero.
scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(buffer_size));
buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig(
kFakeKeyId, arraysize(kFakeKeyId),
kFakeIv, DecryptConfig::kDecryptionKeySize,
kFakeCheckSum, arraysize(kFakeCheckSum),
encrypted_frame_offset)));
std::string(reinterpret_cast<const char*>(kFakeKeyId),
arraysize(kFakeKeyId)),
std::string(reinterpret_cast<const char*>(kFakeIv),
DecryptConfig::kDecryptionKeySize),
std::string(reinterpret_cast<const char*>(kFakeCheckSum),
arraysize(kFakeCheckSum)),
encrypted_frame_offset,
std::vector<SubsampleEntry>())));
return buffer;
}
......
......@@ -32,7 +32,7 @@ static bool ConvertAVCToAnnexBInPlaceForLengthSize4(std::vector<uint8>* buf) {
}
// static
bool AVC::ConvertToAnnexB(int length_size, std::vector<uint8>* buffer) {
bool AVC::ConvertFrameToAnnexB(int length_size, std::vector<uint8>* buffer) {
RCHECK(length_size == 1 || length_size == 2 || length_size == 4);
if (length_size == 4)
......@@ -59,32 +59,31 @@ bool AVC::ConvertToAnnexB(int length_size, std::vector<uint8>* buffer) {
}
// static
bool AVC::InsertParameterSets(const AVCDecoderConfigurationRecord& avc_config,
std::vector<uint8>* buffer) {
bool AVC::ConvertConfigToAnnexB(
const AVCDecoderConfigurationRecord& avc_config,
std::vector<uint8>* buffer) {
DCHECK(buffer->empty());
buffer->clear();
int total_size = 0;
for (size_t i = 0; i < avc_config.sps_list.size(); i++)
total_size += avc_config.sps_list[i].size() + kAnnexBStartCodeSize;
for (size_t i = 0; i < avc_config.pps_list.size(); i++)
total_size += avc_config.pps_list[i].size() + kAnnexBStartCodeSize;
std::vector<uint8> temp;
temp.reserve(total_size);
buffer->reserve(total_size);
for (size_t i = 0; i < avc_config.sps_list.size(); i++) {
temp.insert(temp.end(), kAnnexBStartCode,
buffer->insert(buffer->end(), kAnnexBStartCode,
kAnnexBStartCode + kAnnexBStartCodeSize);
temp.insert(temp.end(), avc_config.sps_list[i].begin(),
buffer->insert(buffer->end(), avc_config.sps_list[i].begin(),
avc_config.sps_list[i].end());
}
for (size_t i = 0; i < avc_config.pps_list.size(); i++) {
temp.insert(temp.end(), kAnnexBStartCode,
kAnnexBStartCode + kAnnexBStartCodeSize);
temp.insert(temp.end(), avc_config.pps_list[i].begin(),
avc_config.pps_list[i].end());
buffer->insert(buffer->end(), kAnnexBStartCode,
kAnnexBStartCode + kAnnexBStartCodeSize);
buffer->insert(buffer->end(), avc_config.pps_list[i].begin(),
avc_config.pps_list[i].end());
}
buffer->insert(buffer->begin(), temp.begin(), temp.end());
return true;
}
......
......@@ -17,9 +17,9 @@ struct AVCDecoderConfigurationRecord;
class MEDIA_EXPORT AVC {
public:
static bool ConvertToAnnexB(int length_size, std::vector<uint8>* buffer);
static bool ConvertFrameToAnnexB(int length_size, std::vector<uint8>* buffer);
static bool InsertParameterSets(
static bool ConvertConfigToAnnexB(
const AVCDecoderConfigurationRecord& avc_config,
std::vector<uint8>* buffer);
};
......
......@@ -23,7 +23,7 @@ static const uint8 kExpected[] = {
static const uint8 kExpectedParamSets[] = {
0x00, 0x00, 0x00, 0x01, 0x67, 0x12,
0x00, 0x00, 0x00, 0x01, 0x67, 0x34,
0x00, 0x00, 0x00, 0x01, 0x68, 0x56, 0x78, 0x9a};
0x00, 0x00, 0x00, 0x01, 0x68, 0x56, 0x78};
class AVCConversionTest : public testing::TestWithParam<int> {
protected:
......@@ -44,7 +44,7 @@ class AVCConversionTest : public testing::TestWithParam<int> {
TEST_P(AVCConversionTest, ParseCorrectly) {
std::vector<uint8> buf;
MakeInputForLength(GetParam(), &buf);
EXPECT_TRUE(AVC::ConvertToAnnexB(GetParam(), &buf));
EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
EXPECT_EQ(buf.size(), sizeof(kExpected));
EXPECT_EQ(0, memcmp(kExpected, &buf[0], sizeof(kExpected)));
}
......@@ -53,19 +53,19 @@ TEST_P(AVCConversionTest, ParsePartial) {
std::vector<uint8> buf;
MakeInputForLength(GetParam(), &buf);
buf.pop_back();
EXPECT_FALSE(AVC::ConvertToAnnexB(GetParam(), &buf));
EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
// This tests a buffer ending in the middle of a NAL length. For length size
// of one, this can't happen, so we skip that case.
if (GetParam() != 1) {
MakeInputForLength(GetParam(), &buf);
buf.erase(buf.end() - (sizeof(kNALU2) + 1), buf.end());
EXPECT_FALSE(AVC::ConvertToAnnexB(GetParam(), &buf));
EXPECT_FALSE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
}
}
TEST_P(AVCConversionTest, ParseEmpty) {
std::vector<uint8> buf;
EXPECT_TRUE(AVC::ConvertToAnnexB(GetParam(), &buf));
EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
EXPECT_EQ(0u, buf.size());
}
......@@ -73,7 +73,7 @@ INSTANTIATE_TEST_CASE_P(AVCConversionTestValues,
AVCConversionTest,
::testing::Values(1, 2, 4));
TEST(AVC, InsertParameterSetsTest) {
TEST_F(AVCConversionTest, ConvertConfigToAnnexB) {
AVCDecoderConfigurationRecord avc_config;
avc_config.sps_list.resize(2);
avc_config.sps_list[0].push_back(0x67);
......@@ -86,8 +86,7 @@ TEST(AVC, InsertParameterSetsTest) {
avc_config.pps_list[0].push_back(0x78);
std::vector<uint8> buf;
buf.push_back(0x9a);
EXPECT_TRUE(AVC::InsertParameterSets(avc_config, &buf));
EXPECT_TRUE(AVC::ConvertConfigToAnnexB(avc_config, &buf));
EXPECT_EQ(0, memcmp(kExpectedParamSets, &buf[0],
sizeof(kExpectedParamSets)));
}
......
......@@ -27,7 +27,7 @@ FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; }
bool ProtectionSystemSpecificHeader::Parse(BoxReader* reader) {
uint32 size;
return reader->SkipBytes(4) &&
return reader->ReadFullBoxHeader() &&
reader->ReadVec(&system_id, 16) &&
reader->Read4(&size) &&
reader->ReadVec(&data, size);
......@@ -88,7 +88,7 @@ SchemeType::~SchemeType() {}
FourCC SchemeType::BoxType() const { return FOURCC_SCHM; }
bool SchemeType::Parse(BoxReader* reader) {
RCHECK(reader->SkipBytes(4) &&
RCHECK(reader->ReadFullBoxHeader() &&
reader->ReadFourCC(&type) &&
reader->Read4(&version));
RCHECK(type == FOURCC_CENC);
......@@ -103,7 +103,8 @@ FourCC TrackEncryption::BoxType() const { return FOURCC_TENC; }
bool TrackEncryption::Parse(BoxReader* reader) {
uint8 flag;
RCHECK(reader->SkipBytes(2) &&
RCHECK(reader->ReadFullBoxHeader() &&
reader->SkipBytes(2) &&
reader->Read1(&flag) &&
reader->Read1(&default_iv_size) &&
reader->ReadVec(&default_kid, 16));
......@@ -129,9 +130,11 @@ ProtectionSchemeInfo::~ProtectionSchemeInfo() {}
FourCC ProtectionSchemeInfo::BoxType() const { return FOURCC_SINF; }
bool ProtectionSchemeInfo::Parse(BoxReader* reader) {
return reader->ScanChildren() &&
RCHECK(reader->ScanChildren() &&
reader->ReadChild(&format) &&
reader->ReadChild(&type) &&
reader->ReadChild(&info);
reader->ReadChild(&info));
return true;
}
MovieHeader::MovieHeader()
......@@ -375,20 +378,15 @@ bool VideoSampleEntry::Parse(BoxReader* reader) {
reader->Read2(&height) &&
reader->SkipBytes(50));
RCHECK(reader->ScanChildren());
RCHECK(reader->MaybeReadChild(&pixel_aspect));
if (format == FOURCC_ENCV) {
RCHECK(reader->ReadChild(&sinf));
}
RCHECK(reader->ScanChildren() &&
reader->MaybeReadChild(&pixel_aspect));
// TODO(strobe): finalize format signaling for encrypted media
// (http://crbug.com/132351)
//
// if (format == FOURCC_AVC1 ||
// (format == FOURCC_ENCV &&
// sinf.format.format == FOURCC_AVC1)) {
if (format == FOURCC_ENCV)
RCHECK(reader->ReadChild(&sinf));
if (format == FOURCC_AVC1 ||
(format == FOURCC_ENCV && sinf.format.format == FOURCC_AVC1)) {
RCHECK(reader->ReadChild(&avcc));
// }
}
return true;
}
......@@ -444,9 +442,8 @@ bool AudioSampleEntry::Parse(BoxReader* reader) {
samplerate >>= 16;
RCHECK(reader->ScanChildren());
if (format == FOURCC_ENCA) {
if (format == FOURCC_ENCA)
RCHECK(reader->ReadChild(&sinf));
}
RCHECK(reader->ReadChild(&esds));
return true;
}
......@@ -597,6 +594,7 @@ bool MovieFragmentHeader::Parse(BoxReader* reader) {
TrackFragmentHeader::TrackFragmentHeader()
: track_id(0),
sample_description_index(0),
default_sample_duration(0),
default_sample_size(0),
default_sample_flags(0),
......
......@@ -217,7 +217,9 @@ struct MEDIA_EXPORT SampleTable : Box {
DECLARE_BOX_METHODS(SampleTable);
// Media Source specific: we ignore many of the sub-boxes in this box,
// including some that are required to be present in the BMFF spec.
// including some that are required to be present in the BMFF spec. This
// includes the 'stts', 'stsc', and 'stco' boxes, which must contain no
// samples in order to be compliant files.
SampleDescription description;
};
......
......@@ -4,6 +4,8 @@
#include "media/mp4/cenc.h"
#include <cstring>
#include "media/mp4/box_reader.h"
#include "media/mp4/rcheck.h"
......@@ -15,28 +17,35 @@ FrameCENCInfo::~FrameCENCInfo() {}
bool FrameCENCInfo::Parse(int iv_size, BufferReader* reader) {
const int kEntrySize = 6;
// Mandated by CENC spec
RCHECK(iv_size == 8 || iv_size == 16);
iv.resize(iv_size);
memset(iv, 0, sizeof(iv));
for (int i = 0; i < iv_size; i++)
RCHECK(reader->Read1(&iv[i]));
if (!reader->HasBytes(1)) return true;
uint16 subsample_count;
RCHECK(reader->ReadVec(&iv, iv_size) &&
reader->Read2(&subsample_count) &&
RCHECK(reader->Read2(&subsample_count) &&
reader->HasBytes(subsample_count * kEntrySize));
subsamples.resize(subsample_count);
subsamples.resize(subsample_count);
for (int i = 0; i < subsample_count; i++) {
RCHECK(reader->Read2(&subsamples[i].clear_size) &&
reader->Read4(&subsamples[i].encrypted_size));
uint16 clear_bytes;
uint32 cypher_bytes;
RCHECK(reader->Read2(&clear_bytes) &&
reader->Read4(&cypher_bytes));
subsamples[i].clear_bytes = clear_bytes;
subsamples[i].cypher_bytes = cypher_bytes;
}
return true;
}
size_t FrameCENCInfo::GetTotalSize() const {
size_t FrameCENCInfo::GetTotalSizeOfSubsamples() const {
size_t size = 0;
for (size_t i = 0; i < subsamples.size(); i++) {
size += subsamples[i].clear_size + subsamples[i].encrypted_size;
size += subsamples[i].clear_bytes + subsamples[i].cypher_bytes;
}
return size;
}
......
......@@ -8,25 +8,21 @@
#include <vector>
#include "base/basictypes.h"
#include "media/base/decrypt_config.h"
namespace media {
namespace mp4 {
class BufferReader;
struct SubsampleSizes {
uint16 clear_size;
uint32 encrypted_size;
};
struct FrameCENCInfo {
std::vector<uint8> iv;
std::vector<SubsampleSizes> subsamples;
uint8 iv[16];
std::vector<SubsampleEntry> subsamples;
FrameCENCInfo();
~FrameCENCInfo();
bool Parse(int iv_size, BufferReader* r);
size_t GetTotalSize() const;
size_t GetTotalSizeOfSubsamples() const;
};
......
This diff is collapsed.
......@@ -5,6 +5,8 @@
#ifndef MEDIA_MP4_MP4_STREAM_PARSER_H_
#define MEDIA_MP4_MP4_STREAM_PARSER_H_
#include <vector>
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
......@@ -45,11 +47,16 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
bool ParseMoov(mp4::BoxReader* reader);
bool ParseMoof(mp4::BoxReader* reader);
bool EmitKeyNeeded(const TrackEncryption& track_encryption);
bool ReadMDATsUntil(const int64 tgt_tail);
void ChangeState(State new_state);
bool EmitConfigs();
bool PrepareAVCBuffer(const AVCDecoderConfigurationRecord& avc_config,
std::vector<uint8>* frame_buf,
std::vector<SubsampleEntry>* subsamples) const;
bool EnqueueSample(BufferQueue* audio_buffers,
BufferQueue* video_buffers,
bool* err);
......
......@@ -28,21 +28,16 @@ class MP4StreamParserTest : public testing::Test {
public:
MP4StreamParserTest()
: parser_(new MP4StreamParser(false)),
got_configs_(false) {
configs_received_(false) {
}
protected:
scoped_ptr<MP4StreamParser> parser_;
base::TimeDelta segment_start_;
bool got_configs_;
bool configs_received_;
bool AppendData(const uint8* data, size_t length) {
parser_->Parse(data, length);
return true;
}
bool AppendDataInPieces(const uint8* data, size_t length) {
return AppendDataInPieces(data, length, 7);
return parser_->Parse(data, length);
}
bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) {
......@@ -71,8 +66,8 @@ class MP4StreamParserTest : public testing::Test {
// TODO(strobe): Until http://crbug.com/122913 is fixed, we want to make
// sure that this callback isn't called more than once per stream. Remove
// when that bug is fixed.
EXPECT_FALSE(got_configs_);
got_configs_ = true;
EXPECT_FALSE(configs_received_);
configs_received_ = true;
return true;
}
......@@ -83,7 +78,7 @@ class MP4StreamParserTest : public testing::Test {
DVLOG(3) << " n=" << buf - bufs.begin()
<< ", size=" << (*buf)->GetDataSize()
<< ", dur=" << (*buf)->GetDuration().InMilliseconds();
EXPECT_LE(segment_start_, (*buf)->GetTimestamp());
EXPECT_GE((*buf)->GetTimestamp(), segment_start_);
}
return true;
}
......@@ -108,22 +103,28 @@ class MP4StreamParserTest : public testing::Test {
base::Bind(&MP4StreamParserTest::NewSegmentF, base::Unretained(this)));
}
bool ParseMP4File(const std::string& filename, int append_size) {
bool ParseMP4File(const std::string& filename, int append_bytes) {
InitializeParser();
scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
EXPECT_TRUE(AppendDataInPieces(buffer->GetData(),
buffer->GetDataSize(),
append_size));
append_bytes));
return true;
}
};
TEST_F(MP4StreamParserTest, TestParseBearDASH) {
TEST_F(MP4StreamParserTest, TestUnalignedAppend) {
// Test small, non-segment-aligned appends (small enough to exercise
// incremental append system)
ParseMP4File("bear.1280x720_dash.mp4", 512);
}
TEST_F(MP4StreamParserTest, TestMultiFragmentAppend) {
// Large size ensures multiple fragments are appended in one call (size is
// larger than this particular test file)
ParseMP4File("bear.1280x720_dash.mp4", 768432);
}
......
This diff is collapsed.
......@@ -11,12 +11,16 @@
#include "base/time.h"
#include "media/base/media_export.h"
#include "media/mp4/box_definitions.h"
#include "media/mp4/cenc.h"
namespace media {
class DecryptConfig;
namespace mp4 {
using base::TimeDelta;
base::TimeDelta MEDIA_EXPORT TimeDeltaFromFrac(int64 numer, int64 denom);
base::TimeDelta MEDIA_EXPORT TimeDeltaFromRational(int64 numer, int64 denom);
struct SampleInfo;
struct TrackRunInfo;
......@@ -34,14 +38,23 @@ class MEDIA_EXPORT TrackRunIterator {
bool Init(const MovieFragment& moof);
// Returns true if the properties of the current run or sample are valid.
bool RunIsValid() const;
bool SampleIsValid() const;
bool IsRunValid() const;
bool IsSampleValid() const;
// Advance the properties to refer to the next run or sample. Requires that
// the current sample be valid.
void AdvanceRun();
void AdvanceSample();
// Returns true if this track run has auxiliary information and has not yet
// been cached. Only valid if IsRunValid().
bool AuxInfoNeedsToBeCached();
// Caches the CENC data from the given buffer. |buf| must be a buffer starting
// at the offset given by cenc_offset(), with a |size| of at least
// cenc_size(). Returns true on success, false on error.
bool CacheAuxInfo(const uint8* buf, int size);
// Returns the maximum buffer location at which no data earlier in the stream
// will be required in order to read the current or any subsequent sample. You
// may clear all data up to this offset before reading the current sample
......@@ -52,15 +65,17 @@ class MEDIA_EXPORT TrackRunIterator {
// Returns the minimum timestamp (or kInfiniteDuration if no runs present).
TimeDelta GetMinDecodeTimestamp();
// Property of the current run. Only valid if RunIsValid().
// Property of the current run. Only valid if IsRunValid().
uint32 track_id() const;
int64 aux_info_offset() const;
int aux_info_size() const;
bool is_encrypted() const;
bool is_audio() const;
// Only one is valid, based on the value of is_audio().
const AudioSampleEntry& audio_description() const;
const VideoSampleEntry& video_description() const;
// Properties of the current sample. Only valid if SampleIsValid().
// Properties of the current sample. Only valid if IsSampleValid().
int64 sample_offset() const;
int sample_size() const;
TimeDelta dts() const;
......@@ -68,8 +83,13 @@ class MEDIA_EXPORT TrackRunIterator {
TimeDelta duration() const;
bool is_keyframe() const;
// Only call when is_encrypted() is true and AuxInfoNeedsToBeCached() is
// false. Result is owned by caller.
scoped_ptr<DecryptConfig> GetDecryptConfig();
private:
void ResetRun();
const TrackEncryption& track_encryption() const;
const Movie* moov_;
......@@ -77,6 +97,8 @@ class MEDIA_EXPORT TrackRunIterator {
std::vector<TrackRunInfo>::const_iterator run_itr_;
std::vector<SampleInfo>::const_iterator sample_itr_;
std::vector<FrameCENCInfo> cenc_info_;
int64 sample_dts_;
int64 sample_offset_;
......
This diff is collapsed.
......@@ -235,10 +235,15 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode,
scoped_array<uint8> counter_block(GenerateCounterBlock(iv));
buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig(
video_encryption_key_id_.get(), video_encryption_key_id_size_,
counter_block.get(), DecryptConfig::kDecryptionKeySize,
data, kWebMHmacSize,
sizeof(iv))));
std::string(
reinterpret_cast<const char*>(video_encryption_key_id_.get()),
video_encryption_key_id_size_),
std::string(
reinterpret_cast<const char*>(counter_block.get()),
DecryptConfig::kDecryptionKeySize),
std::string(reinterpret_cast<const char*>(data), kWebMHmacSize),
sizeof(iv),
std::vector<SubsampleEntry>())));
}
buffer->SetTimestamp(timestamp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment