• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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