Commit 74f559c2 authored by kjellander's avatar kjellander Committed by Commit bot

Revert of Combine 'pssh' parsing routines. (patchset #8 id:140001 of...

Revert of Combine 'pssh' parsing routines. (patchset #8 id:140001 of https://codereview.chromium.org/1149023002/)

Reason for revert:
Breaks compile on 'Google Chrome Win' bot:
https://build.chromium.org/p/chromium.chrome/builders/Google%20Chrome%20Win/builds/1066

Original issue's description:
> Combine CENC 'pssh' box parsing routines.
>
> Also update the routines to ignore 'pssh' boxes with version 2 or
> later.
>
> BUG=460359, 460360
> TEST=new unittests pass
>
> Committed: https://crrev.com/b666d7874efac44b359a95329f0cb890e97671df
> Cr-Commit-Position: refs/heads/master@{#333556}
>
> Committed: https://crrev.com/7a84443597e92a03f90806712f8629df40bf408e
> Cr-Commit-Position: refs/heads/master@{#333611}

TBR=sandersd@chromium.org,ddorwin@chromium.org,xhwang@chromium.org,jrummell@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=460359, 460360

Review URL: https://codereview.chromium.org/1165313003

Cr-Commit-Position: refs/heads/master@{#333694}
parent 61457e1b
include_rules = [ include_rules = [
"+content/public/browser", "+content/public/browser",
"+media/cdm", "+media/base/android",
] ]
...@@ -4,17 +4,151 @@ ...@@ -4,17 +4,151 @@
#include "components/cdm/browser/widevine_drm_delegate_android.h" #include "components/cdm/browser/widevine_drm_delegate_android.h"
#include "media/cdm/cenc_utils.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h"
namespace cdm { namespace cdm {
namespace { namespace {
uint32_t ReadUint32(const uint8_t* data) {
uint32_t value = 0;
for (int i = 0; i < 4; ++i)
value = (value << 8) | data[i];
return value;
}
uint64_t ReadUint64(const uint8_t* data) {
uint64_t value = 0;
for (int i = 0; i < 8; ++i)
value = (value << 8) | data[i];
return value;
}
// The structure of an ISO CENC Protection System Specific Header (PSSH) box is
// as follows. (See ISO/IEC FDIS 23001-7:2011(E).)
// Note: ISO boxes use big-endian values.
//
// PSSH {
// uint32 Size
// uint32 Type
// uint64 LargeSize # Field is only present if value(Size) == 1.
// uint8 Version
// uint24 Flags
// uint8[16] SystemId
// if (version > 0) {
// uint32 KID_count;
// uint8[16][KID_Count] KID;
// }
// uint32 DataSize
// uint8[DataSize] Data
// }
const int kBoxHeaderSize = 8; // Box's header contains Size and Type.
const int kBoxLargeSizeSize = 8;
const int kPsshVersionFlagSize = 4;
const uint32_t k24BitMask = 0x00ffffff;
const int kPsshSystemIdSize = 16;
const int kPsshKidCountSize = 4;
const int kPsshKidSize = 16;
const int kPsshDataSizeSize = 4;
const uint32_t kPsshType = 0x70737368;
const uint8_t kWidevineUuid[16] = { const uint8_t kWidevineUuid[16] = {
0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE, 0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED }; 0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
} // namespace // Tries to find a PSSH box with the Widevine UUID, parses the
// "Data" of the box and put it in |pssh_data|. Returns true if such a box is
// found and successfully parsed. Returns false otherwise.
// Notes:
// 1, If multiple PSSH boxes are found,the "Data" of the first matching PSSH box
// will be set in |pssh_data|.
// 2, Only PSSH boxes are allowed in |data|.
bool GetPsshData(const std::vector<uint8_t>& data,
std::vector<uint8_t>* pssh_data) {
int bytes_left = base::checked_cast<int>(data.size());
const uint8_t* cur = &data[0];
const uint8_t* data_end = cur + bytes_left;
while (bytes_left > 0) {
const uint8_t* box_head = cur;
if (bytes_left < kBoxHeaderSize)
return false;
uint64_t box_size = ReadUint32(cur);
uint32_t type = ReadUint32(cur + 4);
cur += kBoxHeaderSize;
bytes_left -= kBoxHeaderSize;
if (box_size == 1) { // LargeSize is present.
if (bytes_left < kBoxLargeSizeSize)
return false;
box_size = ReadUint64(cur);
cur += kBoxLargeSizeSize;
bytes_left -= kBoxLargeSizeSize;
} else if (box_size == 0) {
box_size = bytes_left + kBoxHeaderSize;
}
const uint8_t* box_end = box_head + box_size;
if (data_end < box_end)
return false;
if (type != kPsshType)
return false;
const int kPsshBoxMinimumSize =
kPsshVersionFlagSize + kPsshSystemIdSize + kPsshDataSizeSize;
if (box_end < cur + kPsshBoxMinimumSize)
return false;
uint8_t version = cur[0];
uint32_t flags = ReadUint32(cur) & k24BitMask;
cur += kPsshVersionFlagSize;
bytes_left -= kPsshVersionFlagSize;
if (flags != 0)
return false;
DCHECK_GE(bytes_left, kPsshSystemIdSize);
if (!std::equal(kWidevineUuid,
kWidevineUuid + sizeof(kWidevineUuid), cur)) {
cur = box_end;
bytes_left = data_end - cur;
continue;
}
cur += kPsshSystemIdSize;
bytes_left -= kPsshSystemIdSize;
// If KeyIDs specified, skip them.
if (version > 0) {
DCHECK_GE(bytes_left, kPsshKidCountSize);
uint32_t kid_count = ReadUint32(cur);
cur += kPsshKidCountSize + kid_count * kPsshKidSize;
bytes_left -= kPsshKidCountSize + kid_count * kPsshKidSize;
// Must be bytes left in this box for data_size.
if (box_end < cur + kPsshDataSizeSize)
return false;
}
DCHECK_GE(bytes_left, kPsshDataSizeSize);
uint32_t data_size = ReadUint32(cur);
cur += kPsshDataSizeSize;
bytes_left -= kPsshDataSizeSize;
if (box_end < cur + data_size)
return false;
pssh_data->assign(cur, cur + data_size);
return true;
}
return false;
}
}
WidevineDrmDelegateAndroid::WidevineDrmDelegateAndroid() { WidevineDrmDelegateAndroid::WidevineDrmDelegateAndroid() {
} }
...@@ -37,7 +171,7 @@ bool WidevineDrmDelegateAndroid::OnCreateSession( ...@@ -37,7 +171,7 @@ bool WidevineDrmDelegateAndroid::OnCreateSession(
// Widevine MediaDrm plugin only accepts the "data" part of the PSSH box as // Widevine MediaDrm plugin only accepts the "data" part of the PSSH box as
// the init data when using MP4 container. // the init data when using MP4 container.
return media::GetPsshData(init_data, GetUUID(), init_data_out); return GetPsshData(init_data, init_data_out);
} }
} // namespace cdm } // namespace cdm
...@@ -4,9 +4,7 @@ ...@@ -4,9 +4,7 @@
#include "media/cdm/cenc_utils.h" #include "media/cdm/cenc_utils.h"
#include "base/stl_util.h" #include "media/base/bit_reader.h"
#include "media/formats/mp4/box_definitions.h"
#include "media/formats/mp4/box_reader.h"
namespace media { namespace media {
...@@ -14,6 +12,31 @@ namespace media { ...@@ -14,6 +12,31 @@ namespace media {
// Encryption ('cenc') protection scheme may contain one or more protection // Encryption ('cenc') protection scheme may contain one or more protection
// system specific header ('pssh') boxes. // system specific header ('pssh') boxes.
// ref: https://w3c.github.io/encrypted-media/cenc-format.html // ref: https://w3c.github.io/encrypted-media/cenc-format.html
//
// The format of a 'pssh' box is as follows:
// unsigned int(32) size;
// unsigned int(32) type = "pssh";
// if (size==1) {
// unsigned int(64) largesize;
// } else if (size==0) {
// -- box extends to end of file
// }
// unsigned int(8) version;
// bit(24) flags;
// unsigned int(8)[16] SystemID;
// if (version > 0)
// {
// unsigned int(32) KID_count;
// {
// unsigned int(8)[16] KID;
// } [KID_count]
// }
// unsigned int(32) DataSize;
// unsigned int(8)[DataSize] Data;
// Minimum size of a 'pssh' box includes all the required fields (size, type,
// version, flags, SystemID, DataSize).
const int kMinimumBoxSizeInBytes = 32;
// SystemID for the Common System. // SystemID for the Common System.
// https://w3c.github.io/encrypted-media/cenc-format.html#common-system // https://w3c.github.io/encrypted-media/cenc-format.html#common-system
...@@ -22,102 +45,136 @@ const uint8_t kCommonSystemId[] = { 0x10, 0x77, 0xef, 0xec, ...@@ -22,102 +45,136 @@ const uint8_t kCommonSystemId[] = { 0x10, 0x77, 0xef, 0xec,
0xac, 0xe3, 0x3c, 0x1e, 0xac, 0xe3, 0x3c, 0x1e,
0x52, 0xe2, 0xfb, 0x4b }; 0x52, 0xe2, 0xfb, 0x4b };
static bool ReadAllPsshBoxes( #define RCHECK(x) \
const std::vector<uint8_t>& input, do { \
std::vector<mp4::FullProtectionSystemSpecificHeader>* pssh_boxes) { if (!(x)) \
DCHECK(!input.empty()); return false; \
} while (0)
// Verify that |input| contains only 'pssh' boxes. ReadAllChildren() is
// templated, so it checks that each box in |input| matches the box type of // Helper function to read up to 32 bits from a bit stream.
// the parameter (in this case mp4::ProtectionSystemSpecificHeader is a static uint32_t ReadBits(BitReader* reader, int num_bits) {
// 'pssh' box). mp4::ProtectionSystemSpecificHeader doesn't validate the DCHECK_GE(reader->bits_available(), num_bits);
// 'pssh' contents, so this simply verifies that |input| only contains DCHECK((num_bits > 0) && (num_bits <= 32));
// 'pssh' boxes and nothing else. uint32_t value;
scoped_ptr<mp4::BoxReader> input_reader( reader->ReadBits(num_bits, &value);
mp4::BoxReader::ReadConcatentatedBoxes( return value;
vector_as_array(&input), input.size())); }
std::vector<mp4::ProtectionSystemSpecificHeader> raw_pssh_boxes;
if (!input_reader->ReadAllChildren(&raw_pssh_boxes)) // Checks whether the next 16 bytes matches the Common SystemID.
// Assumes |reader| has enough data.
static bool IsCommonSystemID(BitReader* reader) {
for (uint32_t i = 0; i < arraysize(kCommonSystemId); ++i) {
if (ReadBits(reader, 8) != kCommonSystemId[i])
return false; return false;
}
return true;
}
// Now that we have |input| parsed into |raw_pssh_boxes|, reparse each one // Checks that |reader| contains a valid 'ppsh' box header. |reader| is updated
// into a mp4::FullProtectionSystemSpecificHeader, which extracts all the // to point to the content immediately following the box header. Returns true
// relevant fields from the box. Since there may be unparseable 'pssh' boxes // if the header looks valid and |reader| contains enough data for the size of
// (due to unsupported version, for example), this is done one by one, // header. |size| is updated as the computed size of the box header. Otherwise
// ignoring any boxes that can't be parsed. // false is returned.
for (const auto& raw_pssh_box : raw_pssh_boxes) { static bool ValidBoxHeader(BitReader* reader, uint32* size) {
scoped_ptr<mp4::BoxReader> raw_pssh_reader( // Enough data for a miniumum size 'pssh' box?
mp4::BoxReader::ReadConcatentatedBoxes( uint32 available_bytes = reader->bits_available() / 8;
vector_as_array(&raw_pssh_box.raw_box), RCHECK(available_bytes >= kMinimumBoxSizeInBytes);
raw_pssh_box.raw_box.size()));
// ReadAllChildren() appends any successfully parsed box onto it's *size = ReadBits(reader, 32);
// parameter, so |pssh_boxes| will contain the collection of successfully
// parsed 'pssh' boxes. If an error occurs, try the next box. // Must be a 'pssh' box or else fail.
if (!raw_pssh_reader->ReadAllChildren(pssh_boxes)) RCHECK(ReadBits(reader, 8) == 'p');
continue; RCHECK(ReadBits(reader, 8) == 's');
RCHECK(ReadBits(reader, 8) == 's');
RCHECK(ReadBits(reader, 8) == 'h');
if (*size == 1) {
// If largesize > 2**32 it is too big.
RCHECK(ReadBits(reader, 32) == 0);
*size = ReadBits(reader, 32);
} else if (*size == 0) {
*size = available_bytes;
} }
// Must have successfully parsed at least one 'pssh' box. // Check that the buffer contains at least size bytes.
return pssh_boxes->size() > 0; return available_bytes >= *size;
} }
bool ValidatePsshInput(const std::vector<uint8_t>& input) { bool ValidatePsshInput(const std::vector<uint8_t>& input) {
// No 'pssh' boxes is considered valid. size_t offset = 0;
if (input.empty()) while (offset < input.size()) {
return true; // Create a BitReader over the remaining part of the buffer.
BitReader reader(&input[offset], input.size() - offset);
uint32 size;
RCHECK(ValidBoxHeader(&reader, &size));
// Update offset to point at the next 'pssh' box (may not be one).
offset += size;
}
std::vector<mp4::FullProtectionSystemSpecificHeader> children; // Only valid if this contains 0 or more 'pssh' boxes.
return ReadAllPsshBoxes(input, &children); return offset == input.size();
} }
bool GetKeyIdsForCommonSystemId(const std::vector<uint8_t>& input, bool GetKeyIdsForCommonSystemId(const std::vector<uint8_t>& input,
KeyIdList* key_ids) { KeyIdList* key_ids) {
size_t offset = 0;
KeyIdList result; KeyIdList result;
std::vector<uint8_t> common_system_id(
kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId));
if (!input.empty()) { while (offset < input.size()) {
std::vector<mp4::FullProtectionSystemSpecificHeader> children; BitReader reader(&input[offset], input.size() - offset);
if (!ReadAllPsshBoxes(input, &children)) uint32 size;
return false; RCHECK(ValidBoxHeader(&reader, &size));
// Check all children for an appropriate 'pssh' box, concatenating any // Update offset to point at the next 'pssh' box (may not be one).
// key IDs found. offset += size;
for (const auto& child : children) {
if (child.system_id == common_system_id && child.key_ids.size() > 0)
result.insert(result.end(), child.key_ids.begin(), child.key_ids.end());
}
}
// No matching 'pssh' box found. // Check the version, as KIDs only available if version > 0.
// TODO(jrummell): This should return true only if there was at least one uint8_t version = ReadBits(&reader, 8);
// key ID present. However, numerous test files don't contain the 'pssh' box if (version == 0)
// for Common Format, so no keys are found. http://crbug.com/460308 continue;
key_ids->swap(result);
return true;
}
bool GetPsshData(const std::vector<uint8_t>& input, // flags must be 0. If not, assume incorrect 'pssh' box and move to the
const std::vector<uint8_t>& system_id, // next one.
std::vector<uint8_t>* pssh_data) { if (ReadBits(&reader, 24) != 0)
if (input.empty()) continue;
return false;
std::vector<mp4::FullProtectionSystemSpecificHeader> children; // Validate SystemID
if (!ReadAllPsshBoxes(input, &children)) RCHECK(static_cast<uint32_t>(reader.bits_available()) >=
return false; arraysize(kCommonSystemId) * 8);
if (!IsCommonSystemID(&reader))
continue; // Not Common System, so try the next pssh box.
// Check all children for an appropriate 'pssh' box, returning |data| from // Since version > 0, next field is the KID_count.
// the first one found. RCHECK(static_cast<uint32_t>(reader.bits_available()) >=
for (const auto& child : children) { sizeof(uint32_t) * 8);
if (child.system_id == system_id) { uint32_t count = ReadBits(&reader, 32);
pssh_data->assign(child.data.begin(), child.data.end());
return true; if (count == 0)
continue;
// Make sure there is enough data for all the KIDs specified, and then
// extract them.
RCHECK(static_cast<uint32_t>(reader.bits_available()) > count * 16 * 8);
while (count > 0) {
std::vector<uint8_t> key;
key.reserve(16);
for (int i = 0; i < 16; ++i) {
key.push_back(ReadBits(&reader, 8));
} }
result.push_back(key);
--count;
} }
// No matching 'pssh' box found. // Don't bother checking DataSize and Data.
return false; }
key_ids->swap(result);
// TODO(jrummell): This should return true only if there was at least one
// key ID present. However, numerous test files don't contain the 'pssh' box
// for Common Format, so no keys are found. http://crbug.com/460308
return true;
} }
} // namespace media } // namespace media
...@@ -28,17 +28,6 @@ MEDIA_EXPORT bool ValidatePsshInput(const std::vector<uint8_t>& input); ...@@ -28,17 +28,6 @@ MEDIA_EXPORT bool ValidatePsshInput(const std::vector<uint8_t>& input);
MEDIA_EXPORT bool GetKeyIdsForCommonSystemId(const std::vector<uint8_t>& input, MEDIA_EXPORT bool GetKeyIdsForCommonSystemId(const std::vector<uint8_t>& input,
KeyIdList* key_ids); KeyIdList* key_ids);
// Gets the data field from the first 'pssh' box containing |system_id| UUID.
// Returns true if such a box is found and successfully parsed. Returns false
// otherwise.
// Notes:
// 1. If multiple PSSH boxes are found, the "Data" of the first matching 'pssh'
// box will be set in |pssh_data|.
// 2. Only PSSH boxes are allowed in |input|.
MEDIA_EXPORT bool GetPsshData(const std::vector<uint8_t>& input,
const std::vector<uint8_t>& system_id,
std::vector<uint8_t>* pssh_data);
} // namespace media } // namespace media
#endif // MEDIA_CDM_CENC_UTILS_H_ #endif // MEDIA_CDM_CENC_UTILS_H_
...@@ -25,10 +25,6 @@ const uint8_t kKey4Data[] = { ...@@ -25,10 +25,6 @@ const uint8_t kKey4Data[] = {
0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x06, 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x06,
0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x06, 0x7E, 0x57, 0x1D, 0x04, 0x7E, 0x57, 0x1D, 0x06,
}; };
const uint8_t kCommonSystemSystemId[] = {
0x10, 0x77, 0xEF, 0xEC, 0xC0, 0xB2, 0x4D, 0x02,
0xAC, 0xE3, 0x3C, 0x1E, 0x52, 0xE2, 0xFB, 0x4B
};
class CencUtilsTest : public testing::Test { class CencUtilsTest : public testing::Test {
public: public:
...@@ -36,10 +32,7 @@ class CencUtilsTest : public testing::Test { ...@@ -36,10 +32,7 @@ class CencUtilsTest : public testing::Test {
: key1_(kKey1Data, kKey1Data + arraysize(kKey1Data)), : key1_(kKey1Data, kKey1Data + arraysize(kKey1Data)),
key2_(kKey2Data, kKey2Data + arraysize(kKey2Data)), key2_(kKey2Data, kKey2Data + arraysize(kKey2Data)),
key3_(kKey3Data, kKey3Data + arraysize(kKey3Data)), key3_(kKey3Data, kKey3Data + arraysize(kKey3Data)),
key4_(kKey4Data, kKey4Data + arraysize(kKey4Data)), key4_(kKey4Data, kKey4Data + arraysize(kKey4Data)) {}
common_system_system_id_(
kCommonSystemSystemId,
kCommonSystemSystemId + arraysize(kCommonSystemSystemId)) {}
protected: protected:
// Initialize the start of the 'pssh' box (up to key_count) // Initialize the start of the 'pssh' box (up to key_count)
...@@ -65,9 +58,23 @@ class CencUtilsTest : public testing::Test { ...@@ -65,9 +58,23 @@ class CencUtilsTest : public testing::Test {
box->push_back(0); box->push_back(0);
box->push_back(0); box->push_back(0);
box->push_back(0); box->push_back(0);
// Add Common Encryption SystemID. // Add Clear Key SystemID.
box->insert(box->end(), common_system_system_id_.begin(), box->push_back(0x10);
common_system_system_id_.end()); box->push_back(0x77);
box->push_back(0xEF);
box->push_back(0xEC);
box->push_back(0xC0);
box->push_back(0xB2);
box->push_back(0x4D);
box->push_back(0x02);
box->push_back(0xAC);
box->push_back(0xE3);
box->push_back(0x3C);
box->push_back(0x1E);
box->push_back(0x52);
box->push_back(0xE2);
box->push_back(0xFB);
box->push_back(0x4B);
} }
std::vector<uint8_t> MakePSSHBox(uint8_t version) { std::vector<uint8_t> MakePSSHBox(uint8_t version) {
...@@ -105,7 +112,7 @@ class CencUtilsTest : public testing::Test { ...@@ -105,7 +112,7 @@ class CencUtilsTest : public testing::Test {
box.push_back(1); box.push_back(1);
// Add key1. // Add key1.
for (uint i = 0; i < key1.size(); ++i) for (int i = 0; i < 16; ++i)
box.push_back(key1[i]); box.push_back(key1[i]);
// Add data_size (= 0). // Add data_size (= 0).
...@@ -134,11 +141,11 @@ class CencUtilsTest : public testing::Test { ...@@ -134,11 +141,11 @@ class CencUtilsTest : public testing::Test {
box.push_back(2); box.push_back(2);
// Add key1. // Add key1.
for (uint i = 0; i < key1.size(); ++i) for (int i = 0; i < 16; ++i)
box.push_back(key1[i]); box.push_back(key1[i]);
// Add key2. // Add key2.
for (uint i = 0; i < key2.size(); ++i) for (int i = 0; i < 16; ++i)
box.push_back(key2[i]); box.push_back(key2[i]);
// Add data_size (= 0). // Add data_size (= 0).
...@@ -149,32 +156,16 @@ class CencUtilsTest : public testing::Test { ...@@ -149,32 +156,16 @@ class CencUtilsTest : public testing::Test {
return box; return box;
} }
void AppendData(std::vector<uint8_t>& pssh_box,
const std::vector<uint8_t>& data) {
// This assumes that |pssh_box| has been created using the routines above,
// and simply appends the data to the end of it. It updates the box size
// and sets the data size.
DCHECK(data.size() < 100);
pssh_box[3] += data.size();
pssh_box.pop_back();
pssh_box.push_back(data.size());
pssh_box.insert(pssh_box.end(), data.begin(), data.end());
}
const std::vector<uint8_t>& Key1() { return key1_; } const std::vector<uint8_t>& Key1() { return key1_; }
const std::vector<uint8_t>& Key2() { return key2_; } const std::vector<uint8_t>& Key2() { return key2_; }
const std::vector<uint8_t>& Key3() { return key3_; } const std::vector<uint8_t>& Key3() { return key3_; }
const std::vector<uint8_t>& Key4() { return key4_; } const std::vector<uint8_t>& Key4() { return key4_; }
const std::vector<uint8_t>& CommonSystemSystemId() {
return common_system_system_id_;
}
private: private:
std::vector<uint8_t> key1_; std::vector<uint8_t> key1_;
std::vector<uint8_t> key2_; std::vector<uint8_t> key2_;
std::vector<uint8_t> key3_; std::vector<uint8_t> key3_;
std::vector<uint8_t> key4_; std::vector<uint8_t> key4_;
std::vector<uint8_t> common_system_system_id_;
}; };
TEST_F(CencUtilsTest, EmptyPSSH) { TEST_F(CencUtilsTest, EmptyPSSH) {
...@@ -223,8 +214,9 @@ TEST_F(CencUtilsTest, PSSHVersion0Plus1) { ...@@ -223,8 +214,9 @@ TEST_F(CencUtilsTest, PSSHVersion0Plus1) {
std::vector<uint8_t> box0 = MakePSSHBox(0); std::vector<uint8_t> box0 = MakePSSHBox(0);
std::vector<uint8_t> box1 = MakePSSHBox(1, Key1()); std::vector<uint8_t> box1 = MakePSSHBox(1, Key1());
// Concatentate box1 onto end of box0. // Concatentate box1 into box0.
box0.insert(box0.end(), box1.begin(), box1.end()); for (const auto& value : box1)
box0.push_back(value);
KeyIdList key_ids; KeyIdList key_ids;
EXPECT_TRUE(ValidatePsshInput(box0)); EXPECT_TRUE(ValidatePsshInput(box0));
...@@ -237,8 +229,9 @@ TEST_F(CencUtilsTest, PSSHVersion1Plus0) { ...@@ -237,8 +229,9 @@ TEST_F(CencUtilsTest, PSSHVersion1Plus0) {
std::vector<uint8_t> box0 = MakePSSHBox(0); std::vector<uint8_t> box0 = MakePSSHBox(0);
std::vector<uint8_t> box1 = MakePSSHBox(1, Key1()); std::vector<uint8_t> box1 = MakePSSHBox(1, Key1());
// Concatentate box0 onto end of box1. // Concatentate box0 into box1.
box1.insert(box1.end(), box0.begin(), box0.end()); for (const auto& value : box0)
box1.push_back(value);
KeyIdList key_ids; KeyIdList key_ids;
EXPECT_TRUE(ValidatePsshInput(box1)); EXPECT_TRUE(ValidatePsshInput(box1));
...@@ -252,9 +245,12 @@ TEST_F(CencUtilsTest, MultiplePSSHVersion1) { ...@@ -252,9 +245,12 @@ TEST_F(CencUtilsTest, MultiplePSSHVersion1) {
std::vector<uint8_t> box1 = MakePSSHBox(1, Key3()); std::vector<uint8_t> box1 = MakePSSHBox(1, Key3());
std::vector<uint8_t> box2 = MakePSSHBox(1, Key4()); std::vector<uint8_t> box2 = MakePSSHBox(1, Key4());
// Concatentate box1 and box2 onto end of box. // Concatentate box1 into box.
box.insert(box.end(), box1.begin(), box1.end()); for (const auto& value : box1)
box.insert(box.end(), box2.begin(), box2.end()); box.push_back(value);
// Concatentate box2 into box.
for (const auto& value : box2)
box.push_back(value);
KeyIdList key_ids; KeyIdList key_ids;
EXPECT_TRUE(ValidatePsshInput(box)); EXPECT_TRUE(ValidatePsshInput(box));
...@@ -272,12 +268,11 @@ TEST_F(CencUtilsTest, InvalidPSSH) { ...@@ -272,12 +268,11 @@ TEST_F(CencUtilsTest, InvalidPSSH) {
for (uint32 i = 1; i < box.size(); ++i) { for (uint32 i = 1; i < box.size(); ++i) {
// Modify size of data passed to be less than real size. // Modify size of data passed to be less than real size.
std::vector<uint8_t> truncated(&box[0], &box[0] + i); std::vector<uint8_t> truncated(&box[0], &box[0] + i);
EXPECT_FALSE(ValidatePsshInput(truncated)) << "Failed for length " << i; EXPECT_FALSE(ValidatePsshInput(truncated));
EXPECT_FALSE(GetKeyIdsForCommonSystemId(truncated, &key_ids)); EXPECT_FALSE(GetKeyIdsForCommonSystemId(truncated, &key_ids));
// Modify starting point. // Modify starting point.
std::vector<uint8_t> changed_offset(&box[i], &box[i] + box.size() - i); std::vector<uint8_t> changed_offset(&box[i], &box[i] + box.size() - i);
EXPECT_FALSE(ValidatePsshInput(changed_offset)) << "Failed for offset " EXPECT_FALSE(ValidatePsshInput(changed_offset));
<< i;
EXPECT_FALSE(GetKeyIdsForCommonSystemId(changed_offset, &key_ids)); EXPECT_FALSE(GetKeyIdsForCommonSystemId(changed_offset, &key_ids));
} }
} }
...@@ -300,7 +295,9 @@ TEST_F(CencUtilsTest, InvalidFlags) { ...@@ -300,7 +295,9 @@ TEST_F(CencUtilsTest, InvalidFlags) {
box[10] = 3; box[10] = 3;
KeyIdList key_ids; KeyIdList key_ids;
EXPECT_FALSE(GetKeyIdsForCommonSystemId(box, &key_ids)); // TODO(jrummell): This should fail as the 'pssh' box is skipped.
EXPECT_TRUE(GetKeyIdsForCommonSystemId(box, &key_ids));
EXPECT_EQ(0u, key_ids.size());
} }
TEST_F(CencUtilsTest, LongSize) { TEST_F(CencUtilsTest, LongSize) {
...@@ -376,140 +373,4 @@ TEST_F(CencUtilsTest, HugeSize) { ...@@ -376,140 +373,4 @@ TEST_F(CencUtilsTest, HugeSize) {
std::vector<uint8_t>(data, data + arraysize(data)), &key_ids)); std::vector<uint8_t>(data, data + arraysize(data)), &key_ids));
} }
TEST_F(CencUtilsTest, GetPsshData_Version0) {
const uint8_t data_bytes[] = {0x01, 0x02, 0x03, 0x04};
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> box = MakePSSHBox(0);
EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
EXPECT_EQ(0u, pssh_data.size());
std::vector<uint8_t> data(data_bytes, data_bytes + arraysize(data_bytes));
AppendData(box, data);
EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
EXPECT_EQ(data, pssh_data);
}
TEST_F(CencUtilsTest, GetPsshData_Version1NoKeys) {
const uint8_t data_bytes[] = {0x05, 0x06, 0x07, 0x08};
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> box = MakePSSHBox(1);
EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
EXPECT_EQ(0u, pssh_data.size());
std::vector<uint8_t> data(data_bytes, data_bytes + arraysize(data_bytes));
AppendData(box, data);
EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
EXPECT_EQ(data, pssh_data);
}
TEST_F(CencUtilsTest, GetPsshData_Version1WithKeys) {
const uint8_t data_bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> box = MakePSSHBox(1, Key1());
EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
EXPECT_EQ(0u, pssh_data.size());
std::vector<uint8_t> data(data_bytes, data_bytes + arraysize(data_bytes));
AppendData(box, data);
EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
EXPECT_EQ(data, pssh_data);
}
TEST_F(CencUtilsTest, GetPsshData_Version2) {
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> box = MakePSSHBox(1, Key1());
EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
// Change the version manually, since we don't know what v2 will contain.
box[8] = 2;
EXPECT_FALSE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
}
TEST_F(CencUtilsTest, GetPsshData_Version2ThenVersion1) {
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> box_v1 = MakePSSHBox(1, Key1());
std::vector<uint8_t> box_v2 = MakePSSHBox(2, Key2(), Key3());
// Concatentate the boxes together (v2 first).
std::vector<uint8_t> boxes;
boxes.insert(boxes.end(), box_v2.begin(), box_v2.end());
boxes.insert(boxes.end(), box_v1.begin(), box_v1.end());
EXPECT_TRUE(GetPsshData(boxes, CommonSystemSystemId(), &pssh_data));
// GetKeyIdsForCommonSystemId() should return the single key from the v1
// 'pssh' box.
KeyIdList key_ids;
EXPECT_TRUE(GetKeyIdsForCommonSystemId(boxes, &key_ids));
EXPECT_EQ(1u, key_ids.size());
EXPECT_EQ(key_ids[0], Key1());
}
TEST_F(CencUtilsTest, GetPsshData_Version1ThenVersion2) {
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> box_v1 = MakePSSHBox(1, Key3());
std::vector<uint8_t> box_v2 = MakePSSHBox(2, Key4());
// Concatentate the boxes together (v1 first).
std::vector<uint8_t> boxes;
boxes.insert(boxes.end(), box_v1.begin(), box_v1.end());
boxes.insert(boxes.end(), box_v2.begin(), box_v2.end());
EXPECT_TRUE(GetPsshData(boxes, CommonSystemSystemId(), &pssh_data));
// GetKeyIdsForCommonSystemId() should return the single key from the v1
// 'pssh' box.
KeyIdList key_ids;
EXPECT_TRUE(GetKeyIdsForCommonSystemId(boxes, &key_ids));
EXPECT_EQ(1u, key_ids.size());
EXPECT_EQ(key_ids[0], Key3());
}
TEST_F(CencUtilsTest, GetPsshData_DifferentSystemID) {
std::vector<uint8_t> unknown_system_id(kKey1Data,
kKey1Data + arraysize(kKey1Data));
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> box = MakePSSHBox(1, Key1());
EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
EXPECT_FALSE(GetPsshData(box, unknown_system_id, &pssh_data));
}
TEST_F(CencUtilsTest, GetPsshData_MissingData) {
const uint8_t data_bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> box = MakePSSHBox(1, Key1());
std::vector<uint8_t> data(data_bytes, data_bytes + arraysize(data_bytes));
AppendData(box, data);
EXPECT_TRUE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
// Remove some data from the end, so now the size is incorrect.
box.pop_back();
box.pop_back();
EXPECT_FALSE(GetPsshData(box, CommonSystemSystemId(), &pssh_data));
}
TEST_F(CencUtilsTest, GetPsshData_MultiplePssh) {
const uint8_t data1_bytes[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
const uint8_t data2_bytes[] = {0xa1, 0xa2, 0xa3, 0xa4};
std::vector<uint8_t> pssh_data;
std::vector<uint8_t> box1 = MakePSSHBox(1, Key1());
std::vector<uint8_t> data1(data1_bytes, data1_bytes + arraysize(data1_bytes));
AppendData(box1, data1);
std::vector<uint8_t> box2 = MakePSSHBox(0);
std::vector<uint8_t> data2(data2_bytes, data2_bytes + arraysize(data2_bytes));
AppendData(box2, data2);
box1.insert(box1.end(), box2.begin(), box2.end());
EXPECT_TRUE(GetPsshData(box1, CommonSystemSystemId(), &pssh_data));
EXPECT_EQ(data1, pssh_data);
EXPECT_NE(data2, pssh_data);
}
} // namespace media } // namespace media
...@@ -26,65 +26,16 @@ ProtectionSystemSpecificHeader::~ProtectionSystemSpecificHeader() {} ...@@ -26,65 +26,16 @@ ProtectionSystemSpecificHeader::~ProtectionSystemSpecificHeader() {}
FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; } FourCC ProtectionSystemSpecificHeader::BoxType() const { return FOURCC_PSSH; }
bool ProtectionSystemSpecificHeader::Parse(BoxReader* reader) { bool ProtectionSystemSpecificHeader::Parse(BoxReader* reader) {
// Don't bother validating the box's contents. // Validate the box's contents and hang on to the system ID.
RCHECK(reader->ReadFullBoxHeader() &&
reader->ReadVec(&system_id, 16));
// Copy the entire box, including the header, for passing to EME as initData. // Copy the entire box, including the header, for passing to EME as initData.
DCHECK(raw_box.empty()); DCHECK(raw_box.empty());
raw_box.assign(reader->data(), reader->data() + reader->size()); raw_box.assign(reader->data(), reader->data() + reader->size());
return true; return true;
} }
FullProtectionSystemSpecificHeader::FullProtectionSystemSpecificHeader() {}
FullProtectionSystemSpecificHeader::~FullProtectionSystemSpecificHeader() {}
FourCC FullProtectionSystemSpecificHeader::BoxType() const {
return FOURCC_PSSH;
}
// The format of a 'pssh' box is as follows:
// unsigned int(32) size;
// unsigned int(32) type = "pssh";
// if (size==1) {
// unsigned int(64) largesize;
// } else if (size==0) {
// -- box extends to end of file
// }
// unsigned int(8) version;
// bit(24) flags;
// unsigned int(8)[16] SystemID;
// if (version > 0)
// {
// unsigned int(32) KID_count;
// {
// unsigned int(8)[16] KID;
// } [KID_count]
// }
// unsigned int(32) DataSize;
// unsigned int(8)[DataSize] Data;
bool FullProtectionSystemSpecificHeader::Parse(mp4::BoxReader* reader) {
RCHECK(reader->type() == BoxType() && reader->ReadFullBoxHeader());
// Only versions 0 and 1 of the 'pssh' boxes are supported. Any other
// versions are ignored.
RCHECK(reader->version() == 0 || reader->version() == 1);
RCHECK(reader->flags() == 0);
RCHECK(reader->ReadVec(&system_id, 16));
if (reader->version() > 0) {
uint32_t kid_count;
RCHECK(reader->Read4(&kid_count));
for (uint32_t i = 0; i < kid_count; ++i) {
std::vector<uint8_t> kid;
RCHECK(reader->ReadVec(&kid, 16));
key_ids.push_back(kid);
}
}
uint32_t data_size;
RCHECK(reader->Read4(&data_size));
RCHECK(reader->ReadVec(&data, data_size));
return true;
}
SampleAuxiliaryInformationOffset::SampleAuxiliaryInformationOffset() {} SampleAuxiliaryInformationOffset::SampleAuxiliaryInformationOffset() {}
SampleAuxiliaryInformationOffset::~SampleAuxiliaryInformationOffset() {} SampleAuxiliaryInformationOffset::~SampleAuxiliaryInformationOffset() {}
FourCC SampleAuxiliaryInformationOffset::BoxType() const { return FOURCC_SAIO; } FourCC SampleAuxiliaryInformationOffset::BoxType() const { return FOURCC_SAIO; }
......
...@@ -44,21 +44,11 @@ struct MEDIA_EXPORT FileType : Box { ...@@ -44,21 +44,11 @@ struct MEDIA_EXPORT FileType : Box {
uint32 minor_version; uint32 minor_version;
}; };
// If only copying the 'pssh' boxes, use ProtectionSystemSpecificHeader.
// If access to the individual fields is needed, use
// FullProtectionSystemSpecificHeader.
struct MEDIA_EXPORT ProtectionSystemSpecificHeader : Box { struct MEDIA_EXPORT ProtectionSystemSpecificHeader : Box {
DECLARE_BOX_METHODS(ProtectionSystemSpecificHeader); DECLARE_BOX_METHODS(ProtectionSystemSpecificHeader);
std::vector<uint8> raw_box;
};
struct MEDIA_EXPORT FullProtectionSystemSpecificHeader : Box {
DECLARE_BOX_METHODS(FullProtectionSystemSpecificHeader);
std::vector<uint8> system_id; std::vector<uint8> system_id;
std::vector<std::vector<uint8>> key_ids; std::vector<uint8> raw_box;
std::vector<uint8> data;
}; };
struct MEDIA_EXPORT SampleAuxiliaryInformationOffset : Box { struct MEDIA_EXPORT SampleAuxiliaryInformationOffset : Box {
......
...@@ -75,17 +75,15 @@ bool BufferReader::Read4sInto8s(int64* v) { ...@@ -75,17 +75,15 @@ bool BufferReader::Read4sInto8s(int64* v) {
return true; return true;
} }
BoxReader::BoxReader(const uint8* buf,
const int size, BoxReader::BoxReader(const uint8* buf, const int size,
const LogCB& log_cb, const LogCB& log_cb)
bool is_EOS)
: BufferReader(buf, size), : BufferReader(buf, size),
log_cb_(log_cb), log_cb_(log_cb),
type_(FOURCC_NULL), type_(FOURCC_NULL),
version_(0), version_(0),
flags_(0), flags_(0),
scanned_(false), scanned_(false) {
is_EOS_(is_EOS) {
} }
BoxReader::~BoxReader() { BoxReader::~BoxReader() {
...@@ -102,8 +100,7 @@ BoxReader* BoxReader::ReadTopLevelBox(const uint8* buf, ...@@ -102,8 +100,7 @@ BoxReader* BoxReader::ReadTopLevelBox(const uint8* buf,
const int buf_size, const int buf_size,
const LogCB& log_cb, const LogCB& log_cb,
bool* err) { bool* err) {
scoped_ptr<BoxReader> reader( scoped_ptr<BoxReader> reader(new BoxReader(buf, buf_size, log_cb));
new BoxReader(buf, buf_size, log_cb, false));
if (!reader->ReadHeader(err)) if (!reader->ReadHeader(err))
return NULL; return NULL;
...@@ -125,7 +122,7 @@ bool BoxReader::StartTopLevelBox(const uint8* buf, ...@@ -125,7 +122,7 @@ bool BoxReader::StartTopLevelBox(const uint8* buf,
FourCC* type, FourCC* type,
int* box_size, int* box_size,
bool* err) { bool* err) {
BoxReader reader(buf, buf_size, log_cb, false); BoxReader reader(buf, buf_size, log_cb);
if (!reader.ReadHeader(err)) return false; if (!reader.ReadHeader(err)) return false;
if (!IsValidTopLevelBox(reader.type(), log_cb)) { if (!IsValidTopLevelBox(reader.type(), log_cb)) {
*err = true; *err = true;
...@@ -136,12 +133,6 @@ bool BoxReader::StartTopLevelBox(const uint8* buf, ...@@ -136,12 +133,6 @@ bool BoxReader::StartTopLevelBox(const uint8* buf,
return true; return true;
} }
// static
BoxReader* BoxReader::ReadConcatentatedBoxes(const uint8* buf,
const int buf_size) {
return new BoxReader(buf, buf_size, LogCB(), true);
}
// static // static
bool BoxReader::IsValidTopLevelBox(const FourCC& type, bool BoxReader::IsValidTopLevelBox(const FourCC& type,
const LogCB& log_cb) { const LogCB& log_cb) {
...@@ -178,7 +169,7 @@ bool BoxReader::ScanChildren() { ...@@ -178,7 +169,7 @@ bool BoxReader::ScanChildren() {
bool err = false; bool err = false;
while (pos() < size()) { while (pos() < size()) {
BoxReader child(&buf_[pos_], size_ - pos_, log_cb_, is_EOS_); BoxReader child(&buf_[pos_], size_ - pos_, log_cb_);
if (!child.ReadHeader(&err)) break; if (!child.ReadHeader(&err)) break;
children_.insert(std::pair<FourCC, BoxReader>(child.type(), child)); children_.insert(std::pair<FourCC, BoxReader>(child.type(), child));
...@@ -224,30 +215,16 @@ bool BoxReader::ReadHeader(bool* err) { ...@@ -224,30 +215,16 @@ bool BoxReader::ReadHeader(bool* err) {
uint64 size = 0; uint64 size = 0;
*err = false; *err = false;
if (!HasBytes(8)) { if (!HasBytes(8)) return false;
// If EOS is known, then this is an error. If not, additional data may be
// appended later, so this is a soft error.
*err = is_EOS_;
return false;
}
CHECK(Read4Into8(&size) && ReadFourCC(&type_)); CHECK(Read4Into8(&size) && ReadFourCC(&type_));
if (size == 0) { if (size == 0) {
if (is_EOS_) { MEDIA_LOG(DEBUG, log_cb_) << "Media Source Extensions do not support ISO "
// All the data bytes are expected to be provided. "BMFF boxes that run to EOS";
size = size_;
} else {
MEDIA_LOG(DEBUG, log_cb_)
<< "ISO BMFF boxes that run to EOS are not supported";
*err = true; *err = true;
return false; return false;
}
} else if (size == 1) { } else if (size == 1) {
if (!HasBytes(8)) { if (!HasBytes(8)) return false;
// If EOS is known, then this is an error. If not, it's a soft error.
*err = is_EOS_;
return false;
}
CHECK(Read8(&size)); CHECK(Read8(&size));
} }
...@@ -259,13 +236,6 @@ bool BoxReader::ReadHeader(bool* err) { ...@@ -259,13 +236,6 @@ bool BoxReader::ReadHeader(bool* err) {
return false; return false;
} }
// Make sure the buffer contains at least the expected number of bytes.
// Since the data may be appended in pieces, this can only be checked if EOS.
if (is_EOS_ && size > static_cast<uint64>(size_)) {
*err = true;
return false;
}
// Note that the pos_ head has advanced to the byte immediately after the // Note that the pos_ head has advanced to the byte immediately after the
// header, which is where we want it. // header, which is where we want it.
size_ = size; size_ = size;
......
...@@ -101,14 +101,6 @@ class MEDIA_EXPORT BoxReader : public BufferReader { ...@@ -101,14 +101,6 @@ class MEDIA_EXPORT BoxReader : public BufferReader {
int* box_size, int* box_size,
bool* err) WARN_UNUSED_RESULT; bool* err) WARN_UNUSED_RESULT;
// Create a BoxReader from a buffer. |buf| must be the complete buffer, as
// errors are returned when sufficient data is not available. |buf| can start
// with any type of box -- it does not have to be IsValidTopLevelBox().
//
// |buf| is retained but not owned, and must outlive the BoxReader instance.
static BoxReader* ReadConcatentatedBoxes(const uint8* buf,
const int buf_size);
// Returns true if |type| is recognized to be a top-level box, false // Returns true if |type| is recognized to be a top-level box, false
// otherwise. This returns true for some boxes which we do not parse. // otherwise. This returns true for some boxes which we do not parse.
// Helpful in debugging misaligned appends. // Helpful in debugging misaligned appends.
...@@ -156,9 +148,7 @@ class MEDIA_EXPORT BoxReader : public BufferReader { ...@@ -156,9 +148,7 @@ class MEDIA_EXPORT BoxReader : public BufferReader {
const LogCB& log_cb() const { return log_cb_; } const LogCB& log_cb() const { return log_cb_; }
private: private:
// Create a BoxReader from |buf|. |is_EOS| should be true if |buf| is BoxReader(const uint8* buf, const int size, const LogCB& log_cb);
// complete stream (i.e. no additional data is expected to be appended).
BoxReader(const uint8* buf, const int size, const LogCB& log_cb, bool is_EOS);
// Must be called immediately after init. If the return is false, this // Must be called immediately after init. If the return is false, this
// indicates that the box header and its contents were not available in the // indicates that the box header and its contents were not available in the
...@@ -180,9 +170,6 @@ class MEDIA_EXPORT BoxReader : public BufferReader { ...@@ -180,9 +170,6 @@ class MEDIA_EXPORT BoxReader : public BufferReader {
// valid if scanned_ is true. // valid if scanned_ is true.
ChildMap children_; ChildMap children_;
bool scanned_; bool scanned_;
// True if the buffer provided to the reader is the complete stream.
const bool is_EOS_;
}; };
// Template definitions // Template definitions
...@@ -220,8 +207,8 @@ bool BoxReader::ReadAllChildren(std::vector<T>* children) { ...@@ -220,8 +207,8 @@ bool BoxReader::ReadAllChildren(std::vector<T>* children) {
scanned_ = true; scanned_ = true;
bool err = false; bool err = false;
while (pos_ < size_) { while (pos() < size()) {
BoxReader child_reader(&buf_[pos_], size_ - pos_, log_cb_, is_EOS_); BoxReader child_reader(&buf_[pos_], size_ - pos_, log_cb_);
if (!child_reader.ReadHeader(&err)) break; if (!child_reader.ReadHeader(&err)) break;
T child; T child;
RCHECK(child.Parse(&child_reader)); RCHECK(child.Parse(&child_reader));
......
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