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 "mpegtsbase.h"
32
#include "mpegtsparse.h"
33
#include "gstmpegdesc.h"
35
/* latency in mseconds */
36
#define TS_LATENCY 700
38
#define TABLE_ID_UNSET 0xFF
39
#define RUNNING_STATUS_RUNNING 4
41
GST_DEBUG_CATEGORY_STATIC (mpegts_parse_debug);
42
#define GST_CAT_DEFAULT mpegts_parse_debug
44
typedef struct _MpegTSParsePad MpegTSParsePad;
48
MpegTSBaseProgram program;
51
MpegTSParsePad *tspad;
54
struct _MpegTSParsePad
58
/* the program number that the peer wants on this pad */
60
MpegTSParseProgram *program;
62
/* set to FALSE before a push and TRUE after */
65
/* the return of the latest push */
66
GstFlowReturn flow_return;
72
static GstStaticPadTemplate src_template =
73
GST_STATIC_PAD_TEMPLATE ("src%d", GST_PAD_SRC,
75
GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
78
static GstStaticPadTemplate program_template =
79
GST_STATIC_PAD_TEMPLATE ("program_%d", GST_PAD_SRC,
81
GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
92
mpegts_parse_program_started (MpegTSBase * base, MpegTSBaseProgram * program);
94
mpegts_parse_program_stopped (MpegTSBase * base, MpegTSBaseProgram * program);
97
mpegts_parse_push (MpegTSBase * base, MpegTSPacketizerPacket * packet,
98
MpegTSPacketizerSection * section);
99
static void mpegts_parse_set_property (GObject * object, guint prop_id,
100
const GValue * value, GParamSpec * pspec);
101
static void mpegts_parse_get_property (GObject * object, guint prop_id,
102
GValue * value, GParamSpec * pspec);
103
static void mpegts_parse_finalize (GObject * object);
105
static MpegTSParsePad *mpegts_parse_create_tspad (MpegTSParse2 * parse,
107
static void mpegts_parse_destroy_tspad (MpegTSParse2 * parse,
108
MpegTSParsePad * tspad);
109
static GstPad *mpegts_parse_activate_program (MpegTSParse2 * parse,
110
MpegTSParseProgram * program);
111
static void mpegts_parse_reset_selected_programs (MpegTSParse2 * parse,
114
static void mpegts_parse_pad_removed (GstElement * element, GstPad * pad);
115
static GstPad *mpegts_parse_request_new_pad (GstElement * element,
116
GstPadTemplate * templ, const gchar * name);
117
static void mpegts_parse_release_pad (GstElement * element, GstPad * pad);
118
static gboolean mpegts_parse_src_pad_query (GstPad * pad, GstQuery * query);
119
static gboolean push_event (MpegTSBase * base, GstEvent * event);
121
GST_BOILERPLATE (MpegTSParse2, mpegts_parse, MpegTSBase, GST_TYPE_MPEGTS_BASE);
124
mpegts_parse_base_init (gpointer klass)
126
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
128
gst_element_class_add_pad_template (element_class,
129
gst_static_pad_template_get (&src_template));
130
gst_element_class_add_pad_template (element_class,
131
gst_static_pad_template_get (&program_template));
133
gst_element_class_set_details_simple (element_class,
134
"MPEG transport stream parser", "Codec/Parser",
135
"Parses MPEG2 transport streams",
136
"Alessandro Decina <alessandro@nnva.org>, "
137
"Zaheer Abbas Merali <zaheerabbas at merali dot org>");
141
mpegts_parse_class_init (MpegTSParse2Class * klass)
143
GObjectClass *gobject_class;
144
GstElementClass *element_class;
145
MpegTSBaseClass *ts_class;
147
element_class = GST_ELEMENT_CLASS (klass);
148
element_class->pad_removed = mpegts_parse_pad_removed;
149
element_class->request_new_pad = mpegts_parse_request_new_pad;
150
element_class->release_pad = mpegts_parse_release_pad;
152
gobject_class = G_OBJECT_CLASS (klass);
153
gobject_class->set_property = mpegts_parse_set_property;
154
gobject_class->get_property = mpegts_parse_get_property;
155
gobject_class->finalize = mpegts_parse_finalize;
157
g_object_class_install_property (gobject_class, PROP_PROGRAM_NUMBERS,
158
g_param_spec_string ("program-numbers",
160
"Colon separated list of programs", "",
161
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
163
ts_class = GST_MPEGTS_BASE_CLASS (klass);
164
ts_class->push = GST_DEBUG_FUNCPTR (mpegts_parse_push);
165
ts_class->push_event = GST_DEBUG_FUNCPTR (push_event);
166
ts_class->program_started = GST_DEBUG_FUNCPTR (mpegts_parse_program_started);
167
ts_class->program_stopped = GST_DEBUG_FUNCPTR (mpegts_parse_program_stopped);
171
mpegts_parse_init (MpegTSParse2 * parse, MpegTSParse2Class * klass)
173
parse->need_sync_program_pads = FALSE;
174
parse->program_numbers = g_strdup ("");
175
parse->pads_to_add = NULL;
176
parse->pads_to_remove = NULL;
177
GST_MPEGTS_BASE (parse)->program_size = sizeof (MpegTSParseProgram);
181
mpegts_parse_finalize (GObject * object)
183
MpegTSParse2 *parse = GST_MPEGTS_PARSE (object);
185
g_free (parse->program_numbers);
187
if (G_OBJECT_CLASS (parent_class)->finalize)
188
G_OBJECT_CLASS (parent_class)->finalize (object);
192
mpegts_parse_set_property (GObject * object, guint prop_id,
193
const GValue * value, GParamSpec * pspec)
195
MpegTSParse2 *parse = GST_MPEGTS_PARSE (object);
198
case PROP_PROGRAM_NUMBERS:
199
mpegts_parse_reset_selected_programs (parse, g_value_dup_string (value));
202
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
207
mpegts_parse_get_property (GObject * object, guint prop_id,
208
GValue * value, GParamSpec * pspec)
210
MpegTSParse2 *parse = GST_MPEGTS_PARSE (object);
213
case PROP_PROGRAM_NUMBERS:
214
g_value_set_string (value, parse->program_numbers);
217
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
222
mpegts_parse_activate_program (MpegTSParse2 * parse,
223
MpegTSParseProgram * program)
225
MpegTSParsePad *tspad;
229
g_strdup_printf ("program_%d",
230
((MpegTSBaseProgram *) program)->program_number);
232
tspad = mpegts_parse_create_tspad (parse, pad_name);
233
tspad->program_number = ((MpegTSBaseProgram *) program)->program_number;
234
tspad->program = program;
235
program->tspad = tspad;
237
gst_pad_set_active (tspad->pad, TRUE);
238
program->active = TRUE;
244
push_event (MpegTSBase * base, GstEvent * event)
246
MpegTSParse2 *parse = (MpegTSParse2 *) base;
249
for (tmp = GST_ELEMENT_CAST (parse)->srcpads; tmp; tmp = tmp->next) {
250
GstPad *pad = (GstPad *) tmp->data;
252
gst_event_ref (event);
253
gst_pad_push_event (pad, event);
260
mpegts_parse_deactivate_program (MpegTSParse2 * parse,
261
MpegTSParseProgram * program)
263
MpegTSParsePad *tspad;
265
tspad = program->tspad;
266
gst_pad_set_active (tspad->pad, FALSE);
267
program->active = FALSE;
269
/* tspad will be destroyed in GstElementClass::pad_removed */
275
mpegts_parse_sync_program_pads (MpegTSParse2 * parse)
279
GST_INFO_OBJECT (parse, "begin sync pads");
280
for (walk = parse->pads_to_remove; walk; walk = walk->next)
281
gst_element_remove_pad (GST_ELEMENT (parse), GST_PAD (walk->data));
283
for (walk = parse->pads_to_add; walk; walk = walk->next)
284
gst_element_add_pad (GST_ELEMENT (parse), GST_PAD (walk->data));
286
if (parse->pads_to_add)
287
g_list_free (parse->pads_to_add);
289
if (parse->pads_to_remove)
290
g_list_free (parse->pads_to_remove);
292
GST_OBJECT_LOCK (parse);
293
parse->pads_to_remove = NULL;
294
parse->pads_to_add = NULL;
295
parse->need_sync_program_pads = FALSE;
296
GST_OBJECT_UNLOCK (parse);
298
GST_INFO_OBJECT (parse, "end sync pads");
302
foreach_program_activate_or_deactivate (gpointer key, gpointer value,
305
MpegTSParse2 *parse = GST_MPEGTS_PARSE (data);
306
MpegTSParseProgram *program = (MpegTSParseProgram *) value;
308
/* at this point selected programs have program->selected == 2,
309
* unselected programs thay may have to be deactivated have selected == 1 and
310
* unselected inactive programs have selected == 0 */
312
switch (--program->selected) {
316
&& ((MpegTSBaseProgram *) program)->pmt_pid != G_MAXUINT16)
318
g_list_append (parse->pads_to_add,
319
mpegts_parse_activate_program (parse, program));
321
program->selected = 2;
327
parse->pads_to_remove = g_list_append (parse->pads_to_remove,
328
mpegts_parse_deactivate_program (parse, program));
331
/* was already unselected */
332
program->selected = 0;
335
g_return_if_reached ();
340
mpegts_parse_reset_selected_programs (MpegTSParse2 * parse,
341
gchar * program_numbers)
343
GST_OBJECT_LOCK (parse);
344
if (parse->program_numbers)
345
g_free (parse->program_numbers);
347
parse->program_numbers = program_numbers;
349
if (*parse->program_numbers != '\0') {
351
MpegTSParseProgram *program;
352
gchar **progs, **walk;
354
progs = g_strsplit (parse->program_numbers, ":", 0);
357
while (*walk != NULL) {
358
program_number = strtol (*walk, NULL, 0);
360
(MpegTSParseProgram *) mpegts_base_get_program ((MpegTSBase *) parse,
363
/* create the program, it will get activated once we get a PMT for it */
364
program = (MpegTSParseProgram *) mpegts_base_add_program ((MpegTSBase *)
365
parse, program_number, G_MAXUINT16);
366
program->selected = 2;
372
g_hash_table_foreach (((MpegTSBase *) parse)->programs,
373
foreach_program_activate_or_deactivate, parse);
375
if (parse->pads_to_remove || parse->pads_to_add)
376
parse->need_sync_program_pads = TRUE;
377
GST_OBJECT_UNLOCK (parse);
381
static MpegTSParsePad *
382
mpegts_parse_create_tspad (MpegTSParse2 * parse, const gchar * pad_name)
385
MpegTSParsePad *tspad;
387
pad = gst_pad_new_from_static_template (&program_template, pad_name);
388
gst_pad_set_query_function (pad,
389
GST_DEBUG_FUNCPTR (mpegts_parse_src_pad_query));
391
/* create our wrapper */
392
tspad = g_new0 (MpegTSParsePad, 1);
394
tspad->program_number = -1;
395
tspad->program = NULL;
396
tspad->pushed = FALSE;
397
tspad->flow_return = GST_FLOW_NOT_LINKED;
398
gst_pad_set_element_private (pad, tspad);
404
mpegts_parse_destroy_tspad (MpegTSParse2 * parse, MpegTSParsePad * tspad)
407
gst_tag_list_free (tspad->tags);
410
/* free the wrapper */
415
mpegts_parse_pad_removed (GstElement * element, GstPad * pad)
417
MpegTSParsePad *tspad;
418
MpegTSParse2 *parse = GST_MPEGTS_PARSE (element);
420
if (gst_pad_get_direction (pad) == GST_PAD_SINK)
423
tspad = (MpegTSParsePad *) gst_pad_get_element_private (pad);
424
mpegts_parse_destroy_tspad (parse, tspad);
426
if (GST_ELEMENT_CLASS (parent_class)->pad_removed)
427
GST_ELEMENT_CLASS (parent_class)->pad_removed (element, pad);
431
mpegts_parse_request_new_pad (GstElement * element, GstPadTemplate * template,
432
const gchar * unused)
438
g_return_val_if_fail (template != NULL, NULL);
439
g_return_val_if_fail (GST_IS_MPEGTS_PARSE (element), NULL);
441
parse = GST_MPEGTS_PARSE (element);
443
GST_OBJECT_LOCK (element);
444
name = g_strdup_printf ("src%d", parse->req_pads++);
445
GST_OBJECT_UNLOCK (element);
447
pad = mpegts_parse_create_tspad (parse, name)->pad;
448
gst_pad_set_active (pad, TRUE);
449
gst_element_add_pad (element, pad);
456
mpegts_parse_release_pad (GstElement * element, GstPad * pad)
458
g_return_if_fail (GST_IS_MPEGTS_PARSE (element));
460
gst_pad_set_active (pad, FALSE);
461
/* we do the cleanup in GstElement::pad-removed */
462
gst_element_remove_pad (element, pad);
466
mpegts_parse_tspad_push_section (MpegTSParse2 * parse, MpegTSParsePad * tspad,
467
MpegTSPacketizerSection * section, GstBuffer * buffer)
469
GstFlowReturn ret = GST_FLOW_NOT_LINKED;
470
gboolean to_push = TRUE;
472
if (tspad->program_number != -1) {
473
if (tspad->program) {
474
/* we push all sections to all pads except PMTs which we
475
* only push to pads meant to receive that program number */
476
if (section->table_id == 0x02) {
478
if (section->subtable_extension != tspad->program_number)
482
/* there's a program filter on the pad but the PMT for the program has not
483
* been parsed yet, ignore the pad until we get a PMT */
488
GST_DEBUG_OBJECT (parse,
489
"pushing section: %d program number: %d table_id: %d", to_push,
490
tspad->program_number, section->table_id);
492
ret = gst_pad_push (tspad->pad, buffer);
494
gst_buffer_unref (buffer);
495
if (gst_pad_is_linked (tspad->pad))
503
mpegts_parse_tspad_push (MpegTSParse2 * parse, MpegTSParsePad * tspad,
504
guint16 pid, GstBuffer * buffer)
506
GstFlowReturn ret = GST_FLOW_NOT_LINKED;
507
MpegTSBaseStream **pad_pids = NULL;
509
if (tspad->program_number != -1) {
510
if (tspad->program) {
511
MpegTSBaseProgram *bp = (MpegTSBaseProgram *) tspad->program;
512
pad_pids = bp->streams;
514
gst_element_found_tags_for_pad (GST_ELEMENT_CAST (parse), tspad->pad,
519
/* there's a program filter on the pad but the PMT for the program has not
520
* been parsed yet, ignore the pad until we get a PMT */
521
gst_buffer_unref (buffer);
527
if (pad_pids == NULL || pad_pids[pid]) {
528
/* push if there's no filter or if the pid is in the filter */
529
ret = gst_pad_push (tspad->pad, buffer);
531
gst_buffer_unref (buffer);
532
if (gst_pad_is_linked (tspad->pad))
541
pad_clear_for_push (GstPad * pad, MpegTSParse2 * parse)
543
MpegTSParsePad *tspad = (MpegTSParsePad *) gst_pad_get_element_private (pad);
545
tspad->flow_return = GST_FLOW_NOT_LINKED;
546
tspad->pushed = FALSE;
550
mpegts_parse_push (MpegTSBase * base, MpegTSPacketizerPacket * packet,
551
MpegTSPacketizerSection * section)
553
MpegTSParse2 *parse = (MpegTSParse2 *) base;
555
gboolean done = FALSE;
557
MpegTSParsePad *tspad;
563
if (G_UNLIKELY (parse->need_sync_program_pads))
564
mpegts_parse_sync_program_pads (parse);
567
buffer = gst_buffer_make_metadata_writable (packet->buffer);
568
/* we have the same caps on all the src pads */
569
gst_buffer_set_caps (buffer, base->packetizer->caps);
571
GST_OBJECT_LOCK (parse);
572
/* clear tspad->pushed on pads */
573
g_list_foreach (GST_ELEMENT_CAST (parse)->srcpads,
574
(GFunc) pad_clear_for_push, parse);
575
if (GST_ELEMENT_CAST (parse)->srcpads)
576
ret = GST_FLOW_NOT_LINKED;
580
/* Get cookie and source pads list */
581
pads_cookie = GST_ELEMENT_CAST (parse)->pads_cookie;
582
srcpads = GST_ELEMENT_CAST (parse)->srcpads;
583
if (G_LIKELY (srcpads)) {
584
pad = GST_PAD_CAST (srcpads->data);
587
GST_OBJECT_UNLOCK (parse);
589
while (pad && !done) {
590
tspad = gst_pad_get_element_private (pad);
592
if (G_LIKELY (!tspad->pushed)) {
593
/* ref the buffer as gst_pad_push takes a ref but we want to reuse the
594
* same buffer for next pushes */
595
gst_buffer_ref (buffer);
598
mpegts_parse_tspad_push_section (parse, tspad, section, buffer);
601
mpegts_parse_tspad_push (parse, tspad, pid, buffer);
603
tspad->pushed = TRUE;
605
if (G_UNLIKELY (tspad->flow_return != GST_FLOW_OK
606
&& tspad->flow_return != GST_FLOW_NOT_LINKED)) {
607
/* return the error upstream */
608
ret = tspad->flow_return;
614
if (ret == GST_FLOW_NOT_LINKED)
615
ret = tspad->flow_return;
617
g_object_unref (pad);
619
if (G_UNLIKELY (!done)) {
620
GST_OBJECT_LOCK (parse);
621
if (G_UNLIKELY (pads_cookie != GST_ELEMENT_CAST (parse)->pads_cookie)) {
623
GST_DEBUG ("resync");
624
pads_cookie = GST_ELEMENT_CAST (parse)->pads_cookie;
625
srcpads = GST_ELEMENT_CAST (parse)->srcpads;
627
GST_DEBUG ("getting next pad");
629
srcpads = g_list_next (srcpads);
633
pad = GST_PAD_CAST (srcpads->data);
637
GST_OBJECT_UNLOCK (parse);
641
gst_buffer_unref (buffer);
642
packet->buffer = NULL;
648
mpegts_parse_program_started (MpegTSBase * base, MpegTSBaseProgram * program)
650
MpegTSParse2 *parse = GST_MPEGTS_PARSE (base);
651
MpegTSParseProgram *parseprogram = (MpegTSParseProgram *) program;
652
if (parseprogram->selected == 2) {
654
g_list_append (parse->pads_to_add,
655
mpegts_parse_activate_program (parse, parseprogram));
656
parseprogram->selected = 1;
657
parse->need_sync_program_pads = TRUE;
663
mpegts_parse_program_stopped (MpegTSBase * base, MpegTSBaseProgram * program)
665
MpegTSParse2 *parse = GST_MPEGTS_PARSE (base);
666
MpegTSParseProgram *parseprogram = (MpegTSParseProgram *) program;
668
if (parseprogram->active) {
669
parse->pads_to_remove =
670
g_list_append (parse->pads_to_remove,
671
mpegts_parse_deactivate_program (parse, parseprogram));
672
parse->need_sync_program_pads = TRUE;
677
mpegts_parse_src_pad_query (GstPad * pad, GstQuery * query)
679
MpegTSParse2 *parse = GST_MPEGTS_PARSE (gst_pad_get_parent (pad));
682
switch (GST_QUERY_TYPE (query)) {
683
case GST_QUERY_LATENCY:
685
if ((res = gst_pad_peer_query (((MpegTSBase *) parse)->sinkpad, query))) {
687
GstClockTime min_latency, max_latency;
689
gst_query_parse_latency (query, &is_live, &min_latency, &max_latency);
691
min_latency += TS_LATENCY * GST_MSECOND;
692
if (max_latency != GST_CLOCK_TIME_NONE)
693
max_latency += TS_LATENCY * GST_MSECOND;
696
gst_query_set_latency (query, is_live, min_latency, max_latency);
702
res = gst_pad_query_default (pad, query);
704
gst_object_unref (parse);
709
gst_mpegtsparse_plugin_init (GstPlugin * plugin)
711
GST_DEBUG_CATEGORY_INIT (mpegts_parse_debug, "tsparse", 0,
712
"MPEG transport stream parser");
714
gst_mpegtsdesc_init_debug ();
716
return gst_element_register (plugin, "tsparse",
717
GST_RANK_NONE, GST_TYPE_MPEGTS_PARSE);