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 @@ ...@@ -8,25 +8,19 @@
namespace media { namespace media {
DecryptConfig::DecryptConfig(const uint8* key_id, int key_id_size, DecryptConfig::DecryptConfig(const std::string& key_id,
const uint8* iv, int iv_size, const std::string& iv,
const uint8* checksum, int checksum_size, const std::string& checksum,
int encrypted_frame_offset) const int data_offset,
: key_id_(new uint8[key_id_size]), const std::vector<SubsampleEntry>& subsamples)
key_id_size_(key_id_size), : key_id_(key_id),
iv_(new uint8[iv_size]), iv_(iv),
iv_size_(iv_size), checksum_(checksum),
checksum_(checksum_size > 0 ? new uint8[checksum_size] : NULL), data_offset_(data_offset),
checksum_size_(checksum_size), subsamples_(subsamples) {
encrypted_frame_offset_(encrypted_frame_offset) { CHECK_GT(key_id.size(), 0u);
CHECK_GT(key_id_size, 0); CHECK_EQ(iv.size(), static_cast<size_t>(DecryptConfig::kDecryptionKeySize));
CHECK_EQ(iv_size, DecryptConfig::kDecryptionKeySize); CHECK_GE(data_offset, 0);
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() {} DecryptConfig::~DecryptConfig() {}
......
...@@ -5,55 +5,77 @@ ...@@ -5,55 +5,77 @@
#ifndef MEDIA_BASE_DECRYPT_CONFIG_H_ #ifndef MEDIA_BASE_DECRYPT_CONFIG_H_
#define MEDIA_BASE_DECRYPT_CONFIG_H_ #define MEDIA_BASE_DECRYPT_CONFIG_H_
#include <string>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h" #include "media/base/media_export.h"
namespace media { 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 { class MEDIA_EXPORT DecryptConfig {
public: public:
// Keys are always 128 bits. // Keys are always 128 bits.
static const int kDecryptionKeySize = 16; static const int kDecryptionKeySize = 16;
// |key_id| is the ID that references the decryption key for this frame. |iv| // |key_id| is the ID that references the decryption key for this sample.
// is the initialization vector defined by the encrypted format. Currently // |iv| is the initialization vector defined by the encrypted format.
// |iv_size| must be 16 bytes as defined by WebM and ISO. |checksum| is the // Currently |iv_size| must be 16 bytes as defined by WebM and ISO.
// hash value of the encrypted buffer. |checksum| is defined by the // |checksum| is the hash value of the encrypted buffer. |checksum| is
// encrypted format and may be NULL. |encrypted_frame_offset| is the offset // defined by the encrypted format and may be NULL.
// into the encrypted buffer that the encrypted frame starts. The class // |data_offset| is the amount of data that should be discarded from the
// will copy the data from |key_id|, |iv|, and |checksum|. // head of the sample buffer before applying subsample information. A
DecryptConfig(const uint8* key_id, int key_id_size, // decrypted buffer will be shorter than an encrypted buffer by this amount.
const uint8* iv, int iv_size, // |subsamples| defines the clear and encrypted portions of the sample as
const uint8* checksum, int checksum_size, // described above. A decrypted buffer will be equal in size to the sum
int encrypted_frame_offset); // 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(); ~DecryptConfig();
const uint8* key_id() const { return key_id_.get(); } const std::string& key_id() const { return key_id_; }
int key_id_size() const { return key_id_size_; } const std::string& iv() const { return iv_; }
const uint8* iv() const { return iv_.get(); } const std::string& checksum() const { return checksum_; }
int iv_size() const { return iv_size_; } int data_offset() const { return data_offset_; }
const uint8* checksum() const { return checksum_.get(); } const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
int checksum_size() const { return checksum_size_; }
int encrypted_frame_offset() const { return encrypted_frame_offset_; }
private: private:
const scoped_array<uint8> key_id_; const std::string key_id_;
const int key_id_size_;
// Initialization vector. // Initialization vector.
const scoped_array<uint8> iv_; const std::string iv_;
const int iv_size_;
// Checksum of the data to be verified before decrypting the data. This may // Checksum of the data to be verified before decrypting the data. This may
// be NULL for some formats. // be empty for some formats.
const scoped_array<uint8> checksum_; const std::string checksum_;
const int checksum_size_;
// 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 // Subsample information. May be empty for some formats, meaning entire frame
// the input buffer. // (less data ignored by data_offset_) is encrypted.
const int encrypted_frame_offset_; const std::vector<SubsampleEntry> subsamples_;
DISALLOW_COPY_AND_ASSIGN(DecryptConfig); DISALLOW_COPY_AND_ASSIGN(DecryptConfig);
}; };
......
This diff is collapsed.
...@@ -61,12 +61,12 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor { ...@@ -61,12 +61,12 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor {
explicit DecryptionKey(const std::string& secret); explicit DecryptionKey(const std::string& secret);
~DecryptionKey(); ~DecryptionKey();
// Creates the encryption key and HMAC. If |derive_webm_keys| is true then // Creates the encryption key, and derives the WebM decryption key and HMAC.
// the object will derive the decryption key and the HMAC key from bool Init();
// |secret_|.
bool Init(bool derive_webm_keys);
crypto::SymmetricKey* decryption_key() { return decryption_key_.get(); } 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_); } base::StringPiece hmac_key() { return base::StringPiece(hmac_key_); }
private: private:
...@@ -77,6 +77,9 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor { ...@@ -77,6 +77,9 @@ class MEDIA_EXPORT AesDecryptor : public Decryptor {
// The key used to decrypt the data. // The key used to decrypt the data.
scoped_ptr<crypto::SymmetricKey> decryption_key_; 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 // The key used to perform the integrity check. Currently the HMAC key is
// defined by the WebM encrypted specification. Current encrypted WebM // defined by the WebM encrypted specification. Current encrypted WebM
// request for comments specification is here // request for comments specification is here
......
This diff is collapsed.
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <deque> #include <deque>
#include <string> #include <string>
#include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/message_loop.h" #include "base/message_loop.h"
...@@ -47,10 +48,14 @@ static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() { ...@@ -47,10 +48,14 @@ static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() {
const int encrypted_frame_offset = 1; // This should be non-zero. const int encrypted_frame_offset = 1; // This should be non-zero.
scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(buffer_size)); scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(buffer_size));
buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig( buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig(
kFakeKeyId, arraysize(kFakeKeyId), std::string(reinterpret_cast<const char*>(kFakeKeyId),
kFakeIv, DecryptConfig::kDecryptionKeySize, arraysize(kFakeKeyId)),
kFakeCheckSum, arraysize(kFakeCheckSum), std::string(reinterpret_cast<const char*>(kFakeIv),
encrypted_frame_offset))); DecryptConfig::kDecryptionKeySize),
std::string(reinterpret_cast<const char*>(kFakeCheckSum),
arraysize(kFakeCheckSum)),
encrypted_frame_offset,
std::vector<SubsampleEntry>())));
return buffer; return buffer;
} }
......
...@@ -32,7 +32,7 @@ static bool ConvertAVCToAnnexBInPlaceForLengthSize4(std::vector<uint8>* buf) { ...@@ -32,7 +32,7 @@ static bool ConvertAVCToAnnexBInPlaceForLengthSize4(std::vector<uint8>* buf) {
} }
// static // 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); RCHECK(length_size == 1 || length_size == 2 || length_size == 4);
if (length_size == 4) if (length_size == 4)
...@@ -59,32 +59,31 @@ bool AVC::ConvertToAnnexB(int length_size, std::vector<uint8>* buffer) { ...@@ -59,32 +59,31 @@ bool AVC::ConvertToAnnexB(int length_size, std::vector<uint8>* buffer) {
} }
// static // static
bool AVC::InsertParameterSets(const AVCDecoderConfigurationRecord& avc_config, bool AVC::ConvertConfigToAnnexB(
std::vector<uint8>* buffer) { const AVCDecoderConfigurationRecord& avc_config,
std::vector<uint8>* buffer) {
DCHECK(buffer->empty());
buffer->clear();
int total_size = 0; int total_size = 0;
for (size_t i = 0; i < avc_config.sps_list.size(); i++) for (size_t i = 0; i < avc_config.sps_list.size(); i++)
total_size += avc_config.sps_list[i].size() + kAnnexBStartCodeSize; total_size += avc_config.sps_list[i].size() + kAnnexBStartCodeSize;
for (size_t i = 0; i < avc_config.pps_list.size(); i++) for (size_t i = 0; i < avc_config.pps_list.size(); i++)
total_size += avc_config.pps_list[i].size() + kAnnexBStartCodeSize; total_size += avc_config.pps_list[i].size() + kAnnexBStartCodeSize;
buffer->reserve(total_size);
std::vector<uint8> temp;
temp.reserve(total_size);
for (size_t i = 0; i < avc_config.sps_list.size(); i++) { for (size_t i = 0; i < avc_config.sps_list.size(); i++) {
temp.insert(temp.end(), kAnnexBStartCode, buffer->insert(buffer->end(), kAnnexBStartCode,
kAnnexBStartCode + kAnnexBStartCodeSize); 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()); avc_config.sps_list[i].end());
} }
for (size_t i = 0; i < avc_config.pps_list.size(); i++) { for (size_t i = 0; i < avc_config.pps_list.size(); i++) {
temp.insert(temp.end(), kAnnexBStartCode, buffer->insert(buffer->end(), kAnnexBStartCode,
kAnnexBStartCode + kAnnexBStartCodeSize); kAnnexBStartCode + kAnnexBStartCodeSize);
temp.insert(temp.end(), avc_config.pps_list[i].begin(), buffer->insert(buffer->end(), avc_config.pps_list[i].begin(),
avc_config.pps_list[i].end()); avc_config.pps_list[i].end());
} }
buffer->insert(buffer->begin(), temp.begin(), temp.end());
return true; return true;
} }
......
...@@ -17,9 +17,9 @@ struct AVCDecoderConfigurationRecord; ...@@ -17,9 +17,9 @@ struct AVCDecoderConfigurationRecord;
class MEDIA_EXPORT AVC { class MEDIA_EXPORT AVC {
public: 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, const AVCDecoderConfigurationRecord& avc_config,
std::vector<uint8>* buffer); std::vector<uint8>* buffer);
}; };
......
...@@ -23,7 +23,7 @@ static const uint8 kExpected[] = { ...@@ -23,7 +23,7 @@ static const uint8 kExpected[] = {
static const uint8 kExpectedParamSets[] = { static const uint8 kExpectedParamSets[] = {
0x00, 0x00, 0x00, 0x01, 0x67, 0x12, 0x00, 0x00, 0x00, 0x01, 0x67, 0x12,
0x00, 0x00, 0x00, 0x01, 0x67, 0x34, 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> { class AVCConversionTest : public testing::TestWithParam<int> {
protected: protected:
...@@ -44,7 +44,7 @@ class AVCConversionTest : public testing::TestWithParam<int> { ...@@ -44,7 +44,7 @@ class AVCConversionTest : public testing::TestWithParam<int> {
TEST_P(AVCConversionTest, ParseCorrectly) { TEST_P(AVCConversionTest, ParseCorrectly) {
std::vector<uint8> buf; std::vector<uint8> buf;
MakeInputForLength(GetParam(), &buf); MakeInputForLength(GetParam(), &buf);
EXPECT_TRUE(AVC::ConvertToAnnexB(GetParam(), &buf)); EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
EXPECT_EQ(buf.size(), sizeof(kExpected)); EXPECT_EQ(buf.size(), sizeof(kExpected));
EXPECT_EQ(0, memcmp(kExpected, &buf[0], sizeof(kExpected))); EXPECT_EQ(0, memcmp(kExpected, &buf[0], sizeof(kExpected)));
} }
...@@ -53,19 +53,19 @@ TEST_P(AVCConversionTest, ParsePartial) { ...@@ -53,19 +53,19 @@ TEST_P(AVCConversionTest, ParsePartial) {
std::vector<uint8> buf; std::vector<uint8> buf;
MakeInputForLength(GetParam(), &buf); MakeInputForLength(GetParam(), &buf);
buf.pop_back(); 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 // 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. // of one, this can't happen, so we skip that case.
if (GetParam() != 1) { if (GetParam() != 1) {
MakeInputForLength(GetParam(), &buf); MakeInputForLength(GetParam(), &buf);
buf.erase(buf.end() - (sizeof(kNALU2) + 1), buf.end()); 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) { TEST_P(AVCConversionTest, ParseEmpty) {
std::vector<uint8> buf; std::vector<uint8> buf;
EXPECT_TRUE(AVC::ConvertToAnnexB(GetParam(), &buf)); EXPECT_TRUE(AVC::ConvertFrameToAnnexB(GetParam(), &buf));
EXPECT_EQ(0u, buf.size()); EXPECT_EQ(0u, buf.size());
} }
...@@ -73,7 +73,7 @@ INSTANTIATE_TEST_CASE_P(AVCConversionTestValues, ...@@ -73,7 +73,7 @@ INSTANTIATE_TEST_CASE_P(AVCConversionTestValues,
AVCConversionTest, AVCConversionTest,
::testing::Values(1, 2, 4)); ::testing::Values(1, 2, 4));
TEST(AVC, InsertParameterSetsTest) { TEST_F(AVCConversionTest, ConvertConfigToAnnexB) {
AVCDecoderConfigurationRecord avc_config; AVCDecoderConfigurationRecord avc_config;
avc_config.sps_list.resize(2); avc_config.sps_list.resize(2);
avc_config.sps_list[0].push_back(0x67); avc_config.sps_list[0].push_back(0x67);
...@@ -86,8 +86,7 @@ TEST(AVC, InsertParameterSetsTest) { ...@@ -86,8 +86,7 @@ TEST(AVC, InsertParameterSetsTest) {
avc_config.pps_list[0].push_back(0x78); avc_config.pps_list[0].push_back(0x78);
std::vector<uint8> buf; std::vector<uint8> buf;
buf.push_back(0x9a); EXPECT_TRUE(AVC::ConvertConfigToAnnexB(avc_config, &buf));
EXPECT_TRUE(AVC::InsertParameterSets(avc_config, &buf));
EXPECT_EQ(0, memcmp(kExpectedParamSets, &buf[0], EXPECT_EQ(0, memcmp(kExpectedParamSets, &buf[0],
sizeof(kExpectedParamSets))); sizeof(kExpectedParamSets)));
} }
......
...@@ -27,7 +27,7 @@ FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; } ...@@ -27,7 +27,7 @@ FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; }
bool ProtectionSystemSpecificHeader::Parse(BoxReader* reader) { bool ProtectionSystemSpecificHeader::Parse(BoxReader* reader) {
uint32 size; uint32 size;
return reader->SkipBytes(4) && return reader->ReadFullBoxHeader() &&
reader->ReadVec(&system_id, 16) && reader->ReadVec(&system_id, 16) &&
reader->Read4(&size) && reader->Read4(&size) &&
reader->ReadVec(&data, size); reader->ReadVec(&data, size);
...@@ -88,7 +88,7 @@ SchemeType::~SchemeType() {} ...@@ -88,7 +88,7 @@ SchemeType::~SchemeType() {}
FourCC SchemeType::BoxType() const { return FOURCC_SCHM; } FourCC SchemeType::BoxType() const { return FOURCC_SCHM; }
bool SchemeType::Parse(BoxReader* reader) { bool SchemeType::Parse(BoxReader* reader) {
RCHECK(reader->SkipBytes(4) && RCHECK(reader->ReadFullBoxHeader() &&
reader->ReadFourCC(&type) && reader->ReadFourCC(&type) &&
reader->Read4(&version)); reader->Read4(&version));
RCHECK(type == FOURCC_CENC); RCHECK(type == FOURCC_CENC);
...@@ -103,7 +103,8 @@ FourCC TrackEncryption::BoxType() const { return FOURCC_TENC; } ...@@ -103,7 +103,8 @@ FourCC TrackEncryption::BoxType() const { return FOURCC_TENC; }
bool TrackEncryption::Parse(BoxReader* reader) { bool TrackEncryption::Parse(BoxReader* reader) {
uint8 flag; uint8 flag;
RCHECK(reader->SkipBytes(2) && RCHECK(reader->ReadFullBoxHeader() &&
reader->SkipBytes(2) &&
reader->Read1(&flag) && reader->Read1(&flag) &&
reader->Read1(&default_iv_size) && reader->Read1(&default_iv_size) &&
reader->ReadVec(&default_kid, 16)); reader->ReadVec(&default_kid, 16));
...@@ -129,9 +130,11 @@ ProtectionSchemeInfo::~ProtectionSchemeInfo() {} ...@@ -129,9 +130,11 @@ ProtectionSchemeInfo::~ProtectionSchemeInfo() {}
FourCC ProtectionSchemeInfo::BoxType() const { return FOURCC_SINF; } FourCC ProtectionSchemeInfo::BoxType() const { return FOURCC_SINF; }
bool ProtectionSchemeInfo::Parse(BoxReader* reader) { bool ProtectionSchemeInfo::Parse(BoxReader* reader) {
return reader->ScanChildren() && RCHECK(reader->ScanChildren() &&
reader->ReadChild(&format) &&
reader->ReadChild(&type) && reader->ReadChild(&type) &&
reader->ReadChild(&info); reader->ReadChild(&info));
return true;
} }
MovieHeader::MovieHeader() MovieHeader::MovieHeader()
...@@ -375,20 +378,15 @@ bool VideoSampleEntry::Parse(BoxReader* reader) { ...@@ -375,20 +378,15 @@ bool VideoSampleEntry::Parse(BoxReader* reader) {
reader->Read2(&height) && reader->Read2(&height) &&
reader->SkipBytes(50)); reader->SkipBytes(50));
RCHECK(reader->ScanChildren()); RCHECK(reader->ScanChildren() &&
RCHECK(reader->MaybeReadChild(&pixel_aspect)); reader->MaybeReadChild(&pixel_aspect));
if (format == FOURCC_ENCV) {
RCHECK(reader->ReadChild(&sinf));
}
// TODO(strobe): finalize format signaling for encrypted media if (format == FOURCC_ENCV)
// (http://crbug.com/132351) RCHECK(reader->ReadChild(&sinf));
// if (format == FOURCC_AVC1 ||
// if (format == FOURCC_AVC1 || (format == FOURCC_ENCV && sinf.format.format == FOURCC_AVC1)) {
// (format == FOURCC_ENCV &&
// sinf.format.format == FOURCC_AVC1)) {
RCHECK(reader->ReadChild(&avcc)); RCHECK(reader->ReadChild(&avcc));
// } }
return true; return true;
} }
...@@ -444,9 +442,8 @@ bool AudioSampleEntry::Parse(BoxReader* reader) { ...@@ -444,9 +442,8 @@ bool AudioSampleEntry::Parse(BoxReader* reader) {
samplerate >>= 16; samplerate >>= 16;
RCHECK(reader->ScanChildren()); RCHECK(reader->ScanChildren());
if (format == FOURCC_ENCA) { if (format == FOURCC_ENCA)
RCHECK(reader->ReadChild(&sinf)); RCHECK(reader->ReadChild(&sinf));
}
RCHECK(reader->ReadChild(&esds)); RCHECK(reader->ReadChild(&esds));
return true; return true;
} }
...@@ -597,6 +594,7 @@ bool MovieFragmentHeader::Parse(BoxReader* reader) { ...@@ -597,6 +594,7 @@ bool MovieFragmentHeader::Parse(BoxReader* reader) {
TrackFragmentHeader::TrackFragmentHeader() TrackFragmentHeader::TrackFragmentHeader()
: track_id(0), : track_id(0),
sample_description_index(0),
default_sample_duration(0), default_sample_duration(0),
default_sample_size(0), default_sample_size(0),
default_sample_flags(0), default_sample_flags(0),
......
...@@ -217,7 +217,9 @@ struct MEDIA_EXPORT SampleTable : Box { ...@@ -217,7 +217,9 @@ struct MEDIA_EXPORT SampleTable : Box {
DECLARE_BOX_METHODS(SampleTable); DECLARE_BOX_METHODS(SampleTable);
// Media Source specific: we ignore many of the sub-boxes in this box, // 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; SampleDescription description;
}; };
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
#include "media/mp4/cenc.h" #include "media/mp4/cenc.h"
#include <cstring>
#include "media/mp4/box_reader.h" #include "media/mp4/box_reader.h"
#include "media/mp4/rcheck.h" #include "media/mp4/rcheck.h"
...@@ -15,28 +17,35 @@ FrameCENCInfo::~FrameCENCInfo() {} ...@@ -15,28 +17,35 @@ FrameCENCInfo::~FrameCENCInfo() {}
bool FrameCENCInfo::Parse(int iv_size, BufferReader* reader) { bool FrameCENCInfo::Parse(int iv_size, BufferReader* reader) {
const int kEntrySize = 6; const int kEntrySize = 6;
// Mandated by CENC spec // Mandated by CENC spec
RCHECK(iv_size == 8 || iv_size == 16); 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; uint16 subsample_count;
RCHECK(reader->ReadVec(&iv, iv_size) && RCHECK(reader->Read2(&subsample_count) &&
reader->Read2(&subsample_count) &&
reader->HasBytes(subsample_count * kEntrySize)); reader->HasBytes(subsample_count * kEntrySize));
subsamples.resize(subsample_count);
subsamples.resize(subsample_count);
for (int i = 0; i < subsample_count; i++) { for (int i = 0; i < subsample_count; i++) {
RCHECK(reader->Read2(&subsamples[i].clear_size) && uint16 clear_bytes;
reader->Read4(&subsamples[i].encrypted_size)); 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; return true;
} }
size_t FrameCENCInfo::GetTotalSize() const { size_t FrameCENCInfo::GetTotalSizeOfSubsamples() const {
size_t size = 0; size_t size = 0;
for (size_t i = 0; i < subsamples.size(); i++) { 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; return size;
} }
......
...@@ -8,25 +8,21 @@ ...@@ -8,25 +8,21 @@
#include <vector> #include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "media/base/decrypt_config.h"
namespace media { namespace media {
namespace mp4 { namespace mp4 {
class BufferReader; class BufferReader;
struct SubsampleSizes {
uint16 clear_size;
uint32 encrypted_size;
};
struct FrameCENCInfo { struct FrameCENCInfo {
std::vector<uint8> iv; uint8 iv[16];
std::vector<SubsampleSizes> subsamples; std::vector<SubsampleEntry> subsamples;
FrameCENCInfo(); FrameCENCInfo();
~FrameCENCInfo(); ~FrameCENCInfo();
bool Parse(int iv_size, BufferReader* r); bool Parse(int iv_size, BufferReader* r);
size_t GetTotalSize() const; size_t GetTotalSizeOfSubsamples() const;
}; };
......
This diff is collapsed.
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef MEDIA_MP4_MP4_STREAM_PARSER_H_ #ifndef MEDIA_MP4_MP4_STREAM_PARSER_H_
#define MEDIA_MP4_MP4_STREAM_PARSER_H_ #define MEDIA_MP4_MP4_STREAM_PARSER_H_
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
...@@ -45,11 +47,16 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser { ...@@ -45,11 +47,16 @@ class MEDIA_EXPORT MP4StreamParser : public StreamParser {
bool ParseMoov(mp4::BoxReader* reader); bool ParseMoov(mp4::BoxReader* reader);
bool ParseMoof(mp4::BoxReader* reader); bool ParseMoof(mp4::BoxReader* reader);
bool EmitKeyNeeded(const TrackEncryption& track_encryption);
bool ReadMDATsUntil(const int64 tgt_tail); bool ReadMDATsUntil(const int64 tgt_tail);
void ChangeState(State new_state); void ChangeState(State new_state);
bool EmitConfigs(); bool EmitConfigs();
bool PrepareAVCBuffer(const AVCDecoderConfigurationRecord& avc_config,
std::vector<uint8>* frame_buf,
std::vector<SubsampleEntry>* subsamples) const;
bool EnqueueSample(BufferQueue* audio_buffers, bool EnqueueSample(BufferQueue* audio_buffers,
BufferQueue* video_buffers, BufferQueue* video_buffers,
bool* err); bool* err);
......
...@@ -28,21 +28,16 @@ class MP4StreamParserTest : public testing::Test { ...@@ -28,21 +28,16 @@ class MP4StreamParserTest : public testing::Test {
public: public:
MP4StreamParserTest() MP4StreamParserTest()
: parser_(new MP4StreamParser(false)), : parser_(new MP4StreamParser(false)),
got_configs_(false) { configs_received_(false) {
} }
protected: protected:
scoped_ptr<MP4StreamParser> parser_; scoped_ptr<MP4StreamParser> parser_;
base::TimeDelta segment_start_; base::TimeDelta segment_start_;
bool got_configs_; bool configs_received_;
bool AppendData(const uint8* data, size_t length) { bool AppendData(const uint8* data, size_t length) {
parser_->Parse(data, length); return parser_->Parse(data, length);
return true;
}
bool AppendDataInPieces(const uint8* data, size_t length) {
return AppendDataInPieces(data, length, 7);
} }
bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) { bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) {
...@@ -71,8 +66,8 @@ class MP4StreamParserTest : public testing::Test { ...@@ -71,8 +66,8 @@ class MP4StreamParserTest : public testing::Test {
// TODO(strobe): Until http://crbug.com/122913 is fixed, we want to make // 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 // sure that this callback isn't called more than once per stream. Remove
// when that bug is fixed. // when that bug is fixed.
EXPECT_FALSE(got_configs_); EXPECT_FALSE(configs_received_);
got_configs_ = true; configs_received_ = true;
return true; return true;
} }
...@@ -83,7 +78,7 @@ class MP4StreamParserTest : public testing::Test { ...@@ -83,7 +78,7 @@ class MP4StreamParserTest : public testing::Test {
DVLOG(3) << " n=" << buf - bufs.begin() DVLOG(3) << " n=" << buf - bufs.begin()
<< ", size=" << (*buf)->GetDataSize() << ", size=" << (*buf)->GetDataSize()
<< ", dur=" << (*buf)->GetDuration().InMilliseconds(); << ", dur=" << (*buf)->GetDuration().InMilliseconds();
EXPECT_LE(segment_start_, (*buf)->GetTimestamp()); EXPECT_GE((*buf)->GetTimestamp(), segment_start_);
} }
return true; return true;
} }
...@@ -108,22 +103,28 @@ class MP4StreamParserTest : public testing::Test { ...@@ -108,22 +103,28 @@ class MP4StreamParserTest : public testing::Test {
base::Bind(&MP4StreamParserTest::NewSegmentF, base::Unretained(this))); 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(); InitializeParser();
scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename); scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
EXPECT_TRUE(AppendDataInPieces(buffer->GetData(), EXPECT_TRUE(AppendDataInPieces(buffer->GetData(),
buffer->GetDataSize(), buffer->GetDataSize(),
append_size)); append_bytes));
return true; 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); ParseMP4File("bear.1280x720_dash.mp4", 512);
} }
TEST_F(MP4StreamParserTest, TestMultiFragmentAppend) { 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); ParseMP4File("bear.1280x720_dash.mp4", 768432);
} }
......
This diff is collapsed.
...@@ -11,12 +11,16 @@ ...@@ -11,12 +11,16 @@
#include "base/time.h" #include "base/time.h"
#include "media/base/media_export.h" #include "media/base/media_export.h"
#include "media/mp4/box_definitions.h" #include "media/mp4/box_definitions.h"
#include "media/mp4/cenc.h"
namespace media { namespace media {
class DecryptConfig;
namespace mp4 { namespace mp4 {
using base::TimeDelta; using base::TimeDelta;
base::TimeDelta MEDIA_EXPORT TimeDeltaFromFrac(int64 numer, int64 denom); base::TimeDelta MEDIA_EXPORT TimeDeltaFromRational(int64 numer, int64 denom);
struct SampleInfo; struct SampleInfo;
struct TrackRunInfo; struct TrackRunInfo;
...@@ -34,14 +38,23 @@ class MEDIA_EXPORT TrackRunIterator { ...@@ -34,14 +38,23 @@ class MEDIA_EXPORT TrackRunIterator {
bool Init(const MovieFragment& moof); bool Init(const MovieFragment& moof);
// Returns true if the properties of the current run or sample are valid. // Returns true if the properties of the current run or sample are valid.
bool RunIsValid() const; bool IsRunValid() const;
bool SampleIsValid() const; bool IsSampleValid() const;
// Advance the properties to refer to the next run or sample. Requires that // Advance the properties to refer to the next run or sample. Requires that
// the current sample be valid. // the current sample be valid.
void AdvanceRun(); void AdvanceRun();
void AdvanceSample(); 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 // 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 // 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 // may clear all data up to this offset before reading the current sample
...@@ -52,15 +65,17 @@ class MEDIA_EXPORT TrackRunIterator { ...@@ -52,15 +65,17 @@ class MEDIA_EXPORT TrackRunIterator {
// Returns the minimum timestamp (or kInfiniteDuration if no runs present). // Returns the minimum timestamp (or kInfiniteDuration if no runs present).
TimeDelta GetMinDecodeTimestamp(); TimeDelta GetMinDecodeTimestamp();
// Property of the current run. Only valid if RunIsValid(). // Property of the current run. Only valid if IsRunValid().
uint32 track_id() const; uint32 track_id() const;
int64 aux_info_offset() const;
int aux_info_size() const;
bool is_encrypted() const; bool is_encrypted() const;
bool is_audio() const; bool is_audio() const;
// Only one is valid, based on the value of is_audio(). // Only one is valid, based on the value of is_audio().
const AudioSampleEntry& audio_description() const; const AudioSampleEntry& audio_description() const;
const VideoSampleEntry& video_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; int64 sample_offset() const;
int sample_size() const; int sample_size() const;
TimeDelta dts() const; TimeDelta dts() const;
...@@ -68,8 +83,13 @@ class MEDIA_EXPORT TrackRunIterator { ...@@ -68,8 +83,13 @@ class MEDIA_EXPORT TrackRunIterator {
TimeDelta duration() const; TimeDelta duration() const;
bool is_keyframe() 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: private:
void ResetRun(); void ResetRun();
const TrackEncryption& track_encryption() const;
const Movie* moov_; const Movie* moov_;
...@@ -77,6 +97,8 @@ class MEDIA_EXPORT TrackRunIterator { ...@@ -77,6 +97,8 @@ class MEDIA_EXPORT TrackRunIterator {
std::vector<TrackRunInfo>::const_iterator run_itr_; std::vector<TrackRunInfo>::const_iterator run_itr_;
std::vector<SampleInfo>::const_iterator sample_itr_; std::vector<SampleInfo>::const_iterator sample_itr_;
std::vector<FrameCENCInfo> cenc_info_;
int64 sample_dts_; int64 sample_dts_;
int64 sample_offset_; int64 sample_offset_;
......
This diff is collapsed.
...@@ -235,10 +235,15 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode, ...@@ -235,10 +235,15 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode,
scoped_array<uint8> counter_block(GenerateCounterBlock(iv)); scoped_array<uint8> counter_block(GenerateCounterBlock(iv));
buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig( buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig(
video_encryption_key_id_.get(), video_encryption_key_id_size_, std::string(
counter_block.get(), DecryptConfig::kDecryptionKeySize, reinterpret_cast<const char*>(video_encryption_key_id_.get()),
data, kWebMHmacSize, video_encryption_key_id_size_),
sizeof(iv)))); 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); 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