Index: media/cdm/cenc_utils.cc |
diff --git a/media/cdm/cenc_utils.cc b/media/cdm/cenc_utils.cc |
index 86779b2174092ce5d0e3b5f5dca6a9c2f965c912..495a6f2fb2f6eba1d741a2dbf1c83bbd22ad5c00 100644 |
--- a/media/cdm/cenc_utils.cc |
+++ b/media/cdm/cenc_utils.cc |
@@ -4,7 +4,9 @@ |
#include "media/cdm/cenc_utils.h" |
-#include "media/base/bit_reader.h" |
+#include "base/stl_util.h" |
+#include "media/formats/mp4/box_definitions.h" |
+#include "media/formats/mp4/box_reader.h" |
namespace media { |
@@ -12,31 +14,6 @@ namespace media { |
// Encryption ('cenc') protection scheme may contain one or more protection |
// system specific header ('pssh') boxes. |
// 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. |
// https://w3c.github.io/encrypted-media/cenc-format.html#common-system |
@@ -45,136 +22,102 @@ const uint8_t kCommonSystemId[] = { 0x10, 0x77, 0xef, 0xec, |
0xac, 0xe3, 0x3c, 0x1e, |
0x52, 0xe2, 0xfb, 0x4b }; |
-#define RCHECK(x) \ |
- do { \ |
- if (!(x)) \ |
- return false; \ |
- } while (0) |
- |
-// Helper function to read up to 32 bits from a bit stream. |
-static uint32_t ReadBits(BitReader* reader, int num_bits) { |
- DCHECK_GE(reader->bits_available(), num_bits); |
- DCHECK((num_bits > 0) && (num_bits <= 32)); |
- uint32_t value; |
- reader->ReadBits(num_bits, &value); |
- return value; |
-} |
- |
-// 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 true; |
-} |
- |
-// Checks that |reader| contains a valid 'ppsh' box header. |reader| is updated |
-// to point to the content immediately following the box header. Returns true |
-// if the header looks valid and |reader| contains enough data for the size of |
-// header. |size| is updated as the computed size of the box header. Otherwise |
-// false is returned. |
-static bool ValidBoxHeader(BitReader* reader, uint32* size) { |
- // Enough data for a miniumum size 'pssh' box? |
- uint32 available_bytes = reader->bits_available() / 8; |
- RCHECK(available_bytes >= kMinimumBoxSizeInBytes); |
- |
- *size = ReadBits(reader, 32); |
- |
- // Must be a 'pssh' box or else fail. |
- RCHECK(ReadBits(reader, 8) == 'p'); |
- 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; |
+static bool ReadAllPsshBoxes( |
+ const std::vector<uint8_t>& input, |
+ std::vector<mp4::FullProtectionSystemSpecificHeader>* pssh_boxes) { |
+ DCHECK(!input.empty()); |
+ |
+ // Verify that |input| contains only 'pssh' boxes. ReadAllChildren() is |
+ // templated, so it checks that each box in |input| matches the box type of |
+ // the parameter (in this case mp4::ProtectionSystemSpecificHeader is a |
+ // 'pssh' box). mp4::ProtectionSystemSpecificHeader doesn't validate the |
+ // 'pssh' contents, so this simply verifies that |input| only contains |
+ // 'pssh' boxes and nothing else. |
+ scoped_ptr<mp4::BoxReader> input_reader( |
+ mp4::BoxReader::ReadConcatentatedBoxes( |
+ vector_as_array(&input), input.size())); |
+ std::vector<mp4::ProtectionSystemSpecificHeader> raw_pssh_boxes; |
+ if (!input_reader->ReadAllChildren(&raw_pssh_boxes)) |
+ return false; |
+ |
+ // Now that we have |input| parsed into |raw_pssh_boxes|, reparse each one |
+ // into a mp4::FullProtectionSystemSpecificHeader, which extracts all the |
+ // relevant fields from the box. Since there may be unparseable 'pssh' boxes |
+ // (due to unsupported version, for example), this is done one by one, |
+ // ignoring any boxes that can't be parsed. |
+ for (const auto& raw_pssh_box : raw_pssh_boxes) { |
+ scoped_ptr<mp4::BoxReader> raw_pssh_reader( |
+ mp4::BoxReader::ReadConcatentatedBoxes( |
+ vector_as_array(&raw_pssh_box.raw_box), |
+ raw_pssh_box.raw_box.size())); |
+ // ReadAllChildren() appends any successfully parsed box onto it's |
+ // parameter, so |pssh_boxes| will contain the collection of successfully |
+ // parsed 'pssh' boxes. If an error occurs, try the next box. |
+ if (!raw_pssh_reader->ReadAllChildren(pssh_boxes)) |
+ continue; |
} |
- // Check that the buffer contains at least size bytes. |
- return available_bytes >= *size; |
+ // Must have successfully parsed at least one 'pssh' box. |
+ return pssh_boxes->size() > 0; |
} |
bool ValidatePsshInput(const std::vector<uint8_t>& input) { |
- size_t offset = 0; |
- while (offset < input.size()) { |
- // 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; |
- } |
+ // No 'pssh' boxes is considered valid. |
+ if (input.empty()) |
+ return true; |
- // Only valid if this contains 0 or more 'pssh' boxes. |
- return offset == input.size(); |
+ std::vector<mp4::FullProtectionSystemSpecificHeader> children; |
+ return ReadAllPsshBoxes(input, &children); |
} |
bool GetKeyIdsForCommonSystemId(const std::vector<uint8_t>& input, |
KeyIdList* key_ids) { |
- size_t offset = 0; |
KeyIdList result; |
+ std::vector<uint8_t> common_system_id( |
+ kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId)); |
- while (offset < input.size()) { |
- 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; |
- |
- // Check the version, as KIDs only available if version > 0. |
- uint8_t version = ReadBits(&reader, 8); |
- if (version == 0) |
- continue; |
- |
- // flags must be 0. If not, assume incorrect 'pssh' box and move to the |
- // next one. |
- if (ReadBits(&reader, 24) != 0) |
- continue; |
- |
- // Validate SystemID |
- RCHECK(static_cast<uint32_t>(reader.bits_available()) >= |
- arraysize(kCommonSystemId) * 8); |
- if (!IsCommonSystemID(&reader)) |
- continue; // Not Common System, so try the next pssh box. |
- |
- // Since version > 0, next field is the KID_count. |
- RCHECK(static_cast<uint32_t>(reader.bits_available()) >= |
- sizeof(uint32_t) * 8); |
- uint32_t count = ReadBits(&reader, 32); |
- |
- if (count == 0) |
- continue; |
+ if (!input.empty()) { |
+ std::vector<mp4::FullProtectionSystemSpecificHeader> children; |
+ if (!ReadAllPsshBoxes(input, &children)) |
+ return false; |
- // 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; |
+ // Check all children for an appropriate 'pssh' box, concatenating any |
+ // key IDs found. |
+ 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()); |
} |
- |
- // Don't bother checking DataSize and Data. |
} |
- key_ids->swap(result); |
- |
+ // No matching 'pssh' box found. |
// 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 |
+ key_ids->swap(result); |
return true; |
} |
+bool GetPsshData(const std::vector<uint8_t>& input, |
+ const std::vector<uint8_t>& system_id, |
+ std::vector<uint8_t>* pssh_data) { |
+ if (input.empty()) |
+ return false; |
+ |
+ std::vector<mp4::FullProtectionSystemSpecificHeader> children; |
+ if (!ReadAllPsshBoxes(input, &children)) |
+ return false; |
+ |
+ // Check all children for an appropriate 'pssh' box, returning |data| from |
+ // the first one found. |
+ for (const auto& child : children) { |
+ if (child.system_id == system_id) { |
+ pssh_data->assign(child.data.begin(), child.data.end()); |
+ return true; |
+ } |
+ } |
+ |
+ // No matching 'pssh' box found. |
+ return false; |
+} |
+ |
} // namespace media |