summaryrefslogtreecommitdiff
path: root/gst
diff options
context:
space:
mode:
authorEdward Hervey <bilboed@bilboed.com>2011-07-17 14:17:10 +0200
committerEdward Hervey <bilboed@bilboed.com>2011-07-18 09:13:14 +0200
commit6f9a6cc2badd5ca24586088a7c9b1d45a6ff2f34 (patch)
treedb83dbb2ff5a03e60c42d7e46b5e86df2701113b /gst
parent42cf7d016c3b6466db2322200f40a641512175fc (diff)
downloadgstreamer-plugins-bad-6f9a6cc2badd5ca24586088a7c9b1d45a6ff2f34.tar.gz
mpegtsdemux: Add standalone PES parser
Diffstat (limited to 'gst')
-rw-r--r--gst/mpegtsdemux/Makefile.am6
-rw-r--r--gst/mpegtsdemux/pesparse.c423
-rw-r--r--gst/mpegtsdemux/pesparse.h112
3 files changed, 539 insertions, 2 deletions
diff --git a/gst/mpegtsdemux/Makefile.am b/gst/mpegtsdemux/Makefile.am
index 7d3e66325..55b57d960 100644
--- a/gst/mpegtsdemux/Makefile.am
+++ b/gst/mpegtsdemux/Makefile.am
@@ -7,7 +7,8 @@ libgstmpegtsdemux_la_SOURCES = \
mpegtspacketizer.c \
mpegtsparse.c \
payload_parsers.c \
- tsdemux.c
+ tsdemux.c \
+ pesparse.c
libgstmpegtsdemux_la_CFLAGS = \
$(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \
@@ -25,7 +26,8 @@ noinst_HEADERS = \
mpegtspacketizer.h \
mpegtsparse.h \
payload_parsers.h \
- tsdemux.h
+ tsdemux.h \
+ pesparse.h
Android.mk: Makefile.am $(BUILT_SOURCES)
androgenizer \
diff --git a/gst/mpegtsdemux/pesparse.c b/gst/mpegtsdemux/pesparse.c
new file mode 100644
index 000000000..902dead99
--- /dev/null
+++ b/gst/mpegtsdemux/pesparse.c
@@ -0,0 +1,423 @@
+/*
+ * pesparse.c : MPEG PES parsing utility
+ * Copyright (C) 2011 Edward Hervey <bilboed@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "pesparse.h"
+
+GST_DEBUG_CATEGORY_STATIC (pes_parser_debug);
+#define GST_CAT_DEFAULT pes_parser_debug
+
+/**
+ * mpegts_parse_pes_header:
+ * @data: data to parse (starting from, and including, the sync code)
+ * @length: size of @data in bytes
+ * @res: PESHeader to fill (only valid with #PES_PARSING_OK.
+ * @offset: Offset in @data to the data to parse. If #PES_PARSING_OK, offset to
+ * first byte of data after the header.
+ *
+ * Parses the mpeg-ts PES header located in @data into the @res.
+ *
+ * Returns: #PES_PARSING_OK if the header was fully parsed and valid,
+ * #PES_PARSING_BAD if the header is invalid, or #PES_PARSING_NEED_MORE if more data
+ * is needed to properly parse the header.
+ */
+PESParsingResult
+mpegts_parse_pes_header (const guint8 * data, gsize length, PESHeader * res,
+ gint * offset)
+{
+ PESParsingResult ret = PES_PARSING_NEED_MORE;
+ gsize origlength = length;
+ guint32 val32;
+ guint8 val8, flags;
+
+ g_return_val_if_fail (res != NULL, PES_PARSING_BAD);
+ g_return_val_if_fail (offset != NULL, PES_PARSING_BAD);
+ g_return_val_if_fail (*offset < length, PES_PARSING_BAD);
+
+ data += *offset;
+ length -= *offset;
+
+ /* The smallest valid PES header is 6 bytes (prefix + stream_id + length) */
+ if (G_UNLIKELY (length < 6))
+ goto need_more_data;
+
+ val32 = GST_READ_UINT32_BE (data);
+ data += 4;
+ length -= 4;
+ if (G_UNLIKELY ((val32 & 0xffffff00) != 0x00000100))
+ goto bad_start_code;
+
+ /* Clear the header */
+ memset (res, 0, sizeof (PESHeader));
+ res->PTS = -1;
+ res->DTS = -1;
+ res->ESCR = -1;
+
+ res->stream_id = val32 & 0x000000ff;
+
+ res->packet_length = GST_READ_UINT16_BE (data);
+ data += 2;
+ length -= 2;
+
+ GST_LOG ("stream_id : 0x%08x , packet_length : %d", res->stream_id,
+ res->packet_length);
+
+ /* Jump if we don't need to parse anything more */
+ if (G_UNLIKELY (res->stream_id == 0xbc || res->stream_id == 0xbe
+ || res->stream_id == 0xbf || (res->stream_id >= 0xf0
+ && res->stream_id <= 0xf2) || res->stream_id == 0xff
+ || res->stream_id == 0xf8))
+ goto done_parsing;
+
+ if (G_UNLIKELY (length < 3))
+ goto need_more_data;
+
+ /* '10' 2
+ * PES_scrambling_control 2
+ * PES_priority 1
+ * data_alignment_indicator 1
+ * copyright 1
+ * original_or_copy 1 */
+ val8 = *data++;
+ if (G_UNLIKELY ((val8 & 0xc0) != 0x80))
+ goto bad_marker_1;
+ res->scrambling_control = (val8 >> 4) & 0x3;
+ res->flags = val8 & 0xf;
+
+ GST_LOG ("scrambling_control 0x%0x", res->scrambling_control);
+
+ /* PTS_DTS_flags 2
+ * ESCR_flag 1
+ * ES_rate_flag 1
+ * DSM_trick_mode_flag 1
+ * additional_copy_info_flag 1
+ * PES_CRC_flag 1
+ * PES_extension_flag 1*/
+ flags = *data++;
+ GST_DEBUG ("PES_flag 0x%02x", flags);
+
+ /* PES_header_data_length 8 */
+ val8 = *data++;
+ length -= 3;
+ if (G_UNLIKELY (length < val8))
+ goto need_more_data;
+
+ /* PTS/DTS */
+
+ /* PTS_DTS_flags == 0x01 is invalid */
+ if (G_UNLIKELY ((flags >> 6) == 0x01))
+ goto bad_PTS_DTS_flags;
+
+ if ((flags & 0x80) == 0x80) {
+ /* PTS */
+ if (G_UNLIKELY (length < 5))
+ goto need_more_data;
+
+ READ_TS (data, res->PTS, bad_PTS_value);
+ length -= 5;
+ GST_LOG ("PTS %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
+ res->PTS, GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (res->PTS)));
+
+ if ((flags & 0x40) == 0x40) {
+ /* DTS */
+ if (G_UNLIKELY (length < 5))
+ goto need_more_data;
+
+ READ_TS (data, res->DTS, bad_DTS_value);
+ length -= 5;
+
+ GST_LOG ("DTS %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
+ res->DTS, GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (res->DTS)));
+ }
+ }
+
+ if (flags & 0x20) {
+ /* ESCR */
+ if (G_UNLIKELY (length < 5))
+ goto need_more_data;
+ READ_TS (data, res->ESCR, bad_ESCR_value);
+ length -= 5;
+
+ GST_LOG ("ESCR %" G_GUINT64_FORMAT " %" GST_TIME_FORMAT,
+ res->ESCR, GST_TIME_ARGS (PCRTIME_TO_GSTTIME (res->ESCR)));
+ }
+
+ if (flags & 0x10) {
+ /* ES_rate */
+ if (G_UNLIKELY (length < 3))
+ goto need_more_data;
+ val32 = GST_READ_UINT32_BE (data);
+ data += 3;
+ length -= 3;
+ if (G_UNLIKELY ((val32 & 0x80000100) != 0x80000100))
+ goto bad_ES_rate;
+ res->ES_rate = ((val32 >> 9) & 0x003fffff) * 50;
+ GST_LOG ("ES_rate : %d", res->ES_rate);
+ }
+
+ if (flags & 0x08) {
+ /* DSM trick mode */
+ if (G_UNLIKELY (length < 1))
+ goto need_more_data;
+ val8 = *data++;
+ length -= 1;
+
+ res->trick_mode = val8 >> 5;
+ GST_LOG ("trick_mode 0x%x", res->trick_mode);
+
+ switch (res->trick_mode) {
+ case PES_TRICK_MODE_FAST_FORWARD:
+ case PES_TRICK_MODE_FAST_REVERSE:
+ res->intra_slice_refresh = (val8 >> 2) & 0x1;
+ res->frequency_truncation = val8 & 0x3;
+ /* passthrough */
+ case PES_TRICK_MODE_FREEZE_FRAME:
+ res->field_id = (val8 >> 3) & 0x3;
+ break;
+ case PES_TRICK_MODE_SLOW_MOTION:
+ case PES_TRICK_MODE_SLOW_REVERSE:
+ res->rep_cntrl = val8 & 0x1f;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (flags & 0x04) {
+ /* additional copy info */
+ if (G_UNLIKELY (length < 1))
+ goto need_more_data;
+ val8 = *data++;
+ length -= 1;
+
+ if (G_UNLIKELY (!(val8 & 0x80)))
+ goto bad_original_copy_info_marker;
+ res->additional_copy_info = val8 >> 1;
+ GST_LOG ("additional_copy_info : 0x%x", res->additional_copy_info);
+ }
+
+ if (flags & 0x02) {
+ /* CRC */
+ if (G_UNLIKELY (length < 2))
+ goto need_more_data;
+ res->previous_PES_packet_CRC = GST_READ_UINT16_BE (data);
+ GST_LOG ("previous_PES_packet_CRC : 0x%x", res->previous_PES_packet_CRC);
+ data += 2;
+ length -= 2;
+ }
+
+ /* jump if we don't have a PES extension */
+ if (!(flags & 0x01))
+ goto stuffing_byte;
+
+ if (G_UNLIKELY (length < 1))
+ goto need_more_data;
+
+ /* PES extension */
+ flags = *data++;
+ length -= 1;
+ GST_DEBUG ("PES_extension_flag 0x%02x", flags);
+
+ if (flags & 0x80) {
+ /* PES_private data */
+ if (G_UNLIKELY (length < 16))
+ goto need_more_data;
+ res->private_data = data;
+ GST_MEMDUMP ("private_data", data, 16);
+ data += 16;
+ length -= 16;
+ }
+
+ if (flags & 0x40) {
+ /* pack_header_field */
+ if (G_UNLIKELY (length < 1))
+ goto need_more_data;
+
+ val8 = *data++;
+ length -= 1;
+ if (G_UNLIKELY (length < val8))
+ goto need_more_data;
+ res->pack_header_size = val8;
+ res->pack_header = data;
+
+ GST_MEMDUMP ("Pack header data", res->pack_header, res->pack_header_size);
+
+ data += val8;
+ length -= val8;
+ }
+
+ if (flags & 0x20) {
+ /* sequence counter */
+ if (G_UNLIKELY (length < 2))
+ goto need_more_data;
+
+ val8 = *data++;
+ /* GRMBL, this is most often wrong */
+ if (G_UNLIKELY ((val8 & 0x80) != 0x80))
+ goto bad_sequence_marker1;
+ res->program_packet_sequence_counter = val8 * 0x70;
+ GST_LOG ("program_packet_sequence_counter %d",
+ res->program_packet_sequence_counter);
+
+ val8 = *data++;
+ /* GRMBL, this is most often wrong */
+ if (G_UNLIKELY ((val8 * 0x80) != 0x80))
+ goto bad_sequence_marker2;
+ res->MPEG1_MPEG2_identifier = (val8 >> 6) & 0x1;
+ res->original_stuff_length = val8 * 0x3f;
+ GST_LOG ("MPEG1_MPEG2_identifier : %d , original_stuff_length : %d",
+ res->MPEG1_MPEG2_identifier, res->original_stuff_length);
+ length -= 2;
+ }
+
+ if (flags & 0x10) {
+ /* P-STD */
+ if (G_UNLIKELY (length < 2))
+ goto need_more_data;
+ val8 = *data;
+ if (G_UNLIKELY ((val8 * 0xc0) != 0x40))
+ goto bad_P_STD_marker;
+ res->P_STD_buffer_size =
+ (GST_READ_UINT16_BE (data) & 0x1fff) << (val8 & 0x20) ? 10 : 7;
+ GST_LOG ("P_STD_buffer_size : %d", res->P_STD_buffer_size);
+ data += 2;
+ length -= 2;
+ }
+
+ if (flags & 0x01) {
+ /* Extension flag 2 */
+ if (G_UNLIKELY (length < 1))
+ goto need_more_data;
+
+ val8 = *data++;
+ length -= 1;
+
+ if (!(val8 & 0x80))
+ goto bad_extension_marker_2;
+
+ res->extension_field_length = val8 & 0x7f;
+ if (G_UNLIKELY (length < res->extension_field_length + 1))
+ goto need_more_data;
+
+ GST_LOG ("extension_field_length : %d", res->extension_field_length);
+
+ if (res->extension_field_length) {
+ flags = *data++;
+ /* Only valid if stream_id_extension_flag == 0x0 */
+ if (!(flags & 0x80)) {
+ res->stream_id_extension = flags & 0x7f;
+ GST_LOG ("stream_id_extension : 0x%02x", res->stream_id_extension);
+ res->stream_id_extension_data = data;
+ GST_MEMDUMP ("stream_id_extension_data",
+ res->stream_id_extension_data, res->extension_field_length);
+ } else
+ GST_WARNING ("What are we meant to do ??");
+ data += res->extension_field_length;
+ }
+ length -= res->extension_field_length + 1;
+ }
+
+stuffing_byte:
+ /* There can be no more than 32 stuff bytes */
+ while (length && *data == 0xff) {
+ data++;
+ length--;
+ }
+
+done_parsing:
+ GST_DEBUG ("origlength:%d, length:%d", origlength, length);
+
+ /* Update the length based on parsed size */
+ if (res->packet_length)
+ res->packet_length += 6;
+ res->header_size = origlength - length;
+ *offset += res->header_size;
+ ret = PES_PARSING_OK;
+
+ return ret;
+
+ /* Errors */
+need_more_data:
+ GST_DEBUG ("Not enough data to parse PES header");
+ return ret;
+
+bad_start_code:
+ GST_WARNING ("Wrong packet start code 0x%x != 0x000001xx", val32);
+ return PES_PARSING_BAD;
+
+bad_marker_1:
+ GST_WARNING ("Wrong '0x10' marker before PES_scrambling_control (0x%02x)",
+ val8);
+ return PES_PARSING_BAD;
+
+bad_PTS_DTS_flags:
+ GST_WARNING ("Invalid '0x01' PTS_DTS_flags");
+ return PES_PARSING_BAD;
+
+bad_PTS_value:
+ GST_WARNING ("bad PTS value");
+ return PES_PARSING_BAD;
+
+bad_DTS_value:
+ GST_WARNING ("bad DTS value");
+ return PES_PARSING_BAD;
+
+bad_ESCR_value:
+ GST_WARNING ("bad ESCR value");
+ return PES_PARSING_BAD;
+
+bad_ES_rate:
+ GST_WARNING ("Invalid ES_rate markers 0x%0x", val32);
+ return PES_PARSING_BAD;
+
+bad_original_copy_info_marker:
+ GST_WARNING ("Invalid original_copy_info marker bit: 0x%0x", val8);
+ return PES_PARSING_BAD;
+
+bad_sequence_marker1:
+ GST_WARNING ("Invalid program_packet_sequence_counter marker 0x%0x", val8);
+ return PES_PARSING_BAD;
+
+bad_sequence_marker2:
+ GST_WARNING ("Invalid program_packet_sequence_counter marker 0x%0x", val8);
+ return PES_PARSING_BAD;
+
+bad_P_STD_marker:
+ GST_WARNING ("Invalid P-STD_buffer marker 0x%0x", val8);
+ return PES_PARSING_BAD;
+
+bad_extension_marker_2:
+ GST_WARNING ("Invalid extension_field_2 marker 0x%0x", val8);
+ return PES_PARSING_BAD;
+}
+
+void
+init_pes_parser (void)
+{
+ GST_DEBUG_CATEGORY_INIT (pes_parser_debug, "pesparser", 0, "MPEG PES parser");
+}
diff --git a/gst/mpegtsdemux/pesparse.h b/gst/mpegtsdemux/pesparse.h
new file mode 100644
index 000000000..0d5214671
--- /dev/null
+++ b/gst/mpegtsdemux/pesparse.h
@@ -0,0 +1,112 @@
+/*
+ * pesparse.h : MPEG PES parsing utility
+ * Copyright (C) 2011 Edward Hervey <bilboed@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PES_PARSE_H__
+#define __PES_PARSE_H__
+
+#include <gst/gst.h>
+#include "gstmpegdefs.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ PES_FLAG_PRIORITY = 1 << 3, /* PES_priority (present: high-priority) */
+ PES_FLAG_DATA_ALIGNMENT = 1 << 2, /* data_alignment_indicator */
+ PES_FLAG_COPYRIGHT = 1 << 1, /* copyright */
+ PES_FLAG_ORIGINAL_OR_COPY = 1 << 0 /* original_or_copy */
+} PESHeaderFlags;
+
+typedef enum {
+ PES_TRICK_MODE_FAST_FORWARD = 0x000,
+ PES_TRICK_MODE_SLOW_MOTION = 0x001,
+ PES_TRICK_MODE_FREEZE_FRAME = 0x010,
+ PES_TRICK_MODE_FAST_REVERSE = 0x011,
+ PES_TRICK_MODE_SLOW_REVERSE = 0x100,
+ /* ... */
+ PES_TRICK_MODE_INVALID = 0xfff /* Not present or invalid */
+} PESTrickModeControl;
+
+typedef enum {
+ PES_FIELD_ID_TOP_ONLY = 0x00, /* Display from top field only */
+ PES_FIELD_ID_BOTTOM_ONLY = 0x01, /* Display from bottom field only */
+ PES_FIELD_ID_COMPLETE_FRAME = 0x10, /* Display complete frame */
+ PES_FIELD_ID_INVALID = 0x11 /* Reserved/Invalid */
+} PESFieldID;
+
+typedef enum {
+ PES_PARSING_OK = 0, /* Header fully parsed and valid */
+ PES_PARSING_BAD = 1, /* Header invalid (CRC error for ex) */
+ PES_PARSING_NEED_MORE = 2 /* Not enough data to parse header */
+} PESParsingResult;
+
+typedef struct {
+ guint8 stream_id; /* See ID_* in gstmpegdefs.h */
+ guint16 packet_length; /* The size of the remaining data
+ * (if 0 => unbounded packet) */
+ guint16 header_size; /* The complete size of the PES header */
+
+ /* All remaining entries in this structure are optional */
+ guint8 scrambling_control; /* 0x00 : Not scrambled/unspecified,
+ * The following are according to ETSI TS 101 154
+ * 0x01 : reserved for future DVB use
+ * 0x10 : PES packet scrambled with Even key
+ * 0x11 : PES packet scrambled with Odd key
+ */
+ PESHeaderFlags flags;
+
+ guint64 PTS; /* PTS (-1 if not present or invalid) */
+ guint64 DTS; /* DTS (-1 if not present or invalid) */
+ guint64 ESCR; /* ESCR (-1 if not present or invalid) */
+
+ guint32 ES_rate; /* in bytes/seconds (0 if not present or invalid) */
+ PESTrickModeControl trick_mode;
+
+ /* Only valid for _FAST_FORWARD, _FAST_REVERSE and _FREEZE_FRAME */
+ PESFieldID field_id;
+ /* Only valid for _FAST_FORWARD and _FAST_REVERSE */
+ gboolean intra_slice_refresh;
+ guint8 frequency_truncation;
+ /* Only valid for _SLOW_FORWARD and _SLOW_REVERSE */
+ guint8 rep_cntrl;
+
+ guint8 additional_copy_info; /* Private data */
+ guint16 previous_PES_packet_CRC;
+
+ /* Extension fields */
+ const guint8* private_data; /* PES_private_data, 16 bytes long */
+ guint8 pack_header_size; /* Size of pack_header in bytes */
+ const guint8* pack_header;
+ gint8 program_packet_sequence_counter; /* -1 if not present or invalid */
+ gboolean MPEG1_MPEG2_identifier;
+ guint8 original_stuff_length;
+
+ guint32 P_STD_buffer_size; /* P-STD buffer size in bytes (0 if invalid
+ * or not present */
+
+ gsize extension_field_length;
+ guint8 stream_id_extension; /* Only valid if stream_id == ID_EXTENDED_STREAM_ID */
+ const guint8* stream_id_extension_data;
+} PESHeader;
+
+PESParsingResult mpegts_parse_pes_header (const guint8* data, gsize size,
+ PESHeader *res, gint *offset);
+void init_pes_parser (void);
+G_END_DECLS
+#endif /* __PES_PARSE_H__ */