Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(150)

Side by Side Diff: media/webm/webm_parser.cc

Issue 7203002: Adding ChunkDemuxer implementation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address CR comments Created 9 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 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.
4
5 #include "media/webm/webm_parser.h"
6
7 // This file contains code to parse WebM file elements. It was created
8 // from information in the Matroska spec.
9 // http://www.matroska.org/technical/specs/index.html
10
11 #include <iomanip>
12
13 #include "base/logging.h"
14 #include "media/webm/webm_constants.h"
15
16 namespace media {
17
18 // Maximum depth of WebM elements. Some WebM elements are lists of
19 // other elements. This limits the number levels of recursion allowed.
20 static const int kMaxLevelDepth = 6;
21
22 WebMParserClient::~WebMParserClient() {}
23
24 // Parses an element header id or size field. These fields are variable length
25 // encoded. The first byte indicates how many bytes the field occupies.
26 // |buf| - The buffer to parse.
27 // |size| - The number of bytes in |buf|
28 // |max_bytes| - The maximum number of bytes the field can be. ID fields
29 // set this to 4 & element size fields set this to 8. If the
30 // first byte indicates a larger field size than this it is a
31 // parser error.
32 // |mask_first_byte| - For element size fields the field length encoding bits
33 // need to be masked off. This parameter is true for
34 // element size fields and is false for ID field values.
35 //
36 // Returns: The number of bytes parsed on success. -1 on error.
37 static int ParseWebMElementHeaderField(const uint8* buf, int size,
38 int max_bytes, bool mask_first_byte,
39 int64* num) {
40 DCHECK(buf);
41 DCHECK(num);
42
43 if (size <= 0)
44 return -1;
45
46 int mask = 0x80;
47 uint8 ch = buf[0];
48 int extra_bytes = -1;
49 for (int i = 0; i < max_bytes; ++i) {
50 if ((ch & mask) == mask) {
51 *num = mask_first_byte ? ch & ~mask : ch;
52 extra_bytes = i;
53 break;
54 }
55 mask >>= 1;
56 }
57
58 if ((extra_bytes == -1) || ((1 + extra_bytes) > size))
59 return -1;
60
61 int bytes_used = 1;
62
63 for (int i = 0; i < extra_bytes; ++i)
64 *num = (*num << 8) | (0xff & buf[bytes_used++]);
65
66 return bytes_used;
67 }
68
69 // Parses an element header & returns the ID and element size.
70 //
71 // Returns: The number of bytes parsed on success. -1 on error.
72 // |*id| contains the element ID on success & undefined on error.
73 // |*element_size| contains the element size on success & undefined on error.
74 static int ParseWebMElementHeader(const uint8* buf, int size,
75 int* id, int64* element_size) {
76 DCHECK(buf);
77 DCHECK_GE(size, 0);
78 DCHECK(id);
79 DCHECK(element_size);
80
81 if (size == 0)
82 return 0;
83
84 int64 tmp;
85 int num_id_bytes = ParseWebMElementHeaderField(buf, size, 4, false, &tmp);
86
87 if (num_id_bytes <= 0)
88 return num_id_bytes;
89
90 *id = static_cast<int>(tmp);
91
92 int num_size_bytes = ParseWebMElementHeaderField(buf + num_id_bytes,
93 size - num_id_bytes,
94 8, true, &tmp);
95
96 if (num_size_bytes <= 0)
97 return num_size_bytes;
98
99 *element_size = tmp;
100 return num_id_bytes + num_size_bytes;
101 }
102
103 enum ElementType {
scherkus (not reviewing) 2011/06/23 18:50:18 nit: constants 'n stuff get declared at the top
acolwell GONE FROM CHROMIUM 2011/06/23 22:42:17 Done.
104 LIST,
105 UINT,
106 FLOAT,
107 BINARY,
108 STRING,
109 SBLOCK,
110 SKIP,
111 };
112
113 struct ElementIdInfo {
114 int level_;
115 ElementType type_;
116 int id_;
117 };
118
119 // The following are tables indicating what IDs are valid sub-elements
120 // of particular elements. If an element is encountered that doesn't
121 // appear in the list, a parsing error is signalled. Some elements are
122 // marked as SKIP because they are valid, but we don't care about them
123 // right now.
124 static const ElementIdInfo kClusterIds[] = {
125 {2, UINT, kWebMIdTimecode},
126 {2, SBLOCK, kWebMIdSimpleBlock},
127 {2, LIST, kWebMIdBlockGroup},
128 };
129
130 static const ElementIdInfo kInfoIds[] = {
131 {2, SKIP, kWebMIdSegmentUID},
132 {2, UINT, kWebMIdTimecodeScale},
133 {2, FLOAT, kWebMIdDuration},
134 {2, SKIP, kWebMIdDateUTC},
135 {2, SKIP, kWebMIdTitle},
136 {2, SKIP, kWebMIdMuxingApp},
137 {2, SKIP, kWebMIdWritingApp},
138 };
139
140 static const ElementIdInfo kTracksIds[] = {
141 {2, LIST, kWebMIdTrackEntry},
142 };
143
144 static const ElementIdInfo kTrackEntryIds[] = {
145 {3, UINT, kWebMIdTrackNumber},
146 {3, SKIP, kWebMIdTrackUID},
147 {3, UINT, kWebMIdTrackType},
148 {3, SKIP, kWebMIdFlagEnabled},
149 {3, SKIP, kWebMIdFlagDefault},
150 {3, SKIP, kWebMIdFlagForced},
151 {3, UINT, kWebMIdFlagLacing},
152 {3, UINT, kWebMIdDefaultDuration},
153 {3, SKIP, kWebMIdName},
154 {3, SKIP, kWebMIdLanguage},
155 {3, STRING, kWebMIdCodecID},
156 {3, BINARY, kWebMIdCodecPrivate},
157 {3, SKIP, kWebMIdCodecName},
158 {3, LIST, kWebMIdVideo},
159 {3, LIST, kWebMIdAudio},
160 };
161
162 static const ElementIdInfo kVideoIds[] = {
163 {4, SKIP, kWebMIdFlagInterlaced},
164 {4, SKIP, kWebMIdStereoMode},
165 {4, UINT, kWebMIdPixelWidth},
166 {4, UINT, kWebMIdPixelHeight},
167 {4, SKIP, kWebMIdPixelCropBottom},
168 {4, SKIP, kWebMIdPixelCropTop},
169 {4, SKIP, kWebMIdPixelCropLeft},
170 {4, SKIP, kWebMIdPixelCropRight},
171 {4, SKIP, kWebMIdDisplayWidth},
172 {4, SKIP, kWebMIdDisplayHeight},
173 {4, SKIP, kWebMIdDisplayUnit},
174 {4, SKIP, kWebMIdAspectRatioType},
175 };
176
177 static const ElementIdInfo kAudioIds[] = {
178 {4, SKIP, kWebMIdSamplingFrequency},
179 {4, SKIP, kWebMIdOutputSamplingFrequency},
180 {4, UINT, kWebMIdChannels},
181 {4, SKIP, kWebMIdBitDepth},
182 };
183
184 static const ElementIdInfo kInfoAndTracksOnly[] = {
185 {1, LIST, kWebMIdInfo},
186 {1, LIST, kWebMIdTracks},
187 };
188
189 static const ElementIdInfo kClustersOnly[] = {
190 {1, LIST, kWebMIdCluster},
191 };
192
193 struct ListElementInfo {
194 int id_;
195 const ElementIdInfo* id_info_;
196 int id_info_size_;
197 };
198
199 const ListElementInfo kListElementInfo[] = {
200 { kWebMIdCluster, kClusterIds, sizeof(kClusterIds) },
201 { kWebMIdInfo, kInfoIds, sizeof(kInfoIds) },
202 { kWebMIdTracks, kTracksIds, sizeof(kTracksIds) },
203 { kWebMIdTrackEntry, kTrackEntryIds, sizeof(kTrackEntryIds) },
204 { kWebMIdVideo, kVideoIds, sizeof(kVideoIds) },
205 { kWebMIdAudio, kAudioIds, sizeof(kAudioIds) },
206 };
207
208 // Number of elements in kListElementInfo.
209 const int kListElementInfoCount =
210 sizeof(kListElementInfo) / sizeof(ListElementInfo);
211
212 // Finds ElementIdInfo for a specific ID.
213 static const ElementIdInfo* FindIdInfo(int id,
214 const ElementIdInfo* id_info,
215 int id_info_size) {
216 int count = id_info_size / sizeof(*id_info);
217 for (int i = 0; i < count; ++i) {
218 if (id == id_info[i].id_)
219 return &id_info[i];
220 }
221
222 return NULL;
223 }
224
225 // Finds ListElementInfo for a specific ID.
226 static const ListElementInfo* FindListInfo(int id) {
227 for (int i = 0; i < kListElementInfoCount; ++i) {
228 if (id == kListElementInfo[i].id_)
229 return &kListElementInfo[i];
230 }
231
232 return NULL;
233 }
234
235 static int ParseSimpleBlock(WebMParserClient* client,
236 const uint8* buf, int size) {
237 if (size < 4)
238 return -1;
239
240 // Return an error if the trackNum > 127. We just aren't
241 // going to support large track numbers right now.
242 if ((buf[0] & 0x80) != 0x80) {
243 VLOG(1) << "TrackNumber over 127 not supported";
244 return -1;
245 }
246
247 int track_num = buf[0] & 0x7f;
248 int timecode = buf[1] << 8 | buf[2];
249 int flags = buf[3] & 0xff;
250 int lacing = (flags >> 1) & 0x3;
251
252 if (lacing != 0) {
253 VLOG(1) << "Lacing " << lacing << " not supported yet.";
254 return -1;
255 }
256
257 const uint8* frame_data = buf + 4;
258 int frame_size = size - (frame_data - buf);
259 if (!client->OnSimpleBlock(track_num, timecode, flags,
260 frame_data, frame_size)) {
261 return -1;
262 }
263
264 return size;
265 }
266
267 static int ParseElements(WebMParserClient* client,
268 const ElementIdInfo* id_info,
269 int id_info_size,
270 const uint8* buf, int size, int level);
271
272 static int ParseElementList(WebMParserClient* client,
273 const uint8* buf, int size,
274 int id, int level) {
275 const ListElementInfo* list_info = FindListInfo(id);
276
277 if (!list_info) {
278 VLOG(1) << "Failed to find list info for ID " << std::hex << id;
279 return -1;
280 }
281
282 if (!client->OnListStart(id))
283 return -1;
284
285 int res = ParseElements(client,
286 list_info->id_info_,
287 list_info->id_info_size_,
288 buf, size,
289 level + 1);
290
291 if (res < 0)
292 return -1;
293
294 if (!client->OnListEnd(id))
295 return -1;
296
297 DCHECK_EQ(res, size);
298 return res;
299 }
300
301 static int ParseUInt(WebMParserClient* client, const uint8* buf, int size,
302 int id) {
303 if ((size <= 0) || (size > 8))
304 return -1;
305
306 // Read in the big-endian integer.
307 int64 value = 0;
308 for (int i = 0; i < size; ++i)
309 value = (value << 8) | buf[i];
310
311 if (!client->OnUInt(id, value))
312 return -1;
313
314 return size;
315 }
316
317 static int ParseFloat(WebMParserClient* client, const uint8* buf, int size,
318 int id) {
319
320 if ((size != 4) && (size != 8))
321 return -1;
322
323 double value = -1;
324
325 // Read the bytes from big-endian form into a native endian integer.
326 int64 tmp = 0;
327 for (int i = 0; i < size; ++i)
328 tmp = (tmp << 8) | buf[i];
329
330 // Use a union to convert the integer bit pattern into a floating point
331 // number.
332 if (size == 4) {
333 union {
334 int32 src;
335 float dst;
336 } tmp2;
337 tmp2.src = static_cast<int32>(tmp);
338 value = tmp2.dst;
339 } else if (size == 8) {
340 union {
341 int64 src;
342 double dst;
343 } tmp2;
344 tmp2.src = tmp;
345 value = tmp2.dst;
346 } else {
347 return -1;
348 }
349
350 if (!client->OnFloat(id, value))
351 return -1;
352
353 return size;
354 }
355
356 static int ParseElements(WebMParserClient* client,
357 const ElementIdInfo* id_info,
358 int id_info_size,
359 const uint8* buf, int size, int level) {
360 DCHECK_GE(id_info_size, 0);
361 DCHECK_GE(size, 0);
362 DCHECK_GE(level, 0);
363
364 const uint8* cur = buf;
365 int cur_size = size;
366 int used = 0;
367
368 if (level > kMaxLevelDepth)
369 return -1;
370
371 while (cur_size > 0) {
372 int id;
373 int64 element_size;
374 int res = ParseWebMElementHeader(cur, cur_size, &id, &element_size);
375
376 if (res < 0)
377 return res;
378
379 if (res == 0)
380 break;
381
382 cur += res;
383 cur_size -= res;
384 used += res;
385
386 // Check to see if the element is larger than the remaining data.
387 if (element_size > cur_size)
388 return -1;
389
390 const ElementIdInfo* info = FindIdInfo(id, id_info, id_info_size);
391
392 if (info == NULL) {
393 VLOG(1) << "No info for ID " << std::hex << id;
394
395 // TODO(acolwell): Change this to return -1 after the API has solidified.
396 // We don't want to allow elements we don't recognize.
397 cur += element_size;
398 cur_size -= element_size;
399 used += element_size;
400 continue;
401 }
402
403 if (info->level_ != level) {
404 VLOG(1) << "ID " << std::hex << id << std::dec << " at level "
405 << level << " instead of " << info->level_;
406 return -1;
407 }
408
409 switch(info->type_) {
410 case SBLOCK:
411 if (ParseSimpleBlock(client, cur, element_size) <= 0)
412 return -1;
413 break;
414 case LIST:
415 if (ParseElementList(client, cur, element_size, id, level) < 0)
416 return -1;
417 break;
418 case UINT:
419 if (ParseUInt(client, cur, element_size, id) <= 0)
420 return -1;
421 break;
422 case FLOAT:
423 if (ParseFloat(client, cur, element_size, id) <= 0)
424 return -1;
425 break;
426 case BINARY:
427 if (!client->OnBinary(id, cur, element_size))
428 return -1;
429 break;
430 case STRING:
431 if (!client->OnString(id,
432 std::string(reinterpret_cast<const char*>(cur),
433 element_size)))
434 return -1;
435 break;
436 case SKIP:
437 // Do nothing
scherkus (not reviewing) 2011/06/23 18:50:18 period
acolwell GONE FROM CHROMIUM 2011/06/23 22:42:17 Done.
438 break;
439 default:
440 VLOG(1) << "Unhandled id type " << info->type_;
441 return -1;
442 };
443
444 cur += element_size;
445 cur_size -= element_size;
446 used += element_size;
447 }
448
449 return used;
450 }
451
452 int ParseWebMHeaders(WebMParserClient* client,
453 const uint8* buf, int size) {
454 return ParseElements(client, kInfoAndTracksOnly, sizeof(kInfoAndTracksOnly),
455 buf, size, 1);
456 }
457
458 int ParseWebMCluster(WebMParserClient* client,
459 const uint8* buf, int size) {
460 return ParseElements(client, kClustersOnly, sizeof(kClustersOnly),
461 buf, size, 1);
462 }
463
464 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698