OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/cdm/cenc_utils.h" | 5 #include "media/cdm/cenc_utils.h" |
6 | 6 |
7 #include "media/base/bit_reader.h" | 7 #include "base/stl_util.h" |
8 #include "media/formats/mp4/box_definitions.h" | |
9 #include "media/formats/mp4/box_reader.h" | |
8 | 10 |
9 namespace media { | 11 namespace media { |
10 | 12 |
11 // The initialization data for encrypted media files using the ISO Common | 13 // The initialization data for encrypted media files using the ISO Common |
12 // Encryption ('cenc') protection scheme may contain one or more protection | 14 // Encryption ('cenc') protection scheme may contain one or more protection |
13 // system specific header ('pssh') boxes. | 15 // system specific header ('pssh') boxes. |
14 // ref: https://w3c.github.io/encrypted-media/cenc-format.html | 16 // ref: https://w3c.github.io/encrypted-media/cenc-format.html |
15 // | |
16 // The format of a 'pssh' box is as follows: | |
17 // unsigned int(32) size; | |
18 // unsigned int(32) type = "pssh"; | |
19 // if (size==1) { | |
20 // unsigned int(64) largesize; | |
21 // } else if (size==0) { | |
22 // -- box extends to end of file | |
23 // } | |
24 // unsigned int(8) version; | |
25 // bit(24) flags; | |
26 // unsigned int(8)[16] SystemID; | |
27 // if (version > 0) | |
28 // { | |
29 // unsigned int(32) KID_count; | |
30 // { | |
31 // unsigned int(8)[16] KID; | |
32 // } [KID_count] | |
33 // } | |
34 // unsigned int(32) DataSize; | |
35 // unsigned int(8)[DataSize] Data; | |
36 | |
37 // Minimum size of a 'pssh' box includes all the required fields (size, type, | |
38 // version, flags, SystemID, DataSize). | |
39 const int kMinimumBoxSizeInBytes = 32; | |
40 | 17 |
41 // SystemID for the Common System. | 18 // SystemID for the Common System. |
42 // https://w3c.github.io/encrypted-media/cenc-format.html#common-system | 19 // https://w3c.github.io/encrypted-media/cenc-format.html#common-system |
43 const uint8_t kCommonSystemId[] = { 0x10, 0x77, 0xef, 0xec, | 20 const uint8_t kCommonSystemId[] = { 0x10, 0x77, 0xef, 0xec, |
44 0xc0, 0xb2, 0x4d, 0x02, | 21 0xc0, 0xb2, 0x4d, 0x02, |
45 0xac, 0xe3, 0x3c, 0x1e, | 22 0xac, 0xe3, 0x3c, 0x1e, |
46 0x52, 0xe2, 0xfb, 0x4b }; | 23 0x52, 0xe2, 0xfb, 0x4b }; |
47 | 24 |
48 #define RCHECK(x) \ | 25 bool ValidatePsshInput(const std::vector<uint8_t>& input) { |
49 do { \ | 26 // No 'pssh' boxes is considered valid. |
50 if (!(x)) \ | 27 if (input.empty()) |
51 return false; \ | 28 return true; |
52 } while (0) | |
53 | 29 |
54 // Helper function to read up to 32 bits from a bit stream. | 30 scoped_ptr<mp4::BoxReader> reader(mp4::BoxReader::ReadConcatentatedBoxes( |
55 static uint32_t ReadBits(BitReader* reader, int num_bits) { | 31 vector_as_array(&input), input.size())); |
56 DCHECK_GE(reader->bits_available(), num_bits); | 32 std::vector<mp4::FullProtectionSystemSpecificHeader> children; |
57 DCHECK((num_bits > 0) && (num_bits <= 32)); | 33 return reader->ReadAllChildren(&children); |
58 uint32_t value; | |
59 reader->ReadBits(num_bits, &value); | |
60 return value; | |
61 } | |
62 | |
63 // Checks whether the next 16 bytes matches the Common SystemID. | |
64 // Assumes |reader| has enough data. | |
65 static bool IsCommonSystemID(BitReader* reader) { | |
66 for (uint32_t i = 0; i < arraysize(kCommonSystemId); ++i) { | |
67 if (ReadBits(reader, 8) != kCommonSystemId[i]) | |
68 return false; | |
69 } | |
70 return true; | |
71 } | |
72 | |
73 // Checks that |reader| contains a valid 'ppsh' box header. |reader| is updated | |
74 // to point to the content immediately following the box header. Returns true | |
75 // if the header looks valid and |reader| contains enough data for the size of | |
76 // header. |size| is updated as the computed size of the box header. Otherwise | |
77 // false is returned. | |
78 static bool ValidBoxHeader(BitReader* reader, uint32* size) { | |
79 // Enough data for a miniumum size 'pssh' box? | |
80 uint32 available_bytes = reader->bits_available() / 8; | |
81 RCHECK(available_bytes >= kMinimumBoxSizeInBytes); | |
82 | |
83 *size = ReadBits(reader, 32); | |
84 | |
85 // Must be a 'pssh' box or else fail. | |
86 RCHECK(ReadBits(reader, 8) == 'p'); | |
87 RCHECK(ReadBits(reader, 8) == 's'); | |
88 RCHECK(ReadBits(reader, 8) == 's'); | |
89 RCHECK(ReadBits(reader, 8) == 'h'); | |
90 | |
91 if (*size == 1) { | |
92 // If largesize > 2**32 it is too big. | |
93 RCHECK(ReadBits(reader, 32) == 0); | |
94 *size = ReadBits(reader, 32); | |
95 } else if (*size == 0) { | |
96 *size = available_bytes; | |
97 } | |
98 | |
99 // Check that the buffer contains at least size bytes. | |
100 return available_bytes >= *size; | |
101 } | |
102 | |
103 bool ValidatePsshInput(const std::vector<uint8_t>& input) { | |
104 size_t offset = 0; | |
105 while (offset < input.size()) { | |
106 // Create a BitReader over the remaining part of the buffer. | |
107 BitReader reader(&input[offset], input.size() - offset); | |
108 uint32 size; | |
109 RCHECK(ValidBoxHeader(&reader, &size)); | |
110 | |
111 // Update offset to point at the next 'pssh' box (may not be one). | |
112 offset += size; | |
113 } | |
114 | |
115 // Only valid if this contains 0 or more 'pssh' boxes. | |
116 return offset == input.size(); | |
117 } | 34 } |
118 | 35 |
119 bool GetKeyIdsForCommonSystemId(const std::vector<uint8_t>& input, | 36 bool GetKeyIdsForCommonSystemId(const std::vector<uint8_t>& input, |
120 KeyIdList* key_ids) { | 37 KeyIdList* key_ids) { |
121 size_t offset = 0; | |
122 KeyIdList result; | 38 KeyIdList result; |
39 std::vector<uint8_t> common_system_id( | |
40 kCommonSystemId, kCommonSystemId + arraysize(kCommonSystemId)); | |
123 | 41 |
124 while (offset < input.size()) { | 42 if (!input.empty()) { |
125 BitReader reader(&input[offset], input.size() - offset); | 43 scoped_ptr<mp4::BoxReader> reader(mp4::BoxReader::ReadConcatentatedBoxes( |
126 uint32 size; | 44 vector_as_array(&input), input.size())); |
127 RCHECK(ValidBoxHeader(&reader, &size)); | 45 std::vector<mp4::FullProtectionSystemSpecificHeader> children; |
46 if (!reader->ReadAllChildren(&children)) | |
47 return false; | |
128 | 48 |
129 // Update offset to point at the next 'pssh' box (may not be one). | 49 // Check all children for an appropriate 'pssh' box, concatenating any |
130 offset += size; | 50 // key IDs found. |
131 | 51 for (const auto& child : children) { |
132 // Check the version, as KIDs only available if version > 0. | 52 if (child.system_id == common_system_id && child.key_ids.size() > 0) |
133 uint8_t version = ReadBits(&reader, 8); | 53 result.insert(result.end(), child.key_ids.begin(), child.key_ids.end()); |
134 if (version == 0) | |
135 continue; | |
136 | |
137 // flags must be 0. If not, assume incorrect 'pssh' box and move to the | |
138 // next one. | |
139 if (ReadBits(&reader, 24) != 0) | |
140 continue; | |
141 | |
142 // Validate SystemID | |
143 RCHECK(static_cast<uint32_t>(reader.bits_available()) >= | |
144 arraysize(kCommonSystemId) * 8); | |
145 if (!IsCommonSystemID(&reader)) | |
146 continue; // Not Common System, so try the next pssh box. | |
147 | |
148 // Since version > 0, next field is the KID_count. | |
149 RCHECK(static_cast<uint32_t>(reader.bits_available()) >= | |
150 sizeof(uint32_t) * 8); | |
151 uint32_t count = ReadBits(&reader, 32); | |
152 | |
153 if (count == 0) | |
154 continue; | |
155 | |
156 // Make sure there is enough data for all the KIDs specified, and then | |
157 // extract them. | |
158 RCHECK(static_cast<uint32_t>(reader.bits_available()) > count * 16 * 8); | |
159 while (count > 0) { | |
160 std::vector<uint8_t> key; | |
161 key.reserve(16); | |
162 for (int i = 0; i < 16; ++i) { | |
163 key.push_back(ReadBits(&reader, 8)); | |
164 } | |
165 result.push_back(key); | |
166 --count; | |
167 } | 54 } |
168 | |
169 // Don't bother checking DataSize and Data. | |
170 } | 55 } |
171 | 56 |
172 key_ids->swap(result); | 57 // No matching 'pssh' box found. |
173 | |
174 // TODO(jrummell): This should return true only if there was at least one | 58 // TODO(jrummell): This should return true only if there was at least one |
175 // key ID present. However, numerous test files don't contain the 'pssh' box | 59 // key ID present. However, numerous test files don't contain the 'pssh' box |
176 // for Common Format, so no keys are found. http://crbug.com/460308 | 60 // for Common Format, so no keys are found. http://crbug.com/460308 |
61 key_ids->swap(result); | |
177 return true; | 62 return true; |
178 } | 63 } |
179 | 64 |
65 bool GetPsshData(const std::vector<uint8_t>& input, | |
66 const std::vector<uint8_t>& system_id, | |
67 std::vector<uint8_t>* pssh_data) { | |
68 if (input.empty()) | |
69 return false; | |
70 | |
71 scoped_ptr<mp4::BoxReader> reader(mp4::BoxReader::ReadConcatentatedBoxes( | |
72 vector_as_array(&input), input.size())); | |
73 std::vector<mp4::FullProtectionSystemSpecificHeader> children; | |
74 if (!reader->ReadAllChildren(&children)) | |
75 return false; | |
sandersd (OOO until July 31)
2015/05/22 19:51:24
This pattern (lines 68 to 75) is repeated 3 times.
jrummell
2015/05/22 23:22:29
Handling of empty() is different. But extracted th
| |
76 | |
77 // Check all children for an appropriate 'pssh' box, returning |data| from | |
78 // the first one found. | |
79 for (const auto& child : children) { | |
80 if (child.system_id == system_id) { | |
81 pssh_data->assign(child.data.begin(), child.data.end()); | |
82 return true; | |
83 } | |
84 } | |
85 | |
86 // No matching 'pssh' box found. | |
87 return false; | |
88 } | |
89 | |
180 } // namespace media | 90 } // namespace media |
OLD | NEW |