blob: fb9938881abb2554e65072e94870c1c5b80bee14 [file] [log] [blame]
hanghang.luofa7b16f2024-05-31 14:44:31 +08001/*
2 * mpegtsparse.c -
3 * Copyright (C) 2007 Alessandro Decina
4 *
5 * Authors:
6 * Alessandro Decina <alessandro@nnva.org>
7 * Zaheer Abbas Merali <zaheerabbas at merali dot org>
8 *
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.
13 *
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.
18 *
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., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include "amltsbase.h"
33#include "amltsparse.h"
34#include "gstamldesc.h"
35#include "amltsdemux.h"
36/* latency in mseconds is maximum 100 ms between PCR */
37#define TS_LATENCY 100
38
39#define TABLE_ID_UNSET 0xFF
40#define RUNNING_STATUS_RUNNING 4
41#define SYNC_BYTE 0x47
42
43GST_DEBUG_CATEGORY_STATIC (mpegts_parse_debug);
44#define GST_CAT_DEFAULT mpegts_parse_debug
45
46typedef struct _AmlTSParsePad AmlTSParsePad;
47
48typedef struct
49{
50 AmlTSBaseProgram program;
51 AmlTSParsePad *tspad;
52} AmlTSParseProgram;
53
54struct _AmlTSParsePad
55{
56 GstPad *pad;
57
58 /* the program number that the peer wants on this pad */
59 gint program_number;
60 AmlTSParseProgram *program;
61
62 /* set to FALSE before a push and TRUE after */
63 gboolean pushed;
64
65 /* the return of the latest push */
66 GstFlowReturn flow_return;
67
68 AmlTSParse2Adapter ts_adapter;
69};
70
71static GstStaticPadTemplate src_template =
72GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC,
73 GST_PAD_ALWAYS,
74 GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
75 );
76
77static GstStaticPadTemplate program_template =
78GST_STATIC_PAD_TEMPLATE ("program_%u", GST_PAD_SRC,
79 GST_PAD_REQUEST,
80 GST_STATIC_CAPS ("video/mpegts, " "systemstream = (boolean) true ")
81 );
82
83enum
84{
85 PROP_0,
86 PROP_SET_TIMESTAMPS,
87 PROP_SMOOTHING_LATENCY,
88 PROP_PCR_PID,
89 PROP_ALIGNMENT,
90 PROP_SPLIT_ON_RAI,
91 /* FILL ME */
92};
93
94static void amlts_parse_set_property (GObject * object, guint prop_id,
95 const GValue * value, GParamSpec * pspec);
96static void amlts_parse_get_property (GObject * object, guint prop_id,
97 GValue * value, GParamSpec * pspec);
98
99static void
100amlts_parse_program_started (AmlTSBase * base, AmlTSBaseProgram * program);
101static void
102amlts_parse_program_stopped (AmlTSBase * base, AmlTSBaseProgram * program);
103
104static GstFlowReturn
105amlts_parse_push (AmlTSBase * base, AmlTSPacketizerPacket * packet,
106 GstMpegtsSection * section);
107static void amlts_parse_inspect_packet (AmlTSBase * base,
108 AmlTSPacketizerPacket * packet);
109static GstFlowReturn amlts_parse_have_buffer (AmlTSBase * base,
110 GstBuffer * buffer);
111
112static AmlTSParsePad *amlts_parse_create_tspad (AmlTSParse2 * parse,
113 const gchar * name);
114static void amlts_parse_destroy_tspad (AmlTSParse2 * parse,
115 AmlTSParsePad * tspad);
116
117static void amlts_parse_pad_removed (GstElement * element, GstPad * pad);
118static GstPad *amlts_parse_request_new_pad (GstElement * element,
119 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
120static void amlts_parse_release_pad (GstElement * element, GstPad * pad);
121static gboolean amlts_parse_src_pad_query (GstPad * pad, GstObject * parent,
122 GstQuery * query);
123static gboolean push_event (AmlTSBase * base, GstEvent * event);
124
125#define amlts_parse_parent_class parent_class
126G_DEFINE_TYPE (AmlTSParse2, amlts_parse, GST_TYPE_AMLTS_BASE);
127
128static void amlts_parse_reset (AmlTSBase * base);
129static GstFlowReturn amlts_parse_input_done (AmlTSBase * base);
130static GstFlowReturn
131drain_pending_buffers (AmlTSParse2 * parse, gboolean drain_all);
132
133static void
134amlts_parse_finalize (GObject * object)
135{
136 AmlTSParse2 *parse = (AmlTSParse2 *) object;
137
138 gst_flow_combiner_free (parse->flowcombiner);
139
140 gst_adapter_clear (parse->ts_adapter.adapter);
141 g_object_unref (parse->ts_adapter.adapter);
142
143 GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
144}
145
146static void
147amlts_parse_class_init (AmlTSParse2Class * klass)
148{
149 GObjectClass *gobject_class = (GObjectClass *) (klass);
150 GstElementClass *element_class;
151 AmlTSBaseClass *ts_class;
152
153 gobject_class->set_property = amlts_parse_set_property;
154 gobject_class->get_property = amlts_parse_get_property;
155 gobject_class->finalize = amlts_parse_finalize;
156
157 g_object_class_install_property (gobject_class, PROP_SET_TIMESTAMPS,
158 g_param_spec_boolean ("set-timestamps",
159 "Timestamp (or re-timestamp) the output stream",
160 "If set, timestamps will be set on the output buffers using "
161 "PCRs and smoothed over the smoothing-latency period", FALSE,
162 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
163 g_object_class_install_property (gobject_class, PROP_SMOOTHING_LATENCY,
164 g_param_spec_uint ("smoothing-latency", "Smoothing Latency",
165 "Additional latency in microseconds for smoothing jitter in input timestamps on live capture",
166 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
167 g_object_class_install_property (gobject_class, PROP_PCR_PID,
168 g_param_spec_int ("pcr-pid", "PID containing PCR",
169 "Set the PID to use for PCR values (-1 for auto)",
170 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
171 g_object_class_install_property (gobject_class, PROP_ALIGNMENT,
172 g_param_spec_uint ("alignment", "Alignment",
173 "Number of packets per buffer (padded with dummy packets on EOS) (0 = auto)",
174 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
175 g_object_class_install_property (gobject_class, PROP_SPLIT_ON_RAI,
176 g_param_spec_boolean ("split-on-rai", "Split on RAI",
177 "If set, buffers sized smaller than the alignment will be sent "
178 "so that RAI packets are at the start of a new buffer", FALSE,
179 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
180
181 element_class = GST_ELEMENT_CLASS (klass);
182 element_class->pad_removed = amlts_parse_pad_removed;
183 element_class->request_new_pad = amlts_parse_request_new_pad;
184 element_class->release_pad = amlts_parse_release_pad;
185
186 gst_element_class_add_static_pad_template (element_class, &src_template);
187 gst_element_class_add_static_pad_template (element_class, &program_template);
188
189 gst_element_class_set_static_metadata (element_class,
190 "MPEG transport stream parser", "Codec/Parser",
191 "Parses MPEG2 transport streams",
192 "Alessandro Decina <alessandro@nnva.org>, "
193 "Zaheer Abbas Merali <zaheerabbas at merali dot org>");
194
195 ts_class = GST_AMLTS_BASE_CLASS (klass);
196 ts_class->push = GST_DEBUG_FUNCPTR (amlts_parse_push);
197 ts_class->push_event = GST_DEBUG_FUNCPTR (push_event);
198 ts_class->program_started = GST_DEBUG_FUNCPTR (amlts_parse_program_started);
199 ts_class->program_stopped = GST_DEBUG_FUNCPTR (amlts_parse_program_stopped);
200 ts_class->reset = GST_DEBUG_FUNCPTR (amlts_parse_reset);
201 ts_class->input_done = GST_DEBUG_FUNCPTR (amlts_parse_input_done);
202 ts_class->inspect_packet = GST_DEBUG_FUNCPTR (amlts_parse_inspect_packet);
203}
204
205static void
206amlts_parse_init (AmlTSParse2 * parse)
207{
208 AmlTSBase *base = (AmlTSBase *) parse;
209
210 base->program_size = sizeof (AmlTSParseProgram);
211 base->push_data = TRUE;
212 base->push_section = TRUE;
213 base->push_unknown = TRUE;
214
215 parse->user_pcr_pid = parse->pcr_pid = -1;
216
217 parse->flowcombiner = gst_flow_combiner_new ();
218
219 parse->srcpad = gst_pad_new_from_static_template (&src_template, "src");
220 gst_flow_combiner_add_pad (parse->flowcombiner, parse->srcpad);
221 parse->first = TRUE;
222 gst_pad_set_query_function (parse->srcpad,
223 GST_DEBUG_FUNCPTR (amlts_parse_src_pad_query));
224 gst_element_add_pad (GST_ELEMENT (parse), parse->srcpad);
225
226 parse->have_group_id = FALSE;
227 parse->group_id = G_MAXUINT;
228
229 parse->ts_adapter.adapter = gst_adapter_new ();
230 parse->ts_adapter.packets_in_adapter = 0;
231 parse->ts_adapter.first_is_keyframe = TRUE;
232 parse->alignment = 0;
233 parse->is_eos = FALSE;
234 parse->header = 0;
235 parse->split_on_rai = FALSE;
236}
237
238static void
239amlts_parse_reset (AmlTSBase * base)
240{
241 AmlTSParse2 *parse = (AmlTSParse2 *) base;
242
243 /* Set the various know PIDs we are interested in */
244
245 /* CAT */
246 MPEGTS_BIT_SET (base->known_psi, 1);
247 /* NIT, ST */
248 MPEGTS_BIT_SET (base->known_psi, 0x10);
249 /* SDT, BAT, ST */
250 MPEGTS_BIT_SET (base->known_psi, 0x11);
251 /* EIT, ST, CIT (TS 102 323) */
252 MPEGTS_BIT_SET (base->known_psi, 0x12);
253 /* RST, ST */
254 MPEGTS_BIT_SET (base->known_psi, 0x13);
255 /* RNT (TS 102 323) */
256 MPEGTS_BIT_SET (base->known_psi, 0x16);
257 /* inband signalling */
258 MPEGTS_BIT_SET (base->known_psi, 0x1c);
259 /* measurement */
260 MPEGTS_BIT_SET (base->known_psi, 0x1d);
261 /* DIT */
262 MPEGTS_BIT_SET (base->known_psi, 0x1e);
263 /* SIT */
264 MPEGTS_BIT_SET (base->known_psi, 0x1f);
265
266 parse->first = TRUE;
267 parse->have_group_id = FALSE;
268 parse->group_id = G_MAXUINT;
269
270 g_list_free_full (parse->pending_buffers, (GDestroyNotify) gst_buffer_unref);
271 parse->pending_buffers = NULL;
272
273 parse->current_pcr = GST_CLOCK_TIME_NONE;
274 parse->previous_pcr = GST_CLOCK_TIME_NONE;
275 parse->base_pcr = GST_CLOCK_TIME_NONE;
276 parse->bytes_since_pcr = 0;
277 parse->pcr_pid = parse->user_pcr_pid;
278 parse->ts_offset = 0;
279
280 gst_adapter_clear (parse->ts_adapter.adapter);
281 parse->ts_adapter.packets_in_adapter = 0;
282 parse->ts_adapter.first_is_keyframe = TRUE;
283 parse->is_eos = FALSE;
284 parse->header = 0;
285}
286
287static void
288amlts_parse_set_property (GObject * object, guint prop_id,
289 const GValue * value, GParamSpec * pspec)
290{
291 AmlTSParse2 *parse = (AmlTSParse2 *) object;
292
293 switch (prop_id) {
294 case PROP_SET_TIMESTAMPS:
295 parse->set_timestamps = g_value_get_boolean (value);
296 break;
297 case PROP_SMOOTHING_LATENCY:
298 parse->smoothing_latency = GST_USECOND * g_value_get_uint (value);
299 amlts_packetizer_set_pcr_discont_threshold (GST_AMLTS_BASE
300 (parse)->packetizer, parse->smoothing_latency);
301 break;
302 case PROP_PCR_PID:
303 parse->pcr_pid = parse->user_pcr_pid = g_value_get_int (value);
304 break;
305 case PROP_ALIGNMENT:
306 parse->alignment = g_value_get_uint (value);
307 break;
308 case PROP_SPLIT_ON_RAI:
309 parse->split_on_rai = g_value_get_boolean (value);
310 break;
311 default:
312 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
313 }
314}
315
316static void
317amlts_parse_get_property (GObject * object, guint prop_id,
318 GValue * value, GParamSpec * pspec)
319{
320 AmlTSParse2 *parse = (AmlTSParse2 *) object;
321
322 switch (prop_id) {
323 case PROP_SET_TIMESTAMPS:
324 g_value_set_boolean (value, parse->set_timestamps);
325 break;
326 case PROP_SMOOTHING_LATENCY:
327 g_value_set_uint (value, parse->smoothing_latency / GST_USECOND);
328 break;
329 case PROP_PCR_PID:
330 g_value_set_int (value, parse->pcr_pid);
331 break;
332 case PROP_ALIGNMENT:
333 g_value_set_uint (value, parse->alignment);
334 break;
335 case PROP_SPLIT_ON_RAI:
336 g_value_set_boolean (value, parse->split_on_rai);
337 break;
338 default:
339 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
340 }
341}
342
343static gboolean
344prepare_src_pad (AmlTSBase * base, AmlTSParse2 * parse)
345{
346 GstEvent *event;
347 gchar *stream_id;
348 GstCaps *caps;
349
350 if (!parse->first)
351 return TRUE;
352
353 /* If there's no packet_size yet, we can't set caps yet */
354 if (G_UNLIKELY (base->packetizer->packet_size == 0))
355 return FALSE;
356
357 stream_id =
358 gst_pad_create_stream_id (parse->srcpad, GST_ELEMENT_CAST (base),
359 "multi-program");
360
361 event =
362 gst_pad_get_sticky_event (parse->parent.sinkpad, GST_EVENT_STREAM_START,
363 0);
364 if (event) {
365 if (gst_event_parse_group_id (event, &parse->group_id))
366 parse->have_group_id = TRUE;
367 else
368 parse->have_group_id = FALSE;
369 gst_event_unref (event);
370 } else if (!parse->have_group_id) {
371 parse->have_group_id = TRUE;
372 parse->group_id = gst_util_group_id_next ();
373 }
374 event = gst_event_new_stream_start (stream_id);
375 if (parse->have_group_id)
376 gst_event_set_group_id (event, parse->group_id);
377
378 gst_pad_push_event (parse->srcpad, event);
379 g_free (stream_id);
380
381 caps = gst_caps_new_simple ("video/mpegts",
382 "systemstream", G_TYPE_BOOLEAN, TRUE,
383 "packetsize", G_TYPE_INT, base->packetizer->packet_size, NULL);
384
385 gst_pad_set_caps (parse->srcpad, caps);
386 gst_caps_unref (caps);
387
388 /* If setting output timestamps, ensure that the output segment is TIME */
389 if (parse->set_timestamps == FALSE || base->segment.format == GST_FORMAT_TIME)
390 /* Just use the upstream segment */
391 base->out_segment = base->segment;
392 else {
393 GstSegment *seg = &base->out_segment;
394 gst_segment_init (seg, GST_FORMAT_TIME);
395 GST_DEBUG_OBJECT (parse,
396 "Generating time output segment %" GST_SEGMENT_FORMAT, seg);
397 }
398 gst_pad_push_event (parse->srcpad,
399 gst_event_new_segment (&base->out_segment));
400
401 parse->first = FALSE;
402
403 return TRUE;
404}
405
406static gboolean
407push_event (AmlTSBase * base, GstEvent * event)
408{
409 AmlTSParse2 *parse = (AmlTSParse2 *) base;
410 GList *tmp;
411
412 if (G_UNLIKELY (parse->first)) {
413 /* We will send the segment when really starting */
414 if (G_UNLIKELY (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT)) {
415 gst_event_unref (event);
416 return TRUE;
417 }
418 prepare_src_pad (base, parse);
419 }
420 if (G_UNLIKELY (GST_EVENT_TYPE (event) == GST_EVENT_EOS)) {
421 gsize packet_size = base->packetizer->packet_size;
422
423 parse->is_eos = TRUE;
424
425 if (packet_size > 0 && parse->alignment > 0 &&
426 parse->ts_adapter.packets_in_adapter > 0
427 && parse->ts_adapter.packets_in_adapter < parse->alignment) {
428 GstBuffer *buf;
429 GstMapInfo map;
430 guint8 *data;
431 gint missing_packets =
432 parse->alignment - parse->ts_adapter.packets_in_adapter;
433 gint i = missing_packets;
434
435 GST_DEBUG_OBJECT (parse, "Adding %d dummy packets", missing_packets);
436
437 buf = gst_buffer_new_and_alloc (missing_packets * packet_size);
438 gst_buffer_map (buf, &map, GST_MAP_READWRITE);
439 data = map.data;
440
441 for (; i > 0; i--) {
442 gint offset;
443
444 if (packet_size > MPEGTS_NORMAL_PACKETSIZE) {
445 parse->header++;
446 GST_WRITE_UINT32_BE (data, parse->header);
447 offset = 4;
448 } else {
449 offset = 0;
450 }
451 GST_WRITE_UINT8 (data + offset, SYNC_BYTE);
452 /* null packet PID */
453 GST_WRITE_UINT16_BE (data + offset + 1, 0x1FFF);
454 /* no adaptation field exists | continuity counter undefined */
455 GST_WRITE_UINT8 (data + offset + 3, 0x10);
456 /* payload */
457 memset (data + offset + 4, 0, MPEGTS_NORMAL_PACKETSIZE - 4);
458 data += packet_size;
459 }
460 gst_buffer_unmap (buf, &map);
461 gst_adapter_push (parse->ts_adapter.adapter, buf);
462 parse->ts_adapter.packets_in_adapter += missing_packets;
463 }
464 drain_pending_buffers (parse, TRUE);
465 }
466
467 if (G_UNLIKELY (GST_EVENT_TYPE (event) == GST_EVENT_SEGMENT))
468 parse->ts_offset = 0;
469
470 for (tmp = parse->srcpads; tmp; tmp = tmp->next) {
471 GstPad *pad = (GstPad *) tmp->data;
472 if (pad) {
473 gst_event_ref (event);
474 gst_pad_push_event (pad, event);
475 }
476 }
477
478 gst_pad_push_event (parse->srcpad, event);
479
480 return TRUE;
481}
482
483static AmlTSParsePad *
484amlts_parse_create_tspad (AmlTSParse2 * parse, const gchar * pad_name)
485{
486 GstPad *pad;
487 AmlTSParsePad *tspad;
488
489 pad = gst_pad_new_from_static_template (&program_template, pad_name);
490 gst_pad_set_query_function (pad,
491 GST_DEBUG_FUNCPTR (amlts_parse_src_pad_query));
492
493 /* create our wrapper */
494 tspad = g_new0 (AmlTSParsePad, 1);
495 tspad->pad = pad;
496 tspad->program_number = -1;
497 tspad->program = NULL;
498 tspad->pushed = FALSE;
499 tspad->flow_return = GST_FLOW_NOT_LINKED;
500 tspad->ts_adapter.adapter = gst_adapter_new ();
501 tspad->ts_adapter.packets_in_adapter = 0;
502 tspad->ts_adapter.first_is_keyframe = TRUE;
503 gst_pad_set_element_private (pad, tspad);
504 gst_flow_combiner_add_pad (parse->flowcombiner, pad);
505
506 return tspad;
507}
508
509static void
510amlts_parse_destroy_tspad (AmlTSParse2 * parse, AmlTSParsePad * tspad)
511{
512 gst_adapter_clear (tspad->ts_adapter.adapter);
513 g_object_unref (tspad->ts_adapter.adapter);
514
515 /* free the wrapper */
516 g_free (tspad);
517}
518
519static void
520amlts_parse_pad_removed (GstElement * element, GstPad * pad)
521{
522 AmlTSParsePad *tspad;
523 AmlTSParse2 *parse = GST_AMLTS_PARSE (element);
524
525 if (gst_pad_get_direction (pad) == GST_PAD_SINK)
526 return;
527
528 tspad = (AmlTSParsePad *) gst_pad_get_element_private (pad);
529 if (tspad) {
530 amlts_parse_destroy_tspad (parse, tspad);
531
532 parse->srcpads = g_list_remove_all (parse->srcpads, pad);
533 }
534
535 if (GST_ELEMENT_CLASS (parent_class)->pad_removed)
536 GST_ELEMENT_CLASS (parent_class)->pad_removed (element, pad);
537}
538
539static GstPad *
540amlts_parse_request_new_pad (GstElement * element, GstPadTemplate * template,
541 const gchar * padname, const GstCaps * caps)
542{
543 AmlTSParse2 *parse;
544 AmlTSParsePad *tspad;
545 AmlTSParseProgram *parseprogram;
546 GstPad *pad;
547 gint program_num = -1;
548 GstEvent *event;
549 gchar *stream_id;
550
551 g_return_val_if_fail (template != NULL, NULL);
552 g_return_val_if_fail (GST_IS_AMLTS_PARSE (element), NULL);
553 g_return_val_if_fail (padname != NULL, NULL);
554
555 sscanf (padname + 8, "%d", &program_num);
556
557 GST_DEBUG_OBJECT (element, "padname:%s, program:%d", padname, program_num);
558
559 parse = GST_AMLTS_PARSE (element);
560
561 tspad = amlts_parse_create_tspad (parse, padname);
562 tspad->program_number = program_num;
563
564 /* Find if the program is already active */
565 parseprogram =
566 (AmlTSParseProgram *) amlts_base_get_program (GST_AMLTS_BASE (parse),
567 program_num);
568 if (parseprogram) {
569 tspad->program = parseprogram;
570 parseprogram->tspad = tspad;
571 }
572
573 pad = tspad->pad;
574 parse->srcpads = g_list_append (parse->srcpads, pad);
575
576 gst_pad_set_active (pad, TRUE);
577
578 stream_id = gst_pad_create_stream_id (pad, element, padname + 8);
579
580 event =
581 gst_pad_get_sticky_event (parse->parent.sinkpad, GST_EVENT_STREAM_START,
582 0);
583 if (event) {
584 if (gst_event_parse_group_id (event, &parse->group_id))
585 parse->have_group_id = TRUE;
586 else
587 parse->have_group_id = FALSE;
588 gst_event_unref (event);
589 } else if (!parse->have_group_id) {
590 parse->have_group_id = TRUE;
591 parse->group_id = gst_util_group_id_next ();
592 }
593 event = gst_event_new_stream_start (stream_id);
594 if (parse->have_group_id)
595 gst_event_set_group_id (event, parse->group_id);
596
597 gst_pad_push_event (pad, event);
598 g_free (stream_id);
599
600 gst_element_add_pad (element, pad);
601
602 return pad;
603}
604
605static GstBuffer *
606amlts_packet_to_buffer (AmlTSPacketizerPacket * packet)
607{
608 GstBuffer *buf =
609 gst_buffer_new_and_alloc (packet->data_end - packet->data_start);
610 gst_buffer_fill (buf, 0, packet->data_start,
611 packet->data_end - packet->data_start);
612 return buf;
613}
614
615static void
616amlts_parse_release_pad (GstElement * element, GstPad * pad)
617{
618 AmlTSParse2 *parse = (AmlTSParse2 *) element;
619
620 gst_pad_set_active (pad, FALSE);
621 /* we do the cleanup in GstElement::pad-removed */
622 gst_flow_combiner_remove_pad (parse->flowcombiner, pad);
623 gst_element_remove_pad (element, pad);
624}
625
626static GstFlowReturn
627empty_adapter_into_pad (AmlTSParse2 * parse, AmlTSParse2Adapter * ts_adapter,
628 GstPad * pad)
629{
630 GstAdapter *adapter = ts_adapter->adapter;
631 GstBuffer *buf = NULL;
632 guint64 pts_dist, dts_dist;
633 GstClockTime pts, dts;
634 gsize avail = gst_adapter_available (adapter);
635 GstFlowReturn ret = GST_FLOW_OK;
636 gsize offset;
637
638 if (avail > 0)
639 buf = gst_adapter_take_buffer (adapter, avail);
640 /* Find the previous PTS/DTS. We also handle un-aligned input since want to
641 * use the most recent PTS/DTS if present */
642 offset = MIN (GST_AMLTS_BASE (parse)->packetizer->packet_size, 188);
643 pts = gst_adapter_prev_pts_at_offset (adapter, offset, &pts_dist);
644 dts = gst_adapter_prev_dts_at_offset (adapter, offset, &dts_dist);
645
646 GST_LOG_OBJECT (pad,
647 "prev pts:%" GST_TIME_FORMAT " (dist:%" G_GUINT64_FORMAT ") dts:%"
648 GST_TIME_FORMAT " (dist:%" G_GUINT64_FORMAT ")",
649 GST_TIME_ARGS (pts), pts_dist, GST_TIME_ARGS (dts), dts_dist);
650
651 ts_adapter->packets_in_adapter = 0;
652
653 if (buf) {
654 GST_BUFFER_PTS (buf) = pts;
655 GST_BUFFER_DTS (buf) = dts;
656 if (!ts_adapter->first_is_keyframe)
657 gst_buffer_set_flags (buf, GST_BUFFER_FLAG_DELTA_UNIT);
658 ret = gst_pad_push (pad, buf);
659 }
660
661 return ret;
662}
663
664static GstFlowReturn
665enqueue_and_maybe_push_buffer (AmlTSParse2 * parse, GstPad * pad,
666 AmlTSParse2Adapter * ts_adapter, GstBuffer * buffer)
667{
668 GstFlowReturn ret = GST_FLOW_OK;
669
670 if (buffer != NULL) {
671 if (parse->alignment == 1) {
672 ret = gst_pad_push (pad, buffer);
673 ret = gst_flow_combiner_update_flow (parse->flowcombiner, ret);
674 } else {
675 if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)
676 && parse->split_on_rai) {
677 ret = empty_adapter_into_pad (parse, ts_adapter, pad);
678 ret = gst_flow_combiner_update_flow (parse->flowcombiner, ret);
679 }
680 gst_adapter_push (ts_adapter->adapter, buffer);
681 ts_adapter->packets_in_adapter++;
682 if (ts_adapter->packets_in_adapter == 1 && parse->split_on_rai) {
683 ts_adapter->first_is_keyframe =
684 !GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT);
685 }
686
687 if (ts_adapter->packets_in_adapter == parse->alignment
688 && ts_adapter->packets_in_adapter > 0) {
689 ret = empty_adapter_into_pad (parse, ts_adapter, pad);
690 ret = gst_flow_combiner_update_flow (parse->flowcombiner, ret);
691 }
692 }
693 }
694
695 return ret;
696}
697
698static GstFlowReturn
699amlts_parse_tspad_push_section (AmlTSParse2 * parse, AmlTSParsePad * tspad,
700 GstMpegtsSection * section, AmlTSPacketizerPacket * packet,
701 GstBuffer * buf)
702{
703 GstFlowReturn ret = GST_FLOW_OK;
704 gboolean to_push = TRUE;
705
706 if (tspad->program_number != -1) {
707 if (tspad->program) {
708 /* we push all sections to all pads except PMTs which we
709 * only push to pads meant to receive that program number */
710 if (section->table_id == 0x02) {
711 /* PMT */
712 if (section->subtable_extension != tspad->program_number)
713 to_push = FALSE;
714 }
715 } else if (section->table_id != 0x00) {
716 /* there's a program filter on the pad but the PMT for the program has not
717 * been parsed yet, ignore the pad until we get a PMT.
718 * But we always allow PAT to go through */
719 to_push = FALSE;
720 }
721 }
722
723 GST_DEBUG_OBJECT (parse,
724 "pushing section: %d program number: %d table_id: %d", to_push,
725 tspad->program_number, section->table_id);
726
727 if (to_push) {
728 ret =
729 enqueue_and_maybe_push_buffer (parse, tspad->pad,
730 &tspad->ts_adapter, gst_buffer_ref (buf));
731 }
732
733 GST_LOG_OBJECT (parse, "Returning %s", gst_flow_get_name (ret));
734 return ret;
735}
736
737static GstFlowReturn
738amlts_parse_tspad_push (AmlTSParse2 * parse, AmlTSParsePad * tspad,
739 AmlTSPacketizerPacket * packet, GstBuffer * buf)
740{
741 GstFlowReturn ret = GST_FLOW_OK;
742 AmlTSBaseProgram *bp = NULL;
743
744 if (tspad->program_number != -1) {
745 if (tspad->program)
746 bp = (AmlTSBaseProgram *) tspad->program;
747 else
748 bp = amlts_base_get_program ((AmlTSBase *) parse,
749 tspad->program_number);
750 }
751
752 if (bp) {
753 if (packet->pid == bp->pmt_pid || bp->streams == NULL
754 || bp->streams[packet->pid]) {
755 /* push if there's no filter or if the pid is in the filter */
756 ret = gst_pad_push (tspad->pad, gst_buffer_ref (buf));
757 ret = gst_flow_combiner_update_flow (parse->flowcombiner, ret);
758 }
759 }
760 GST_DEBUG_OBJECT (parse, "Returning %s", gst_flow_get_name (ret));
761
762 return ret;
763}
764
765static void
766pad_clear_for_push (GstPad * pad, AmlTSParse2 * parse)
767{
768 AmlTSParsePad *tspad = (AmlTSParsePad *) gst_pad_get_element_private (pad);
769
770 tspad->flow_return = GST_FLOW_NOT_LINKED;
771 tspad->pushed = FALSE;
772}
773
774static GstFlowReturn
775amlts_parse_push (AmlTSBase * base, AmlTSPacketizerPacket * packet,
776 GstMpegtsSection * section)
777{
778 AmlTSParse2 *parse = (AmlTSParse2 *) base;
779 guint32 pads_cookie;
780 gboolean done = FALSE;
781 GstPad *pad = NULL;
782 AmlTSParsePad *tspad;
783 GstFlowReturn ret;
784 GList *srcpads;
785 GstBuffer *buf;
786
787 GST_OBJECT_LOCK (parse);
788 srcpads = parse->srcpads;
789
790 /* clear tspad->pushed on pads */
791 g_list_foreach (srcpads, (GFunc) pad_clear_for_push, parse);
792 if (srcpads)
793 ret = GST_FLOW_NOT_LINKED;
794 else
795 ret = GST_FLOW_OK;
796
797 /* Get cookie and source pads list */
798 pads_cookie = GST_ELEMENT_CAST (parse)->pads_cookie;
799 if (G_LIKELY (srcpads)) {
800 pad = GST_PAD_CAST (srcpads->data);
801 g_object_ref (pad);
802 }
803 GST_OBJECT_UNLOCK (parse);
804
805 buf = amlts_packet_to_buffer (packet);
806 if (parse->split_on_rai
807 && !(packet->afc_flags & MPEGTS_AFC_RANDOM_ACCESS_FLAG)) {
808 gst_buffer_set_flags (buf, GST_BUFFER_FLAG_DELTA_UNIT);
809 }
810
811 /* Copy over input PTS/DTS (if present) */
812 GST_BUFFER_DTS (buf) = base->packetizer->last_dts;
813 GST_BUFFER_PTS (buf) = base->packetizer->last_pts;
814 ret = amlts_parse_have_buffer (base, gst_buffer_ref (buf));
815
816 while (pad && !done) {
817 tspad = gst_pad_get_element_private (pad);
818
819 if (G_LIKELY (!tspad->pushed)) {
820 if (section) {
821 tspad->flow_return =
822 amlts_parse_tspad_push_section (parse, tspad, section, packet,
823 buf);
824 } else {
825 tspad->flow_return =
826 amlts_parse_tspad_push (parse, tspad, packet, buf);
827 }
828 tspad->pushed = TRUE;
829
830 if (G_UNLIKELY (tspad->flow_return != GST_FLOW_OK
831 && tspad->flow_return != GST_FLOW_NOT_LINKED)) {
832 /* return the error upstream */
833 ret = tspad->flow_return;
834 done = TRUE;
835 }
836
837 }
838
839 if (ret == GST_FLOW_NOT_LINKED)
840 ret = tspad->flow_return;
841
842 g_object_unref (pad);
843
844 if (G_UNLIKELY (!done)) {
845 GST_OBJECT_LOCK (parse);
846 if (G_UNLIKELY (pads_cookie != GST_ELEMENT_CAST (parse)->pads_cookie)) {
847 /* resync */
848 GST_DEBUG ("resync");
849 pads_cookie = GST_ELEMENT_CAST (parse)->pads_cookie;
850 srcpads = parse->srcpads;
851 } else {
852 GST_DEBUG ("getting next pad");
853 /* Get next pad */
854 srcpads = g_list_next (srcpads);
855 }
856
857 if (srcpads) {
858 pad = GST_PAD_CAST (srcpads->data);
859 g_object_ref (pad);
860 } else
861 done = TRUE;
862 GST_OBJECT_UNLOCK (parse);
863 }
864 }
865
866 gst_buffer_unref (buf);
867 return ret;
868}
869
870static void
871amlts_parse_inspect_packet (AmlTSBase * base, AmlTSPacketizerPacket * packet)
872{
873 AmlTSParse2 *parse = GST_AMLTS_PARSE (base);
874 GST_LOG ("pid 0x%04x pusi:%d, afc:%d, cont:%d, payload:%p PCR %"
875 G_GUINT64_FORMAT, packet->pid, packet->payload_unit_start_indicator,
876 packet->scram_afc_cc & 0x30,
877 FLAGS_CONTINUITY_COUNTER (packet->scram_afc_cc), packet->payload,
878 packet->pcr);
879
880 /* Store the PCR if desired */
881 if (parse->current_pcr == GST_CLOCK_TIME_NONE &&
882 packet->afc_flags & MPEGTS_AFC_PCR_FLAG) {
883 /* Take this as the pcr_pid if set to auto-select */
884 if (parse->pcr_pid == -1)
885 parse->pcr_pid = packet->pid;
886 /* Check the PCR-PID matches the program we want for multiple programs */
887 if (parse->pcr_pid == packet->pid) {
888 parse->current_pcr = amlts_packetizer_pts_to_ts (base->packetizer,
889 PCRTIME_TO_GSTTIME (packet->pcr), parse->pcr_pid);
890 GST_DEBUG ("Got new PCR %" GST_TIME_FORMAT " raw %" G_GUINT64_FORMAT,
891 GST_TIME_ARGS (parse->current_pcr), packet->pcr);
892 if (parse->base_pcr == GST_CLOCK_TIME_NONE) {
893 parse->base_pcr = parse->current_pcr;
894 }
895 }
896 }
897}
898
899static GstClockTime
900get_pending_timestamp_diff (AmlTSParse2 * parse)
901{
902 GList *l;
903 GstClockTime first_ts, last_ts;
904
905 if (parse->pending_buffers == NULL)
906 return GST_CLOCK_TIME_NONE;
907
908 l = g_list_last (parse->pending_buffers);
909 first_ts = GST_BUFFER_PTS (l->data);
910 if (first_ts == GST_CLOCK_TIME_NONE)
911 return GST_CLOCK_TIME_NONE;
912
913 l = g_list_first (parse->pending_buffers);
914 last_ts = GST_BUFFER_PTS (l->data);
915 if (last_ts == GST_CLOCK_TIME_NONE)
916 return GST_CLOCK_TIME_NONE;
917
918 return last_ts - first_ts;
919}
920
921static GstFlowReturn
922drain_pending_buffers (AmlTSParse2 * parse, gboolean drain_all)
923{
924 GstFlowReturn ret = GST_FLOW_OK;
925 GstClockTime start_ts;
926 GstClockTime pcr = GST_CLOCK_TIME_NONE;
927 GstClockTime pcr_diff = 0;
928 gsize pcr_bytes, bytes_since_pcr, pos;
929 GstBuffer *buffer;
930 GList *l, *end = NULL;
931
932 if (parse->pending_buffers == NULL)
933 return GST_FLOW_OK; /* Nothing to push */
934
935 /*
936 * There are 4 cases:
937 * 1 We get a buffer with no PCR -> it's the head of the list
938 * -> Do nothing, unless it's EOS
939 * 2 We get a buffer with a PCR, it's the first PCR we've seen, and belongs
940 * to the buffer at the head of the list
941 * -> Push any buffers in the list except the head,
942 * using a smoothing of their timestamps to land at the PCR
943 * -> store new PCR as the previous PCR, bytes_since_pcr = sizeof (buffer);
944 * 3 It's EOS (drain_all == TRUE, current_pcr == NONE)
945 * -> Push any buffers in the list using a smoothing of their timestamps
946 * starting at the previous PCR or first TS
947 * 4 We get a buffer with a PCR, and have a previous PCR
948 * -> If distance > smoothing_latency,
949 * output buffers except the last in the pending queue using
950 * piecewise-linear timestamps
951 * -> store new PCR as the previous PCR, bytes_since_pcr = sizeof (buffer);
952 */
953
954 /* Case 1 */
955 if (!GST_CLOCK_TIME_IS_VALID (parse->current_pcr) && !drain_all)
956 return GST_FLOW_OK;
957
958 if (GST_CLOCK_TIME_IS_VALID (parse->current_pcr)) {
959 pcr = parse->current_pcr;
960 parse->current_pcr = GST_CLOCK_TIME_NONE;
961 }
962
963 /* The bytes of the last buffer are after the PCR */
964 buffer = GST_BUFFER (g_list_nth_data (parse->pending_buffers, 0));
965 bytes_since_pcr = gst_buffer_get_size (buffer);
966
967 pcr_bytes = parse->bytes_since_pcr - bytes_since_pcr;
968
969 if (!drain_all)
970 end = g_list_first (parse->pending_buffers);
971
972 /* Case 2 */
973 if (!GST_CLOCK_TIME_IS_VALID (parse->previous_pcr)) {
974 pcr_diff = get_pending_timestamp_diff (parse);
975
976 /* Calculate the start_ts that ends at the end timestamp */
977 start_ts = GST_CLOCK_TIME_NONE;
978 if (end) {
979 start_ts = GST_BUFFER_PTS (GST_BUFFER (end->data));
980 if (start_ts > pcr_diff)
981 start_ts -= pcr_diff;
982 }
983 } else if (drain_all) { /* Case 3 */
984 start_ts = parse->previous_pcr;
985 pcr_diff = get_pending_timestamp_diff (parse);
986 } else { /* Case 4 */
987 start_ts = parse->previous_pcr;
988 if (GST_CLOCK_TIME_IS_VALID (pcr) && pcr > start_ts)
989 pcr_diff = GST_CLOCK_DIFF (start_ts, pcr);
990
991 /* Make sure PCR observations are sufficiently far apart */
992 if (drain_all == FALSE && pcr_diff < parse->smoothing_latency)
993 return GST_FLOW_OK;
994 }
995
996 GST_INFO_OBJECT (parse, "Pushing buffers - startTS %" GST_TIME_FORMAT
997 " duration %" GST_TIME_FORMAT " %" G_GSIZE_FORMAT " bytes",
998 GST_TIME_ARGS (start_ts), GST_TIME_ARGS (pcr_diff), pcr_bytes);
999
1000 /* Now, push buffers out pacing timestamps over pcr_diff time and pcr_bytes */
1001 pos = 0;
1002 l = g_list_last (parse->pending_buffers);
1003 while (l != end) {
1004 GList *p;
1005 GstClockTime out_ts = start_ts;
1006
1007 buffer = gst_buffer_make_writable (GST_BUFFER (l->data));
1008
1009 if (out_ts != GST_CLOCK_TIME_NONE && pcr_diff != GST_CLOCK_TIME_NONE &&
1010 pcr_bytes && pos)
1011 out_ts += gst_util_uint64_scale (pcr_diff, pos, pcr_bytes);
1012
1013 pos += gst_buffer_get_size (buffer);
1014
1015 GST_DEBUG_OBJECT (parse,
1016 "InputTS %" GST_TIME_FORMAT " out %" GST_TIME_FORMAT,
1017 GST_TIME_ARGS (GST_BUFFER_PTS (buffer)), GST_TIME_ARGS (out_ts));
1018
1019 GST_BUFFER_PTS (buffer) = out_ts + parse->ts_offset;
1020 GST_BUFFER_DTS (buffer) = out_ts + parse->ts_offset;
1021 if (ret == GST_FLOW_OK) {
1022 ret =
1023 enqueue_and_maybe_push_buffer (parse, parse->srcpad,
1024 &parse->ts_adapter, buffer);
1025 } else {
1026 gst_buffer_unref (buffer);
1027 }
1028
1029 /* Free this list node and move to the next */
1030 p = g_list_previous (l);
1031 parse->pending_buffers = g_list_delete_link (parse->pending_buffers, l);
1032 l = p;
1033 }
1034
1035 if (parse->is_eos) {
1036 empty_adapter_into_pad (parse, &parse->ts_adapter, parse->srcpad);
1037 }
1038
1039 parse->pending_buffers = end;
1040 parse->bytes_since_pcr = bytes_since_pcr;
1041 parse->previous_pcr = pcr;
1042 return ret;
1043}
1044
1045static GstFlowReturn
1046amlts_parse_have_buffer (AmlTSBase * base, GstBuffer * buffer)
1047{
1048 AmlTSParse2 *parse = GST_AMLTS_PARSE (base);
1049 GstFlowReturn ret = GST_FLOW_OK;
1050
1051 GST_LOG_OBJECT (parse, "Received buffer %" GST_PTR_FORMAT, buffer);
1052
1053 /* Assume all packets have equal size */
1054 if (parse->alignment > 0 &&
1055 base->packetizer->packet_size != MPEGTS_NORMAL_PACKETSIZE) {
1056 GstMapInfo map;
1057 guint8 *data;
1058
1059 gst_buffer_map (buffer, &map, GST_MAP_READ);
1060 data = map.data;
1061
1062 parse->header = GST_READ_UINT32_BE (data);
1063 gst_buffer_unmap (buffer, &map);
1064 }
1065
1066 if (parse->current_pcr != GST_CLOCK_TIME_NONE) {
1067 GST_DEBUG_OBJECT (parse,
1068 "InputTS %" GST_TIME_FORMAT " PCR %" GST_TIME_FORMAT,
1069 GST_TIME_ARGS (GST_BUFFER_PTS (buffer)),
1070 GST_TIME_ARGS (parse->current_pcr));
1071 }
1072
1073 if (parse->set_timestamps || parse->first) {
1074 parse->pending_buffers = g_list_prepend (parse->pending_buffers, buffer);
1075 parse->bytes_since_pcr += gst_buffer_get_size (buffer);
1076 buffer = NULL;
1077 }
1078
1079 if (!prepare_src_pad (base, parse))
1080 return GST_FLOW_OK;
1081
1082 if (parse->pending_buffers != NULL) {
1083 /* Don't keep pending_buffers if not setting output timestamps */
1084 gboolean drain_all = (parse->set_timestamps == FALSE);
1085 ret = drain_pending_buffers (parse, drain_all);
1086 if (ret != GST_FLOW_OK) {
1087 if (buffer)
1088 gst_buffer_unref (buffer);
1089 return ret;
1090 }
1091 }
1092
1093 ret =
1094 enqueue_and_maybe_push_buffer (parse, parse->srcpad,
1095 &parse->ts_adapter, buffer);
1096 return ret;
1097}
1098
1099static void
1100empty_pad (GstPad * pad, AmlTSParse2 * parse)
1101{
1102 AmlTSParsePad *tspad = (AmlTSParsePad *) gst_pad_get_element_private (pad);
1103 GstFlowReturn ret;
1104 ret = empty_adapter_into_pad (parse, &tspad->ts_adapter, tspad->pad);
1105 ret = gst_flow_combiner_update_flow (parse->flowcombiner, ret);
1106}
1107
1108static GstFlowReturn
1109amlts_parse_input_done (AmlTSBase * base)
1110{
1111 AmlTSParse2 *parse = GST_AMLTS_PARSE (base);
1112 GstFlowReturn ret = GST_FLOW_OK;
1113
1114 if (!prepare_src_pad (base, parse))
1115 return GST_FLOW_OK;
1116
1117 if (parse->alignment == 0) {
1118 ret = empty_adapter_into_pad (parse, &parse->ts_adapter, parse->srcpad);
1119 ret = gst_flow_combiner_update_flow (parse->flowcombiner, ret);
1120 g_list_foreach (parse->srcpads, (GFunc) empty_pad, parse);
1121 }
1122 return ret;
1123}
1124
1125static AmlTSParsePad *
1126find_pad_for_program (AmlTSParse2 * parse, guint program_number)
1127{
1128 GList *tmp;
1129
1130 for (tmp = parse->srcpads; tmp; tmp = tmp->next) {
1131 AmlTSParsePad *tspad = gst_pad_get_element_private ((GstPad *) tmp->data);
1132
1133 if (tspad->program_number == program_number)
1134 return tspad;
1135 }
1136
1137 return NULL;
1138}
1139
1140static void
1141amlts_parse_program_started (AmlTSBase * base, AmlTSBaseProgram * program)
1142{
1143 AmlTSParse2 *parse = GST_AMLTS_PARSE (base);
1144 AmlTSParseProgram *parseprogram = (AmlTSParseProgram *) program;
1145 AmlTSParsePad *tspad;
1146
1147 /* If we have a request pad for that program, activate it */
1148 tspad = find_pad_for_program (parse, program->program_number);
1149
1150 if (tspad) {
1151 tspad->program = parseprogram;
1152 parseprogram->tspad = tspad;
1153 }
1154}
1155
1156static void
1157amlts_parse_program_stopped (AmlTSBase * base, AmlTSBaseProgram * program)
1158{
1159 AmlTSParse2 *parse = GST_AMLTS_PARSE (base);
1160 AmlTSParseProgram *parseprogram = (AmlTSParseProgram *) program;
1161 AmlTSParsePad *tspad;
1162
1163 /* If we have a request pad for that program, activate it */
1164 tspad = find_pad_for_program (parse, program->program_number);
1165
1166 if (tspad) {
1167 tspad->program = NULL;
1168 parseprogram->tspad = NULL;
1169 }
1170
1171 parse->pcr_pid = -1;
1172 parse->ts_offset += parse->current_pcr - parse->base_pcr;
1173 parse->base_pcr = GST_CLOCK_TIME_NONE;
1174}
1175
1176static gboolean
1177amlts_parse_src_pad_query (GstPad * pad, GstObject * parent, GstQuery * query)
1178{
1179 AmlTSParse2 *parse = GST_AMLTS_PARSE (parent);
1180 gboolean res;
1181
1182 switch (GST_QUERY_TYPE (query)) {
1183 case GST_QUERY_LATENCY:
1184 {
1185 if ((res = gst_pad_peer_query (((AmlTSBase *) parse)->sinkpad, query))) {
1186 gboolean is_live;
1187 GstClockTime min_latency, max_latency;
1188
1189 gst_query_parse_latency (query, &is_live, &min_latency, &max_latency);
1190 if (is_live) {
1191 GstClockTime extra_latency = TS_LATENCY * GST_MSECOND;
1192 if (parse->set_timestamps) {
1193 extra_latency = MAX (extra_latency, parse->smoothing_latency);
1194 }
1195 min_latency += extra_latency;
1196 if (max_latency != GST_CLOCK_TIME_NONE)
1197 max_latency += extra_latency;
1198 }
1199
1200 gst_query_set_latency (query, is_live, min_latency, max_latency);
1201 }
1202 break;
1203 }
1204 default:
1205 res = gst_pad_query_default (pad, parent, query);
1206 }
1207 return res;
1208}
1209
1210gboolean
1211gst_amltsparse_plugin_init (GstPlugin * plugin)
1212{
1213 GST_DEBUG_CATEGORY_INIT (mpegts_parse_debug, "amltsparse", 0,
1214 "MPEG transport stream parser");
1215
1216 return gst_element_register (plugin, "amltsparse",
1217 GST_RANK_NONE, GST_TYPE_AMLTS_PARSE);
1218}
1219