2 * Copyright 2006 BBC and Fluendo S.A.
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
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/.
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
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.
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.
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., 59 Temple Place - Suite 330,
36 * Boston, MA 02111-1307, USA.
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
56 * Unless otherwise indicated, Source Code is licensed under MIT license.
57 * See further explanation attached in License Statement (distributed in the file
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:
67 * The above copyright notice and this permission notice shall be included in all
68 * copies or substantial portions of the Software.
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
87 #include "tsmuxstream.h"
90 /* Maximum total data length for a PAT section is 1024 bytes, minus an
91 * 8 byte header, then the length of each program entry is 32 bits,
92 * then finally a 32 bit CRC. Thus the maximum number of programs in this mux
93 * is (1024 - 8 - 4) / 4 = 253 because it only supports single section PATs */
94 #define TSMUX_MAX_PROGRAMS 253
96 #define TSMUX_SECTION_HDR_SIZE 8
98 #define TSMUX_DEFAULT_NETWORK_ID 0x0001
99 #define TSMUX_DEFAULT_TS_ID 0x0001
101 #define TSMUX_START_PROGRAM_ID 0x0001
102 #define TSMUX_START_PMT_PID 0x0010
103 #define TSMUX_START_ES_PID 0x0040
105 /* HACK: We use a fixed buffering offset for the PCR at the moment -
106 * this is the amount 'in advance' of the stream that the PCR sits.
108 #define TSMUX_PCR_OFFSET (TSMUX_CLOCK_FREQ / 8)
110 /* Times per second to write PCR */
111 #define TSMUX_DEFAULT_PCR_FREQ (25)
113 static gboolean tsmux_write_pat (TsMux * mux);
114 static gboolean tsmux_write_pmt (TsMux * mux, TsMuxProgram * program);
119 * Create a new muxer session.
121 * Returns: A new #TsMux object.
128 mux = g_slice_new0 (TsMux);
130 mux->transport_id = TSMUX_DEFAULT_TS_ID;
132 mux->next_pgm_no = TSMUX_START_PROGRAM_ID;
133 mux->next_pmt_pid = TSMUX_START_PMT_PID;
134 mux->next_stream_pid = TSMUX_START_ES_PID;
136 mux->pat_changed = TRUE;
137 mux->last_pat_ts = -1;
138 mux->pat_interval = TSMUX_DEFAULT_PAT_INTERVAL;
144 * tsmux_set_write_func:
146 * @func: a user callback function
147 * @user_data: user data passed to @func
149 * Set the callback function and user data to be called when @mux has output to
150 * produce. @user_data will be passed as user data in @func.
153 tsmux_set_write_func (TsMux * mux, TsMuxWriteFunc func, void *user_data)
155 g_return_if_fail (mux != NULL);
157 mux->write_func = func;
158 mux->write_func_data = user_data;
162 * tsmux_set_pat_interval:
164 * @freq: a new PAT interval
166 * Set the interval (in cycles of the 90kHz clock) for writing out the PAT table.
168 * Many transport stream clients might have problems if the PAT table is not
169 * inserted in the stream at regular intervals, especially when initially trying
170 * to figure out the contents of the stream.
173 tsmux_set_pat_interval (TsMux * mux, guint freq)
175 g_return_if_fail (mux != NULL);
177 mux->pat_interval = freq;
181 * tsmux_get_pat_interval:
184 * Get the configured PAT interval. See also tsmux_set_pat_interval().
186 * Returns: the configured PAT interval
189 tsmux_get_pat_interval (TsMux * mux)
191 g_return_val_if_fail (mux != NULL, 0);
193 return mux->pat_interval;
200 * Free all resources associated with @mux. After calling this function @mux can
201 * not be used anymore.
204 tsmux_free (TsMux * mux)
208 g_return_if_fail (mux != NULL);
210 /* Free all programs */
211 for (cur = g_list_first (mux->programs); cur != NULL; cur = g_list_next (cur)) {
212 TsMuxProgram *program = (TsMuxProgram *) cur->data;
214 tsmux_program_free (program);
216 g_list_free (mux->programs);
218 /* Free all streams */
219 for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) {
220 TsMuxStream *stream = (TsMuxStream *) cur->data;
222 tsmux_stream_free (stream);
224 g_list_free (mux->streams);
226 g_slice_free (TsMux, mux);
233 * Create a new program in the mising session @mux.
235 * Returns: a new #TsMuxProgram or %NULL when the maximum number of programs has
239 tsmux_program_new (TsMux * mux)
241 TsMuxProgram *program;
243 g_return_val_if_fail (mux != NULL, NULL);
245 /* Ensure we have room for another program */
246 if (mux->nb_programs == TSMUX_MAX_PROGRAMS)
249 program = g_slice_new0 (TsMuxProgram);
251 program->pmt_changed = TRUE;
252 program->last_pmt_ts = -1;
253 program->pmt_interval = TSMUX_DEFAULT_PMT_INTERVAL;
255 program->pgm_number = mux->next_pgm_no++;
256 program->pmt_pid = mux->next_pmt_pid++;
257 program->pcr_stream = NULL;
258 program->last_pcr = -1;
260 program->streams = g_array_sized_new (FALSE, TRUE, sizeof (TsMuxStream *), 1);
262 mux->programs = g_list_prepend (mux->programs, program);
264 mux->pat_changed = TRUE;
270 * tsmux_set_pmt_interval:
271 * @program: a #TsMuxProgram
272 * @freq: a new PMT interval
274 * Set the interval (in cycles of the 90kHz clock) for writing out the PMT table.
276 * Many transport stream clients might have problems if the PMT table is not
277 * inserted in the stream at regular intervals, especially when initially trying
278 * to figure out the contents of the stream.
281 tsmux_set_pmt_interval (TsMuxProgram * program, guint freq)
283 g_return_if_fail (program != NULL);
285 program->pmt_interval = freq;
289 * tsmux_get_pmt_interval:
290 * @program: a #TsMuxProgram
292 * Get the configured PMT interval. See also tsmux_set_pmt_interval().
294 * Returns: the configured PMT interval
297 tsmux_get_pmt_interval (TsMuxProgram * program)
299 g_return_val_if_fail (program != NULL, 0);
301 return program->pmt_interval;
305 * tsmux_program_add_stream:
306 * @program: a #TsMuxProgram
307 * @stream: a #TsMuxStream
309 * Add @stream to @program.
312 tsmux_program_add_stream (TsMuxProgram * program, TsMuxStream * stream)
314 g_return_if_fail (program != NULL);
315 g_return_if_fail (stream != NULL);
317 program->nb_streams++;
318 g_array_append_val (program->streams, stream);
319 program->pmt_changed = TRUE;
323 * tsmux_program_set_pcr_stream:
324 * @program: a #TsMuxProgram
325 * @stream: a #TsMuxStream
327 * Set @stream as the PCR stream for @program, overwriting the previously
328 * configured PCR stream. When @stream is NULL, program will have no PCR stream
332 tsmux_program_set_pcr_stream (TsMuxProgram * program, TsMuxStream * stream)
334 g_return_if_fail (program != NULL);
336 if (program->pcr_stream == stream)
339 if (program->pcr_stream != NULL)
340 tsmux_stream_pcr_unref (program->pcr_stream);
342 tsmux_stream_pcr_ref (stream);
343 program->pcr_stream = stream;
345 program->pmt_changed = TRUE;
352 * Get a new free PID.
354 * Returns: a new free PID.
357 tsmux_get_new_pid (TsMux * mux)
359 g_return_val_if_fail (mux != NULL, -1);
361 /* FIXME: It's possible that this next PID is already taken if a
362 * specific PID was requested earlier. We should find a free PID */
363 return mux->next_stream_pid++;
367 * tsmux_create_stream:
369 * @stream_type: a #TsMuxStreamType
370 * @pid: the PID of the new stream.
372 * Create a new stream of @stream_type in the muxer session @mux.
374 * When @pid is set to #TSMUX_PID_AUTO, a new free PID will automatically
375 * be allocated for the new stream.
377 * Returns: a new #TsMuxStream.
380 tsmux_create_stream (TsMux * mux, TsMuxStreamType stream_type, guint16 pid)
385 g_return_val_if_fail (mux != NULL, NULL);
387 if (pid == TSMUX_PID_AUTO) {
388 new_pid = tsmux_get_new_pid (mux);
390 new_pid = pid & 0x1FFF;
393 /* Ensure we're not creating a PID collision */
394 if (tsmux_find_stream (mux, new_pid))
397 stream = tsmux_stream_new (new_pid, stream_type);
399 mux->streams = g_list_prepend (mux->streams, stream);
408 * @pid: the PID to find.
410 * Find the stream associated wih PID.
412 * Returns: a #TsMuxStream with @pid or NULL when the stream was not found.
415 tsmux_find_stream (TsMux * mux, guint16 pid)
417 TsMuxStream *found = NULL;
420 g_return_val_if_fail (mux != NULL, NULL);
422 for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) {
423 TsMuxStream *stream = (TsMuxStream *) cur->data;
425 if (tsmux_stream_get_pid (stream) == pid) {
434 tsmux_packet_out (TsMux * mux)
436 if (G_UNLIKELY (mux->write_func == NULL))
439 return mux->write_func (mux->packet_buf, TSMUX_PACKET_LENGTH,
440 mux->write_func_data, mux->new_pcr);
444 * adaptation_field() {
445 * adaptation_field_length 8 uimsbf
446 * if(adaptation_field_length >0) {
447 * discontinuity_indicator 1 bslbf
448 * random_access_indicator 1 bslbf
449 * elementary_stream_priority_indicator 1 bslbf
452 * splicing_point_flag 1 bslbf
453 * transport_private_data_flag 1 bslbf
454 * adaptation_field_extension_flag 1 bslbf
455 * if(PCR_flag == '1') {
456 * program_clock_reference_base 33 uimsbf
458 * program_clock_reference_extension 9 uimsbf
460 * if(OPCR_flag == '1') {
461 * original_program_clock_reference_base 33 uimsbf
463 * original_program_clock_reference_extension 9 uimsbf
465 * if (splicing_point_flag == '1') {
466 * splice_countdown 8 tcimsbf
468 * if(transport_private_data_flag == '1') {
469 * transport_private_data_length 8 uimsbf
470 * for (i=0; i<transport_private_data_length;i++){
471 * private_data_byte 8 bslbf
474 * if (adaptation_field_extension_flag == '1' ) {
475 * adaptation_field_extension_length 8 uimsbf
477 * piecewise_rate_flag 1 bslbf
478 * seamless_splice_flag 1 bslbf
480 * if (ltw_flag == '1') {
481 * ltw_valid_flag 1 bslbf
482 * ltw_offset 15 uimsbf
484 * if (piecewise_rate_flag == '1') {
486 * piecewise_rate 22 uimsbf
488 * if (seamless_splice_flag == '1'){
489 * splice_type 4 bslbf
490 * DTS_next_AU[32..30] 3 bslbf
492 * DTS_next_AU[29..15] 15 bslbf
494 * DTS_next_AU[14..0] 15 bslbf
497 * for ( i=0;i<N;i++) {
502 * stuffing_byte 8 bslbf
508 tsmux_write_adaptation_field (guint8 * buf,
509 TsMuxPacketInfo * pi, guint8 min_length, guint8 * written)
514 g_assert (min_length <= TSMUX_PAYLOAD_LENGTH);
516 /* Write out all the fields from the packet info only if the
517 * user set the flag to request the adaptation field - if the flag
518 * isn't set, we're just supposed to write stuffing bytes */
519 if (pi->flags & TSMUX_PACKET_FLAG_ADAPTATION) {
520 TS_DEBUG ("writing adaptation fields");
521 if (pi->flags & TSMUX_PACKET_FLAG_DISCONT)
523 if (pi->flags & TSMUX_PACKET_FLAG_RANDOM_ACCESS)
525 if (pi->flags & TSMUX_PACKET_FLAG_PRIORITY)
527 if (pi->flags & TSMUX_PACKET_FLAG_WRITE_PCR) {
531 pcr_base = (pi->pcr / 300);
532 pcr_ext = (pi->pcr % 300);
535 TS_DEBUG ("Writing PCR %" G_GUINT64_FORMAT " + ext %u", pcr_base,
537 buf[pos++] = (pcr_base >> 25) & 0xff;
538 buf[pos++] = (pcr_base >> 17) & 0xff;
539 buf[pos++] = (pcr_base >> 9) & 0xff;
540 buf[pos++] = (pcr_base >> 1) & 0xff;
541 buf[pos++] = ((pcr_base << 7) & 0x80) | ((pcr_ext >> 8) & 0x01);
542 buf[pos++] = (pcr_ext) & 0xff;
544 if (pi->flags & TSMUX_PACKET_FLAG_WRITE_OPCR) {
548 opcr_base = (pi->opcr / 300);
549 opcr_ext = (pi->opcr % 300);
552 TS_DEBUG ("Writing OPCR");
553 buf[pos++] = (opcr_base >> 25) & 0xff;
554 buf[pos++] = (opcr_base >> 17) & 0xff;
555 buf[pos++] = (opcr_base >> 9) & 0xff;
556 buf[pos++] = (opcr_base >> 1) & 0xff;
557 buf[pos++] = ((opcr_base << 7) & 0x80) | ((opcr_ext >> 8) & 0x01);
558 buf[pos++] = (opcr_ext) & 0xff;
560 if (pi->flags & TSMUX_PACKET_FLAG_WRITE_SPLICE) {
562 buf[pos++] = pi->splice_countdown;
564 if (pi->private_data_len > 0) {
566 /* Private data to write, ensure we have enough room */
567 if ((1 + pi->private_data_len) > (TSMUX_PAYLOAD_LENGTH - pos))
569 buf[pos++] = pi->private_data_len;
570 memcpy (&(buf[pos]), pi->private_data, pi->private_data_len);
571 pos += pi->private_data_len;
572 TS_DEBUG ("%u bytes of private data", pi->private_data_len);
574 if (pi->flags & TSMUX_PACKET_FLAG_WRITE_ADAPT_EXT) {
576 TS_DEBUG ("FIXME: write Adaptation extension");
577 /* Write an empty extension for now */
582 /* Write the flags at the start */
585 /* Stuffing bytes if needed */
586 while (pos < min_length)
589 /* Write the adaptation field length, which doesn't include its own byte */
599 tsmux_write_ts_header (guint8 * buf, TsMuxPacketInfo * pi,
600 guint * payload_len_out, guint * payload_offset_out)
603 guint8 adaptation_flag;
604 guint8 adapt_min_length = 0;
605 guint8 adapt_len = 0;
607 gboolean write_adapt = FALSE;
610 buf[0] = TSMUX_SYNC_BYTE;
612 TS_DEBUG ("PID 0x%04x, counter = 0x%01x, %u bytes avail", pi->pid,
613 pi->packet_count & 0x0f, pi->stream_avail);
616 * transport_error_indicator
617 * payload_unit_start_indicator
618 * transport_priority: (00)
622 if (pi->packet_start_unit_indicator) {
623 tsmux_put16 (&tmp, 0x4000 | pi->pid);
625 tsmux_put16 (&tmp, pi->pid);
627 /* 2 bits: scrambling_control (NOT SUPPORTED) (00)
628 * 2 bits: adaptation field control (1x has_adaptation_field | x1 has_payload)
629 * 4 bits: continuity counter (xxxx)
631 adaptation_flag = pi->packet_count & 0x0f;
633 if (pi->flags & TSMUX_PACKET_FLAG_ADAPTATION) {
637 if (pi->stream_avail < TSMUX_PAYLOAD_LENGTH) {
638 /* Need an adaptation field regardless for stuffing */
639 adapt_min_length = TSMUX_PAYLOAD_LENGTH - pi->stream_avail;
646 /* Flag the adaptation field presence */
647 adaptation_flag |= 0x20;
648 res = tsmux_write_adaptation_field (buf + TSMUX_HEADER_LENGTH,
649 pi, adapt_min_length, &adapt_len);
650 if (G_UNLIKELY (res == FALSE))
653 /* Should have written at least the number of bytes we requested */
654 g_assert (adapt_len >= adapt_min_length);
657 /* The amount of packet data we wrote is the remaining space after
658 * the adaptation field */
659 *payload_len_out = payload_len = TSMUX_PAYLOAD_LENGTH - adapt_len;
660 *payload_offset_out = TSMUX_HEADER_LENGTH + adapt_len;
662 /* Now if we are going to write out some payload, flag that fact */
663 if (payload_len > 0 && pi->stream_avail > 0) {
664 /* Flag the presence of a payload */
665 adaptation_flag |= 0x10;
667 /* We must have enough data to fill the payload, or some calculation
669 g_assert (payload_len <= pi->stream_avail);
671 /* Packet with payload, increment the continuity counter */
675 /* Write the byte of transport_scrambling_control, adaptation_field_control
676 * + continuity counter out */
677 buf[3] = adaptation_flag;
681 TS_DEBUG ("Adaptation field of size >= %d + %d bytes payload",
682 adapt_len, payload_len);
684 TS_DEBUG ("Payload of %d bytes only", payload_len);
691 * tsmux_write_stream_packet:
693 * @stream: a #TsMuxStream
695 * Write a packet of @stream.
697 * Returns: TRUE if the packet could be written.
700 tsmux_write_stream_packet (TsMux * mux, TsMuxStream * stream)
702 guint payload_len, payload_offs;
703 TsMuxPacketInfo *pi = &stream->pi;
708 g_return_val_if_fail (mux != NULL, FALSE);
709 g_return_val_if_fail (stream != NULL, FALSE);
711 if (tsmux_stream_is_pcr (stream)) {
713 gint64 cur_pts = tsmux_stream_get_pts (stream);
718 TS_DEBUG ("TS for PCR stream is %" G_GINT64_FORMAT, cur_pts);
721 /* FIXME: The current PCR needs more careful calculation than just
722 * writing a fixed offset */
723 if (cur_pts != -1 && (cur_pts >= TSMUX_PCR_OFFSET))
724 cur_pcr = (cur_pts - TSMUX_PCR_OFFSET) *
725 (TSMUX_SYS_CLOCK_FREQ / TSMUX_CLOCK_FREQ);
727 /* Need to decide whether to write a new PCR in this packet */
728 if (stream->last_pcr == -1 ||
729 (cur_pcr - stream->last_pcr >
730 (TSMUX_CLOCK_FREQ / TSMUX_DEFAULT_PCR_FREQ))) {
733 TSMUX_PACKET_FLAG_ADAPTATION | TSMUX_PACKET_FLAG_WRITE_PCR;
734 stream->pi.pcr = cur_pcr;
735 stream->last_pcr = cur_pcr;
736 mux->new_pcr = cur_pcr;
739 /* check if we need to rewrite pat */
740 if (mux->last_pat_ts == -1 || mux->pat_changed)
742 else if (cur_pts >= mux->last_pat_ts + mux->pat_interval)
748 mux->last_pat_ts = cur_pts;
749 if (!tsmux_write_pat (mux))
753 /* check if we need to rewrite any of the current pmts */
754 for (cur = g_list_first (mux->programs); cur != NULL;
755 cur = g_list_next (cur)) {
756 TsMuxProgram *program = (TsMuxProgram *) cur->data;
759 if (program->last_pmt_ts == -1 || program->pmt_changed)
761 else if (cur_pts >= program->last_pmt_ts + program->pmt_interval)
767 program->last_pmt_ts = cur_pts;
768 if (!tsmux_write_pmt (mux, program))
774 pi->stream_avail = tsmux_stream_bytes_avail (stream);
775 pi->packet_start_unit_indicator = tsmux_stream_at_pes_start (stream);
777 if (!tsmux_write_ts_header (mux->packet_buf, pi, &payload_len, &payload_offs))
780 if (!tsmux_stream_get_data (stream, mux->packet_buf + payload_offs,
784 res = tsmux_packet_out (mux);
786 /* Reset all dynamic flags */
787 stream->pi.flags &= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
793 * tsmux_program_free:
794 * @program: a #TsMuxProgram
796 * Free the resources of @program. After this call @program can not be used
800 tsmux_program_free (TsMuxProgram * program)
802 g_return_if_fail (program != NULL);
804 g_array_free (program->streams, TRUE);
805 g_slice_free (TsMuxProgram, program);
809 tsmux_write_section (TsMux * mux, TsMuxSection * section)
812 guint payload_remain;
813 guint payload_len, payload_offs;
818 pi->packet_start_unit_indicator = TRUE;
820 cur_in = section->data;
821 payload_remain = pi->stream_avail;
823 while (payload_remain > 0) {
824 if (pi->packet_start_unit_indicator) {
825 /* Need to write an extra single byte start pointer */
828 if (!tsmux_write_ts_header (mux->packet_buf, pi,
829 &payload_len, &payload_offs)) {
835 /* Write the pointer byte */
836 mux->packet_buf[payload_offs] = 0x00;
840 pi->packet_start_unit_indicator = FALSE;
842 if (!tsmux_write_ts_header (mux->packet_buf, pi,
843 &payload_len, &payload_offs))
847 TS_DEBUG ("Outputting %d bytes to section. %d remaining after",
848 payload_len, payload_remain - payload_len);
850 memcpy (mux->packet_buf + payload_offs, cur_in, payload_len);
852 cur_in += payload_len;
853 payload_remain -= payload_len;
855 if (G_UNLIKELY (!tsmux_packet_out (mux))) {
866 tsmux_write_section_hdr (guint8 * pos, guint8 table_id, guint16 len,
867 guint16 id, guint8 version, guint8 section_nr, guint8 last_section_nr)
869 /* The length passed is the total length of the section, but we're not
870 * supposed to include the first 3 bytes of the header in the count */
873 /* 1 byte table identifier */
875 /* section_syntax_indicator = '0' | '0' | '11' reserved bits | (len >> 8) */
876 tsmux_put16 (&pos, 0xB000 | len);
877 /* 2 bytes transport/program id */
878 tsmux_put16 (&pos, id);
880 /* '11' reserved | version 'xxxxxx' | 'x' current_next */
881 *pos++ = 0xC0 | ((version & 0x1F) << 1) | 0x01;
883 *pos++ = last_section_nr;
887 tsmux_write_pat (TsMux * mux)
890 TsMuxSection *pat = &mux->pat;
892 if (mux->pat_changed) {
893 /* program_association_section ()
895 * section_syntax_indicator 1 bslbf
898 * section_length 12 uimsbf
899 * transport_stream_id 16 uimsbf
901 * version_number 5 uimsbf
902 * current_next_indicator 1 bslbf
903 * section_number 8 uimsbf
904 * last_section_number 8 uimsbf
905 * for (i = 0; i < N; i++) {
906 * program_number 16 uimsbf
908 * network_PID_or_program_map_PID 13 uimbsf
915 /* Prepare the section data after the section header */
916 pos = pat->data + TSMUX_SECTION_HDR_SIZE;
918 for (cur = g_list_first (mux->programs); cur != NULL;
919 cur = g_list_next (cur)) {
920 TsMuxProgram *program = (TsMuxProgram *) cur->data;
922 tsmux_put16 (&pos, program->pgm_number);
923 tsmux_put16 (&pos, 0xE000 | program->pmt_pid);
926 /* Measure the section length, include extra 4 bytes for CRC below */
927 pat->pi.stream_avail = pos - pat->data + 4;
929 /* Go back and write the header now that we know the final length.
930 * table_id = 0 for PAT */
931 tsmux_write_section_hdr (pat->data, 0x00, pat->pi.stream_avail,
932 mux->transport_id, mux->pat_version, 0, 0);
934 /* Calc and output CRC for data bytes, not including itself */
935 crc = calc_crc32 (pat->data, pat->pi.stream_avail - 4);
936 tsmux_put32 (&pos, crc);
938 TS_DEBUG ("PAT has %d programs, is %u bytes",
939 mux->nb_programs, pat->pi.stream_avail);
940 mux->pat_changed = FALSE;
944 return tsmux_write_section (mux, pat);
948 tsmux_write_pmt (TsMux * mux, TsMuxProgram * program)
950 TsMuxSection *pmt = &program->pmt;
952 if (program->pmt_changed) {
953 /* program_association_section ()
955 * section_syntax_indicator 1 bslbf
958 * section_length 12 uimsbf
959 * program_id 16 uimsbf
961 * version_number 5 uimsbf
962 * current_next_indicator 1 bslbf
963 * section_number 8 uimsbf
964 * last_section_number 8 uimsbf
968 * program_info_length 12 uimsbf
969 * for (i = 0; i < N; i++)
972 * for (i = 0; i < N1; i++) {
973 * stream_type 8 uimsbf
975 * elementary_PID 13 uimbsf
977 * ES_info_length 12 uimbsf
978 * for (i = 0; i < N1; i++) {
988 /* Prepare the section data after the basic section header */
989 pos = pmt->data + TSMUX_SECTION_HDR_SIZE;
991 if (program->pcr_stream == NULL)
992 tsmux_put16 (&pos, 0xFFFF);
994 tsmux_put16 (&pos, 0xE000 | tsmux_stream_get_pid (program->pcr_stream));
996 /* 4 bits reserved, 12 bits program_info_length, descriptor : HDMV */
997 tsmux_put16 (&pos, 0xF00C);
998 tsmux_put16 (&pos, 0x0504);
999 tsmux_put16 (&pos, 0x4844);
1000 tsmux_put16 (&pos, 0x4D56);
1001 tsmux_put16 (&pos, 0x8804);
1002 tsmux_put16 (&pos, 0x0FFF);
1003 tsmux_put16 (&pos, 0xFCFC);
1005 /* Write out the entries */
1006 for (i = 0; i < program->nb_streams; i++) {
1007 TsMuxStream *stream = g_array_index (program->streams, TsMuxStream *, i);
1008 guint16 es_info_len;
1010 /* FIXME: Use API to retrieve this from the stream */
1011 *pos++ = stream->stream_type;
1012 tsmux_put16 (&pos, 0xE000 | tsmux_stream_get_pid (stream));
1014 /* Write any ES descriptors needed */
1015 tsmux_stream_get_es_descrs (stream, mux->es_info_buf, &es_info_len);
1016 tsmux_put16 (&pos, 0xF000 | es_info_len);
1018 if (es_info_len > 0) {
1019 TS_DEBUG ("Writing descriptor of len %d for PID 0x%04x",
1020 es_info_len, tsmux_stream_get_pid (stream));
1021 if (G_UNLIKELY (pos + es_info_len >=
1022 pmt->data + TSMUX_MAX_SECTION_LENGTH))
1025 memcpy (pos, mux->es_info_buf, es_info_len);
1030 /* Include the CRC in the byte count */
1031 pmt->pi.stream_avail = pos - pmt->data + 4;
1033 /* Go back and patch the pmt_header now that we know the length.
1034 * table_id = 2 for PMT */
1035 tsmux_write_section_hdr (pmt->data, 0x02, pmt->pi.stream_avail,
1036 program->pgm_number, program->pmt_version, 0, 0);
1038 /* Calc and output CRC for data bytes,
1039 * but not counting the CRC bytes this time */
1040 crc = calc_crc32 (pmt->data, pmt->pi.stream_avail - 4);
1041 tsmux_put32 (&pos, crc);
1043 TS_DEBUG ("PMT for program %d has %d streams, is %u bytes",
1044 program->pgm_number, program->nb_streams, pmt->pi.stream_avail);
1046 pmt->pi.pid = program->pmt_pid;
1047 program->pmt_changed = FALSE;
1048 program->pmt_version++;
1051 return tsmux_write_section (mux, pmt);