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 // SystemID for the Common System.
19 // https://w3c.github.io/encrypted-media/cenc-format.html#common-system
20 const uint8_t kCommonSystemId
[] = { 0x10, 0x77, 0xef, 0xec,
21 0xc0, 0xb2, 0x4d, 0x02,
22 0xac, 0xe3, 0x3c, 0x1e,
23 0x52, 0xe2, 0xfb, 0x4b };
25 static bool ReadAllPsshBoxes(
26 const std::vector
<uint8_t>& input
,
27 std::vector
<mp4::FullProtectionSystemSpecificHeader
>* pssh_boxes
) {
28 DCHECK(!input
.empty());
30 // Verify that |input| contains only 'pssh' boxes. ReadAllChildren() is
31 // templated, so it checks that each box in |input| matches the box type of
32 // the parameter (in this case mp4::ProtectionSystemSpecificHeader is a
33 // 'pssh' box). mp4::ProtectionSystemSpecificHeader doesn't validate the
34 // 'pssh' contents, so this simply verifies that |input| only contains
35 // 'pssh' boxes and nothing else.
36 scoped_ptr
<mp4::BoxReader
> input_reader(
37 mp4::BoxReader::ReadConcatentatedBoxes(
38 vector_as_array(&input
), input
.size()));
39 std::vector
<mp4::ProtectionSystemSpecificHeader
> raw_pssh_boxes
;
40 if (!input_reader
->ReadAllChildren(&raw_pssh_boxes
))
43 // Now that we have |input| parsed into |raw_pssh_boxes|, reparse each one
44 // into a mp4::FullProtectionSystemSpecificHeader, which extracts all the
45 // relevant fields from the box. Since there may be unparseable 'pssh' boxes
46 // (due to unsupported version, for example), this is done one by one,
47 // ignoring any boxes that can't be parsed.
48 for (const auto& raw_pssh_box
: raw_pssh_boxes
) {
49 scoped_ptr
<mp4::BoxReader
> raw_pssh_reader(
50 mp4::BoxReader::ReadConcatentatedBoxes(
51 vector_as_array(&raw_pssh_box
.raw_box
),
52 raw_pssh_box
.raw_box
.size()));
53 // ReadAllChildren() appends any successfully parsed box onto it's
54 // parameter, so |pssh_boxes| will contain the collection of successfully
55 // parsed 'pssh' boxes. If an error occurs, try the next box.
56 if (!raw_pssh_reader
->ReadAllChildren(pssh_boxes
))
60 // Must have successfully parsed at least one 'pssh' box.
61 return pssh_boxes
->size() > 0;
64 bool ValidatePsshInput(const std::vector
<uint8_t>& input
) {
65 // No 'pssh' boxes is considered valid.
69 std::vector
<mp4::FullProtectionSystemSpecificHeader
> children
;
70 return ReadAllPsshBoxes(input
, &children
);
73 bool GetKeyIdsForCommonSystemId(const std::vector
<uint8_t>& input
,
76 std::vector
<uint8_t> common_system_id(
77 kCommonSystemId
, kCommonSystemId
+ arraysize(kCommonSystemId
));
80 std::vector
<mp4::FullProtectionSystemSpecificHeader
> children
;
81 if (!ReadAllPsshBoxes(input
, &children
))
84 // Check all children for an appropriate 'pssh' box, concatenating any
86 for (const auto& child
: children
) {
87 if (child
.system_id
== common_system_id
&& child
.key_ids
.size() > 0)
88 result
.insert(result
.end(), child
.key_ids
.begin(), child
.key_ids
.end());
92 key_ids
->swap(result
);
93 return key_ids
->size() > 0;
96 bool GetPsshData(const std::vector
<uint8_t>& input
,
97 const std::vector
<uint8_t>& system_id
,
98 std::vector
<uint8_t>* pssh_data
) {
102 std::vector
<mp4::FullProtectionSystemSpecificHeader
> children
;
103 if (!ReadAllPsshBoxes(input
, &children
))
106 // Check all children for an appropriate 'pssh' box, returning |data| from
107 // the first one found.
108 for (const auto& child
: children
) {
109 if (child
.system_id
== system_id
) {
110 pssh_data
->assign(child
.data
.begin(), child
.data
.end());
115 // No matching 'pssh' box found.