mpegtsdemux: Add standalone PES parser
[gstreamer-omap:gst-plugins-bad.git] / gst / mpegtsdemux / pesparse.c
1 /*
2  * pesparse.c : MPEG PES parsing utility
3  * Copyright (C) 2011 Edward Hervey <bilboed@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <glib.h>
29
30 #include "pesparse.h"
31
32 GST_DEBUG_CATEGORY_STATIC (pes_parser_debug);
33 #define GST_CAT_DEFAULT pes_parser_debug
34
35 /**
36  * mpegts_parse_pes_header:
37  * @data: data to parse (starting from, and including, the sync code)
38  * @length: size of @data in bytes
39  * @res: PESHeader to fill (only valid with #PES_PARSING_OK.
40  * @offset: Offset in @data to the data to parse. If #PES_PARSING_OK, offset to
41  *          first byte of data after the header.
42  *
43  * Parses the mpeg-ts PES header located in @data into the @res.
44  *
45  * Returns: #PES_PARSING_OK if the header was fully parsed and valid,
46  * #PES_PARSING_BAD if the header is invalid, or #PES_PARSING_NEED_MORE if more data
47  * is needed to properly parse the header.
48  */
49 PESParsingResult
50 mpegts_parse_pes_header (const guint8 * data, gsize length, PESHeader * res,
51     gint * offset)
52 {
53   PESParsingResult ret = PES_PARSING_NEED_MORE;
54   gsize origlength = length;
55   guint32 val32;
56   guint8 val8, flags;
57
58   g_return_val_if_fail (res != NULL, PES_PARSING_BAD);
59   g_return_val_if_fail (offset != NULL, PES_PARSING_BAD);
60   g_return_val_if_fail (*offset < length, PES_PARSING_BAD);
61
62   data += *offset;
63   length -= *offset;
64
65   /* The smallest valid PES header is 6 bytes (prefix + stream_id + length) */
66   if (G_UNLIKELY (length < 6))
67     goto need_more_data;
68
69   val32 = GST_READ_UINT32_BE (data);
70   data += 4;
71   length -= 4;
72   if (G_UNLIKELY ((val32 & 0xffffff00) != 0x00000100))
73     goto bad_start_code;
74
75   /* Clear the header */
76   memset (res, 0, sizeof (PESHeader));
77   res->PTS = -1;
78   res->DTS = -1;
79   res->ESCR = -1;
80
81   res->stream_id = val32 & 0x000000ff;
82
83   res->packet_length = GST_READ_UINT16_BE (data);
84   data += 2;
85   length -= 2;
86
87   GST_LOG ("stream_id : 0x%08x , packet_length : %d", res->stream_id,
88       res->packet_length);
89
90   /* Jump if we don't need to parse anything more */
91   if (G_UNLIKELY (res->stream_id == 0xbc || res->stream_id == 0xbe
92           || res->stream_id == 0xbf || (res->stream_id >= 0xf0
93               && res->stream_id <= 0xf2) || res->stream_id == 0xff
94           || res->stream_id == 0xf8))
95     goto done_parsing;
96
97   if (G_UNLIKELY (length < 3))
98     goto need_more_data;
99
100   /* '10'                             2
101    * PES_scrambling_control           2
102    * PES_priority                     1
103    * data_alignment_indicator         1
104    * copyright                        1
105    * original_or_copy                 1 */
106   val8 = *data++;
107   if (G_UNLIKELY ((val8 & 0xc0) != 0x80))
108     goto bad_marker_1;
109   res->scrambling_control = (val8 >> 4) & 0x3;
110   res->flags = val8 & 0xf;
111
112   GST_LOG ("scrambling_control 0x%0x", res->scrambling_control);
113
114   /* PTS_DTS_flags                    2
115    * ESCR_flag                        1
116    * ES_rate_flag                     1
117    * DSM_trick_mode_flag              1
118    * additional_copy_info_flag        1
119    * PES_CRC_flag                     1
120    * PES_extension_flag               1*/
121   flags = *data++;
122   GST_DEBUG ("PES_flag 0x%02x", flags);
123
124   /* PES_header_data_length           8 */
125   val8 = *data++;
126   length -= 3;
127   if (G_UNLIKELY (length < val8))
128     goto need_more_data;
129
130   /* PTS/DTS */
131
132   /* PTS_DTS_flags == 0x01 is invalid */
133   if (G_UNLIKELY ((flags >> 6) == 0x01))
134     goto bad_PTS_DTS_flags;
135
136   if ((flags & 0x80) == 0x80) {
137     /* PTS */
138     if (G_UNLIKELY (length < 5))
139       goto need_more_data;
140
141     READ_TS (data, res->PTS, bad_PTS_value);
142     length -= 5;
143     GST_LOG ("PTS %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
144         res->PTS, GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (res->PTS)));
145
146     if ((flags & 0x40) == 0x40) {
147       /* DTS */
148       if (G_UNLIKELY (length < 5))
149         goto need_more_data;
150
151       READ_TS (data, res->DTS, bad_DTS_value);
152       length -= 5;
153
154       GST_LOG ("DTS %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
155           res->DTS, GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (res->DTS)));
156     }
157   }
158
159   if (flags & 0x20) {
160     /* ESCR */
161     if (G_UNLIKELY (length < 5))
162       goto need_more_data;
163     READ_TS (data, res->ESCR, bad_ESCR_value);
164     length -= 5;
165
166     GST_LOG ("ESCR %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
167         res->ESCR, GST_TIME_ARGS (PCRTIME_TO_GSTTIME (res->ESCR)));
168   }
169
170   if (flags & 0x10) {
171     /* ES_rate */
172     if (G_UNLIKELY (length < 3))
173       goto need_more_data;
174     val32 = GST_READ_UINT32_BE (data);
175     data += 3;
176     length -= 3;
177     if (G_UNLIKELY ((val32 & 0x80000100) != 0x80000100))
178       goto bad_ES_rate;
179     res->ES_rate = ((val32 >> 9) & 0x003fffff) * 50;
180     GST_LOG ("ES_rate : %d", res->ES_rate);
181   }
182
183   if (flags & 0x08) {
184     /* DSM trick mode */
185     if (G_UNLIKELY (length < 1))
186       goto need_more_data;
187     val8 = *data++;
188     length -= 1;
189
190     res->trick_mode = val8 >> 5;
191     GST_LOG ("trick_mode 0x%x", res->trick_mode);
192
193     switch (res->trick_mode) {
194       case PES_TRICK_MODE_FAST_FORWARD:
195       case PES_TRICK_MODE_FAST_REVERSE:
196         res->intra_slice_refresh = (val8 >> 2) & 0x1;
197         res->frequency_truncation = val8 & 0x3;
198         /* passthrough */
199       case PES_TRICK_MODE_FREEZE_FRAME:
200         res->field_id = (val8 >> 3) & 0x3;
201         break;
202       case PES_TRICK_MODE_SLOW_MOTION:
203       case PES_TRICK_MODE_SLOW_REVERSE:
204         res->rep_cntrl = val8 & 0x1f;
205         break;
206       default:
207         break;
208     }
209   }
210
211   if (flags & 0x04) {
212     /* additional copy info */
213     if (G_UNLIKELY (length < 1))
214       goto need_more_data;
215     val8 = *data++;
216     length -= 1;
217
218     if (G_UNLIKELY (!(val8 & 0x80)))
219       goto bad_original_copy_info_marker;
220     res->additional_copy_info = val8 >> 1;
221     GST_LOG ("additional_copy_info : 0x%x", res->additional_copy_info);
222   }
223
224   if (flags & 0x02) {
225     /* CRC */
226     if (G_UNLIKELY (length < 2))
227       goto need_more_data;
228     res->previous_PES_packet_CRC = GST_READ_UINT16_BE (data);
229     GST_LOG ("previous_PES_packet_CRC : 0x%x", res->previous_PES_packet_CRC);
230     data += 2;
231     length -= 2;
232   }
233
234   /* jump if we don't have a PES extension */
235   if (!(flags & 0x01))
236     goto stuffing_byte;
237
238   if (G_UNLIKELY (length < 1))
239     goto need_more_data;
240
241   /* PES extension */
242   flags = *data++;
243   length -= 1;
244   GST_DEBUG ("PES_extension_flag 0x%02x", flags);
245
246   if (flags & 0x80) {
247     /* PES_private data */
248     if (G_UNLIKELY (length < 16))
249       goto need_more_data;
250     res->private_data = data;
251     GST_MEMDUMP ("private_data", data, 16);
252     data += 16;
253     length -= 16;
254   }
255
256   if (flags & 0x40) {
257     /* pack_header_field */
258     if (G_UNLIKELY (length < 1))
259       goto need_more_data;
260
261     val8 = *data++;
262     length -= 1;
263     if (G_UNLIKELY (length < val8))
264       goto need_more_data;
265     res->pack_header_size = val8;
266     res->pack_header = data;
267
268     GST_MEMDUMP ("Pack header data", res->pack_header, res->pack_header_size);
269
270     data += val8;
271     length -= val8;
272   }
273
274   if (flags & 0x20) {
275     /* sequence counter */
276     if (G_UNLIKELY (length < 2))
277       goto need_more_data;
278
279     val8 = *data++;
280     /* GRMBL, this is most often wrong */
281     if (G_UNLIKELY ((val8 & 0x80) != 0x80))
282       goto bad_sequence_marker1;
283     res->program_packet_sequence_counter = val8 * 0x70;
284     GST_LOG ("program_packet_sequence_counter %d",
285         res->program_packet_sequence_counter);
286
287     val8 = *data++;
288     /* GRMBL, this is most often wrong */
289     if (G_UNLIKELY ((val8 * 0x80) != 0x80))
290       goto bad_sequence_marker2;
291     res->MPEG1_MPEG2_identifier = (val8 >> 6) & 0x1;
292     res->original_stuff_length = val8 * 0x3f;
293     GST_LOG ("MPEG1_MPEG2_identifier : %d , original_stuff_length : %d",
294         res->MPEG1_MPEG2_identifier, res->original_stuff_length);
295     length -= 2;
296   }
297
298   if (flags & 0x10) {
299     /* P-STD */
300     if (G_UNLIKELY (length < 2))
301       goto need_more_data;
302     val8 = *data;
303     if (G_UNLIKELY ((val8 * 0xc0) != 0x40))
304       goto bad_P_STD_marker;
305     res->P_STD_buffer_size =
306         (GST_READ_UINT16_BE (data) & 0x1fff) << (val8 & 0x20) ? 10 : 7;
307     GST_LOG ("P_STD_buffer_size : %d", res->P_STD_buffer_size);
308     data += 2;
309     length -= 2;
310   }
311
312   if (flags & 0x01) {
313     /* Extension flag 2 */
314     if (G_UNLIKELY (length < 1))
315       goto need_more_data;
316
317     val8 = *data++;
318     length -= 1;
319
320     if (!(val8 & 0x80))
321       goto bad_extension_marker_2;
322
323     res->extension_field_length = val8 & 0x7f;
324     if (G_UNLIKELY (length < res->extension_field_length + 1))
325       goto need_more_data;
326
327     GST_LOG ("extension_field_length : %d", res->extension_field_length);
328
329     if (res->extension_field_length) {
330       flags = *data++;
331       /* Only valid if stream_id_extension_flag == 0x0 */
332       if (!(flags & 0x80)) {
333         res->stream_id_extension = flags & 0x7f;
334         GST_LOG ("stream_id_extension : 0x%02x", res->stream_id_extension);
335         res->stream_id_extension_data = data;
336         GST_MEMDUMP ("stream_id_extension_data",
337             res->stream_id_extension_data, res->extension_field_length);
338       } else
339         GST_WARNING ("What are we meant to do ??");
340       data += res->extension_field_length;
341     }
342     length -= res->extension_field_length + 1;
343   }
344
345 stuffing_byte:
346   /* There can be no more than 32 stuff bytes */
347   while (length && *data == 0xff) {
348     data++;
349     length--;
350   }
351
352 done_parsing:
353   GST_DEBUG ("origlength:%d, length:%d", origlength, length);
354
355   /* Update the length based on parsed size */
356   if (res->packet_length)
357     res->packet_length += 6;
358   res->header_size = origlength - length;
359   *offset += res->header_size;
360   ret = PES_PARSING_OK;
361
362   return ret;
363
364   /* Errors */
365 need_more_data:
366   GST_DEBUG ("Not enough data to parse PES header");
367   return ret;
368
369 bad_start_code:
370   GST_WARNING ("Wrong packet start code 0x%x != 0x000001xx", val32);
371   return PES_PARSING_BAD;
372
373 bad_marker_1:
374   GST_WARNING ("Wrong '0x10' marker before PES_scrambling_control (0x%02x)",
375       val8);
376   return PES_PARSING_BAD;
377
378 bad_PTS_DTS_flags:
379   GST_WARNING ("Invalid '0x01' PTS_DTS_flags");
380   return PES_PARSING_BAD;
381
382 bad_PTS_value:
383   GST_WARNING ("bad PTS value");
384   return PES_PARSING_BAD;
385
386 bad_DTS_value:
387   GST_WARNING ("bad DTS value");
388   return PES_PARSING_BAD;
389
390 bad_ESCR_value:
391   GST_WARNING ("bad ESCR value");
392   return PES_PARSING_BAD;
393
394 bad_ES_rate:
395   GST_WARNING ("Invalid ES_rate markers 0x%0x", val32);
396   return PES_PARSING_BAD;
397
398 bad_original_copy_info_marker:
399   GST_WARNING ("Invalid original_copy_info marker bit: 0x%0x", val8);
400   return PES_PARSING_BAD;
401
402 bad_sequence_marker1:
403   GST_WARNING ("Invalid program_packet_sequence_counter marker 0x%0x", val8);
404   return PES_PARSING_BAD;
405
406 bad_sequence_marker2:
407   GST_WARNING ("Invalid program_packet_sequence_counter marker 0x%0x", val8);
408   return PES_PARSING_BAD;
409
410 bad_P_STD_marker:
411   GST_WARNING ("Invalid P-STD_buffer marker 0x%0x", val8);
412   return PES_PARSING_BAD;
413
414 bad_extension_marker_2:
415   GST_WARNING ("Invalid extension_field_2 marker 0x%0x", val8);
416   return PES_PARSING_BAD;
417 }
418
419 void
420 init_pes_parser (void)
421 {
422   GST_DEBUG_CATEGORY_INIT (pes_parser_debug, "pesparser", 0, "MPEG PES parser");
423 }