3 * Copyright (C) 2007 Alessandro Decina
6 * Alessandro Decina <alessandro@nnva.org>
7 * Zaheer Abbas Merali <zaheerabbas at merali dot org>
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 of the License, or (at your option) any later version.
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.
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
31 #include "mpegtsparse.h"
32 #include "gstmpegdesc.h"
34 /* latency in mseconds */
35 #define TS_LATENCY 700
37 #define TABLE_ID_UNSET 0xFF
38 #define RUNNING_STATUS_RUNNING 4
40 GST_DEBUG_CATEGORY_STATIC (mpegts_parse_debug);
41 #define GST_CAT_DEFAULT mpegts_parse_debug
43 typedef struct _MpegTSParsePad MpegTSParsePad;
56 GstStructure *pmt_info;
61 MpegTSParsePad *tspad;
64 struct _MpegTSParsePad
68 /* the program number that the peer wants on this pad */
70 MpegTSParseProgram *program;
72 /* set to FALSE before a push and TRUE after */
75 /* the return of the latest push */
76 GstFlowReturn flow_return;
82 static GQuark QUARK_PROGRAMS;
83 static GQuark QUARK_PROGRAM_NUMBER;
84 static GQuark QUARK_PID;
85 static GQuark QUARK_PCR_PID;
86 static GQuark QUARK_STREAMS;
87 static GQuark QUARK_STREAM_TYPE;
89 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
92 GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
95 static GstStaticPadTemplate src_template =
96 GST_STATIC_PAD_TEMPLATE ("src%d", GST_PAD_SRC,
98 GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
101 static GstStaticPadTemplate program_template =
102 GST_STATIC_PAD_TEMPLATE ("program_%d", GST_PAD_SRC,
104 GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
110 PROP_PROGRAM_NUMBERS,
114 static void mpegts_parse_set_property (GObject * object, guint prop_id,
115 const GValue * value, GParamSpec * pspec);
116 static void mpegts_parse_get_property (GObject * object, guint prop_id,
117 GValue * value, GParamSpec * pspec);
118 static void mpegts_parse_dispose (GObject * object);
119 static void mpegts_parse_finalize (GObject * object);
121 static MpegTSParsePad *mpegts_parse_create_tspad (MpegTSParse * parse,
123 static void mpegts_parse_destroy_tspad (MpegTSParse * parse,
124 MpegTSParsePad * tspad);
125 static GstPad *mpegts_parse_activate_program (MpegTSParse * parse,
126 MpegTSParseProgram * program);
127 static void mpegts_parse_free_program (MpegTSParseProgram * program);
128 static void mpegts_parse_free_stream (MpegTSParseStream * ptream);
129 static void mpegts_parse_reset_selected_programs (MpegTSParse * parse,
132 static void mpegts_parse_pad_removed (GstElement * element, GstPad * pad);
133 static GstPad *mpegts_parse_request_new_pad (GstElement * element,
134 GstPadTemplate * templ, const gchar * name);
135 static void mpegts_parse_release_pad (GstElement * element, GstPad * pad);
136 static GstFlowReturn mpegts_parse_chain (GstPad * pad, GstBuffer * buf);
137 static gboolean mpegts_parse_sink_event (GstPad * pad, GstEvent * event);
138 static GstStateChangeReturn mpegts_parse_change_state (GstElement * element,
139 GstStateChange transition);
140 static gboolean mpegts_parse_src_pad_query (GstPad * pad, GstQuery * query);
141 static void _extra_init (GType type);
142 static void mpegts_parse_get_tags_from_sdt (MpegTSParse * parse,
143 GstStructure * sdt_info);
144 static void mpegts_parse_get_tags_from_eit (MpegTSParse * parse,
145 GstStructure * eit_info);
147 GST_BOILERPLATE_FULL (MpegTSParse, mpegts_parse, GstElement, GST_TYPE_ELEMENT,
150 static const guint32 crc_tab[256] = {
151 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
152 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
153 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
154 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
155 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
156 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
157 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
158 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
159 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
160 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
161 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
162 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
163 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
164 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
165 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
166 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
167 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
168 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
169 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
170 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
171 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
172 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
173 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
174 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
175 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
176 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
177 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
178 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
179 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
180 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
181 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
182 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
183 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
184 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
185 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
186 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
187 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
188 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
189 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
190 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
191 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
192 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
193 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
196 /* relicenced to LGPL from fluendo ts demuxer */
198 mpegts_parse_calc_crc32 (guint8 * data, guint datalen)
201 guint32 crc = 0xffffffff;
203 for (i = 0; i < datalen; i++) {
204 crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
210 _extra_init (GType type)
212 QUARK_PROGRAMS = g_quark_from_string ("programs");
213 QUARK_PROGRAM_NUMBER = g_quark_from_string ("program-number");
214 QUARK_PID = g_quark_from_string ("pid");
215 QUARK_PCR_PID = g_quark_from_string ("pcr-pid");
216 QUARK_STREAMS = g_quark_from_string ("streams");
217 QUARK_STREAM_TYPE = g_quark_from_string ("stream-type");
221 mpegts_parse_base_init (gpointer klass)
223 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
225 gst_element_class_add_static_pad_template (element_class,
227 gst_element_class_add_static_pad_template (element_class, &src_template);
228 gst_element_class_add_static_pad_template (element_class,
231 gst_element_class_set_details_simple (element_class,
232 "MPEG transport stream parser", "Codec/Parser",
233 "Parses MPEG2 transport streams",
234 "Alessandro Decina <alessandro@nnva.org>, "
235 "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
239 mpegts_parse_class_init (MpegTSParseClass * klass)
241 GObjectClass *gobject_class;
242 GstElementClass *element_class;
244 element_class = GST_ELEMENT_CLASS (klass);
245 element_class->pad_removed = mpegts_parse_pad_removed;
246 element_class->request_new_pad = mpegts_parse_request_new_pad;
247 element_class->release_pad = mpegts_parse_release_pad;
248 element_class->change_state = mpegts_parse_change_state;
250 gobject_class = G_OBJECT_CLASS (klass);
251 gobject_class->set_property = mpegts_parse_set_property;
252 gobject_class->get_property = mpegts_parse_get_property;
253 gobject_class->dispose = mpegts_parse_dispose;
254 gobject_class->finalize = mpegts_parse_finalize;
256 g_object_class_install_property (gobject_class, PROP_PROGRAM_NUMBERS,
257 g_param_spec_string ("program-numbers",
259 "Colon separated list of programs", "",
260 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
264 foreach_psi_pid_remove (gpointer key, gpointer value, gpointer data)
270 mpegts_parse_reset (MpegTSParse * parse)
272 mpegts_packetizer_clear (parse->packetizer);
273 g_hash_table_foreach_remove (parse->psi_pids, foreach_psi_pid_remove, NULL);
276 g_hash_table_insert (parse->psi_pids,
277 GINT_TO_POINTER (0), GINT_TO_POINTER (1));
278 /* pmt pids will be added and removed dynamically */
283 mpegts_parse_init (MpegTSParse * parse, MpegTSParseClass * klass)
285 parse->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
286 gst_pad_set_chain_function (parse->sinkpad, mpegts_parse_chain);
287 gst_pad_set_event_function (parse->sinkpad, mpegts_parse_sink_event);
288 gst_element_add_pad (GST_ELEMENT (parse), parse->sinkpad);
290 parse->disposed = FALSE;
291 parse->need_sync_program_pads = FALSE;
292 parse->packetizer = mpegts_packetizer_new ();
293 parse->program_numbers = g_strdup ("");
294 parse->pads_to_add = NULL;
295 parse->pads_to_remove = NULL;
296 parse->programs = g_hash_table_new_full (g_direct_hash, g_direct_equal,
297 NULL, (GDestroyNotify) mpegts_parse_free_program);
298 parse->psi_pids = g_hash_table_new (g_direct_hash, g_direct_equal);
299 parse->pes_pids = g_hash_table_new (g_direct_hash, g_direct_equal);
300 mpegts_parse_reset (parse);
305 mpegts_parse_dispose (GObject * object)
307 MpegTSParse *parse = GST_MPEGTS_PARSE (object);
309 if (!parse->disposed) {
310 g_object_unref (parse->packetizer);
311 parse->disposed = TRUE;
314 if (G_OBJECT_CLASS (parent_class)->dispose)
315 G_OBJECT_CLASS (parent_class)->dispose (object);
319 mpegts_parse_finalize (GObject * object)
321 MpegTSParse *parse = GST_MPEGTS_PARSE (object);
323 g_free (parse->program_numbers);
325 gst_structure_free (parse->pat);
328 g_hash_table_destroy (parse->programs);
329 g_hash_table_destroy (parse->psi_pids);
330 g_hash_table_destroy (parse->pes_pids);
332 if (G_OBJECT_CLASS (parent_class)->finalize)
333 G_OBJECT_CLASS (parent_class)->finalize (object);
337 mpegts_parse_set_property (GObject * object, guint prop_id,
338 const GValue * value, GParamSpec * pspec)
340 MpegTSParse *parse = GST_MPEGTS_PARSE (object);
343 case PROP_PROGRAM_NUMBERS:
344 mpegts_parse_reset_selected_programs (parse, g_value_dup_string (value));
347 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
352 mpegts_parse_get_property (GObject * object, guint prop_id,
353 GValue * value, GParamSpec * pspec)
355 MpegTSParse *parse = GST_MPEGTS_PARSE (object);
358 case PROP_PROGRAM_NUMBERS:
359 g_value_set_string (value, parse->program_numbers);
362 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
366 static MpegTSParseProgram *
367 mpegts_parse_add_program (MpegTSParse * parse,
368 gint program_number, guint16 pmt_pid)
370 MpegTSParseProgram *program;
372 program = g_new0 (MpegTSParseProgram, 1);
373 program->program_number = program_number;
374 program->pmt_pid = pmt_pid;
375 program->pcr_pid = G_MAXUINT16;
376 program->streams = g_hash_table_new_full (g_direct_hash, g_direct_equal,
377 NULL, (GDestroyNotify) mpegts_parse_free_stream);
378 program->patcount = 0;
379 program->selected = 0;
380 program->active = FALSE;
382 g_hash_table_insert (parse->programs,
383 GINT_TO_POINTER (program_number), program);
388 static MpegTSParseProgram *
389 mpegts_parse_get_program (MpegTSParse * parse, gint program_number)
391 MpegTSParseProgram *program;
393 program = (MpegTSParseProgram *) g_hash_table_lookup (parse->programs,
394 GINT_TO_POINTER ((gint) program_number));
400 mpegts_parse_activate_program (MpegTSParse * parse,
401 MpegTSParseProgram * program)
403 MpegTSParsePad *tspad;
406 pad_name = g_strdup_printf ("program_%d", program->program_number);
408 tspad = mpegts_parse_create_tspad (parse, pad_name);
409 tspad->program_number = program->program_number;
410 tspad->program = program;
411 program->tspad = tspad;
413 gst_pad_set_active (tspad->pad, TRUE);
414 program->active = TRUE;
420 mpegts_parse_deactivate_program (MpegTSParse * parse,
421 MpegTSParseProgram * program)
423 MpegTSParsePad *tspad;
425 tspad = program->tspad;
426 gst_pad_set_active (tspad->pad, FALSE);
427 program->active = FALSE;
429 /* tspad will be destroyed in GstElementClass::pad_removed */
435 mpegts_parse_free_program (MpegTSParseProgram * program)
437 if (program->pmt_info)
438 gst_structure_free (program->pmt_info);
440 g_hash_table_destroy (program->streams);
446 mpegts_parse_remove_program (MpegTSParse * parse, gint program_number)
448 g_hash_table_remove (parse->programs, GINT_TO_POINTER (program_number));
452 mpegts_parse_sync_program_pads (MpegTSParse * parse)
456 GST_INFO_OBJECT (parse, "begin sync pads");
457 for (walk = parse->pads_to_remove; walk; walk = walk->next)
458 gst_element_remove_pad (GST_ELEMENT (parse), GST_PAD (walk->data));
460 for (walk = parse->pads_to_add; walk; walk = walk->next)
461 gst_element_add_pad (GST_ELEMENT (parse), GST_PAD (walk->data));
463 if (parse->pads_to_add)
464 g_list_free (parse->pads_to_add);
466 if (parse->pads_to_remove)
467 g_list_free (parse->pads_to_remove);
469 GST_OBJECT_LOCK (parse);
470 parse->pads_to_remove = NULL;
471 parse->pads_to_add = NULL;
472 parse->need_sync_program_pads = FALSE;
473 GST_OBJECT_UNLOCK (parse);
475 GST_INFO_OBJECT (parse, "end sync pads");
479 static MpegTSParseStream *
480 mpegts_parse_program_add_stream (MpegTSParse * parse,
481 MpegTSParseProgram * program, guint16 pid, guint8 stream_type)
483 MpegTSParseStream *stream;
485 stream = g_new0 (MpegTSParseStream, 1);
487 stream->stream_type = stream_type;
489 g_hash_table_insert (program->streams, GINT_TO_POINTER ((gint) pid), stream);
495 foreach_program_activate_or_deactivate (gpointer key, gpointer value,
498 MpegTSParse *parse = GST_MPEGTS_PARSE (data);
499 MpegTSParseProgram *program = (MpegTSParseProgram *) value;
501 /* at this point selected programs have program->selected == 2,
502 * unselected programs thay may have to be deactivated have selected == 1 and
503 * unselected inactive programs have selected == 0 */
505 switch (--program->selected) {
508 if (!program->active && program->pmt_pid != G_MAXUINT16)
509 parse->pads_to_add = g_list_append (parse->pads_to_add,
510 mpegts_parse_activate_program (parse, program));
515 parse->pads_to_remove = g_list_append (parse->pads_to_remove,
516 mpegts_parse_deactivate_program (parse, program));
519 /* was already unselected */
520 program->selected = 0;
523 g_return_if_reached ();
528 mpegts_parse_reset_selected_programs (MpegTSParse * parse,
529 gchar * program_numbers)
531 GST_OBJECT_LOCK (parse);
532 if (parse->program_numbers)
533 g_free (parse->program_numbers);
535 parse->program_numbers = program_numbers;
537 if (*parse->program_numbers != '\0') {
539 MpegTSParseProgram *program;
540 gchar **progs, **walk;
542 progs = g_strsplit (parse->program_numbers, ":", 0);
545 while (*walk != NULL) {
546 program_number = strtol (*walk, NULL, 0);
547 program = mpegts_parse_get_program (parse, program_number);
549 /* create the program, it will get activated once we get a PMT for it */
550 program = mpegts_parse_add_program (parse, program_number, G_MAXUINT16);
552 program->selected = 2;
558 g_hash_table_foreach (parse->programs,
559 foreach_program_activate_or_deactivate, parse);
561 if (parse->pads_to_remove || parse->pads_to_add)
562 parse->need_sync_program_pads = TRUE;
563 GST_OBJECT_UNLOCK (parse);
567 mpegts_parse_free_stream (MpegTSParseStream * stream)
573 mpegts_parse_program_remove_stream (MpegTSParse * parse,
574 MpegTSParseProgram * program, guint16 pid)
576 g_hash_table_remove (program->streams, GINT_TO_POINTER ((gint) pid));
580 mpegts_parse_deactivate_pmt (MpegTSParse * parse, MpegTSParseProgram * program)
585 GstStructure *stream;
586 const GValue *streams;
589 if (program->pmt_info) {
590 streams = gst_structure_id_get_value (program->pmt_info, QUARK_STREAMS);
592 for (i = 0; i < gst_value_list_get_size (streams); ++i) {
593 value = gst_value_list_get_value (streams, i);
594 stream = g_value_get_boxed (value);
595 gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid,
596 QUARK_STREAM_TYPE, G_TYPE_UINT, &stream_type, NULL);
597 mpegts_parse_program_remove_stream (parse, program, (guint16) pid);
598 g_hash_table_remove (parse->pes_pids, GINT_TO_POINTER ((gint) pid));
601 /* remove pcr stream */
602 mpegts_parse_program_remove_stream (parse, program, program->pcr_pid);
603 g_hash_table_remove (parse->pes_pids,
604 GINT_TO_POINTER ((gint) program->pcr_pid));
608 static MpegTSParsePad *
609 mpegts_parse_create_tspad (MpegTSParse * parse, const gchar * pad_name)
612 MpegTSParsePad *tspad;
614 pad = gst_pad_new_from_static_template (&program_template, pad_name);
615 gst_pad_set_query_function (pad,
616 GST_DEBUG_FUNCPTR (mpegts_parse_src_pad_query));
618 /* create our wrapper */
619 tspad = g_new0 (MpegTSParsePad, 1);
621 tspad->program_number = -1;
622 tspad->program = NULL;
623 tspad->pushed = FALSE;
624 tspad->flow_return = GST_FLOW_NOT_LINKED;
625 gst_pad_set_element_private (pad, tspad);
631 mpegts_parse_destroy_tspad (MpegTSParse * parse, MpegTSParsePad * tspad)
634 gst_tag_list_free (tspad->tags);
637 /* free the wrapper */
642 mpegts_parse_pad_removed (GstElement * element, GstPad * pad)
644 MpegTSParsePad *tspad;
645 MpegTSParse *parse = GST_MPEGTS_PARSE (element);
647 if (gst_pad_get_direction (pad) == GST_PAD_SINK)
650 tspad = (MpegTSParsePad *) gst_pad_get_element_private (pad);
651 mpegts_parse_destroy_tspad (parse, tspad);
653 if (GST_ELEMENT_CLASS (parent_class)->pad_removed)
654 GST_ELEMENT_CLASS (parent_class)->pad_removed (element, pad);
658 mpegts_parse_request_new_pad (GstElement * element, GstPadTemplate * template,
659 const gchar * unused)
665 g_return_val_if_fail (template != NULL, NULL);
666 g_return_val_if_fail (GST_IS_MPEGTS_PARSE (element), NULL);
668 parse = GST_MPEGTS_PARSE (element);
670 GST_OBJECT_LOCK (element);
671 name = g_strdup_printf ("src%d", parse->req_pads++);
672 GST_OBJECT_UNLOCK (element);
674 pad = mpegts_parse_create_tspad (parse, name)->pad;
675 gst_pad_set_active (pad, TRUE);
676 gst_element_add_pad (element, pad);
683 mpegts_parse_release_pad (GstElement * element, GstPad * pad)
685 g_return_if_fail (GST_IS_MPEGTS_PARSE (element));
687 gst_pad_set_active (pad, FALSE);
688 /* we do the cleanup in GstElement::pad-removed */
689 gst_element_remove_pad (element, pad);
693 mpegts_parse_tspad_push_section (MpegTSParse * parse, MpegTSParsePad * tspad,
694 MpegTSPacketizerSection * section, GstBuffer * buffer)
696 GstFlowReturn ret = GST_FLOW_NOT_LINKED;
697 gboolean to_push = TRUE;
699 if (tspad->program_number != -1) {
700 if (tspad->program) {
701 /* we push all sections to all pads except PMTs which we
702 * only push to pads meant to receive that program number */
703 if (section->table_id == 0x02) {
705 if (section->subtable_extension != tspad->program_number)
709 /* there's a program filter on the pad but the PMT for the program has not
710 * been parsed yet, ignore the pad until we get a PMT */
715 GST_DEBUG_OBJECT (parse,
716 "pushing section: %d program number: %d table_id: %d", to_push,
717 tspad->program_number, section->table_id);
719 ret = gst_pad_push (tspad->pad, buffer);
721 gst_buffer_unref (buffer);
722 if (gst_pad_is_linked (tspad->pad))
730 mpegts_parse_tspad_push (MpegTSParse * parse, MpegTSParsePad * tspad,
731 guint16 pid, GstBuffer * buffer)
733 GstFlowReturn ret = GST_FLOW_NOT_LINKED;
734 GHashTable *pad_pids = NULL;
736 if (tspad->program_number != -1) {
737 if (tspad->program) {
738 pad_pids = tspad->program->streams;
741 gst_element_found_tags_for_pad (GST_ELEMENT_CAST (parse),
742 tspad->pad, tspad->tags);
746 /* there's a program filter on the pad but the PMT for the program has not
747 * been parsed yet, ignore the pad until we get a PMT */
748 gst_buffer_unref (buffer);
754 if (pad_pids == NULL ||
755 g_hash_table_lookup (pad_pids, GINT_TO_POINTER ((gint) pid)) != NULL) {
756 /* push if there's no filter or if the pid is in the filter */
757 ret = gst_pad_push (tspad->pad, buffer);
759 gst_buffer_unref (buffer);
760 if (gst_pad_is_linked (tspad->pad))
769 pad_clear_for_push (GstPad * pad, MpegTSParse * parse)
771 MpegTSParsePad *tspad = (MpegTSParsePad *) gst_pad_get_element_private (pad);
773 tspad->flow_return = GST_FLOW_NOT_LINKED;
774 tspad->pushed = FALSE;
778 mpegts_parse_push (MpegTSParse * parse, MpegTSPacketizerPacket * packet,
779 MpegTSPacketizerSection * section)
782 gboolean done = FALSE;
784 MpegTSParsePad *tspad;
791 buffer = gst_buffer_make_metadata_writable (packet->buffer);
792 /* we have the same caps on all the src pads */
793 gst_buffer_set_caps (buffer, parse->packetizer->caps);
795 GST_OBJECT_LOCK (parse);
796 /* clear tspad->pushed on pads */
797 g_list_foreach (GST_ELEMENT_CAST (parse)->srcpads,
798 (GFunc) pad_clear_for_push, parse);
799 if (GST_ELEMENT_CAST (parse)->srcpads)
800 ret = GST_FLOW_NOT_LINKED;
804 /* Get cookie and source pads list */
805 pads_cookie = GST_ELEMENT_CAST (parse)->pads_cookie;
806 srcpads = GST_ELEMENT_CAST (parse)->srcpads;
807 if (G_LIKELY (srcpads)) {
808 pad = GST_PAD_CAST (srcpads->data);
811 GST_OBJECT_UNLOCK (parse);
813 while (pad && !done) {
814 tspad = gst_pad_get_element_private (pad);
816 if (G_LIKELY (!tspad->pushed)) {
817 /* ref the buffer as gst_pad_push takes a ref but we want to reuse the
818 * same buffer for next pushes */
819 gst_buffer_ref (buffer);
822 mpegts_parse_tspad_push_section (parse, tspad, section, buffer);
825 mpegts_parse_tspad_push (parse, tspad, pid, buffer);
827 tspad->pushed = TRUE;
829 if (G_UNLIKELY (tspad->flow_return != GST_FLOW_OK
830 && tspad->flow_return != GST_FLOW_NOT_LINKED)) {
831 /* return the error upstream */
832 ret = tspad->flow_return;
838 if (ret == GST_FLOW_NOT_LINKED)
839 ret = tspad->flow_return;
841 g_object_unref (pad);
843 if (G_UNLIKELY (!done)) {
844 GST_OBJECT_LOCK (parse);
845 if (G_UNLIKELY (pads_cookie != GST_ELEMENT_CAST (parse)->pads_cookie)) {
847 GST_DEBUG ("resync");
848 pads_cookie = GST_ELEMENT_CAST (parse)->pads_cookie;
849 srcpads = GST_ELEMENT_CAST (parse)->srcpads;
851 GST_DEBUG ("getting next pad");
853 srcpads = g_list_next (srcpads);
857 pad = GST_PAD_CAST (srcpads->data);
861 GST_OBJECT_UNLOCK (parse);
865 gst_buffer_unref (buffer);
866 packet->buffer = NULL;
872 mpegts_parse_is_psi (MpegTSParse * parse, MpegTSPacketizerPacket * packet)
874 gboolean retval = FALSE;
879 static const guint8 si_tables[] =
880 { 0x00, 0x01, 0x02, 0x03, 0x40, 0x41, 0x42, 0x46, 0x4A, 0x4E, 0x4F, 0x50,
881 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C,
882 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
883 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x7E,
886 if (g_hash_table_lookup (parse->psi_pids,
887 GINT_TO_POINTER ((gint) packet->pid)) != NULL)
889 /* check is it is a pes pid */
890 if (g_hash_table_lookup (parse->pes_pids,
891 GINT_TO_POINTER ((gint) packet->pid)) != NULL)
894 if (packet->payload_unit_start_indicator) {
898 /* 'pointer' value may be invalid on malformed packet
899 * so we need to avoid out of range
901 if (!(data < packet->data_end)) {
902 GST_WARNING_OBJECT (parse,
903 "Wrong offset when retrieving table id: 0x%x", pointer);
908 while (si_tables[i] != TABLE_ID_UNSET) {
909 if (G_UNLIKELY (si_tables[i] == table_id)) {
910 GST_DEBUG_OBJECT (parse, "Packet has table id 0x%x", table_id);
917 MpegTSPacketizerStream *stream = parse->packetizer->streams[packet->pid];
920 GST_DEBUG_OBJECT (parse, "section table id: 0x%x",
921 stream->section_table_id);
922 while (si_tables[i] != TABLE_ID_UNSET) {
923 if (G_UNLIKELY (si_tables[i] == stream->section_table_id)) {
933 GST_DEBUG_OBJECT (parse, "Packet of pid 0x%x is psi: %d", packet->pid,
939 mpegts_parse_apply_pat (MpegTSParse * parse, GstStructure * pat_info)
942 GstStructure *old_pat;
943 GstStructure *program_info;
944 guint program_number;
946 MpegTSParseProgram *program;
948 const GValue *programs;
950 old_pat = parse->pat;
951 parse->pat = gst_structure_copy (pat_info);
953 GST_INFO_OBJECT (parse, "PAT %" GST_PTR_FORMAT, pat_info);
955 gst_element_post_message (GST_ELEMENT_CAST (parse),
956 gst_message_new_element (GST_OBJECT (parse),
957 gst_structure_copy (pat_info)));
959 GST_OBJECT_LOCK (parse);
960 programs = gst_structure_id_get_value (pat_info, QUARK_PROGRAMS);
961 /* activate the new table */
962 for (i = 0; i < gst_value_list_get_size (programs); ++i) {
963 value = gst_value_list_get_value (programs, i);
965 program_info = g_value_get_boxed (value);
966 gst_structure_id_get (program_info, QUARK_PROGRAM_NUMBER, G_TYPE_UINT,
967 &program_number, QUARK_PID, G_TYPE_UINT, &pid, NULL);
969 program = mpegts_parse_get_program (parse, program_number);
971 if (program->pmt_pid != pid) {
972 if (program->pmt_pid != G_MAXUINT16) {
973 /* pmt pid changed */
974 g_hash_table_remove (parse->psi_pids,
975 GINT_TO_POINTER ((gint) program->pmt_pid));
978 program->pmt_pid = pid;
979 g_hash_table_insert (parse->psi_pids,
980 GINT_TO_POINTER ((gint) pid), GINT_TO_POINTER (1));
983 g_hash_table_insert (parse->psi_pids,
984 GINT_TO_POINTER ((gint) pid), GINT_TO_POINTER (1));
985 program = mpegts_parse_add_program (parse, program_number, pid);
987 program->patcount += 1;
988 if (program->selected && !program->active)
989 parse->pads_to_add = g_list_append (parse->pads_to_add,
990 mpegts_parse_activate_program (parse, program));
994 /* deactivate the old table */
996 programs = gst_structure_id_get_value (old_pat, QUARK_PROGRAMS);
997 for (i = 0; i < gst_value_list_get_size (programs); ++i) {
998 value = gst_value_list_get_value (programs, i);
1000 program_info = g_value_get_boxed (value);
1001 gst_structure_id_get (program_info,
1002 QUARK_PROGRAM_NUMBER, G_TYPE_UINT, &program_number,
1003 QUARK_PID, G_TYPE_UINT, &pid, NULL);
1005 program = mpegts_parse_get_program (parse, program_number);
1006 if (program == NULL) {
1007 GST_DEBUG_OBJECT (parse, "broken PAT, duplicated entry for program %d",
1012 if (--program->patcount > 0)
1013 /* the program has been referenced by the new pat, keep it */
1016 GST_INFO_OBJECT (parse, "PAT removing program %" GST_PTR_FORMAT,
1019 if (program->active)
1020 parse->pads_to_remove = g_list_append (parse->pads_to_remove,
1021 mpegts_parse_deactivate_program (parse, program));
1023 mpegts_parse_deactivate_pmt (parse, program);
1024 mpegts_parse_remove_program (parse, program_number);
1025 g_hash_table_remove (parse->psi_pids, GINT_TO_POINTER ((gint) pid));
1026 mpegts_packetizer_remove_stream (parse->packetizer, pid);
1029 gst_structure_free (old_pat);
1032 GST_OBJECT_UNLOCK (parse);
1034 mpegts_parse_sync_program_pads (parse);
1038 mpegts_parse_apply_pmt (MpegTSParse * parse,
1039 guint16 pmt_pid, GstStructure * pmt_info)
1041 MpegTSParseProgram *program;
1042 guint program_number;
1046 GstStructure *stream;
1048 const GValue *new_streams;
1049 const GValue *value;
1051 gst_structure_id_get (pmt_info,
1052 QUARK_PROGRAM_NUMBER, G_TYPE_UINT, &program_number,
1053 QUARK_PCR_PID, G_TYPE_UINT, &pcr_pid, NULL);
1054 new_streams = gst_structure_id_get_value (pmt_info, QUARK_STREAMS);
1056 GST_OBJECT_LOCK (parse);
1057 program = mpegts_parse_get_program (parse, program_number);
1059 /* deactivate old pmt */
1060 mpegts_parse_deactivate_pmt (parse, program);
1061 if (program->pmt_info)
1062 gst_structure_free (program->pmt_info);
1063 program->pmt_info = NULL;
1066 g_hash_table_insert (parse->psi_pids,
1067 GINT_TO_POINTER ((gint) pmt_pid), GINT_TO_POINTER (1));
1068 program = mpegts_parse_add_program (parse, program_number, pid);
1071 /* activate new pmt */
1072 program->pmt_info = gst_structure_copy (pmt_info);
1073 program->pmt_pid = pmt_pid;
1074 program->pcr_pid = pcr_pid;
1075 mpegts_parse_program_add_stream (parse, program, (guint16) pcr_pid, -1);
1076 g_hash_table_insert (parse->pes_pids, GINT_TO_POINTER ((gint) pcr_pid),
1077 GINT_TO_POINTER (1));
1079 for (i = 0; i < gst_value_list_get_size (new_streams); ++i) {
1080 value = gst_value_list_get_value (new_streams, i);
1081 stream = g_value_get_boxed (value);
1083 gst_structure_id_get (stream, QUARK_PID, G_TYPE_UINT, &pid,
1084 QUARK_STREAM_TYPE, G_TYPE_UINT, &stream_type, NULL);
1085 mpegts_parse_program_add_stream (parse, program,
1086 (guint16) pid, (guint8) stream_type);
1087 g_hash_table_insert (parse->pes_pids, GINT_TO_POINTER ((gint) pid),
1088 GINT_TO_POINTER ((gint) 1));
1091 GST_OBJECT_UNLOCK (parse);
1093 GST_DEBUG_OBJECT (parse, "new pmt %" GST_PTR_FORMAT, pmt_info);
1095 gst_element_post_message (GST_ELEMENT_CAST (parse),
1096 gst_message_new_element (GST_OBJECT (parse),
1097 gst_structure_copy (pmt_info)));
1101 mpegts_parse_apply_nit (MpegTSParse * parse,
1102 guint16 pmt_pid, GstStructure * nit_info)
1104 gst_element_post_message (GST_ELEMENT_CAST (parse),
1105 gst_message_new_element (GST_OBJECT (parse),
1106 gst_structure_copy (nit_info)));
1110 mpegts_parse_apply_sdt (MpegTSParse * parse,
1111 guint16 pmt_pid, GstStructure * sdt_info)
1113 mpegts_parse_get_tags_from_sdt (parse, sdt_info);
1115 gst_element_post_message (GST_ELEMENT_CAST (parse),
1116 gst_message_new_element (GST_OBJECT (parse),
1117 gst_structure_copy (sdt_info)));
1121 mpegts_parse_apply_eit (MpegTSParse * parse,
1122 guint16 pmt_pid, GstStructure * eit_info)
1124 mpegts_parse_get_tags_from_eit (parse, eit_info);
1126 gst_element_post_message (GST_ELEMENT_CAST (parse),
1127 gst_message_new_element (GST_OBJECT (parse),
1128 gst_structure_copy (eit_info)));
1132 mpegts_parse_apply_tdt (MpegTSParse * parse,
1133 guint16 tdt_pid, GstStructure * tdt_info)
1135 gst_element_post_message (GST_ELEMENT_CAST (parse),
1136 gst_message_new_element (GST_OBJECT (parse),
1137 gst_structure_copy (tdt_info)));
1139 gst_element_send_event (GST_ELEMENT_CAST (parse),
1140 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
1141 gst_structure_copy (tdt_info)));
1145 mpegts_parse_handle_psi (MpegTSParse * parse, MpegTSPacketizerSection * section)
1147 gboolean res = TRUE;
1148 GstStructure *structure = NULL;
1150 /* table ids 0x70 - 0x73 do not have a crc */
1151 if (G_LIKELY (section->table_id < 0x70 || section->table_id > 0x73)) {
1152 if (G_UNLIKELY (mpegts_parse_calc_crc32 (GST_BUFFER_DATA (section->buffer),
1153 GST_BUFFER_SIZE (section->buffer)) != 0)) {
1154 GST_WARNING_OBJECT (parse, "bad crc in psi pid 0x%x", section->pid);
1159 switch (section->table_id) {
1162 structure = mpegts_packetizer_parse_pat (parse->packetizer, section);
1163 if (G_LIKELY (structure))
1164 mpegts_parse_apply_pat (parse, structure);
1170 structure = mpegts_packetizer_parse_pmt (parse->packetizer, section);
1171 if (G_LIKELY (structure))
1172 mpegts_parse_apply_pmt (parse, section->pid, structure);
1178 /* NIT, actual network */
1180 /* NIT, other network */
1181 structure = mpegts_packetizer_parse_nit (parse->packetizer, section);
1182 if (G_LIKELY (structure))
1183 mpegts_parse_apply_nit (parse, section->pid, structure);
1190 structure = mpegts_packetizer_parse_sdt (parse->packetizer, section);
1191 if (G_LIKELY (structure))
1192 mpegts_parse_apply_sdt (parse, section->pid, structure);
1198 /* EIT, present/following */
1232 structure = mpegts_packetizer_parse_eit (parse->packetizer, section);
1233 if (G_LIKELY (structure))
1234 mpegts_parse_apply_eit (parse, section->pid, structure);
1239 /* TDT (Time and Date table) */
1240 structure = mpegts_packetizer_parse_tdt (parse->packetizer, section);
1241 if (G_LIKELY (structure))
1242 mpegts_parse_apply_tdt (parse, section->pid, structure);
1251 gst_structure_free (structure);
1257 mpegts_parse_get_tags_from_sdt (MpegTSParse * parse, GstStructure * sdt_info)
1259 const GValue *services;
1262 services = gst_structure_get_value (sdt_info, "services");
1264 for (i = 0; i < gst_value_list_get_size (services); i++) {
1265 const GstStructure *service;
1266 const gchar *sid_str;
1268 gint program_number;
1269 MpegTSParseProgram *program;
1271 service = gst_value_get_structure (gst_value_list_get_value (services, i));
1273 /* get program_number from structure name
1274 * which looks like service-%d */
1275 sid_str = gst_structure_get_name (service);
1276 tmp = g_strstr_len (sid_str, -1, "-");
1279 program_number = atoi (++tmp);
1281 program = mpegts_parse_get_program (parse, program_number);
1282 if (program && program->tspad && !program->tspad->tags) {
1283 program->tspad->tags = gst_tag_list_new_full (GST_TAG_ARTIST,
1284 gst_structure_get_string (service, "name"), NULL);
1290 mpegts_parse_get_tags_from_eit (MpegTSParse * parse, GstStructure * eit_info)
1292 const GValue *events;
1294 guint program_number;
1295 MpegTSParseProgram *program;
1296 gboolean present_following;
1298 gst_structure_get_uint (eit_info, "service-id", &program_number);
1299 program = mpegts_parse_get_program (parse, program_number);
1301 gst_structure_get_boolean (eit_info, "present-following", &present_following);
1303 if (program && program->tspad && present_following) {
1304 events = gst_structure_get_value (eit_info, "events");
1306 for (i = 0; i < gst_value_list_get_size (events); i++) {
1307 const GstStructure *event;
1313 event = gst_value_get_structure (gst_value_list_get_value (events, i));
1315 title = gst_structure_get_string (event, "name");
1316 gst_structure_get_uint (event, "event-id", &event_id);
1317 gst_structure_get_uint (event, "running-status", &status);
1319 if (title && event_id != program->tspad->event_id
1320 && status == RUNNING_STATUS_RUNNING) {
1321 gst_structure_get_uint (event, "duration", &duration);
1323 program->tspad->event_id = event_id;
1324 program->tspad->tags = gst_tag_list_new_full (GST_TAG_TITLE,
1325 title, GST_TAG_DURATION, duration * GST_SECOND, NULL);
1332 mpegts_parse_sink_event (GstPad * pad, GstEvent * event)
1335 MpegTSParse *parse =
1336 GST_MPEGTS_PARSE (gst_object_get_parent (GST_OBJECT (pad)));
1338 switch (GST_EVENT_TYPE (event)) {
1339 case GST_EVENT_FLUSH_STOP:
1340 mpegts_packetizer_clear (parse->packetizer);
1341 res = gst_pad_event_default (pad, event);
1344 res = gst_pad_event_default (pad, event);
1347 gst_object_unref (parse);
1351 static GstFlowReturn
1352 mpegts_parse_chain (GstPad * pad, GstBuffer * buf)
1354 GstFlowReturn res = GST_FLOW_OK;
1357 MpegTSPacketizerPacketReturn pret;
1358 MpegTSPacketizer *packetizer;
1359 MpegTSPacketizerPacket packet;
1361 parse = GST_MPEGTS_PARSE (gst_object_get_parent (GST_OBJECT (pad)));
1362 packetizer = parse->packetizer;
1364 mpegts_packetizer_push (parse->packetizer, buf);
1366 mpegts_packetizer_next_packet (parse->packetizer,
1367 &packet)) != PACKET_NEED_MORE) && res == GST_FLOW_OK) {
1368 if (G_UNLIKELY (pret == PACKET_BAD))
1369 /* bad header, skip the packet */
1372 /* parse PSI data */
1373 if (packet.payload != NULL && mpegts_parse_is_psi (parse, &packet)) {
1374 MpegTSPacketizerSection section;
1376 parsed = mpegts_packetizer_push_section (packetizer, &packet, §ion);
1377 if (G_UNLIKELY (!parsed))
1378 /* bad section data */
1381 if (G_LIKELY (section.complete)) {
1382 /* section complete */
1383 parsed = mpegts_parse_handle_psi (parse, §ion);
1384 gst_buffer_unref (section.buffer);
1386 if (G_UNLIKELY (!parsed))
1390 /* we need to push section packet downstream */
1391 res = mpegts_parse_push (parse, &packet, §ion);
1394 /* push the packet downstream */
1395 res = mpegts_parse_push (parse, &packet, NULL);
1399 mpegts_packetizer_clear_packet (parse->packetizer, &packet);
1402 if (parse->need_sync_program_pads)
1403 mpegts_parse_sync_program_pads (parse);
1405 gst_object_unref (parse);
1409 static GstStateChangeReturn
1410 mpegts_parse_change_state (GstElement * element, GstStateChange transition)
1413 GstStateChangeReturn ret;
1415 parse = GST_MPEGTS_PARSE (element);
1416 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1418 switch (transition) {
1419 case GST_STATE_CHANGE_PAUSED_TO_READY:
1420 mpegts_parse_reset (parse);
1430 mpegts_parse_src_pad_query (GstPad * pad, GstQuery * query)
1432 MpegTSParse *parse = GST_MPEGTS_PARSE (gst_pad_get_parent (pad));
1435 switch (GST_QUERY_TYPE (query)) {
1436 case GST_QUERY_LATENCY:
1438 if ((res = gst_pad_peer_query (parse->sinkpad, query))) {
1440 GstClockTime min_latency, max_latency;
1442 gst_query_parse_latency (query, &is_live, &min_latency, &max_latency);
1444 min_latency += TS_LATENCY * GST_MSECOND;
1445 if (max_latency != GST_CLOCK_TIME_NONE)
1446 max_latency += TS_LATENCY * GST_MSECOND;
1449 gst_query_set_latency (query, is_live, min_latency, max_latency);
1455 res = gst_pad_query_default (pad, query);
1457 gst_object_unref (parse);
1462 gst_mpegtsparse_plugin_init (GstPlugin * plugin)
1464 GST_DEBUG_CATEGORY_INIT (mpegts_parse_debug, "mpegtsparse", 0,
1465 "MPEG transport stream parser");
1467 gst_mpegtsdesc_init_debug ();
1469 return gst_element_register (plugin, "mpegtsparse",
1470 GST_RANK_NONE, GST_TYPE_MPEGTS_PARSE);