OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/formats/mp4/avc.h" | 5 #include "media/formats/mp4/avc.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/logging.h" | |
11 #include "media/base/decrypt_config.h" | |
12 #include "media/filters/h264_parser.h" | |
10 #include "media/formats/mp4/box_definitions.h" | 13 #include "media/formats/mp4/box_definitions.h" |
11 #include "media/formats/mp4/box_reader.h" | 14 #include "media/formats/mp4/box_reader.h" |
12 | 15 |
13 namespace media { | 16 namespace media { |
14 namespace mp4 { | 17 namespace mp4 { |
15 | 18 |
16 static const uint8 kAnnexBStartCode[] = {0, 0, 0, 1}; | 19 static const uint8 kAnnexBStartCode[] = {0, 0, 0, 1}; |
17 static const int kAnnexBStartCodeSize = 4; | 20 static const int kAnnexBStartCodeSize = 4; |
18 | 21 |
19 static bool ConvertAVCToAnnexBInPlaceForLengthSize4(std::vector<uint8>* buf) { | 22 static bool ConvertAVCToAnnexBInPlaceForLengthSize4(std::vector<uint8>* buf) { |
20 const int kLengthSize = 4; | 23 const int kLengthSize = 4; |
21 size_t pos = 0; | 24 size_t pos = 0; |
22 while (pos + kLengthSize < buf->size()) { | 25 while (pos + kLengthSize < buf->size()) { |
23 int nal_size = (*buf)[pos]; | 26 uint32 nal_size = (*buf)[pos]; |
24 nal_size = (nal_size << 8) + (*buf)[pos+1]; | 27 nal_size = (nal_size << 8) + (*buf)[pos+1]; |
25 nal_size = (nal_size << 8) + (*buf)[pos+2]; | 28 nal_size = (nal_size << 8) + (*buf)[pos+2]; |
26 nal_size = (nal_size << 8) + (*buf)[pos+3]; | 29 nal_size = (nal_size << 8) + (*buf)[pos+3]; |
30 | |
31 if (nal_size == 0) { | |
32 DVLOG(1) << "nal_size is 0"; | |
33 return false; | |
34 } | |
35 | |
27 std::copy(kAnnexBStartCode, kAnnexBStartCode + kAnnexBStartCodeSize, | 36 std::copy(kAnnexBStartCode, kAnnexBStartCode + kAnnexBStartCodeSize, |
28 buf->begin() + pos); | 37 buf->begin() + pos); |
29 pos += kLengthSize + nal_size; | 38 pos += kLengthSize + nal_size; |
30 } | 39 } |
31 return pos == buf->size(); | 40 return pos == buf->size(); |
32 } | 41 } |
33 | 42 |
34 // static | 43 // static |
35 bool AVC::ConvertFrameToAnnexB(int length_size, std::vector<uint8>* buffer) { | 44 bool AVC::ConvertFrameToAnnexB(int length_size, std::vector<uint8>* buffer) { |
36 RCHECK(length_size == 1 || length_size == 2 || length_size == 4); | 45 RCHECK(length_size == 1 || length_size == 2 || length_size == 4); |
37 | 46 |
38 if (length_size == 4) | 47 if (length_size == 4) |
39 return ConvertAVCToAnnexBInPlaceForLengthSize4(buffer); | 48 return ConvertAVCToAnnexBInPlaceForLengthSize4(buffer); |
40 | 49 |
41 std::vector<uint8> temp; | 50 std::vector<uint8> temp; |
42 temp.swap(*buffer); | 51 temp.swap(*buffer); |
43 buffer->reserve(temp.size() + 32); | 52 buffer->reserve(temp.size() + 32); |
44 | 53 |
45 size_t pos = 0; | 54 size_t pos = 0; |
46 while (pos + length_size < temp.size()) { | 55 while (pos + length_size < temp.size()) { |
47 int nal_size = temp[pos]; | 56 int nal_size = temp[pos]; |
48 if (length_size == 2) nal_size = (nal_size << 8) + temp[pos+1]; | 57 if (length_size == 2) nal_size = (nal_size << 8) + temp[pos+1]; |
49 pos += length_size; | 58 pos += length_size; |
50 | 59 |
60 if (nal_size == 0) { | |
61 DVLOG(1) << "nal_size is 0"; | |
62 return false; | |
63 } | |
64 | |
51 RCHECK(pos + nal_size <= temp.size()); | 65 RCHECK(pos + nal_size <= temp.size()); |
52 buffer->insert(buffer->end(), kAnnexBStartCode, | 66 buffer->insert(buffer->end(), kAnnexBStartCode, |
53 kAnnexBStartCode + kAnnexBStartCodeSize); | 67 kAnnexBStartCode + kAnnexBStartCodeSize); |
54 buffer->insert(buffer->end(), temp.begin() + pos, | 68 buffer->insert(buffer->end(), temp.begin() + pos, |
55 temp.begin() + pos + nal_size); | 69 temp.begin() + pos + nal_size); |
56 pos += nal_size; | 70 pos += nal_size; |
57 } | 71 } |
58 return pos == temp.size(); | 72 return pos == temp.size(); |
59 } | 73 } |
60 | 74 |
61 // static | 75 // static |
76 bool AVC::InsertParamSetsAnnexB(const AVCDecoderConfigurationRecord& avc_config, | |
77 std::vector<uint8>* buffer, | |
78 std::vector<SubsampleEntry>* subsamples) { | |
79 DCHECK(AVC::IsValidAnnexB(*buffer)); | |
80 | |
81 scoped_ptr<H264Parser> parser(new H264Parser()); | |
82 const uint8* start = &(*buffer)[0]; | |
83 parser->SetStream(start, buffer->size()); | |
84 | |
85 H264NALU nalu; | |
86 if (parser->AdvanceToNextNALU(&nalu) != H264Parser::kOk) | |
87 return false; | |
88 | |
89 std::vector<uint8>::iterator config_insert_point = buffer->begin(); | |
90 std::vector<SubsampleEntry>::iterator subsamples_insert_point = | |
91 subsamples->begin(); | |
92 | |
93 if (nalu.nal_unit_type == H264NALU::kAUD) { | |
94 // Move insert point to just after the AUD. | |
95 config_insert_point += (nalu.data + nalu.size) - start; | |
96 | |
97 if (!subsamples->empty()) { | |
98 DCHECK_EQ((*subsamples)[0].clear_bytes + (*subsamples)[0].cypher_bytes, | |
99 config_insert_point - buffer->begin()); | |
damienv1
2014/04/25 00:11:39
I first suggested a DCHECK.
But if the stream is m
acolwell GONE FROM CHROMIUM
2014/04/25 00:29:15
Done.
| |
100 | |
101 subsamples_insert_point++; | |
102 } | |
103 | |
104 } | |
105 | |
106 // Clear |parser| and |start| since they aren't needed anymore and | |
107 // will hold stale pointers once the insert happens. | |
108 parser.reset(); | |
109 start = NULL; | |
110 | |
111 std::vector<uint8> param_sets; | |
112 std::vector<SubsampleEntry> config_subsamples; | |
113 RCHECK(AVC::ConvertConfigToAnnexB(avc_config, | |
114 ¶m_sets, | |
115 &config_subsamples)); | |
116 | |
117 if (!subsamples->empty()) { | |
118 subsamples->insert(subsamples_insert_point, | |
119 config_subsamples.begin(), | |
120 config_subsamples.end()); | |
121 } | |
122 | |
123 buffer->insert(config_insert_point, | |
124 param_sets.begin(), param_sets.end()); | |
125 | |
126 DCHECK(AVC::IsValidAnnexB(*buffer)); | |
127 return true; | |
128 } | |
129 | |
130 // static | |
62 bool AVC::ConvertConfigToAnnexB( | 131 bool AVC::ConvertConfigToAnnexB( |
63 const AVCDecoderConfigurationRecord& avc_config, | 132 const AVCDecoderConfigurationRecord& avc_config, |
64 std::vector<uint8>* buffer) { | 133 std::vector<uint8>* buffer, |
134 std::vector<SubsampleEntry>* subsamples) { | |
65 DCHECK(buffer->empty()); | 135 DCHECK(buffer->empty()); |
66 buffer->clear(); | 136 buffer->clear(); |
67 int total_size = 0; | 137 int total_size = 0; |
68 for (size_t i = 0; i < avc_config.sps_list.size(); i++) | 138 for (size_t i = 0; i < avc_config.sps_list.size(); i++) |
69 total_size += avc_config.sps_list[i].size() + kAnnexBStartCodeSize; | 139 total_size += avc_config.sps_list[i].size() + kAnnexBStartCodeSize; |
70 for (size_t i = 0; i < avc_config.pps_list.size(); i++) | 140 for (size_t i = 0; i < avc_config.pps_list.size(); i++) |
71 total_size += avc_config.pps_list[i].size() + kAnnexBStartCodeSize; | 141 total_size += avc_config.pps_list[i].size() + kAnnexBStartCodeSize; |
72 buffer->reserve(total_size); | 142 buffer->reserve(total_size); |
73 | 143 |
74 for (size_t i = 0; i < avc_config.sps_list.size(); i++) { | 144 for (size_t i = 0; i < avc_config.sps_list.size(); i++) { |
75 buffer->insert(buffer->end(), kAnnexBStartCode, | 145 buffer->insert(buffer->end(), kAnnexBStartCode, |
76 kAnnexBStartCode + kAnnexBStartCodeSize); | 146 kAnnexBStartCode + kAnnexBStartCodeSize); |
77 buffer->insert(buffer->end(), avc_config.sps_list[i].begin(), | 147 buffer->insert(buffer->end(), avc_config.sps_list[i].begin(), |
78 avc_config.sps_list[i].end()); | 148 avc_config.sps_list[i].end()); |
149 | |
150 SubsampleEntry entry; | |
151 entry.clear_bytes = kAnnexBStartCodeSize + avc_config.sps_list[i].size(); | |
152 entry.cypher_bytes = 0; | |
153 subsamples->push_back(entry); | |
79 } | 154 } |
80 | 155 |
81 for (size_t i = 0; i < avc_config.pps_list.size(); i++) { | 156 for (size_t i = 0; i < avc_config.pps_list.size(); i++) { |
82 buffer->insert(buffer->end(), kAnnexBStartCode, | 157 buffer->insert(buffer->end(), kAnnexBStartCode, |
83 kAnnexBStartCode + kAnnexBStartCodeSize); | 158 kAnnexBStartCode + kAnnexBStartCodeSize); |
84 buffer->insert(buffer->end(), avc_config.pps_list[i].begin(), | 159 buffer->insert(buffer->end(), avc_config.pps_list[i].begin(), |
85 avc_config.pps_list[i].end()); | 160 avc_config.pps_list[i].end()); |
161 | |
162 SubsampleEntry entry; | |
163 entry.clear_bytes = kAnnexBStartCodeSize + avc_config.pps_list[i].size(); | |
164 entry.cypher_bytes = 0; | |
165 subsamples->push_back(entry); | |
86 } | 166 } |
87 return true; | 167 return true; |
88 } | 168 } |
89 | 169 |
170 // Verifies AnnexB NALU order according to ISO/IEC 14496-10 Section 7.4.1.2.3 | |
171 bool AVC::IsValidAnnexB(const std::vector<uint8>& buffer) { | |
172 DVLOG(1) << __FUNCTION__; | |
173 | |
174 if (buffer.empty()) | |
175 return true; | |
176 | |
177 H264Parser parser; | |
178 parser.SetStream(&buffer[0], buffer.size()); | |
179 | |
180 typedef enum { | |
181 kAUDAllowed, | |
182 kBeforeFirstVCL, // VCL == nal_unit_types 1-5 | |
183 kAfterFirstVCL, | |
184 kEOStreamAllowed, | |
185 kNoMoreDataAllowed, | |
186 } NALUOrderState; | |
187 | |
188 H264NALU nalu; | |
189 NALUOrderState order_state = kAUDAllowed; | |
190 int last_nalu_type = H264NALU::kUnspecified; | |
191 bool done = false; | |
192 while (!done) { | |
193 switch (parser.AdvanceToNextNALU(&nalu)) { | |
194 case H264Parser::kOk: | |
195 DVLOG(1) << "nal_unit_type " << nalu.nal_unit_type; | |
196 | |
197 switch (nalu.nal_unit_type) { | |
198 case H264NALU::kAUD: | |
199 if (order_state > kAUDAllowed) { | |
200 DVLOG(1) << "Unexpected AUD in order_state " << order_state; | |
201 return false; | |
202 } | |
203 | |
204 order_state = kBeforeFirstVCL; | |
205 break; | |
206 | |
207 case H264NALU::kSEIMessage: | |
208 case H264NALU::kReserved14: | |
209 case H264NALU::kReserved15: | |
210 case H264NALU::kReserved16: | |
211 case H264NALU::kReserved17: | |
212 case H264NALU::kReserved18: | |
213 case H264NALU::kPPS: | |
214 case H264NALU::kSPS: | |
215 if (order_state > kBeforeFirstVCL) { | |
216 DVLOG(1) << "Unexpected NALU type " << nalu.nal_unit_type | |
217 << " in order_state " << order_state; | |
218 return false; | |
219 } | |
220 | |
221 order_state = kBeforeFirstVCL; | |
222 break; | |
223 | |
224 case H264NALU::kSPSExt: | |
225 if (last_nalu_type != H264NALU::kSPS) { | |
226 DVLOG(1) << "SPS extension does not follow an SPS."; | |
227 return false; | |
228 } | |
229 break; | |
230 | |
231 case H264NALU::kNonIDRSlice: | |
232 case H264NALU::kSliceDataA: | |
233 case H264NALU::kSliceDataB: | |
234 case H264NALU::kSliceDataC: | |
235 case H264NALU::kIDRSlice: | |
236 if (order_state > kAfterFirstVCL) { | |
237 DVLOG(1) << "Unexpected VCL in order_state " << order_state; | |
238 return false; | |
239 } | |
240 | |
241 order_state = kAfterFirstVCL; | |
242 break; | |
243 | |
244 case H264NALU::kCodedSliceAux: | |
245 if (order_state != kAfterFirstVCL) { | |
246 DVLOG(1) << "Unexpected extension in order_state " << order_state; | |
247 return false; | |
248 } | |
249 break; | |
250 | |
251 case H264NALU::kEOSeq: | |
252 if (order_state != kAfterFirstVCL) { | |
253 DVLOG(1) << "Unexpected EOSeq in order_state " << order_state; | |
254 return false; | |
255 } | |
256 | |
257 order_state = kEOStreamAllowed; | |
258 break; | |
259 | |
260 case H264NALU::kEOStream: | |
261 if (order_state < kAfterFirstVCL) { | |
262 DVLOG(1) << "Unexpected EOStream in order_state " << order_state; | |
263 return false; | |
264 } | |
265 | |
damienv1
2014/04/25 00:11:39
nit: I would remove the blank line here and in eve
acolwell GONE FROM CHROMIUM
2014/04/25 00:29:15
Done.
| |
266 order_state = kNoMoreDataAllowed; | |
267 break; | |
268 | |
269 case H264NALU::kFiller: | |
270 case H264NALU::kUnspecified: | |
271 if (!(order_state >= kAfterFirstVCL && | |
272 order_state < kEOStreamAllowed)) { | |
273 DVLOG(1) << "Unexpected NALU type " << nalu.nal_unit_type | |
274 << " in order_state " << order_state; | |
275 return false; | |
276 } | |
277 break; | |
278 | |
279 default: | |
280 DCHECK_GE(nalu.nal_unit_type, 20); | |
281 | |
282 if (nalu.nal_unit_type >= 20 && nalu.nal_unit_type <= 31 && | |
283 order_state != kAfterFirstVCL) { | |
284 DVLOG(1) << "Unexpected NALU type " << nalu.nal_unit_type | |
285 << " in order_state " << order_state; | |
286 return false; | |
287 } | |
288 } | |
289 last_nalu_type = nalu.nal_unit_type; | |
290 break; | |
291 | |
292 case H264Parser::kInvalidStream: | |
293 return false; | |
294 | |
295 case H264Parser::kUnsupportedStream: | |
296 NOTREACHED() << "AdvanceToNextNALU() returned kUnsupportedStream!"; | |
297 return false; | |
298 | |
299 case H264Parser::kEOStream: | |
300 done = true; | |
301 } | |
302 } | |
303 | |
304 return order_state >= kAfterFirstVCL; | |
305 } | |
306 | |
90 } // namespace mp4 | 307 } // namespace mp4 |
91 } // namespace media | 308 } // namespace media |
OLD | NEW |