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 
25 #include "qtdemux_debug.h"
26 #include "gstisoff.h"
27 #include <gst/base/gstbytereader.h>
28 
29 #define GST_CAT_DEFAULT qtdemux_debug
30 
31 void
gst_isoff_qt_sidx_parser_init(GstSidxParser * parser)32 gst_isoff_qt_sidx_parser_init (GstSidxParser * parser)
33 {
34   parser->status = GST_ISOFF_QT_SIDX_PARSER_INIT;
35   parser->cumulative_entry_size = 0;
36   parser->sidx.entries = NULL;
37   parser->sidx.entries_count = 0;
38 }
39 
40 void
gst_isoff_qt_sidx_parser_clear(GstSidxParser * parser)41 gst_isoff_qt_sidx_parser_clear (GstSidxParser * parser)
42 {
43   g_free (parser->sidx.entries);
44   parser->sidx.entries = NULL;
45 }
46 
47 static void
gst_isoff_qt_parse_sidx_entry(GstSidxBoxEntry * entry,GstByteReader * reader)48 gst_isoff_qt_parse_sidx_entry (GstSidxBoxEntry * entry, GstByteReader * reader)
49 {
50   guint32 aux;
51 
52   aux = gst_byte_reader_get_uint32_be_unchecked (reader);
53   entry->ref_type = aux >> 31;
54   entry->size = aux & 0x7FFFFFFF;
55   entry->duration = gst_byte_reader_get_uint32_be_unchecked (reader);
56   aux = gst_byte_reader_get_uint32_be_unchecked (reader);
57   entry->starts_with_sap = aux >> 31;
58   entry->sap_type = ((aux >> 28) & 0x7);
59   entry->sap_delta_time = aux & 0xFFFFFFF;
60 }
61 
62 GstIsoffParserResult
gst_isoff_qt_sidx_parser_add_data(GstSidxParser * parser,const guint8 * buffer,gint length,guint * consumed)63 gst_isoff_qt_sidx_parser_add_data (GstSidxParser * parser,
64     const guint8 * buffer, gint length, guint * consumed)
65 {
66   GstIsoffParserResult res = GST_ISOFF_QT_PARSER_OK;
67   GstByteReader reader;
68   gsize remaining;
69   guint32 fourcc;
70 
71   gst_byte_reader_init (&reader, buffer, length);
72 
73   switch (parser->status) {
74     case GST_ISOFF_QT_SIDX_PARSER_INIT:
75       if (gst_byte_reader_get_remaining (&reader) < GST_ISOFF_QT_FULL_BOX_SIZE) {
76         break;
77       }
78 
79       parser->size = gst_byte_reader_get_uint32_be_unchecked (&reader);
80       fourcc = gst_byte_reader_get_uint32_le_unchecked (&reader);
81       if (fourcc != GST_ISOFF_QT_FOURCC_SIDX) {
82         res = GST_ISOFF_QT_PARSER_UNEXPECTED;
83         gst_byte_reader_set_pos (&reader, 0);
84         break;
85       }
86       if (parser->size == 1) {
87         if (gst_byte_reader_get_remaining (&reader) < 12) {
88           gst_byte_reader_set_pos (&reader, 0);
89           break;
90         }
91 
92         parser->size = gst_byte_reader_get_uint64_be_unchecked (&reader);
93       }
94       if (parser->size == 0) {
95         res = GST_ISOFF_QT_PARSER_ERROR;
96         gst_byte_reader_set_pos (&reader, 0);
97         break;
98       }
99       parser->sidx.version = gst_byte_reader_get_uint8_unchecked (&reader);
100       parser->sidx.flags = gst_byte_reader_get_uint24_le_unchecked (&reader);
101 
102       parser->status = GST_ISOFF_QT_SIDX_PARSER_HEADER;
103 
104     case GST_ISOFF_QT_SIDX_PARSER_HEADER:
105       remaining = gst_byte_reader_get_remaining (&reader);
106       if (remaining < 12 + (parser->sidx.version == 0 ? 8 : 16)) {
107         break;
108       }
109 
110       parser->sidx.ref_id = gst_byte_reader_get_uint32_be_unchecked (&reader);
111       parser->sidx.timescale =
112           gst_byte_reader_get_uint32_be_unchecked (&reader);
113       if (parser->sidx.version == 0) {
114         parser->sidx.earliest_pts =
115             gst_byte_reader_get_uint32_be_unchecked (&reader);
116         parser->sidx.first_offset = parser->sidx.earliest_pts =
117             gst_byte_reader_get_uint32_be_unchecked (&reader);
118       } else {
119         parser->sidx.earliest_pts =
120             gst_byte_reader_get_uint64_be_unchecked (&reader);
121         parser->sidx.first_offset =
122             gst_byte_reader_get_uint64_be_unchecked (&reader);
123       }
124       /* skip 2 reserved bytes */
125       gst_byte_reader_skip_unchecked (&reader, 2);
126       parser->sidx.entries_count =
127           gst_byte_reader_get_uint16_be_unchecked (&reader);
128 
129       GST_LOG ("Timescale: %" G_GUINT32_FORMAT, parser->sidx.timescale);
130       GST_LOG ("Earliest pts: %" G_GUINT64_FORMAT, parser->sidx.earliest_pts);
131       GST_LOG ("First offset: %" G_GUINT64_FORMAT, parser->sidx.first_offset);
132 
133       parser->cumulative_pts =
134           gst_util_uint64_scale_int_round (parser->sidx.earliest_pts,
135           GST_SECOND, parser->sidx.timescale);
136 
137       if (parser->sidx.entries_count) {
138         parser->sidx.entries =
139             g_malloc (sizeof (GstSidxBoxEntry) * parser->sidx.entries_count);
140       }
141       parser->sidx.entry_index = 0;
142 
143       parser->status = GST_ISOFF_QT_SIDX_PARSER_DATA;
144 
145     case GST_ISOFF_QT_SIDX_PARSER_DATA:
146       while (parser->sidx.entry_index < parser->sidx.entries_count) {
147         GstSidxBoxEntry *entry =
148             &parser->sidx.entries[parser->sidx.entry_index];
149 
150         remaining = gst_byte_reader_get_remaining (&reader);
151         if (remaining < 12)
152           break;
153 
154         entry->offset = parser->cumulative_entry_size;
155         entry->pts = parser->cumulative_pts;
156         gst_isoff_qt_parse_sidx_entry (entry, &reader);
157         entry->duration = gst_util_uint64_scale_int_round (entry->duration,
158             GST_SECOND, parser->sidx.timescale);
159         parser->cumulative_entry_size += entry->size;
160         parser->cumulative_pts += entry->duration;
161 
162         GST_LOG ("Sidx entry %d) offset: %" G_GUINT64_FORMAT ", pts: %"
163             GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT " - size %"
164             G_GUINT32_FORMAT, parser->sidx.entry_index, entry->offset,
165             GST_TIME_ARGS (entry->pts), GST_TIME_ARGS (entry->duration),
166             entry->size);
167 
168         parser->sidx.entry_index++;
169       }
170 
171       if (parser->sidx.entry_index == parser->sidx.entries_count)
172         parser->status = GST_ISOFF_QT_SIDX_PARSER_FINISHED;
173       else
174         break;
175     case GST_ISOFF_QT_SIDX_PARSER_FINISHED:
176       parser->sidx.entry_index = 0;
177       res = GST_ISOFF_QT_PARSER_DONE;
178       break;
179   }
180 
181   *consumed = gst_byte_reader_get_pos (&reader);
182   return res;
183 }
184 
185 GstIsoffParserResult
gst_isoff_qt_sidx_parser_add_buffer(GstSidxParser * parser,GstBuffer * buffer,guint * consumed)186 gst_isoff_qt_sidx_parser_add_buffer (GstSidxParser * parser, GstBuffer * buffer,
187     guint * consumed)
188 {
189   GstIsoffParserResult res = GST_ISOFF_QT_PARSER_OK;
190   GstMapInfo info;
191 
192   if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
193     *consumed = 0;
194     return GST_ISOFF_QT_PARSER_ERROR;
195   }
196 
197   res =
198       gst_isoff_qt_sidx_parser_add_data (parser, info.data, info.size,
199       consumed);
200 
201   gst_buffer_unmap (buffer, &info);
202   return res;
203 }
204