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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, 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  *
41  * Parses the mpeg-ts PES header located in @data into the @res.
42  *
43  * Returns: #PES_PARSING_OK if the header was fully parsed and valid,
44  * #PES_PARSING_BAD if the header is invalid, or #PES_PARSING_NEED_MORE if more data
45  * is needed to properly parse the header.
46  */
47 PESParsingResult
mpegts_parse_pes_header(const guint8 * data,gsize length,PESHeader * res)48 mpegts_parse_pes_header (const guint8 * data, gsize length, PESHeader * res)
49 {
50   PESParsingResult ret = PES_PARSING_NEED_MORE;
51   gsize origlength = length;
52   const guint8 *origdata = data;
53   guint32 val32;
54   guint8 val8, flags;
55 
56   g_assert (res != NULL);
57 
58   /* The smallest valid PES header is 6 bytes (prefix + stream_id + length) */
59   if (G_UNLIKELY (length < 6))
60     goto need_more_data;
61 
62   val32 = GST_READ_UINT32_BE (data);
63   data += 4;
64   length -= 4;
65   if (G_UNLIKELY ((val32 & 0xffffff00) != 0x00000100))
66     goto bad_start_code;
67 
68   /* Clear the header */
69   memset (res, 0, sizeof (PESHeader));
70   res->PTS = -1;
71   res->DTS = -1;
72   res->ESCR = -1;
73 
74   res->stream_id = val32 & 0x000000ff;
75 
76   res->packet_length = GST_READ_UINT16_BE (data);
77   if (res->packet_length)
78     res->packet_length += 6;
79   data += 2;
80   length -= 2;
81 
82   GST_LOG ("stream_id : 0x%08x , packet_length : %d", res->stream_id,
83       res->packet_length);
84 
85   /* Jump if we don't need to parse anything more */
86   if (G_UNLIKELY (res->stream_id == 0xbc || res->stream_id == 0xbe
87           || res->stream_id == 0xbf || (res->stream_id >= 0xf0
88               && res->stream_id <= 0xf2) || res->stream_id == 0xff
89           || res->stream_id == 0xf8))
90     goto done_parsing;
91 
92   if (G_UNLIKELY (length < 3))
93     goto need_more_data;
94 
95   /* '10'                             2
96    * PES_scrambling_control           2
97    * PES_priority                     1
98    * data_alignment_indicator         1
99    * copyright                        1
100    * original_or_copy                 1 */
101   val8 = *data++;
102   if (G_UNLIKELY ((val8 & 0xc0) != 0x80))
103     goto bad_marker_1;
104   res->scrambling_control = (val8 >> 4) & 0x3;
105   res->flags = val8 & 0xf;
106 
107   GST_LOG ("scrambling_control 0x%0x", res->scrambling_control);
108   GST_LOG ("flags_1: %s%s%s%s%s",
109       val8 & 0x08 ? "priority " : "",
110       val8 & 0x04 ? "data_alignment " : "",
111       val8 & 0x02 ? "copyright " : "",
112       val8 & 0x01 ? "original_or_copy " : "", val8 & 0x0f ? "" : "<none>");
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_LOG ("flags_2: %s%s%s%s%s%s%s%s%s",
123       flags & 0x80 ? "PTS " : "",
124       flags & 0x40 ? "DTS " : "",
125       flags & 0x20 ? "ESCR" : "",
126       flags & 0x10 ? "ES_rate " : "",
127       flags & 0x08 ? "DSM_trick_mode " : "",
128       flags & 0x04 ? "additional_copy_info " : "",
129       flags & 0x02 ? "CRC " : "",
130       flags & 0x01 ? "extension " : "", flags ? "" : "<none>");
131 
132   /* PES_header_data_length           8 */
133   res->header_size = *data++;
134   length -= 3;
135   if (G_UNLIKELY (length < res->header_size))
136     goto need_more_data;
137 
138   res->header_size += 9;        /* We add 9 since that's the offset
139                                  * of the field in the header*/
140   GST_DEBUG ("header_size : %d", res->header_size);
141 
142   /* PTS/DTS */
143 
144   /* PTS_DTS_flags == 0x01 is invalid */
145   if (G_UNLIKELY ((flags >> 6) == 0x01)) {
146     GST_WARNING ("Invalid PTS_DTS_flag (0x01 is forbidden)");
147   }
148 
149   if ((flags & 0x80) == 0x80) {
150     /* PTS */
151     if (G_UNLIKELY (length < 5))
152       goto need_more_data;
153 
154     READ_TS (data, res->PTS, bad_PTS_value);
155     length -= 5;
156     GST_LOG ("PTS %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
157         res->PTS, GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (res->PTS)));
158 
159   }
160 
161   if ((flags & 0x40) == 0x40) {
162     /* DTS */
163     if (G_UNLIKELY (length < 5))
164       goto need_more_data;
165 
166     READ_TS (data, res->DTS, bad_DTS_value);
167     length -= 5;
168 
169     GST_LOG ("DTS %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
170         res->DTS, GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (res->DTS)));
171   }
172 
173   if (flags & 0x20) {
174     /* ESCR */
175     if (G_UNLIKELY (length < 5))
176       goto need_more_data;
177     READ_TS (data, res->ESCR, bad_ESCR_value);
178     length -= 5;
179 
180     GST_LOG ("ESCR %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
181         res->ESCR, GST_TIME_ARGS (PCRTIME_TO_GSTTIME (res->ESCR)));
182   }
183 
184   if (flags & 0x10) {
185     /* ES_rate */
186     if (G_UNLIKELY (length < 3))
187       goto need_more_data;
188     val32 = GST_READ_UINT32_BE (data);
189     data += 3;
190     length -= 3;
191     if (G_UNLIKELY ((val32 & 0x80000100) != 0x80000100))
192       goto bad_ES_rate;
193     res->ES_rate = ((val32 >> 9) & 0x003fffff) * 50;
194     GST_LOG ("ES_rate : %d", res->ES_rate);
195   }
196 
197   if (flags & 0x08) {
198     /* DSM trick mode */
199     if (G_UNLIKELY (length < 1))
200       goto need_more_data;
201     val8 = *data++;
202     length -= 1;
203 
204     res->trick_mode = val8 >> 5;
205     GST_LOG ("trick_mode 0x%x", res->trick_mode);
206 
207     switch (res->trick_mode) {
208       case PES_TRICK_MODE_FAST_FORWARD:
209       case PES_TRICK_MODE_FAST_REVERSE:
210         res->intra_slice_refresh = (val8 >> 2) & 0x1;
211         res->frequency_truncation = val8 & 0x3;
212         /* passthrough */
213       case PES_TRICK_MODE_FREEZE_FRAME:
214         res->field_id = (val8 >> 3) & 0x3;
215         break;
216       case PES_TRICK_MODE_SLOW_MOTION:
217       case PES_TRICK_MODE_SLOW_REVERSE:
218         res->rep_cntrl = val8 & 0x1f;
219         break;
220       default:
221         break;
222     }
223   }
224 
225   if (flags & 0x04) {
226     /* additional copy info */
227     if (G_UNLIKELY (length < 1))
228       goto need_more_data;
229     val8 = *data++;
230     length -= 1;
231 
232     if (G_UNLIKELY (!(val8 & 0x80)))
233       goto bad_original_copy_info_marker;
234     res->additional_copy_info = val8 & 0x7f;
235     GST_LOG ("additional_copy_info : 0x%x", res->additional_copy_info);
236   }
237 
238   if (flags & 0x02) {
239     /* CRC */
240     if (G_UNLIKELY (length < 2))
241       goto need_more_data;
242     res->previous_PES_packet_CRC = GST_READ_UINT16_BE (data);
243     GST_LOG ("previous_PES_packet_CRC : 0x%x", res->previous_PES_packet_CRC);
244     data += 2;
245     length -= 2;
246   }
247 
248 
249   /* jump if we don't have a PES extension */
250   if (!(flags & 0x01))
251     goto stuffing_byte;
252 
253   if (G_UNLIKELY (length < 1))
254     goto need_more_data;
255 
256   /* PES extension */
257   flags = *data++;
258   length -= 1;
259   GST_DEBUG ("PES_extension_flag: %s%s%s%s%s%s",
260       flags & 0x80 ? "PES_private_data " : "",
261       flags & 0x40 ? "pack_header_field " : "",
262       flags & 0x20 ? "program_packet_sequence_counter " : "",
263       flags & 0x10 ? "P-STD_buffer " : "",
264       flags & 0x01 ? "PES_extension_flag_2" : "", flags & 0xf1 ? "" : "<none>");
265 
266   if (flags & 0x80) {
267     /* PES_private data */
268     if (G_UNLIKELY (length < 16))
269       goto need_more_data;
270     res->private_data = data;
271     GST_MEMDUMP ("private_data", data, 16);
272     data += 16;
273     length -= 16;
274   }
275 
276   if (flags & 0x40) {
277     /* pack_header_field */
278     if (G_UNLIKELY (length < 1))
279       goto need_more_data;
280 
281     val8 = *data++;
282     length -= 1;
283     if (G_UNLIKELY (length < val8))
284       goto need_more_data;
285     res->pack_header_size = val8;
286     res->pack_header = data;
287 
288     GST_MEMDUMP ("Pack header data", res->pack_header, res->pack_header_size);
289 
290     data += val8;
291     length -= val8;
292   }
293 
294   if (flags & 0x20) {
295     /* sequence counter */
296     if (G_UNLIKELY (length < 2))
297       goto need_more_data;
298 
299     val8 = *data++;
300     if (G_UNLIKELY ((val8 & 0x80) != 0x80))
301       goto bad_sequence_marker1;
302     res->program_packet_sequence_counter = val8 & 0x7f;
303     GST_LOG ("program_packet_sequence_counter %d",
304         res->program_packet_sequence_counter);
305 
306     val8 = *data++;
307     if (G_UNLIKELY ((val8 & 0x80) != 0x80))
308       goto bad_sequence_marker2;
309     res->MPEG1_MPEG2_identifier = (val8 >> 6) & 0x1;
310     res->original_stuff_length = val8 & 0x3f;
311     GST_LOG ("MPEG1_MPEG2_identifier : %d , original_stuff_length : %d",
312         res->MPEG1_MPEG2_identifier, res->original_stuff_length);
313     length -= 2;
314   }
315 
316   if (flags & 0x10) {
317     /* P-STD
318      * '01'               :  2 bits
319      * P-STD_buffer_scale :  1 bit
320      * P-STD_buffer_size  : 13 bits
321      * */
322     if (G_UNLIKELY (length < 2))
323       goto need_more_data;
324     val8 = *data;
325     if (G_UNLIKELY ((val8 & 0xc0) != 0x40))
326       goto bad_P_STD_marker;
327     /* If P-STD_buffer_scale is 0
328      *   multiply by 128 (i.e. << 7),
329      * else
330      *   multiply by 1024 (i.e. << 10)
331      */
332     res->P_STD_buffer_size =
333         (GST_READ_UINT16_BE (data) & 0x1fff) << ((val8 & 0x20) ? 10 : 7);
334     GST_LOG ("P_STD_buffer_size : %d", res->P_STD_buffer_size);
335     data += 2;
336     length -= 2;
337   }
338 
339   /* jump if we don't have a PES 2nd extension */
340   if (!(flags & 0x01))
341     goto stuffing_byte;
342 
343   /* Extension flag 2 */
344   if (G_UNLIKELY (length < 1))
345     goto need_more_data;
346 
347   val8 = *data++;
348   length -= 1;
349 
350   if (!(val8 & 0x80))
351     goto bad_extension_marker_2;
352 
353   res->extension_field_length = val8 & 0x7f;
354 
355   /* Skip empty extensions */
356   if (G_UNLIKELY (res->extension_field_length == 0))
357     goto stuffing_byte;
358 
359   if (G_UNLIKELY (length < res->extension_field_length))
360     goto need_more_data;
361 
362   flags = *data++;
363   res->extension_field_length -= 1;
364 
365   if (!(flags & 0x80)) {
366     /* Only valid if stream_id_extension_flag == 0x0 */
367     res->stream_id_extension = flags;
368     GST_LOG ("stream_id_extension : 0x%02x", res->stream_id_extension);
369   } else if (!(flags & 0x01)) {
370     /* Skip broken streams (that use stream_id_extension with highest bit set
371      * for example ...) */
372     if (G_UNLIKELY (res->extension_field_length < 5))
373       goto stuffing_byte;
374 
375     GST_LOG ("TREF field present");
376     data += 5;
377     res->extension_field_length -= 5;
378   }
379 
380   /* Extension field data */
381   if (res->extension_field_length) {
382     res->stream_id_extension_data = data;
383     GST_MEMDUMP ("stream_id_extension_data",
384         res->stream_id_extension_data, res->extension_field_length);
385   }
386 
387 stuffing_byte:
388   /* Go to the expected data start position */
389   data = origdata + res->header_size;
390   length = origlength - res->header_size;
391 
392 done_parsing:
393   GST_DEBUG ("origlength:%" G_GSIZE_FORMAT ", length:%" G_GSIZE_FORMAT,
394       origlength, length);
395 
396   res->header_size = origlength - length;
397   ret = PES_PARSING_OK;
398 
399   return ret;
400 
401   /* Errors */
402 need_more_data:
403   GST_DEBUG ("Not enough data to parse PES header");
404   return ret;
405 
406 bad_start_code:
407   GST_WARNING ("Wrong packet start code 0x%x != 0x000001xx", val32);
408   return PES_PARSING_BAD;
409 
410 bad_marker_1:
411   GST_WARNING ("Wrong '0x10' marker before PES_scrambling_control (0x%02x)",
412       val8);
413   return PES_PARSING_BAD;
414 
415 bad_PTS_value:
416   GST_WARNING ("bad PTS value");
417   return PES_PARSING_BAD;
418 
419 bad_DTS_value:
420   GST_WARNING ("bad DTS value");
421   return PES_PARSING_BAD;
422 
423 bad_ESCR_value:
424   GST_WARNING ("bad ESCR value");
425   return PES_PARSING_BAD;
426 
427 bad_ES_rate:
428   GST_WARNING ("Invalid ES_rate markers 0x%0x", val32);
429   return PES_PARSING_BAD;
430 
431 bad_original_copy_info_marker:
432   GST_WARNING ("Invalid original_copy_info marker bit: 0x%0x", val8);
433   return PES_PARSING_BAD;
434 
435 bad_sequence_marker1:
436   GST_WARNING ("Invalid program_packet_sequence_counter marker 0x%0x", val8);
437   return PES_PARSING_BAD;
438 
439 bad_sequence_marker2:
440   GST_WARNING ("Invalid program_packet_sequence_counter marker 0x%0x", val8);
441   return PES_PARSING_BAD;
442 
443 bad_P_STD_marker:
444   GST_WARNING ("Invalid P-STD_buffer marker 0x%0x", val8);
445   return PES_PARSING_BAD;
446 
447 bad_extension_marker_2:
448   GST_WARNING ("Invalid extension_field_2 marker 0x%0x", val8);
449   return PES_PARSING_BAD;
450 }
451 
452 void
init_pes_parser(void)453 init_pes_parser (void)
454 {
455   GST_DEBUG_CATEGORY_INIT (pes_parser_debug, "pesparser", 0, "MPEG PES parser");
456 }
457