1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/cdm/cenc_utils.h"
7 #include "base/stl_util.h"
8 #include "media/formats/mp4/box_definitions.h"
9 #include "media/formats/mp4/box_reader.h"
13 // The initialization data for encrypted media files using the ISO Common
14 // Encryption ('cenc') protection scheme may contain one or more protection
15 // system specific header ('pssh') boxes.
16 // ref: https://w3c.github.io/encrypted-media/cenc-format.html
18 // CENC SystemID for the Common System.
19 // https://w3c.github.io/encrypted-media/cenc-format.html#common-system
20 const uint8_t kCencCommonSystemId
[] = {0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2,
21 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e,
22 0x52, 0xe2, 0xfb, 0x4b};
24 // Returns true if |input| contains only 1 or more valid 'pssh' boxes, false
25 // otherwise. |pssh_boxes| is updated as the set of parsed 'pssh' boxes.
26 // Note: All boxes in |input| must be 'pssh' boxes. However, if they can't be
27 // properly parsed (e.g. unsupported version), then they will be skipped.
28 static bool ReadAllPsshBoxes(
29 const std::vector
<uint8_t>& input
,
30 std::vector
<mp4::FullProtectionSystemSpecificHeader
>* pssh_boxes
) {
31 DCHECK(!input
.empty());
33 // Verify that |input| contains only 'pssh' boxes.
34 // ReadAllChildrenAndCheckFourCC() is templated, so it checks that each
35 // box in |input| matches the box type of the parameter (in this case
36 // mp4::ProtectionSystemSpecificHeader is a 'pssh' box).
37 // mp4::ProtectionSystemSpecificHeader doesn't validate the 'pssh' contents,
38 // so this simply verifies that |input| only contains 'pssh' boxes and
40 scoped_ptr
<mp4::BoxReader
> input_reader(
41 mp4::BoxReader::ReadConcatentatedBoxes(
42 vector_as_array(&input
), input
.size()));
43 std::vector
<mp4::ProtectionSystemSpecificHeader
> raw_pssh_boxes
;
44 if (!input_reader
->ReadAllChildrenAndCheckFourCC(&raw_pssh_boxes
))
47 // Now that we have |input| parsed into |raw_pssh_boxes|, reparse each one
48 // into a mp4::FullProtectionSystemSpecificHeader, which extracts all the
49 // relevant fields from the box. Since there may be unparseable 'pssh' boxes
50 // (due to unsupported version, for example), this is done one by one,
51 // ignoring any boxes that can't be parsed.
52 for (const auto& raw_pssh_box
: raw_pssh_boxes
) {
53 scoped_ptr
<mp4::BoxReader
> raw_pssh_reader(
54 mp4::BoxReader::ReadConcatentatedBoxes(
55 vector_as_array(&raw_pssh_box
.raw_box
),
56 raw_pssh_box
.raw_box
.size()));
57 // ReadAllChildren() appends any successfully parsed box onto it's
58 // parameter, so |pssh_boxes| will contain the collection of successfully
59 // parsed 'pssh' boxes. If an error occurs, try the next box.
60 if (!raw_pssh_reader
->ReadAllChildrenAndCheckFourCC(pssh_boxes
))
64 // Must have successfully parsed at least one 'pssh' box.
65 return pssh_boxes
->size() > 0;
68 bool ValidatePsshInput(const std::vector
<uint8_t>& input
) {
69 // No 'pssh' boxes is considered valid.
73 std::vector
<mp4::FullProtectionSystemSpecificHeader
> children
;
74 return ReadAllPsshBoxes(input
, &children
);
77 bool GetKeyIdsForCommonSystemId(const std::vector
<uint8_t>& pssh_boxes
,
79 // If there are no 'pssh' boxes then no key IDs found.
80 if (pssh_boxes
.empty())
83 std::vector
<mp4::FullProtectionSystemSpecificHeader
> children
;
84 if (!ReadAllPsshBoxes(pssh_boxes
, &children
))
87 // Check all children for an appropriate 'pssh' box, returning the
90 std::vector
<uint8_t> common_system_id(
92 kCencCommonSystemId
+ arraysize(kCencCommonSystemId
));
93 for (const auto& child
: children
) {
94 if (child
.system_id
== common_system_id
) {
95 key_ids
->assign(child
.key_ids
.begin(), child
.key_ids
.end());
96 return key_ids
->size() > 0;
100 // No matching 'pssh' box found.
104 bool GetPsshData(const std::vector
<uint8_t>& input
,
105 const std::vector
<uint8_t>& system_id
,
106 std::vector
<uint8_t>* pssh_data
) {
110 std::vector
<mp4::FullProtectionSystemSpecificHeader
> children
;
111 if (!ReadAllPsshBoxes(input
, &children
))
114 // Check all children for an appropriate 'pssh' box, returning |data| from
115 // the first one found.
116 for (const auto& child
: children
) {
117 if (child
.system_id
== system_id
) {
118 pssh_data
->assign(child
.data
.begin(), child
.data
.end());
123 // No matching 'pssh' box found.