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, §ion->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, §ion->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