1 /*
2  * Copyright 2006 BBC and Fluendo S.A.
3  *
4  * This library is licensed under 4 different licenses and you
5  * can choose to use it under the terms of any one of them. The
6  * four licenses are the MPL 1.1, the LGPL, the GPL and the MIT
7  * license.
8  *
9  * MPL:
10  *
11  * The contents of this file are subject to the Mozilla Public License
12  * Version 1.1 (the "License"); you may not use this file except in
13  * compliance with the License. You may obtain a copy of the License at
14  * http://www.mozilla.org/MPL/.
15  *
16  * Software distributed under the License is distributed on an "AS IS"
17  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
18  * License for the specific language governing rights and limitations
19  * under the License.
20  *
21  * LGPL:
22  *
23  * This library is free software; you can redistribute it and/or
24  * modify it under the terms of the GNU Library General Public
25  * License as published by the Free Software Foundation; either
26  * version 2 of the License, or (at your option) any later version.
27  *
28  * This library is distributed in the hope that it will be useful,
29  * but WITHOUT ANY WARRANTY; without even the implied warranty of
30  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
31  * Library General Public License for more details.
32  *
33  * You should have received a copy of the GNU Library General Public
34  * License along with this library; if not, write to the
35  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
36  * Boston, MA 02110-1301, USA.
37  *
38  * GPL:
39  *
40  * This program is free software; you can redistribute it and/or modify
41  * it under the terms of the GNU General Public License as published by
42  * the Free Software Foundation; either version 2 of the License, or
43  * (at your option) any later version.
44  *
45  * This program is distributed in the hope that it will be useful,
46  * but WITHOUT ANY WARRANTY; without even the implied warranty of
47  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
48  * GNU General Public License for more details.
49  *
50  * You should have received a copy of the GNU General Public License
51  * along with this program; if not, write to the Free Software
52  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
53  *
54  * MIT:
55  *
56  * Unless otherwise indicated, Source Code is licensed under MIT license.
57  * See further explanation attached in License Statement (distributed in the file
58  * LICENSE).
59  *
60  * Permission is hereby granted, free of charge, to any person obtaining a copy of
61  * this software and associated documentation files (the "Software"), to deal in
62  * the Software without restriction, including without limitation the rights to
63  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
64  * of the Software, and to permit persons to whom the Software is furnished to do
65  * so, subject to the following conditions:
66  *
67  * The above copyright notice and this permission notice shall be included in all
68  * copies or substantial portions of the Software.
69  *
70  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
71  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
72  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
73  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
74  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
75  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
76  * SOFTWARE.
77  *
78  */
79 
80 #ifdef HAVE_CONFIG_H
81 #include "config.h"
82 #endif
83 
84 #include <string.h>
85 
86 #include <gst/mpegts/mpegts.h>
87 
88 #include "tsmux.h"
89 #include "tsmuxstream.h"
90 
91 #define GST_CAT_DEFAULT mpegtsmux_debug
92 
93 /* Maximum total data length for a PAT section is 1024 bytes, minus an
94  * 8 byte header, then the length of each program entry is 32 bits,
95  * then finally a 32 bit CRC. Thus the maximum number of programs in this mux
96  * is (1024 - 8 - 4) / 4 = 253 because it only supports single section PATs */
97 #define TSMUX_MAX_PROGRAMS 253
98 
99 #define TSMUX_SECTION_HDR_SIZE 8
100 
101 #define TSMUX_DEFAULT_NETWORK_ID 0x0001
102 #define TSMUX_DEFAULT_TS_ID 0x0001
103 
104 /* HACK: We use a fixed buffering offset for the PCR at the moment -
105  * this is the amount 'in advance' of the stream that the PCR sits.
106  * 1/8 second atm */
107 #define TSMUX_PCR_OFFSET (TSMUX_CLOCK_FREQ / 8)
108 
109 /* Times per second to write PCR */
110 #define TSMUX_DEFAULT_PCR_FREQ (25)
111 
112 /* Base for all written PCR and DTS/PTS,
113  * so we have some slack to go backwards */
114 #define CLOCK_BASE (TSMUX_CLOCK_FREQ * 10 * 360)
115 
116 static gboolean tsmux_write_pat (TsMux * mux);
117 static gboolean tsmux_write_pmt (TsMux * mux, TsMuxProgram * program);
118 static void
tsmux_section_free(TsMuxSection * section)119 tsmux_section_free (TsMuxSection * section)
120 {
121   gst_mpegts_section_unref (section->section);
122   g_slice_free (TsMuxSection, section);
123 }
124 
125 /**
126  * tsmux_new:
127  *
128  * Create a new muxer session.
129  *
130  * Returns: A new #TsMux object.
131  */
132 TsMux *
tsmux_new(void)133 tsmux_new (void)
134 {
135   TsMux *mux;
136 
137   mux = g_slice_new0 (TsMux);
138 
139   mux->transport_id = TSMUX_DEFAULT_TS_ID;
140 
141   mux->next_pgm_no = TSMUX_START_PROGRAM_ID;
142   mux->next_pmt_pid = TSMUX_START_PMT_PID;
143   mux->next_stream_pid = TSMUX_START_ES_PID;
144 
145   mux->pat_changed = TRUE;
146   mux->last_pat_ts = G_MININT64;
147   mux->pat_interval = TSMUX_DEFAULT_PAT_INTERVAL;
148 
149   mux->si_changed = TRUE;
150   mux->last_si_ts = G_MININT64;
151   mux->si_interval = TSMUX_DEFAULT_SI_INTERVAL;
152 
153   mux->si_sections = g_hash_table_new_full (g_direct_hash, g_direct_equal,
154       NULL, (GDestroyNotify) tsmux_section_free);
155 
156   return mux;
157 }
158 
159 /**
160  * tsmux_set_write_func:
161  * @mux: a #TsMux
162  * @func: a user callback function
163  * @user_data: user data passed to @func
164  *
165  * Set the callback function and user data to be called when @mux has output to
166  * produce. @user_data will be passed as user data in @func.
167  */
168 void
tsmux_set_write_func(TsMux * mux,TsMuxWriteFunc func,void * user_data)169 tsmux_set_write_func (TsMux * mux, TsMuxWriteFunc func, void *user_data)
170 {
171   g_return_if_fail (mux != NULL);
172 
173   mux->write_func = func;
174   mux->write_func_data = user_data;
175 }
176 
177 /**
178  * tsmux_set_alloc_func:
179  * @mux: a #TsMux
180  * @func: a user callback function
181  * @user_data: user data passed to @func
182  *
183  * Set the callback function and user data to be called when @mux needs
184  * a new buffer to write a packet into.
185  * @user_data will be passed as user data in @func.
186  */
187 void
tsmux_set_alloc_func(TsMux * mux,TsMuxAllocFunc func,void * user_data)188 tsmux_set_alloc_func (TsMux * mux, TsMuxAllocFunc func, void *user_data)
189 {
190   g_return_if_fail (mux != NULL);
191 
192   mux->alloc_func = func;
193   mux->alloc_func_data = user_data;
194 }
195 
196 /**
197  * tsmux_set_pat_interval:
198  * @mux: a #TsMux
199  * @freq: a new PAT interval
200  *
201  * Set the interval (in cycles of the 90kHz clock) for writing out the PAT table.
202  *
203  * Many transport stream clients might have problems if the PAT table is not
204  * inserted in the stream at regular intervals, especially when initially trying
205  * to figure out the contents of the stream.
206  */
207 void
tsmux_set_pat_interval(TsMux * mux,guint freq)208 tsmux_set_pat_interval (TsMux * mux, guint freq)
209 {
210   g_return_if_fail (mux != NULL);
211 
212   mux->pat_interval = freq;
213 }
214 
215 /**
216  * tsmux_get_pat_interval:
217  * @mux: a #TsMux
218  *
219  * Get the configured PAT interval. See also tsmux_set_pat_interval().
220  *
221  * Returns: the configured PAT interval
222  */
223 guint
tsmux_get_pat_interval(TsMux * mux)224 tsmux_get_pat_interval (TsMux * mux)
225 {
226   g_return_val_if_fail (mux != NULL, 0);
227 
228   return mux->pat_interval;
229 }
230 
231 /**
232  * tsmux_resend_pat:
233  * @mux: a #TsMux
234  *
235  * Resends the PAT before the next stream packet.
236  */
237 void
tsmux_resend_pat(TsMux * mux)238 tsmux_resend_pat (TsMux * mux)
239 {
240   g_return_if_fail (mux != NULL);
241 
242   mux->last_pat_ts = G_MININT64;
243 }
244 
245 /**
246  * tsmux_set_si_interval:
247  * @mux: a #TsMux
248  * @freq: a new SI table interval
249  *
250  * Set the interval (in cycles of the 90kHz clock) for writing out the SI tables.
251  *
252  */
253 void
tsmux_set_si_interval(TsMux * mux,guint freq)254 tsmux_set_si_interval (TsMux * mux, guint freq)
255 {
256   g_return_if_fail (mux != NULL);
257 
258   mux->si_interval = freq;
259 }
260 
261 /**
262  * tsmux_get_si_interval:
263  * @mux: a #TsMux
264  *
265  * Get the configured SI table interval. See also tsmux_set_si_interval().
266  *
267  * Returns: the configured SI interval
268  */
269 guint
tsmux_get_si_interval(TsMux * mux)270 tsmux_get_si_interval (TsMux * mux)
271 {
272   g_return_val_if_fail (mux != NULL, 0);
273 
274   return mux->si_interval;
275 }
276 
277 /**
278  * tsmux_resend_si:
279  * @mux: a #TsMux
280  *
281  * Resends the SI tables before the next stream packet.
282  *
283  */
284 void
tsmux_resend_si(TsMux * mux)285 tsmux_resend_si (TsMux * mux)
286 {
287   g_return_if_fail (mux != NULL);
288 
289   mux->last_si_ts = G_MININT64;
290 }
291 
292 /**
293  * tsmux_add_mpegts_si_section:
294  * @mux: a #TsMux
295  * @section: (transfer full): a #GstMpegtsSection to add
296  *
297  * Add a Service Information #GstMpegtsSection to the stream
298  *
299  * Returns: %TRUE on success, %FALSE otherwise
300  */
301 gboolean
tsmux_add_mpegts_si_section(TsMux * mux,GstMpegtsSection * section)302 tsmux_add_mpegts_si_section (TsMux * mux, GstMpegtsSection * section)
303 {
304   TsMuxSection *tsmux_section;
305 
306   g_return_val_if_fail (mux != NULL, FALSE);
307   g_return_val_if_fail (section != NULL, FALSE);
308   g_return_val_if_fail (mux->si_sections != NULL, FALSE);
309 
310   tsmux_section = g_slice_new0 (TsMuxSection);
311 
312   GST_DEBUG ("Adding mpegts section with type %d to mux",
313       section->section_type);
314 
315   tsmux_section->section = section;
316   tsmux_section->pi.pid = section->pid;
317 
318   g_hash_table_insert (mux->si_sections,
319       GINT_TO_POINTER (section->section_type), tsmux_section);
320 
321   mux->si_changed = TRUE;
322 
323   return TRUE;
324 }
325 
326 /**
327  * tsmux_free:
328  * @mux: a #TsMux
329  *
330  * Free all resources associated with @mux. After calling this function @mux can
331  * not be used anymore.
332  */
333 void
tsmux_free(TsMux * mux)334 tsmux_free (TsMux * mux)
335 {
336   GList *cur;
337 
338   g_return_if_fail (mux != NULL);
339 
340   /* Free PAT section */
341   if (mux->pat.section)
342     gst_mpegts_section_unref (mux->pat.section);
343 
344   /* Free all programs */
345   for (cur = mux->programs; cur; cur = cur->next) {
346     TsMuxProgram *program = (TsMuxProgram *) cur->data;
347 
348     tsmux_program_free (program);
349   }
350   g_list_free (mux->programs);
351 
352   /* Free all streams */
353   for (cur = mux->streams; cur; cur = cur->next) {
354     TsMuxStream *stream = (TsMuxStream *) cur->data;
355 
356     tsmux_stream_free (stream);
357   }
358   g_list_free (mux->streams);
359 
360   /* Free SI table sections */
361   g_hash_table_destroy (mux->si_sections);
362 
363   g_slice_free (TsMux, mux);
364 }
365 
366 static gint
tsmux_program_compare(TsMuxProgram * program,gint * needle)367 tsmux_program_compare (TsMuxProgram * program, gint * needle)
368 {
369   return (program->pgm_number - *needle);
370 }
371 
372 /**
373  * tsmux_program_new:
374  * @mux: a #TsMux
375  *
376  * Create a new program in the mising session @mux.
377  *
378  * Returns: a new #TsMuxProgram or %NULL when the maximum number of programs has
379  * been reached.
380  */
381 TsMuxProgram *
tsmux_program_new(TsMux * mux,gint prog_id)382 tsmux_program_new (TsMux * mux, gint prog_id)
383 {
384   TsMuxProgram *program;
385 
386   g_return_val_if_fail (mux != NULL, NULL);
387 
388   /* Ensure we have room for another program */
389   if (mux->nb_programs == TSMUX_MAX_PROGRAMS)
390     return NULL;
391 
392   program = g_slice_new0 (TsMuxProgram);
393 
394   program->pmt_changed = TRUE;
395   program->last_pmt_ts = G_MININT64;
396   program->pmt_interval = TSMUX_DEFAULT_PMT_INTERVAL;
397 
398   if (prog_id == 0) {
399     program->pgm_number = mux->next_pgm_no++;
400     while (g_list_find_custom (mux->programs, &program->pgm_number,
401             (GCompareFunc) tsmux_program_compare) != NULL) {
402       program->pgm_number = mux->next_pgm_no++;
403     }
404   } else {
405     program->pgm_number = prog_id;
406     while (g_list_find_custom (mux->programs, &program->pgm_number,
407             (GCompareFunc) tsmux_program_compare) != NULL) {
408       program->pgm_number++;
409     }
410   }
411 
412   program->pmt_pid = mux->next_pmt_pid++;
413   program->pcr_stream = NULL;
414 
415   program->streams = g_array_sized_new (FALSE, TRUE, sizeof (TsMuxStream *), 1);
416 
417   mux->programs = g_list_prepend (mux->programs, program);
418   mux->nb_programs++;
419   mux->pat_changed = TRUE;
420 
421   return program;
422 }
423 
424 /**
425  * tsmux_set_pmt_interval:
426  * @program: a #TsMuxProgram
427  * @freq: a new PMT interval
428  *
429  * Set the interval (in cycles of the 90kHz clock) for writing out the PMT table.
430  *
431  * Many transport stream clients might have problems if the PMT table is not
432  * inserted in the stream at regular intervals, especially when initially trying
433  * to figure out the contents of the stream.
434  */
435 void
tsmux_set_pmt_interval(TsMuxProgram * program,guint freq)436 tsmux_set_pmt_interval (TsMuxProgram * program, guint freq)
437 {
438   g_return_if_fail (program != NULL);
439 
440   program->pmt_interval = freq;
441 }
442 
443 /**
444  * tsmux_get_pmt_interval:
445  * @program: a #TsMuxProgram
446  *
447  * Get the configured PMT interval. See also tsmux_set_pmt_interval().
448  *
449  * Returns: the configured PMT interval
450  */
451 guint
tsmux_get_pmt_interval(TsMuxProgram * program)452 tsmux_get_pmt_interval (TsMuxProgram * program)
453 {
454   g_return_val_if_fail (program != NULL, 0);
455 
456   return program->pmt_interval;
457 }
458 
459 /**
460  * tsmux_resend_pmt:
461  * @program: a #TsMuxProgram
462  *
463  * Resends the PMT before the next stream packet.
464  */
465 void
tsmux_resend_pmt(TsMuxProgram * program)466 tsmux_resend_pmt (TsMuxProgram * program)
467 {
468   g_return_if_fail (program != NULL);
469 
470   program->last_pmt_ts = G_MININT64;
471 }
472 
473 /**
474  * tsmux_program_add_stream:
475  * @program: a #TsMuxProgram
476  * @stream: a #TsMuxStream
477  *
478  * Add @stream to @program.
479  */
480 void
tsmux_program_add_stream(TsMuxProgram * program,TsMuxStream * stream)481 tsmux_program_add_stream (TsMuxProgram * program, TsMuxStream * stream)
482 {
483   g_return_if_fail (program != NULL);
484   g_return_if_fail (stream != NULL);
485 
486   g_array_append_val (program->streams, stream);
487   program->pmt_changed = TRUE;
488 }
489 
490 /**
491  * tsmux_program_set_pcr_stream:
492  * @program: a #TsMuxProgram
493  * @stream: a #TsMuxStream
494  *
495  * Set @stream as the PCR stream for @program, overwriting the previously
496  * configured PCR stream. When @stream is NULL, program will have no PCR stream
497  * configured.
498  */
499 void
tsmux_program_set_pcr_stream(TsMuxProgram * program,TsMuxStream * stream)500 tsmux_program_set_pcr_stream (TsMuxProgram * program, TsMuxStream * stream)
501 {
502   g_return_if_fail (program != NULL);
503 
504   if (program->pcr_stream == stream)
505     return;
506 
507   if (program->pcr_stream != NULL)
508     tsmux_stream_pcr_unref (program->pcr_stream);
509   if (stream)
510     tsmux_stream_pcr_ref (stream);
511   program->pcr_stream = stream;
512 
513   program->pmt_changed = TRUE;
514 }
515 
516 /**
517  * tsmux_get_new_pid:
518  * @mux: a #TsMux
519  *
520  * Get a new free PID.
521  *
522  * Returns: a new free PID.
523  */
524 guint16
tsmux_get_new_pid(TsMux * mux)525 tsmux_get_new_pid (TsMux * mux)
526 {
527   g_return_val_if_fail (mux != NULL, -1);
528 
529   /* make sure this PID is free
530    * (and not taken by a specific earlier request) */
531   do {
532     mux->next_stream_pid++;
533   } while (tsmux_find_stream (mux, mux->next_stream_pid));
534 
535   return mux->next_stream_pid;
536 }
537 
538 /**
539  * tsmux_create_stream:
540  * @mux: a #TsMux
541  * @stream_type: a #TsMuxStreamType
542  * @pid: the PID of the new stream.
543  *
544  * Create a new stream of @stream_type in the muxer session @mux.
545  *
546  * When @pid is set to #TSMUX_PID_AUTO, a new free PID will automatically
547  * be allocated for the new stream.
548  *
549  * Returns: a new #TsMuxStream.
550  */
551 TsMuxStream *
tsmux_create_stream(TsMux * mux,TsMuxStreamType stream_type,guint16 pid,gchar * language)552 tsmux_create_stream (TsMux * mux, TsMuxStreamType stream_type, guint16 pid,
553     gchar * language)
554 {
555   TsMuxStream *stream;
556   guint16 new_pid;
557 
558   g_return_val_if_fail (mux != NULL, NULL);
559 
560   if (pid == TSMUX_PID_AUTO) {
561     new_pid = tsmux_get_new_pid (mux);
562   } else {
563     new_pid = pid & 0x1FFF;
564   }
565 
566   /* Ensure we're not creating a PID collision */
567   if (tsmux_find_stream (mux, new_pid))
568     return NULL;
569 
570   stream = tsmux_stream_new (new_pid, stream_type);
571 
572   mux->streams = g_list_prepend (mux->streams, stream);
573   mux->nb_streams++;
574 
575   if (language)
576     g_strlcat (stream->language, language, 3 * sizeof (gchar));
577   else
578     g_strlcat (stream->language, "eng", 3 * sizeof (gchar));
579 
580   return stream;
581 }
582 
583 /**
584  * tsmux_find_stream:
585  * @mux: a #TsMux
586  * @pid: the PID to find.
587  *
588  * Find the stream associated wih PID.
589  *
590  * Returns: a #TsMuxStream with @pid or NULL when the stream was not found.
591  */
592 TsMuxStream *
tsmux_find_stream(TsMux * mux,guint16 pid)593 tsmux_find_stream (TsMux * mux, guint16 pid)
594 {
595   TsMuxStream *found = NULL;
596   GList *cur;
597 
598   g_return_val_if_fail (mux != NULL, NULL);
599 
600   for (cur = mux->streams; cur; cur = cur->next) {
601     TsMuxStream *stream = (TsMuxStream *) cur->data;
602 
603     if (tsmux_stream_get_pid (stream) == pid) {
604       found = stream;
605       break;
606     }
607   }
608   return found;
609 }
610 
611 static gboolean
tsmux_get_buffer(TsMux * mux,GstBuffer ** buf)612 tsmux_get_buffer (TsMux * mux, GstBuffer ** buf)
613 {
614   g_return_val_if_fail (buf, FALSE);
615 
616   if (G_UNLIKELY (!mux->alloc_func))
617     return FALSE;
618 
619   mux->alloc_func (buf, mux->alloc_func_data);
620 
621   if (!*buf)
622     return FALSE;
623 
624   g_assert (gst_buffer_get_size (*buf) == TSMUX_PACKET_LENGTH);
625   return TRUE;
626 }
627 
628 static gboolean
tsmux_packet_out(TsMux * mux,GstBuffer * buf,gint64 pcr)629 tsmux_packet_out (TsMux * mux, GstBuffer * buf, gint64 pcr)
630 {
631   if (G_UNLIKELY (mux->write_func == NULL)) {
632     if (buf)
633       gst_buffer_unref (buf);
634     return TRUE;
635   }
636 
637   return mux->write_func (buf, mux->write_func_data, pcr);
638 }
639 
640 /*
641  * adaptation_field() {
642  *   adaptation_field_length                              8 uimsbf
643  *   if(adaptation_field_length >0) {
644  *     discontinuity_indicator                            1 bslbf
645  *     random_access_indicator                            1 bslbf
646  *     elementary_stream_priority_indicator               1 bslbf
647  *     PCR_flag                                           1 bslbf
648  *     OPCR_flag                                          1 bslbf
649  *     splicing_point_flag                                1 bslbf
650  *     transport_private_data_flag                        1 bslbf
651  *     adaptation_field_extension_flag                    1 bslbf
652  *     if(PCR_flag == '1') {
653  *       program_clock_reference_base                    33 uimsbf
654  *       reserved                                         6 bslbf
655  *       program_clock_reference_extension                9 uimsbf
656  *     }
657  *     if(OPCR_flag == '1') {
658  *       original_program_clock_reference_base           33 uimsbf
659  *       reserved                                         6 bslbf
660  *       original_program_clock_reference_extension       9 uimsbf
661  *     }
662  *     if (splicing_point_flag == '1') {
663  *       splice_countdown                                 8 tcimsbf
664  *     }
665  *     if(transport_private_data_flag == '1') {
666  *       transport_private_data_length                    8 uimsbf
667  *       for (i=0; i<transport_private_data_length;i++){
668  *         private_data_byte                              8 bslbf
669  *       }
670  *     }
671  *     if (adaptation_field_extension_flag == '1' ) {
672  *       adaptation_field_extension_length                8 uimsbf
673  *       ltw_flag                                         1 bslbf
674  *       piecewise_rate_flag                              1 bslbf
675  *       seamless_splice_flag                             1 bslbf
676  *       reserved                                         5 bslbf
677  *       if (ltw_flag == '1') {
678  *         ltw_valid_flag                                 1 bslbf
679  *         ltw_offset                                    15 uimsbf
680  *       }
681  *       if (piecewise_rate_flag == '1') {
682  *         reserved                                       2 bslbf
683  *         piecewise_rate                                22 uimsbf
684  *       }
685  *       if (seamless_splice_flag == '1'){
686  *         splice_type                                    4 bslbf
687  *         DTS_next_AU[32..30]                            3 bslbf
688  *         marker_bit                                     1 bslbf
689  *         DTS_next_AU[29..15]                           15 bslbf
690  *         marker_bit                                     1 bslbf
691  *         DTS_next_AU[14..0]                            15 bslbf
692  *         marker_bit                                     1 bslbf
693  *       }
694  *       for ( i=0;i<N;i++) {
695  *         reserved                                       8 bslbf
696  *       }
697  *     }
698  *     for (i=0;i<N;i++){
699  *       stuffing_byte                                    8 bslbf
700  *     }
701  *   }
702  * }
703  */
704 static gboolean
tsmux_write_adaptation_field(guint8 * buf,TsMuxPacketInfo * pi,guint8 min_length,guint8 * written)705 tsmux_write_adaptation_field (guint8 * buf,
706     TsMuxPacketInfo * pi, guint8 min_length, guint8 * written)
707 {
708   guint8 pos = 2;
709   guint8 flags = 0;
710 
711   g_assert (min_length <= TSMUX_PAYLOAD_LENGTH);
712 
713   /* Write out all the fields from the packet info only if the
714    * user set the flag to request the adaptation field - if the flag
715    * isn't set, we're just supposed to write stuffing bytes */
716   if (pi->flags & TSMUX_PACKET_FLAG_ADAPTATION) {
717     TS_DEBUG ("writing adaptation fields");
718     if (pi->flags & TSMUX_PACKET_FLAG_DISCONT)
719       flags |= 0x80;
720     if (pi->flags & TSMUX_PACKET_FLAG_RANDOM_ACCESS)
721       flags |= 0x40;
722     if (pi->flags & TSMUX_PACKET_FLAG_PRIORITY)
723       flags |= 0x20;
724     if (pi->flags & TSMUX_PACKET_FLAG_WRITE_PCR) {
725       guint64 pcr_base;
726       guint32 pcr_ext;
727 
728       pcr_base = (pi->pcr / 300);
729       pcr_ext = (pi->pcr % 300);
730 
731       flags |= 0x10;
732       TS_DEBUG ("Writing PCR %" G_GUINT64_FORMAT " + ext %u", pcr_base,
733           pcr_ext);
734       buf[pos++] = (pcr_base >> 25) & 0xff;
735       buf[pos++] = (pcr_base >> 17) & 0xff;
736       buf[pos++] = (pcr_base >> 9) & 0xff;
737       buf[pos++] = (pcr_base >> 1) & 0xff;
738       buf[pos++] = ((pcr_base << 7) & 0x80) | 0x7e | ((pcr_ext >> 8) & 0x01);   /* set 6 reserve bits to 1 */
739       buf[pos++] = (pcr_ext) & 0xff;
740     }
741     if (pi->flags & TSMUX_PACKET_FLAG_WRITE_OPCR) {
742       guint64 opcr_base;
743       guint32 opcr_ext;
744 
745       opcr_base = (pi->opcr / 300);
746       opcr_ext = (pi->opcr % 300);
747 
748       flags |= 0x08;
749       TS_DEBUG ("Writing OPCR");
750       buf[pos++] = (opcr_base >> 25) & 0xff;
751       buf[pos++] = (opcr_base >> 17) & 0xff;
752       buf[pos++] = (opcr_base >> 9) & 0xff;
753       buf[pos++] = (opcr_base >> 1) & 0xff;
754       buf[pos++] = ((opcr_base << 7) & 0x80) | 0x7e | ((opcr_ext >> 8) & 0x01); /* set 6 reserve bits to 1 */
755       buf[pos++] = (opcr_ext) & 0xff;
756     }
757     if (pi->flags & TSMUX_PACKET_FLAG_WRITE_SPLICE) {
758       flags |= 0x04;
759       buf[pos++] = pi->splice_countdown;
760     }
761     if (pi->private_data_len > 0) {
762       flags |= 0x02;
763       /* Private data to write, ensure we have enough room */
764       if ((1 + pi->private_data_len) > (TSMUX_PAYLOAD_LENGTH - pos))
765         return FALSE;
766       buf[pos++] = pi->private_data_len;
767       memcpy (&(buf[pos]), pi->private_data, pi->private_data_len);
768       pos += pi->private_data_len;
769       TS_DEBUG ("%u bytes of private data", pi->private_data_len);
770     }
771     if (pi->flags & TSMUX_PACKET_FLAG_WRITE_ADAPT_EXT) {
772       flags |= 0x01;
773       TS_DEBUG ("FIXME: write Adaptation extension");
774       /* Write an empty extension for now */
775       buf[pos++] = 1;
776       buf[pos++] = 0x1f;        /* lower 5 bits are reserved, and should be all 1 */
777     }
778   }
779   /* Write the flags at the start */
780   buf[1] = flags;
781 
782   /* Stuffing bytes if needed */
783   while (pos < min_length)
784     buf[pos++] = 0xff;
785 
786   /* Write the adaptation field length, which doesn't include its own byte */
787   buf[0] = pos - 1;
788 
789   if (written)
790     *written = pos;
791 
792   return TRUE;
793 }
794 
795 static gboolean
tsmux_write_ts_header(guint8 * buf,TsMuxPacketInfo * pi,guint * payload_len_out,guint * payload_offset_out)796 tsmux_write_ts_header (guint8 * buf, TsMuxPacketInfo * pi,
797     guint * payload_len_out, guint * payload_offset_out)
798 {
799   guint8 *tmp;
800   guint8 adaptation_flag;
801   guint8 adapt_min_length = 0;
802   guint8 adapt_len = 0;
803   guint payload_len;
804   gboolean write_adapt = FALSE;
805 
806   /* Sync byte */
807   buf[0] = TSMUX_SYNC_BYTE;
808 
809   TS_DEBUG ("PID 0x%04x, counter = 0x%01x, %u bytes avail", pi->pid,
810       pi->packet_count & 0x0f, pi->stream_avail);
811 
812   /* 3 bits:
813    *   transport_error_indicator
814    *   payload_unit_start_indicator
815    *   transport_priority: (00)
816    * 13 bits: PID
817    */
818   tmp = buf + 1;
819   if (pi->packet_start_unit_indicator) {
820     tsmux_put16 (&tmp, 0x4000 | pi->pid);
821   } else
822     tsmux_put16 (&tmp, pi->pid);
823 
824   /* 2 bits: scrambling_control (NOT SUPPORTED) (00)
825    * 2 bits: adaptation field control (1x has_adaptation_field | x1 has_payload)
826    * 4 bits: continuity counter (xxxx)
827    */
828   adaptation_flag = pi->packet_count & 0x0f;
829 
830   if (pi->flags & TSMUX_PACKET_FLAG_ADAPTATION) {
831     write_adapt = TRUE;
832   }
833 
834   if (pi->stream_avail < TSMUX_PAYLOAD_LENGTH) {
835     /* Need an adaptation field regardless for stuffing */
836     adapt_min_length = TSMUX_PAYLOAD_LENGTH - pi->stream_avail;
837     write_adapt = TRUE;
838   }
839 
840   if (write_adapt) {
841     gboolean res;
842 
843     /* Flag the adaptation field presence */
844     adaptation_flag |= 0x20;
845     res = tsmux_write_adaptation_field (buf + TSMUX_HEADER_LENGTH,
846         pi, adapt_min_length, &adapt_len);
847     if (G_UNLIKELY (res == FALSE))
848       return FALSE;
849 
850     /* Should have written at least the number of bytes we requested */
851     g_assert (adapt_len >= adapt_min_length);
852   }
853 
854   /* The amount of packet data we wrote is the remaining space after
855    * the adaptation field */
856   *payload_len_out = payload_len = TSMUX_PAYLOAD_LENGTH - adapt_len;
857   *payload_offset_out = TSMUX_HEADER_LENGTH + adapt_len;
858 
859   /* Now if we are going to write out some payload, flag that fact */
860   if (payload_len > 0 && pi->stream_avail > 0) {
861     /* Flag the presence of a payload */
862     adaptation_flag |= 0x10;
863 
864     /* We must have enough data to fill the payload, or some calculation
865      * went wrong */
866     g_assert (payload_len <= pi->stream_avail);
867 
868     /* Packet with payload, increment the continuity counter */
869     pi->packet_count++;
870   }
871 
872   /* Write the byte of transport_scrambling_control, adaptation_field_control
873    * + continuity counter out */
874   buf[3] = adaptation_flag;
875 
876 
877   if (write_adapt) {
878     TS_DEBUG ("Adaptation field of size >= %d + %d bytes payload",
879         adapt_len, payload_len);
880   } else {
881     TS_DEBUG ("Payload of %d bytes only", payload_len);
882   }
883 
884   return TRUE;
885 }
886 
887 static gboolean
tsmux_section_write_packet(GstMpegtsSectionType * type,TsMuxSection * section,TsMux * mux)888 tsmux_section_write_packet (GstMpegtsSectionType * type,
889     TsMuxSection * section, TsMux * mux)
890 {
891   GstBuffer *section_buffer;
892   GstBuffer *packet_buffer = NULL;
893   GstMemory *mem;
894   guint8 *packet;
895   guint8 *data;
896   gsize data_size = 0;
897   gsize payload_written;
898   guint len = 0, offset = 0, payload_len = 0;
899   guint extra_alloc_bytes = 0;
900 
901   g_return_val_if_fail (section != NULL, FALSE);
902   g_return_val_if_fail (mux != NULL, FALSE);
903 
904   /* Mark the start of new PES unit */
905   section->pi.packet_start_unit_indicator = TRUE;
906 
907   data = gst_mpegts_section_packetize (section->section, &data_size);
908 
909   if (!data) {
910     TS_DEBUG ("Could not packetize section");
911     return FALSE;
912   }
913 
914   /* Mark payload data size */
915   section->pi.stream_avail = data_size;
916   payload_written = 0;
917 
918   /* Wrap section data in a buffer without free function.
919      The data will be freed when the GstMpegtsSection is destroyed. */
920   section_buffer = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
921       data, data_size, 0, data_size, NULL, NULL);
922 
923   TS_DEBUG ("Section buffer with size %" G_GSIZE_FORMAT " created",
924       gst_buffer_get_size (section_buffer));
925 
926   while (section->pi.stream_avail > 0) {
927 
928     packet = g_malloc (TSMUX_PACKET_LENGTH);
929 
930     if (section->pi.packet_start_unit_indicator) {
931       /* Wee need room for a pointer byte */
932       section->pi.stream_avail++;
933 
934       if (!tsmux_write_ts_header (packet, &section->pi, &len, &offset))
935         goto fail;
936 
937       /* Write the pointer byte */
938       packet[offset++] = 0x00;
939       payload_len = len - 1;
940 
941     } else {
942       if (!tsmux_write_ts_header (packet, &section->pi, &len, &offset))
943         goto fail;
944       payload_len = len;
945     }
946 
947     /* Wrap the TS header and adaption field in a GstMemory */
948     mem = gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY,
949         packet, TSMUX_PACKET_LENGTH, 0, offset, packet, g_free);
950 
951     TS_DEBUG ("Creating packet buffer at offset "
952         "%" G_GSIZE_FORMAT " with length %u", payload_written, payload_len);
953 
954     /* If in M2TS mode, we will need to resize to 4 bytes after the end
955        of the buffer. For performance reasons, we will now try to include
956        4 extra bytes from the source buffer, then resize down, to avoid
957        having an extra 4 byte GstMemory appended. If the source buffer
958        does not have enough data for this, a new GstMemory will be used */
959     if (gst_buffer_get_size (section_buffer) - (payload_written +
960             payload_len) >= 4) {
961       /* enough space */
962       extra_alloc_bytes = 4;
963     } else {
964       extra_alloc_bytes = 0;
965     }
966     packet_buffer = gst_buffer_copy_region (section_buffer, GST_BUFFER_COPY_ALL,
967         payload_written, payload_len + extra_alloc_bytes);
968 
969     /* Prepend the header to the section data */
970     gst_buffer_prepend_memory (packet_buffer, mem);
971 
972     /* add an extra 4 bytes if it could not be reserved already */
973     if (extra_alloc_bytes == 4) {
974       /* we allocated those already, resize */
975       gst_buffer_set_size (packet_buffer,
976           gst_buffer_get_size (packet_buffer) - extra_alloc_bytes);
977     } else {
978       void *ptr = g_malloc (4);
979       GstMemory *extra =
980           gst_memory_new_wrapped (GST_MEMORY_FLAG_READONLY, ptr, 4, 0, 0, ptr,
981           g_free);
982       gst_buffer_append_memory (packet_buffer, extra);
983     }
984 
985     TS_DEBUG ("Writing %d bytes to section. %d bytes remaining",
986         len, section->pi.stream_avail - len);
987 
988     /* Push the packet without PCR */
989     if (G_UNLIKELY (!tsmux_packet_out (mux, packet_buffer, -1))) {
990       /* Buffer given away */
991       packet_buffer = NULL;
992       goto fail;
993     }
994 
995     packet_buffer = NULL;
996     section->pi.stream_avail -= len;
997     payload_written += payload_len;
998     section->pi.packet_start_unit_indicator = FALSE;
999   }
1000 
1001   gst_buffer_unref (section_buffer);
1002 
1003   return TRUE;
1004 
1005 fail:
1006   g_free (packet);
1007   if (section_buffer)
1008     gst_buffer_unref (section_buffer);
1009   return FALSE;
1010 }
1011 
1012 static gboolean
tsmux_write_si(TsMux * mux)1013 tsmux_write_si (TsMux * mux)
1014 {
1015   g_hash_table_foreach (mux->si_sections,
1016       (GHFunc) tsmux_section_write_packet, mux);
1017 
1018   mux->si_changed = FALSE;
1019 
1020   return TRUE;
1021 
1022 }
1023 
1024 /**
1025  * tsmux_write_stream_packet:
1026  * @mux: a #TsMux
1027  * @stream: a #TsMuxStream
1028  *
1029  * Write a packet of @stream.
1030  *
1031  * Returns: TRUE if the packet could be written.
1032  */
1033 gboolean
tsmux_write_stream_packet(TsMux * mux,TsMuxStream * stream)1034 tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
1035 {
1036   guint payload_len, payload_offs;
1037   TsMuxPacketInfo *pi = &stream->pi;
1038   gboolean res;
1039   gint64 cur_pcr = -1;
1040   GstBuffer *buf = NULL;
1041   GstMapInfo map;
1042 
1043   g_return_val_if_fail (mux != NULL, FALSE);
1044   g_return_val_if_fail (stream != NULL, FALSE);
1045 
1046   if (tsmux_stream_is_pcr (stream)) {
1047     gint64 cur_pts = tsmux_stream_get_pts (stream);
1048     gboolean write_pat;
1049     gboolean write_si;
1050     GList *cur;
1051 
1052     cur_pcr = 0;
1053     if (cur_pts != G_MININT64) {
1054       TS_DEBUG ("TS for PCR stream is %" G_GINT64_FORMAT, cur_pts);
1055     }
1056 
1057     /* FIXME: The current PCR needs more careful calculation than just
1058      * writing a fixed offset */
1059     if (cur_pts != G_MININT64) {
1060       /* CLOCK_BASE >= TSMUX_PCR_OFFSET */
1061       cur_pts += CLOCK_BASE;
1062       cur_pcr = (cur_pts - TSMUX_PCR_OFFSET) *
1063           (TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
1064     }
1065 
1066     /* Need to decide whether to write a new PCR in this packet */
1067     if (stream->last_pcr == -1 ||
1068         (cur_pcr - stream->last_pcr >
1069             (TSMUX_SYS_CLOCK_FREQ / TSMUX_DEFAULT_PCR_FREQ))) {
1070 
1071       stream->pi.flags |=
1072           TSMUX_PACKET_FLAG_ADAPTATION | TSMUX_PACKET_FLAG_WRITE_PCR;
1073       stream->pi.pcr = cur_pcr;
1074       stream->last_pcr = cur_pcr;
1075     } else {
1076       cur_pcr = -1;
1077     }
1078 
1079     /* check if we need to rewrite pat */
1080     if (mux->last_pat_ts == G_MININT64 || mux->pat_changed)
1081       write_pat = TRUE;
1082     else if (cur_pts >= mux->last_pat_ts + mux->pat_interval)
1083       write_pat = TRUE;
1084     else
1085       write_pat = FALSE;
1086 
1087     if (write_pat) {
1088       mux->last_pat_ts = cur_pts;
1089       if (!tsmux_write_pat (mux))
1090         return FALSE;
1091     }
1092 
1093     /* check if we need to rewrite sit */
1094     if (mux->last_si_ts == G_MININT64 || mux->si_changed)
1095       write_si = TRUE;
1096     else if (cur_pts >= mux->last_si_ts + mux->si_interval)
1097       write_si = TRUE;
1098     else
1099       write_si = FALSE;
1100 
1101     if (write_si) {
1102       mux->last_si_ts = cur_pts;
1103       if (!tsmux_write_si (mux))
1104         return FALSE;
1105     }
1106 
1107     /* check if we need to rewrite any of the current pmts */
1108     for (cur = mux->programs; cur; cur = cur->next) {
1109       TsMuxProgram *program = (TsMuxProgram *) cur->data;
1110       gboolean write_pmt;
1111 
1112       if (program->last_pmt_ts == G_MININT64 || program->pmt_changed)
1113         write_pmt = TRUE;
1114       else if (cur_pts >= program->last_pmt_ts + program->pmt_interval)
1115         write_pmt = TRUE;
1116       else
1117         write_pmt = FALSE;
1118 
1119       if (write_pmt) {
1120         program->last_pmt_ts = cur_pts;
1121         if (!tsmux_write_pmt (mux, program))
1122           return FALSE;
1123       }
1124     }
1125   }
1126 
1127   pi->packet_start_unit_indicator = tsmux_stream_at_pes_start (stream);
1128   if (pi->packet_start_unit_indicator) {
1129     tsmux_stream_initialize_pes_packet (stream);
1130     if (stream->dts != G_MININT64)
1131       stream->dts += CLOCK_BASE;
1132     if (stream->pts != G_MININT64)
1133       stream->pts += CLOCK_BASE;
1134   }
1135   pi->stream_avail = tsmux_stream_bytes_avail (stream);
1136 
1137   /* obtain buffer */
1138   if (!tsmux_get_buffer (mux, &buf))
1139     return FALSE;
1140 
1141   gst_buffer_map (buf, &map, GST_MAP_READ);
1142 
1143   if (!tsmux_write_ts_header (map.data, pi, &payload_len, &payload_offs))
1144     goto fail;
1145 
1146 
1147   if (!tsmux_stream_get_data (stream, map.data + payload_offs, payload_len))
1148     goto fail;
1149 
1150   gst_buffer_unmap (buf, &map);
1151 
1152   GST_DEBUG ("Writing PES of size %d", (int) gst_buffer_get_size (buf));
1153   res = tsmux_packet_out (mux, buf, cur_pcr);
1154 
1155   /* Reset all dynamic flags */
1156   stream->pi.flags &= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
1157 
1158   return res;
1159 
1160   /* ERRORS */
1161 fail:
1162   {
1163     gst_buffer_unmap (buf, &map);
1164     if (buf)
1165       gst_buffer_unref (buf);
1166     return FALSE;
1167   }
1168 }
1169 
1170 /**
1171  * tsmux_program_free:
1172  * @program: a #TsMuxProgram
1173  *
1174  * Free the resources of @program. After this call @program can not be used
1175  * anymore.
1176  */
1177 void
tsmux_program_free(TsMuxProgram * program)1178 tsmux_program_free (TsMuxProgram * program)
1179 {
1180   g_return_if_fail (program != NULL);
1181 
1182   /* Free PMT section */
1183   if (program->pmt.section)
1184     gst_mpegts_section_unref (program->pmt.section);
1185 
1186   g_array_free (program->streams, TRUE);
1187   g_slice_free (TsMuxProgram, program);
1188 }
1189 
1190 static gboolean
tsmux_write_pat(TsMux * mux)1191 tsmux_write_pat (TsMux * mux)
1192 {
1193 
1194   if (mux->pat_changed) {
1195     /* program_association_section ()
1196      * for (i = 0; i < N; i++) {
1197      *    program_number                         16   uimsbf
1198      *    reserved                                3   bslbf
1199      *    network_PID_or_program_map_PID         13   uimbsf
1200      * }
1201      * CRC_32                                    32   rbchof
1202      */
1203     GList *cur;
1204     GPtrArray *pat;
1205 
1206     pat = gst_mpegts_pat_new ();
1207 
1208     for (cur = mux->programs; cur; cur = cur->next) {
1209       GstMpegtsPatProgram *pat_pgm;
1210       TsMuxProgram *program = (TsMuxProgram *) cur->data;
1211 
1212       pat_pgm = gst_mpegts_pat_program_new ();
1213       pat_pgm->program_number = program->pgm_number;
1214       pat_pgm->network_or_program_map_PID = program->pmt_pid;
1215 
1216       g_ptr_array_add (pat, pat_pgm);
1217     }
1218 
1219     if (mux->pat.section)
1220       gst_mpegts_section_unref (mux->pat.section);
1221 
1222     mux->pat.section = gst_mpegts_section_from_pat (pat, mux->transport_id);
1223 
1224     mux->pat.section->version_number = mux->pat_version++;
1225 
1226     TS_DEBUG ("PAT has %d programs", mux->nb_programs);
1227     mux->pat_changed = FALSE;
1228   }
1229 
1230   return tsmux_section_write_packet (GINT_TO_POINTER (GST_MPEGTS_SECTION_PAT),
1231       &mux->pat, mux);
1232 }
1233 
1234 static gboolean
tsmux_write_pmt(TsMux * mux,TsMuxProgram * program)1235 tsmux_write_pmt (TsMux * mux, TsMuxProgram * program)
1236 {
1237 
1238   if (program->pmt_changed) {
1239     /* program_association_section ()
1240      * reserved                                   3   bslbf
1241      * PCR_PID                                   13   uimsbf
1242      * reserved                                   4   bslbf
1243      * program_info_length                       12   uimsbf
1244      * for (i = 0; i < N; i++)
1245      *   descriptor ()
1246      *
1247      * for (i = 0; i < N1; i++) {
1248      *    stream_type                             8   uimsbf
1249      *    reserved                                3   bslbf
1250      *    elementary_PID                         13   uimbsf
1251      *    reserved                                4   bslbf
1252      *    ES_info_length                         12   uimbsf
1253      *    for (i = 0; i < N1; i++) {
1254      *      descriptor ();
1255      *    }
1256      * }
1257      */
1258     GstMpegtsDescriptor *descriptor;
1259     GstMpegtsPMT *pmt;
1260     guint8 desc[] = { 0x0F, 0xFF, 0xFC, 0xFC };
1261     guint i;
1262 
1263     pmt = gst_mpegts_pmt_new ();
1264 
1265     if (program->pcr_stream == NULL)
1266       pmt->pcr_pid = 0x1FFF;
1267     else
1268       pmt->pcr_pid = tsmux_stream_get_pid (program->pcr_stream);
1269 
1270     descriptor = gst_mpegts_descriptor_from_registration ("HDMV", NULL, 0);
1271     g_ptr_array_add (pmt->descriptors, descriptor);
1272 
1273     descriptor = gst_mpegts_descriptor_from_custom (0x88, desc, 4);
1274     g_ptr_array_add (pmt->descriptors, descriptor);
1275 
1276     /* Write out the entries */
1277     for (i = 0; i < program->streams->len; i++) {
1278       GstMpegtsPMTStream *pmt_stream;
1279       TsMuxStream *stream = g_array_index (program->streams, TsMuxStream *, i);
1280 
1281       pmt_stream = gst_mpegts_pmt_stream_new ();
1282 
1283       /* FIXME: Use API to retrieve this from the stream */
1284       pmt_stream->stream_type = stream->stream_type;
1285       pmt_stream->pid = tsmux_stream_get_pid (stream);
1286 
1287       /* Write any ES descriptors needed */
1288       tsmux_stream_get_es_descrs (stream, pmt_stream);
1289       g_ptr_array_add (pmt->streams, pmt_stream);
1290     }
1291 
1292     TS_DEBUG ("PMT for program %d has %d streams",
1293         program->pgm_number, program->streams->len);
1294 
1295     pmt->program_number = program->pgm_number;
1296 
1297     program->pmt.pi.pid = program->pmt_pid;
1298     program->pmt_changed = FALSE;
1299 
1300     if (program->pmt.section)
1301       gst_mpegts_section_unref (program->pmt.section);
1302 
1303     program->pmt.section = gst_mpegts_section_from_pmt (pmt, program->pmt_pid);
1304     program->pmt.section->version_number = program->pmt_version++;
1305   }
1306 
1307   return tsmux_section_write_packet (GINT_TO_POINTER (GST_MPEGTS_SECTION_PMT),
1308       &program->pmt, mux);
1309 }
1310