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