1 /*
2 * ISO File Format parsing library
3 *
4 * gstisoff.h
5 *
6 * Copyright (C) 2015 Samsung Electronics. All rights reserved.
7 * Author: Thiago Santos <thiagoss@osg.samsung.com>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library (COPYING); if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
23 */
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "gstisoff.h"
29 #include <gst/base/gstbytereader.h>
30
31 #include <string.h>
32
33 GST_DEBUG_CATEGORY_STATIC (gst_isoff_debug);
34 #define GST_CAT_DEFAULT gst_isoff_debug
35
36 static gboolean initialized = FALSE;
37
38 #define INITIALIZE_DEBUG_CATEGORY \
39 if (!initialized) { \
40 GST_DEBUG_CATEGORY_INIT (gst_isoff_debug, "isoff", 0, \
41 "ISO File Format parsing library"); \
42 initialized = TRUE; \
43 }
44
45 static const guint8 tfrf_uuid[] = {
46 0xd4, 0x80, 0x7e, 0xf2, 0xca, 0x39, 0x46, 0x95,
47 0x8e, 0x54, 0x26, 0xcb, 0x9e, 0x46, 0xa7, 0x9f
48 };
49
50 static const guint8 tfxd_uuid[] = {
51 0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
52 0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
53 };
54
55 /* gst_isoff_parse_box_header:
56 * @reader:
57 * @type: type that was found at the current position
58 * @extended_type: (allow-none): extended type if type=='uuid'
59 * @header_size: (allow-none): size of the box header (type, extended type and size)
60 * @size: size of the complete box including type, extended type and size
61 *
62 * Advances the byte reader to the start of the box content. To skip
63 * over the complete box, skip size - header_size bytes.
64 *
65 * Returns: TRUE if a box header could be parsed, FALSE if more data is needed
66 */
67 gboolean
gst_isoff_parse_box_header(GstByteReader * reader,guint32 * type,guint8 extended_type[16],guint * header_size,guint64 * size)68 gst_isoff_parse_box_header (GstByteReader * reader, guint32 * type,
69 guint8 extended_type[16], guint * header_size, guint64 * size)
70 {
71 guint header_start_offset;
72 guint32 size_field;
73
74 INITIALIZE_DEBUG_CATEGORY;
75 header_start_offset = gst_byte_reader_get_pos (reader);
76
77 if (gst_byte_reader_get_remaining (reader) < 8)
78 goto not_enough_data;
79
80 size_field = gst_byte_reader_get_uint32_be_unchecked (reader);
81 *type = gst_byte_reader_get_uint32_le_unchecked (reader);
82
83 if (size_field == 1) {
84 if (gst_byte_reader_get_remaining (reader) < 8)
85 goto not_enough_data;
86 *size = gst_byte_reader_get_uint64_be_unchecked (reader);
87 } else {
88 *size = size_field;
89 }
90
91 if (*type == GST_ISOFF_FOURCC_UUID) {
92 if (gst_byte_reader_get_remaining (reader) < 16)
93 goto not_enough_data;
94
95 if (extended_type)
96 memcpy (extended_type, gst_byte_reader_get_data_unchecked (reader, 16),
97 16);
98 }
99
100 if (header_size)
101 *header_size = gst_byte_reader_get_pos (reader) - header_start_offset;
102
103 return TRUE;
104
105 not_enough_data:
106 gst_byte_reader_set_pos (reader, header_start_offset);
107 return FALSE;
108 }
109
110 static void
gst_isoff_trun_box_clear(GstTrunBox * trun)111 gst_isoff_trun_box_clear (GstTrunBox * trun)
112 {
113 if (trun->samples)
114 g_array_free (trun->samples, TRUE);
115 }
116
117 static void
gst_isoff_tfrf_box_free(GstTfrfBox * tfrf)118 gst_isoff_tfrf_box_free (GstTfrfBox * tfrf)
119 {
120 if (tfrf->entries)
121 g_array_free (tfrf->entries, TRUE);
122
123 g_free (tfrf);
124 }
125
126 static void
gst_isoff_traf_box_clear(GstTrafBox * traf)127 gst_isoff_traf_box_clear (GstTrafBox * traf)
128 {
129 if (traf->trun)
130 g_array_free (traf->trun, TRUE);
131
132 if (traf->tfrf)
133 gst_isoff_tfrf_box_free (traf->tfrf);
134
135 g_free (traf->tfxd);
136 traf->trun = NULL;
137 traf->tfrf = NULL;
138 traf->tfxd = NULL;
139 }
140
141 static gboolean
gst_isoff_mfhd_box_parse(GstMfhdBox * mfhd,GstByteReader * reader)142 gst_isoff_mfhd_box_parse (GstMfhdBox * mfhd, GstByteReader * reader)
143 {
144 guint8 version;
145 guint32 flags;
146
147 if (gst_byte_reader_get_remaining (reader) != 8)
148 return FALSE;
149
150 version = gst_byte_reader_get_uint8_unchecked (reader);
151 if (version != 0)
152 return FALSE;
153
154 flags = gst_byte_reader_get_uint24_be_unchecked (reader);
155 if (flags != 0)
156 return FALSE;
157
158 mfhd->sequence_number = gst_byte_reader_get_uint32_be_unchecked (reader);
159
160 return TRUE;
161 }
162
163 static gboolean
gst_isoff_tfhd_box_parse(GstTfhdBox * tfhd,GstByteReader * reader)164 gst_isoff_tfhd_box_parse (GstTfhdBox * tfhd, GstByteReader * reader)
165 {
166 memset (tfhd, 0, sizeof (*tfhd));
167
168 if (gst_byte_reader_get_remaining (reader) < 4)
169 return FALSE;
170
171 tfhd->version = gst_byte_reader_get_uint8_unchecked (reader);
172 if (tfhd->version != 0)
173 return FALSE;
174
175 tfhd->flags = gst_byte_reader_get_uint24_be_unchecked (reader);
176
177 if (!gst_byte_reader_get_uint32_be (reader, &tfhd->track_id))
178 return FALSE;
179
180 if ((tfhd->flags & GST_TFHD_FLAGS_BASE_DATA_OFFSET_PRESENT) &&
181 !gst_byte_reader_get_uint64_be (reader, &tfhd->base_data_offset))
182 return FALSE;
183
184 if ((tfhd->flags & GST_TFHD_FLAGS_SAMPLE_DESCRIPTION_INDEX_PRESENT) &&
185 !gst_byte_reader_get_uint32_be (reader, &tfhd->sample_description_index))
186 return FALSE;
187
188 if ((tfhd->flags & GST_TFHD_FLAGS_DEFAULT_SAMPLE_DURATION_PRESENT) &&
189 !gst_byte_reader_get_uint32_be (reader, &tfhd->default_sample_duration))
190 return FALSE;
191
192 if ((tfhd->flags & GST_TFHD_FLAGS_DEFAULT_SAMPLE_SIZE_PRESENT) &&
193 !gst_byte_reader_get_uint32_be (reader, &tfhd->default_sample_size))
194 return FALSE;
195
196 if ((tfhd->flags & GST_TFHD_FLAGS_DEFAULT_SAMPLE_FLAGS_PRESENT) &&
197 !gst_byte_reader_get_uint32_be (reader, &tfhd->default_sample_flags))
198 return FALSE;
199
200 return TRUE;
201 }
202
203 static gboolean
gst_isoff_trun_box_parse(GstTrunBox * trun,GstByteReader * reader)204 gst_isoff_trun_box_parse (GstTrunBox * trun, GstByteReader * reader)
205 {
206 gint i;
207
208 memset (trun, 0, sizeof (*trun));
209
210 if (gst_byte_reader_get_remaining (reader) < 4)
211 return FALSE;
212
213 trun->version = gst_byte_reader_get_uint8_unchecked (reader);
214 if (trun->version != 0 && trun->version != 1)
215 return FALSE;
216
217 trun->flags = gst_byte_reader_get_uint24_be_unchecked (reader);
218
219 if (!gst_byte_reader_get_uint32_be (reader, &trun->sample_count))
220 return FALSE;
221
222 trun->samples =
223 g_array_sized_new (FALSE, FALSE, sizeof (GstTrunSample),
224 trun->sample_count);
225
226 if ((trun->flags & GST_TRUN_FLAGS_DATA_OFFSET_PRESENT) &&
227 !gst_byte_reader_get_uint32_be (reader, (guint32 *) & trun->data_offset))
228 return FALSE;
229
230 if ((trun->flags & GST_TRUN_FLAGS_FIRST_SAMPLE_FLAGS_PRESENT) &&
231 !gst_byte_reader_get_uint32_be (reader, &trun->first_sample_flags))
232 return FALSE;
233
234 for (i = 0; i < trun->sample_count; i++) {
235 GstTrunSample sample = { 0, };
236
237 if ((trun->flags & GST_TRUN_FLAGS_SAMPLE_DURATION_PRESENT) &&
238 !gst_byte_reader_get_uint32_be (reader, &sample.sample_duration))
239 goto error;
240
241 if ((trun->flags & GST_TRUN_FLAGS_SAMPLE_SIZE_PRESENT) &&
242 !gst_byte_reader_get_uint32_be (reader, &sample.sample_size))
243 goto error;
244
245 if ((trun->flags & GST_TRUN_FLAGS_SAMPLE_FLAGS_PRESENT) &&
246 !gst_byte_reader_get_uint32_be (reader, &sample.sample_flags))
247 goto error;
248
249 if ((trun->flags & GST_TRUN_FLAGS_SAMPLE_COMPOSITION_TIME_OFFSETS_PRESENT)
250 && !gst_byte_reader_get_uint32_be (reader,
251 &sample.sample_composition_time_offset.u))
252 goto error;
253
254 g_array_append_val (trun->samples, sample);
255 }
256
257 return TRUE;
258
259 error:
260 gst_isoff_trun_box_clear (trun);
261 return FALSE;
262 }
263
264 static gboolean
gst_isoff_tfdt_box_parse(GstTfdtBox * tfdt,GstByteReader * reader)265 gst_isoff_tfdt_box_parse (GstTfdtBox * tfdt, GstByteReader * reader)
266 {
267 gint8 version;
268
269 memset (tfdt, 0, sizeof (*tfdt));
270
271 if (gst_byte_reader_get_remaining (reader) < 4)
272 return FALSE;
273
274 version = gst_byte_reader_get_uint8_unchecked (reader);
275
276 if (!gst_byte_reader_skip (reader, 3))
277 return FALSE;
278
279 if (version == 1) {
280 if (!gst_byte_reader_get_uint64_be (reader, &tfdt->decode_time))
281 return FALSE;
282 } else {
283 guint32 dec_time = 0;
284 if (!gst_byte_reader_get_uint32_be (reader, &dec_time))
285 return FALSE;
286 tfdt->decode_time = dec_time;
287 }
288
289 return TRUE;
290 }
291
292 static gboolean
gst_isoff_tfxd_box_parse(GstTfxdBox * tfxd,GstByteReader * reader)293 gst_isoff_tfxd_box_parse (GstTfxdBox * tfxd, GstByteReader * reader)
294 {
295 guint8 version;
296 guint32 flags = 0;
297 guint64 absolute_time = 0;
298 guint64 absolute_duration = 0;
299
300 memset (tfxd, 0, sizeof (*tfxd));
301
302 if (gst_byte_reader_get_remaining (reader) < 4)
303 return FALSE;
304
305 if (!gst_byte_reader_get_uint8 (reader, &version)) {
306 GST_ERROR ("Error getting box's version field");
307 return FALSE;
308 }
309
310 if (!gst_byte_reader_get_uint24_be (reader, &flags)) {
311 GST_ERROR ("Error getting box's flags field");
312 return FALSE;
313 }
314
315 tfxd->version = version;
316 tfxd->flags = flags;
317
318 if (gst_byte_reader_get_remaining (reader) < ((version & 0x01) ? 16 : 8))
319 return FALSE;
320
321 if (version & 0x01) {
322 gst_byte_reader_get_uint64_be (reader, &absolute_time);
323 gst_byte_reader_get_uint64_be (reader, &absolute_duration);
324 } else {
325 guint32 time = 0;
326 guint32 duration = 0;
327 gst_byte_reader_get_uint32_be (reader, &time);
328 gst_byte_reader_get_uint32_be (reader, &duration);
329 absolute_time = time;
330 absolute_duration = duration;
331 }
332
333 tfxd->time = absolute_time;
334 tfxd->duration = absolute_duration;
335
336 return TRUE;
337 }
338
339 static gboolean
gst_isoff_tfrf_box_parse(GstTfrfBox * tfrf,GstByteReader * reader)340 gst_isoff_tfrf_box_parse (GstTfrfBox * tfrf, GstByteReader * reader)
341 {
342 guint8 version;
343 guint32 flags = 0;
344 guint8 fragment_count = 0;
345 guint8 index = 0;
346
347 memset (tfrf, 0, sizeof (*tfrf));
348
349 if (gst_byte_reader_get_remaining (reader) < 4)
350 return FALSE;
351
352 if (!gst_byte_reader_get_uint8 (reader, &version)) {
353 GST_ERROR ("Error getting box's version field");
354 return FALSE;
355 }
356
357 if (!gst_byte_reader_get_uint24_be (reader, &flags)) {
358 GST_ERROR ("Error getting box's flags field");
359 return FALSE;
360 }
361
362 tfrf->version = version;
363 tfrf->flags = flags;
364
365 if (!gst_byte_reader_get_uint8 (reader, &fragment_count))
366 return FALSE;
367
368 tfrf->entries_count = fragment_count;
369 tfrf->entries =
370 g_array_sized_new (FALSE, FALSE, sizeof (GstTfrfBoxEntry),
371 tfrf->entries_count);
372
373 for (index = 0; index < fragment_count; index++) {
374 GstTfrfBoxEntry entry = { 0, };
375 guint64 absolute_time = 0;
376 guint64 absolute_duration = 0;
377 if (gst_byte_reader_get_remaining (reader) < ((version & 0x01) ? 16 : 8))
378 return FALSE;
379
380 if (version & 0x01) {
381 if (!gst_byte_reader_get_uint64_be (reader, &absolute_time) ||
382 !gst_byte_reader_get_uint64_be (reader, &absolute_duration)) {
383 return FALSE;
384 }
385 } else {
386 guint32 time = 0;
387 guint32 duration = 0;
388 if (!gst_byte_reader_get_uint32_be (reader, &time) ||
389 !gst_byte_reader_get_uint32_be (reader, &duration)) {
390 return FALSE;
391 }
392 absolute_time = time;
393 absolute_duration = duration;
394 }
395 entry.time = absolute_time;
396 entry.duration = absolute_duration;
397
398 g_array_append_val (tfrf->entries, entry);
399 }
400
401 return TRUE;
402 }
403
404 static gboolean
gst_isoff_traf_box_parse(GstTrafBox * traf,GstByteReader * reader)405 gst_isoff_traf_box_parse (GstTrafBox * traf, GstByteReader * reader)
406 {
407 gboolean had_tfhd = FALSE;
408
409 memset (traf, 0, sizeof (*traf));
410 traf->trun = g_array_new (FALSE, FALSE, sizeof (GstTrunBox));
411 g_array_set_clear_func (traf->trun,
412 (GDestroyNotify) gst_isoff_trun_box_clear);
413
414 traf->tfdt.decode_time = GST_CLOCK_TIME_NONE;
415
416 while (gst_byte_reader_get_remaining (reader) > 0) {
417 guint32 fourcc;
418 guint header_size;
419 guint64 size;
420 GstByteReader sub_reader;
421 guint8 extended_type[16] = { 0, };
422
423 if (!gst_isoff_parse_box_header (reader, &fourcc, extended_type,
424 &header_size, &size))
425 goto error;
426 if (gst_byte_reader_get_remaining (reader) < size - header_size)
427 goto error;
428
429 switch (fourcc) {
430 case GST_ISOFF_FOURCC_TFHD:{
431 gst_byte_reader_get_sub_reader (reader, &sub_reader,
432 size - header_size);
433 if (!gst_isoff_tfhd_box_parse (&traf->tfhd, &sub_reader))
434 goto error;
435 had_tfhd = TRUE;
436 break;
437 }
438 case GST_ISOFF_FOURCC_TFDT:{
439 gst_byte_reader_get_sub_reader (reader, &sub_reader,
440 size - header_size);
441 if (!gst_isoff_tfdt_box_parse (&traf->tfdt, &sub_reader))
442 goto error;
443 break;
444 }
445 case GST_ISOFF_FOURCC_TRUN:{
446 GstTrunBox trun;
447
448 gst_byte_reader_get_sub_reader (reader, &sub_reader,
449 size - header_size);
450 if (!gst_isoff_trun_box_parse (&trun, &sub_reader))
451 goto error;
452
453 g_array_append_val (traf->trun, trun);
454 break;
455 }
456 case GST_ISOFF_FOURCC_UUID:{
457 /* smooth-streaming specific */
458 if (memcmp (extended_type, tfrf_uuid, 16) == 0) {
459 traf->tfrf = g_new0 (GstTfrfBox, 1);
460 gst_byte_reader_get_sub_reader (reader, &sub_reader,
461 size - header_size);
462
463 if (!gst_isoff_tfrf_box_parse (traf->tfrf, &sub_reader))
464 goto error;
465 } else if (memcmp (extended_type, tfxd_uuid, 16) == 0) {
466 traf->tfxd = g_new0 (GstTfxdBox, 1);
467 gst_byte_reader_get_sub_reader (reader, &sub_reader,
468 size - header_size);
469
470 if (!gst_isoff_tfxd_box_parse (traf->tfxd, &sub_reader))
471 goto error;
472 } else {
473 gst_byte_reader_skip (reader, size - header_size);
474 }
475 break;
476 }
477 default:
478 gst_byte_reader_skip (reader, size - header_size);
479 break;
480 }
481 }
482
483 if (!had_tfhd)
484 goto error;
485
486 return TRUE;
487
488 error:
489 gst_isoff_traf_box_clear (traf);
490
491 return FALSE;
492 }
493
494 GstMoofBox *
gst_isoff_moof_box_parse(GstByteReader * reader)495 gst_isoff_moof_box_parse (GstByteReader * reader)
496 {
497 GstMoofBox *moof;
498 gboolean had_mfhd = FALSE;
499 GstByteReader sub_reader;
500
501
502 INITIALIZE_DEBUG_CATEGORY;
503 moof = g_new0 (GstMoofBox, 1);
504 moof->traf = g_array_new (FALSE, FALSE, sizeof (GstTrafBox));
505 g_array_set_clear_func (moof->traf,
506 (GDestroyNotify) gst_isoff_traf_box_clear);
507
508 while (gst_byte_reader_get_remaining (reader) > 0) {
509 guint32 fourcc;
510 guint header_size;
511 guint64 size;
512
513 if (!gst_isoff_parse_box_header (reader, &fourcc, NULL, &header_size,
514 &size))
515 goto error;
516 if (gst_byte_reader_get_remaining (reader) < size - header_size)
517 goto error;
518
519 switch (fourcc) {
520 case GST_ISOFF_FOURCC_MFHD:{
521 gst_byte_reader_get_sub_reader (reader, &sub_reader,
522 size - header_size);
523 if (!gst_isoff_mfhd_box_parse (&moof->mfhd, &sub_reader))
524 goto error;
525 had_mfhd = TRUE;
526 break;
527 }
528 case GST_ISOFF_FOURCC_TRAF:{
529 GstTrafBox traf;
530
531 gst_byte_reader_get_sub_reader (reader, &sub_reader,
532 size - header_size);
533 if (!gst_isoff_traf_box_parse (&traf, &sub_reader))
534 goto error;
535
536 g_array_append_val (moof->traf, traf);
537 break;
538 }
539 default:
540 gst_byte_reader_skip (reader, size - header_size);
541 break;
542 }
543 }
544
545 if (!had_mfhd)
546 goto error;
547
548 return moof;
549
550 error:
551 gst_isoff_moof_box_free (moof);
552 return NULL;
553 }
554
555 void
gst_isoff_moof_box_free(GstMoofBox * moof)556 gst_isoff_moof_box_free (GstMoofBox * moof)
557 {
558 g_array_free (moof->traf, TRUE);
559 g_free (moof);
560 }
561
562 static gboolean
gst_isoff_mdhd_box_parse(GstMdhdBox * mdhd,GstByteReader * reader)563 gst_isoff_mdhd_box_parse (GstMdhdBox * mdhd, GstByteReader * reader)
564 {
565 guint8 version;
566
567 memset (mdhd, 0, sizeof (*mdhd));
568
569 if (gst_byte_reader_get_remaining (reader) < 4)
570 return FALSE;
571
572 version = gst_byte_reader_get_uint8_unchecked (reader);
573
574 if (!gst_byte_reader_skip (reader, 3))
575 return FALSE;
576
577 /* skip {creation, modification}_time, we don't have interest */
578 if (version == 1) {
579 if (!gst_byte_reader_skip (reader, 16))
580 return FALSE;
581 } else {
582 if (!gst_byte_reader_skip (reader, 8))
583 return FALSE;
584 }
585
586 if (!gst_byte_reader_get_uint32_be (reader, &mdhd->timescale))
587 return FALSE;
588
589 return TRUE;
590 }
591
592 static gboolean
gst_isoff_hdlr_box_parse(GstHdlrBox * hdlr,GstByteReader * reader)593 gst_isoff_hdlr_box_parse (GstHdlrBox * hdlr, GstByteReader * reader)
594 {
595 memset (hdlr, 0, sizeof (*hdlr));
596
597 if (gst_byte_reader_get_remaining (reader) < 4)
598 return FALSE;
599
600 /* version & flag */
601 if (!gst_byte_reader_skip (reader, 4))
602 return FALSE;
603
604 /* pre_defined = 0 */
605 if (!gst_byte_reader_skip (reader, 4))
606 return FALSE;
607
608 if (!gst_byte_reader_get_uint32_le (reader, &hdlr->handler_type))
609 return FALSE;
610
611 return TRUE;
612 }
613
614 static gboolean
gst_isoff_mdia_box_parse(GstMdiaBox * mdia,GstByteReader * reader)615 gst_isoff_mdia_box_parse (GstMdiaBox * mdia, GstByteReader * reader)
616 {
617 gboolean had_mdhd = FALSE, had_hdlr = FALSE;
618 while (gst_byte_reader_get_remaining (reader) > 0) {
619 guint32 fourcc;
620 guint header_size;
621 guint64 size;
622 GstByteReader sub_reader;
623
624 if (!gst_isoff_parse_box_header (reader, &fourcc, NULL, &header_size,
625 &size))
626 return FALSE;
627 if (gst_byte_reader_get_remaining (reader) < size - header_size)
628 return FALSE;
629
630 switch (fourcc) {
631 case GST_ISOFF_FOURCC_MDHD:{
632 gst_byte_reader_get_sub_reader (reader, &sub_reader,
633 size - header_size);
634 if (!gst_isoff_mdhd_box_parse (&mdia->mdhd, &sub_reader))
635 return FALSE;
636
637 had_mdhd = TRUE;
638 break;
639 }
640 case GST_ISOFF_FOURCC_HDLR:{
641 gst_byte_reader_get_sub_reader (reader, &sub_reader,
642 size - header_size);
643 if (!gst_isoff_hdlr_box_parse (&mdia->hdlr, &sub_reader))
644 return FALSE;
645
646 had_hdlr = TRUE;
647 break;
648 }
649 default:
650 gst_byte_reader_skip (reader, size - header_size);
651 break;
652 }
653 }
654
655 if (!had_mdhd || !had_hdlr)
656 return FALSE;
657
658 return TRUE;
659 }
660
661 static gboolean
gst_isoff_tkhd_box_parse(GstTkhdBox * tkhd,GstByteReader * reader)662 gst_isoff_tkhd_box_parse (GstTkhdBox * tkhd, GstByteReader * reader)
663 {
664 guint8 version;
665
666 memset (tkhd, 0, sizeof (*tkhd));
667
668 if (gst_byte_reader_get_remaining (reader) < 4)
669 return FALSE;
670
671 if (!gst_byte_reader_get_uint8 (reader, &version))
672 return FALSE;
673
674 if (!gst_byte_reader_skip (reader, 3))
675 return FALSE;
676
677 /* skip {creation, modification}_time, we don't have interest */
678 if (version == 1) {
679 if (!gst_byte_reader_skip (reader, 16))
680 return FALSE;
681 } else {
682 if (!gst_byte_reader_skip (reader, 8))
683 return FALSE;
684 }
685
686 if (!gst_byte_reader_get_uint32_be (reader, &tkhd->track_id))
687 return FALSE;
688
689 return TRUE;
690 }
691
692 static gboolean
gst_isoff_trak_box_parse(GstTrakBox * trak,GstByteReader * reader)693 gst_isoff_trak_box_parse (GstTrakBox * trak, GstByteReader * reader)
694 {
695 gboolean had_mdia = FALSE, had_tkhd = FALSE;
696 while (gst_byte_reader_get_remaining (reader) > 0) {
697 guint32 fourcc;
698 guint header_size;
699 guint64 size;
700 GstByteReader sub_reader;
701
702 if (!gst_isoff_parse_box_header (reader, &fourcc, NULL, &header_size,
703 &size))
704 return FALSE;
705 if (gst_byte_reader_get_remaining (reader) < size - header_size)
706 return FALSE;
707
708 switch (fourcc) {
709 case GST_ISOFF_FOURCC_MDIA:{
710 gst_byte_reader_get_sub_reader (reader, &sub_reader,
711 size - header_size);
712 if (!gst_isoff_mdia_box_parse (&trak->mdia, &sub_reader))
713 return FALSE;
714
715 had_mdia = TRUE;
716 break;
717 }
718 case GST_ISOFF_FOURCC_TKHD:{
719 gst_byte_reader_get_sub_reader (reader, &sub_reader,
720 size - header_size);
721 if (!gst_isoff_tkhd_box_parse (&trak->tkhd, &sub_reader))
722 return FALSE;
723
724 had_tkhd = TRUE;
725 break;
726 }
727 default:
728 gst_byte_reader_skip (reader, size - header_size);
729 break;
730 }
731 }
732
733 if (!had_tkhd || !had_mdia)
734 return FALSE;
735
736 return TRUE;
737 }
738
739 GstMoovBox *
gst_isoff_moov_box_parse(GstByteReader * reader)740 gst_isoff_moov_box_parse (GstByteReader * reader)
741 {
742 GstMoovBox *moov;
743 gboolean had_trak = FALSE;
744 moov = g_new0 (GstMoovBox, 1);
745 moov->trak = g_array_new (FALSE, FALSE, sizeof (GstTrakBox));
746
747 while (gst_byte_reader_get_remaining (reader) > 0) {
748 guint32 fourcc;
749 guint header_size;
750 guint64 size;
751
752 if (!gst_isoff_parse_box_header (reader, &fourcc, NULL, &header_size,
753 &size))
754 goto error;
755 if (gst_byte_reader_get_remaining (reader) < size - header_size)
756 goto error;
757
758 switch (fourcc) {
759 case GST_ISOFF_FOURCC_TRAK:{
760 GstByteReader sub_reader;
761 GstTrakBox trak;
762
763 gst_byte_reader_get_sub_reader (reader, &sub_reader,
764 size - header_size);
765 if (!gst_isoff_trak_box_parse (&trak, &sub_reader))
766 goto error;
767
768 had_trak = TRUE;
769 g_array_append_val (moov->trak, trak);
770 break;
771 }
772 default:
773 gst_byte_reader_skip (reader, size - header_size);
774 break;
775 }
776 }
777
778 if (!had_trak)
779 goto error;
780
781 return moov;
782
783 error:
784 gst_isoff_moov_box_free (moov);
785 return NULL;
786 }
787
788 void
gst_isoff_moov_box_free(GstMoovBox * moov)789 gst_isoff_moov_box_free (GstMoovBox * moov)
790 {
791 g_array_free (moov->trak, TRUE);
792 g_free (moov);
793 }
794
795 void
gst_isoff_sidx_parser_init(GstSidxParser * parser)796 gst_isoff_sidx_parser_init (GstSidxParser * parser)
797 {
798 parser->status = GST_ISOFF_SIDX_PARSER_INIT;
799 parser->cumulative_entry_size = 0;
800 parser->sidx.entries = NULL;
801 parser->sidx.entries_count = 0;
802 }
803
804 void
gst_isoff_sidx_parser_clear(GstSidxParser * parser)805 gst_isoff_sidx_parser_clear (GstSidxParser * parser)
806 {
807 g_free (parser->sidx.entries);
808 memset (parser, 0, sizeof (*parser));
809
810 gst_isoff_sidx_parser_init (parser);
811 }
812
813 static void
gst_isoff_parse_sidx_entry(GstSidxBoxEntry * entry,GstByteReader * reader)814 gst_isoff_parse_sidx_entry (GstSidxBoxEntry * entry, GstByteReader * reader)
815 {
816 guint32 aux;
817
818 aux = gst_byte_reader_get_uint32_be_unchecked (reader);
819 entry->ref_type = aux >> 31;
820 entry->size = aux & 0x7FFFFFFF;
821 entry->duration = gst_byte_reader_get_uint32_be_unchecked (reader);
822 aux = gst_byte_reader_get_uint32_be_unchecked (reader);
823 entry->starts_with_sap = aux >> 31;
824 entry->sap_type = ((aux >> 28) & 0x7);
825 entry->sap_delta_time = aux & 0xFFFFFFF;
826 }
827
828 GstIsoffParserResult
gst_isoff_sidx_parser_parse(GstSidxParser * parser,GstByteReader * reader,guint * consumed)829 gst_isoff_sidx_parser_parse (GstSidxParser * parser,
830 GstByteReader * reader, guint * consumed)
831 {
832 GstIsoffParserResult res = GST_ISOFF_PARSER_OK;
833 gsize remaining;
834
835 INITIALIZE_DEBUG_CATEGORY;
836 switch (parser->status) {
837 case GST_ISOFF_SIDX_PARSER_INIT:
838 /* Try again once we have enough data for the FullBox header */
839 if (gst_byte_reader_get_remaining (reader) < 4) {
840 gst_byte_reader_set_pos (reader, 0);
841 break;
842 }
843 parser->sidx.version = gst_byte_reader_get_uint8_unchecked (reader);
844 parser->sidx.flags = gst_byte_reader_get_uint24_le_unchecked (reader);
845
846 parser->status = GST_ISOFF_SIDX_PARSER_HEADER;
847
848 case GST_ISOFF_SIDX_PARSER_HEADER:
849 remaining = gst_byte_reader_get_remaining (reader);
850 if (remaining < 12 + (parser->sidx.version == 0 ? 8 : 16)) {
851 break;
852 }
853
854 parser->sidx.ref_id = gst_byte_reader_get_uint32_be_unchecked (reader);
855 parser->sidx.timescale = gst_byte_reader_get_uint32_be_unchecked (reader);
856 if (parser->sidx.version == 0) {
857 parser->sidx.earliest_pts =
858 gst_byte_reader_get_uint32_be_unchecked (reader);
859 parser->sidx.first_offset =
860 gst_byte_reader_get_uint32_be_unchecked (reader);
861 } else {
862 parser->sidx.earliest_pts =
863 gst_byte_reader_get_uint64_be_unchecked (reader);
864 parser->sidx.first_offset =
865 gst_byte_reader_get_uint64_be_unchecked (reader);
866 }
867 /* skip 2 reserved bytes */
868 gst_byte_reader_skip_unchecked (reader, 2);
869 parser->sidx.entries_count =
870 gst_byte_reader_get_uint16_be_unchecked (reader);
871
872 GST_LOG ("Timescale: %" G_GUINT32_FORMAT, parser->sidx.timescale);
873 GST_LOG ("Earliest pts: %" G_GUINT64_FORMAT, parser->sidx.earliest_pts);
874 GST_LOG ("First offset: %" G_GUINT64_FORMAT, parser->sidx.first_offset);
875
876 parser->cumulative_pts =
877 gst_util_uint64_scale_int_round (parser->sidx.earliest_pts,
878 GST_SECOND, parser->sidx.timescale);
879
880 if (parser->sidx.entries_count) {
881 parser->sidx.entries =
882 g_malloc (sizeof (GstSidxBoxEntry) * parser->sidx.entries_count);
883 }
884 parser->sidx.entry_index = 0;
885
886 parser->status = GST_ISOFF_SIDX_PARSER_DATA;
887
888 case GST_ISOFF_SIDX_PARSER_DATA:
889 while (parser->sidx.entry_index < parser->sidx.entries_count) {
890 GstSidxBoxEntry *entry =
891 &parser->sidx.entries[parser->sidx.entry_index];
892
893 remaining = gst_byte_reader_get_remaining (reader);
894 if (remaining < 12)
895 break;
896
897 entry->offset = parser->cumulative_entry_size;
898 entry->pts = parser->cumulative_pts;
899 gst_isoff_parse_sidx_entry (entry, reader);
900 entry->duration = gst_util_uint64_scale_int_round (entry->duration,
901 GST_SECOND, parser->sidx.timescale);
902 parser->cumulative_entry_size += entry->size;
903 parser->cumulative_pts += entry->duration;
904
905 GST_LOG ("Sidx entry %d) offset: %" G_GUINT64_FORMAT ", pts: %"
906 GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT " - size %"
907 G_GUINT32_FORMAT, parser->sidx.entry_index, entry->offset,
908 GST_TIME_ARGS (entry->pts), GST_TIME_ARGS (entry->duration),
909 entry->size);
910
911 parser->sidx.entry_index++;
912 }
913
914 if (parser->sidx.entry_index == parser->sidx.entries_count)
915 parser->status = GST_ISOFF_SIDX_PARSER_FINISHED;
916 else
917 break;
918 case GST_ISOFF_SIDX_PARSER_FINISHED:
919 parser->sidx.entry_index = 0;
920 res = GST_ISOFF_PARSER_DONE;
921 break;
922 }
923
924 *consumed = gst_byte_reader_get_pos (reader);
925
926 return res;
927 }
928
929 GstIsoffParserResult
gst_isoff_sidx_parser_add_buffer(GstSidxParser * parser,GstBuffer * buffer,guint * consumed)930 gst_isoff_sidx_parser_add_buffer (GstSidxParser * parser, GstBuffer * buffer,
931 guint * consumed)
932 {
933 GstIsoffParserResult res = GST_ISOFF_PARSER_OK;
934 GstByteReader reader;
935 GstMapInfo info;
936 guint32 fourcc;
937
938 INITIALIZE_DEBUG_CATEGORY;
939 if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
940 *consumed = 0;
941 return GST_ISOFF_PARSER_ERROR;
942 }
943
944 gst_byte_reader_init (&reader, info.data, info.size);
945
946 if (parser->status == GST_ISOFF_SIDX_PARSER_INIT) {
947 if (!gst_isoff_parse_box_header (&reader, &fourcc, NULL, NULL,
948 &parser->size))
949 goto done;
950
951 if (fourcc != GST_ISOFF_FOURCC_SIDX) {
952 res = GST_ISOFF_PARSER_UNEXPECTED;
953 gst_byte_reader_set_pos (&reader, 0);
954 goto done;
955 }
956
957 if (parser->size == 0) {
958 res = GST_ISOFF_PARSER_ERROR;
959 gst_byte_reader_set_pos (&reader, 0);
960 goto done;
961 }
962
963 /* Try again once we have enough data for the FullBox header */
964 if (gst_byte_reader_get_remaining (&reader) < 4) {
965 gst_byte_reader_set_pos (&reader, 0);
966 goto done;
967 }
968 }
969
970 res = gst_isoff_sidx_parser_parse (parser, &reader, consumed);
971
972 done:
973 gst_buffer_unmap (buffer, &info);
974 return res;
975 }
976