mpegtsmux: Initialize PES packet before getting the header size.
[platform/upstream/gstreamer.git] / gst / mpegtsmux / tsmux / tsmuxstream.c
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., 59 Temple Place - Suite 330,
36  * Boston, MA 02111-1307, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 "tsmuxcommon.h"
87 #include "tsmuxstream.h"
88
89 static guint8 tsmux_stream_pes_header_length (TsMuxStream * stream);
90 static void tsmux_stream_write_pes_header (TsMuxStream * stream, guint8 * data);
91 static void tsmux_stream_find_pts_dts_within (TsMuxStream * stream, guint bound,
92     gint64 * pts, gint64 * dts);
93
94 struct TsMuxStreamBuffer
95 {
96   guint8 *data;
97   guint32 size;
98
99   /* PTS & DTS associated with the contents of this buffer */
100   gint64 pts;
101   gint64 dts;
102
103   void *user_data;
104 };
105
106 /**
107  * tsmux_stream_new:
108  * @pid: a PID
109  * @stream_type: the stream type
110  *
111  * Create a new stream with PID of @pid and @stream_type.
112  *
113  * Returns: a new #TsMuxStream.
114  */
115 TsMuxStream *
116 tsmux_stream_new (guint16 pid, TsMuxStreamType stream_type)
117 {
118   TsMuxStream *stream = g_slice_new0 (TsMuxStream);
119
120   stream->state = TSMUX_STREAM_STATE_HEADER;
121   stream->pi.pid = pid;
122   stream->stream_type = stream_type;
123
124   stream->pes_payload_size = 0;
125   stream->cur_pes_payload_size = 0;
126   stream->pes_bytes_written = 0;
127
128   switch (stream_type) {
129     case TSMUX_ST_VIDEO_MPEG1:
130     case TSMUX_ST_VIDEO_MPEG2:
131     case TSMUX_ST_VIDEO_MPEG4:
132     case TSMUX_ST_VIDEO_H264:
133       /* FIXME: Assign sequential IDs? */
134       stream->id = 0xE0;
135       stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
136       stream->is_video_stream = TRUE;
137       break;
138     case TSMUX_ST_AUDIO_AAC:
139     case TSMUX_ST_AUDIO_MPEG1:
140     case TSMUX_ST_AUDIO_MPEG2:
141       /* FIXME: Assign sequential IDs? */
142       stream->id = 0xC0;
143       stream->pi.flags |= TSMUX_PACKET_FLAG_PES_FULL_HEADER;
144       break;
145     case TSMUX_ST_VIDEO_DIRAC:
146     case TSMUX_ST_PS_AUDIO_LPCM:
147     case TSMUX_ST_PS_AUDIO_AC3:
148     case TSMUX_ST_PS_AUDIO_DTS:
149       stream->id = 0xFD;
150       /* FIXME: assign sequential extended IDs? */
151       switch (stream_type) {
152         case TSMUX_ST_VIDEO_DIRAC:
153           stream->id_extended = 0x60;
154           stream->is_video_stream = TRUE;
155           break;
156         case TSMUX_ST_PS_AUDIO_LPCM:
157           stream->id_extended = 0x80;
158           break;
159         case TSMUX_ST_PS_AUDIO_AC3:
160           stream->id_extended = 0x71;
161           break;
162         case TSMUX_ST_PS_AUDIO_DTS:
163           stream->id_extended = 0x82;
164           break;
165         default:
166           break;
167       }
168       stream->pi.flags |=
169           TSMUX_PACKET_FLAG_PES_FULL_HEADER |
170           TSMUX_PACKET_FLAG_PES_EXT_STREAMID;
171       break;
172     default:
173       g_critical ("Stream type 0x%0x not yet implemented", stream_type);
174       break;
175   }
176
177   stream->last_pts = -1;
178   stream->last_dts = -1;
179
180   stream->pcr_ref = 0;
181   stream->last_pcr = -1;
182
183   return stream;
184 }
185
186 /**
187  * tsmux_stream_get_pid:
188  * @stream: a #TsMuxStream
189  *
190  * Get the PID of @stream.
191  *
192  * Returns: The PID of @stream. 0xffff on error.
193  */
194 guint16
195 tsmux_stream_get_pid (TsMuxStream * stream)
196 {
197   g_return_val_if_fail (stream != NULL, G_MAXUINT16);
198
199   return stream->pi.pid;
200 }
201
202 /**
203  * tsmux_stream_free:
204  * @stream: a #TsMuxStream
205  *
206  * Free the resources of @stream.
207  */
208 void
209 tsmux_stream_free (TsMuxStream * stream)
210 {
211   g_return_if_fail (stream != NULL);
212
213   g_slice_free (TsMuxStream, stream);
214 }
215
216 /**
217  * tsmux_stream_set_buffer_release_func:
218  * @stream: a #TsMuxStream
219  * @func: the new #TsMuxStreamBufferReleaseFunc
220  *
221  * Set the function that will be called when a a piece of data fed to @stream
222  * with tsmux_stream_add_data() can be freed. @func will be called with user
223  * data as provided with the call to tsmux_stream_add_data().
224  */
225 void
226 tsmux_stream_set_buffer_release_func (TsMuxStream * stream,
227     TsMuxStreamBufferReleaseFunc func)
228 {
229   g_return_if_fail (stream != NULL);
230
231   stream->buffer_release = func;
232 }
233
234 /* Advance the current packet stream position by len bytes.
235  * Mustn't consume more than available in the current packet */
236 static void
237 tsmux_stream_consume (TsMuxStream * stream, guint len)
238 {
239   g_assert (stream->cur_buffer != NULL);
240   g_assert (len <= stream->cur_buffer->size - stream->cur_buffer_consumed);
241
242   stream->cur_buffer_consumed += len;
243   stream->bytes_avail -= len;
244
245   if (stream->cur_buffer_consumed == 0)
246     return;
247
248   if (stream->cur_buffer->pts != -1) {
249     stream->last_pts = stream->cur_buffer->pts;
250     stream->last_dts = stream->cur_buffer->dts;
251   } else if (stream->cur_buffer->dts != -1)
252     stream->last_dts = stream->cur_buffer->dts;
253
254   if (stream->cur_buffer_consumed == stream->cur_buffer->size) {
255     /* Current packet is completed, move along */
256     stream->buffers = g_list_delete_link (stream->buffers, stream->buffers);
257
258     if (stream->buffer_release) {
259       stream->buffer_release (stream->cur_buffer->data,
260           stream->cur_buffer->user_data);
261     }
262
263     g_slice_free (TsMuxStreamBuffer, stream->cur_buffer);
264     stream->cur_buffer = NULL;
265     /* FIXME: As a hack, for unbounded streams, start a new PES packet for each
266      * incoming packet we receive. This assumes that incoming data is 
267      * packetised sensibly - ie, every video frame */
268     if (stream->cur_pes_payload_size == 0)
269       stream->state = TSMUX_STREAM_STATE_HEADER;
270   }
271 }
272
273 /**
274  * tsmux_stream_at_pes_start:
275  * @stream: a #TsMuxStream
276  *
277  * Check if @stream is at the start of a PES packet.
278  *
279  * Returns: TRUE if @stream is at a PES header packet.
280  */
281 gboolean
282 tsmux_stream_at_pes_start (TsMuxStream * stream)
283 {
284   g_return_val_if_fail (stream != NULL, FALSE);
285
286   return stream->state == TSMUX_STREAM_STATE_HEADER;
287 }
288
289 /**
290  * tsmux_stream_bytes_avail:
291  * @stream: a #TsMuxStream
292  *
293  * Calculate how much bytes are available.
294  *
295  * Returns: The number of bytes available.
296  */
297 gint
298 tsmux_stream_bytes_avail (TsMuxStream * stream)
299 {
300   gint bytes_avail;
301
302   g_return_val_if_fail (stream != NULL, 0);
303
304   if (stream->cur_pes_payload_size != 0)
305     bytes_avail = stream->cur_pes_payload_size - stream->pes_bytes_written;
306   else
307     bytes_avail = tsmux_stream_bytes_in_buffer (stream);
308
309   bytes_avail = MIN (bytes_avail, tsmux_stream_bytes_in_buffer (stream));
310
311   /* Calculate the number of bytes available in the current PES */
312   if (stream->state == TSMUX_STREAM_STATE_HEADER)
313     bytes_avail += tsmux_stream_pes_header_length (stream);
314
315   return bytes_avail;
316 }
317
318 /**
319  * tsmux_stream_bytes_in_buffer:
320  * @stream: a #TsMuxStream
321  *
322  * Calculate how much bytes are in the buffer.
323  *
324  * Returns: The number of bytes in the buffer.
325  */
326 gint
327 tsmux_stream_bytes_in_buffer (TsMuxStream * stream)
328 {
329   g_return_val_if_fail (stream != NULL, 0);
330
331   return stream->bytes_avail;
332 }
333
334 /**
335  * tsmux_stream_initialize_pes_packet:
336  * @stream: a #TsMuxStream
337  *
338  * Initializes the PES packet.
339  *
340  * Returns: TRUE if we the packet was initialized.
341  */
342 gboolean
343 tsmux_stream_initialize_pes_packet (TsMuxStream * stream)
344 {
345   if (stream->state != TSMUX_STREAM_STATE_HEADER)
346     return TRUE;
347
348   if (stream->pes_payload_size != 0) {
349     /* Use prescribed fixed PES payload size */
350     stream->cur_pes_payload_size = stream->pes_payload_size;
351     tsmux_stream_find_pts_dts_within (stream, stream->cur_pes_payload_size,
352         &stream->pts, &stream->dts);
353   } else if (stream->is_video_stream) {
354     /* Unbounded for video streams */
355     stream->cur_pes_payload_size = 0;
356     tsmux_stream_find_pts_dts_within (stream,
357         tsmux_stream_bytes_in_buffer (stream), &stream->pts, &stream->dts);
358   } else {
359     /* Output a PES packet of all currently available bytes otherwise */
360     stream->cur_pes_payload_size = tsmux_stream_bytes_in_buffer (stream);
361     tsmux_stream_find_pts_dts_within (stream, stream->cur_pes_payload_size,
362         &stream->pts, &stream->dts);
363   }
364
365   stream->pi.flags &= ~(TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS |
366       TSMUX_PACKET_FLAG_PES_WRITE_PTS);
367
368   if (stream->pts != -1 && stream->dts != -1)
369     stream->pi.flags |= TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS;
370   else {
371     if (stream->pts != -1)
372       stream->pi.flags |= TSMUX_PACKET_FLAG_PES_WRITE_PTS;
373   }
374
375   return TRUE;
376 }
377
378 /**
379  * tsmux_stream_get_data:
380  * @stream: a #TsMuxStream
381  * @buf: a buffer to hold the result
382  * @len: the length of @buf
383  *
384  * Copy up to @len available data in @stream into the buffer @buf.
385  *
386  * Returns: TRUE if @len bytes could be retrieved.
387  */
388 gboolean
389 tsmux_stream_get_data (TsMuxStream * stream, guint8 * buf, guint len)
390 {
391   g_return_val_if_fail (stream != NULL, FALSE);
392   g_return_val_if_fail (buf != NULL, FALSE);
393
394   if (stream->state == TSMUX_STREAM_STATE_HEADER) {
395     guint8 pes_hdr_length;
396
397     pes_hdr_length = tsmux_stream_pes_header_length (stream);
398
399     /* Submitted buffer must be at least as large as the PES header */
400     if (len < pes_hdr_length)
401       return FALSE;
402
403     TS_DEBUG ("Writing PES header of length %u and payload %d",
404         pes_hdr_length, stream->cur_pes_payload_size);
405     tsmux_stream_write_pes_header (stream, buf);
406
407     len -= pes_hdr_length;
408     buf += pes_hdr_length;
409
410     stream->state = TSMUX_STREAM_STATE_PACKET;
411   }
412
413   if (len > (guint) tsmux_stream_bytes_avail (stream))
414     return FALSE;
415
416   stream->pes_bytes_written += len;
417
418   if (stream->cur_pes_payload_size != 0 &&
419       stream->pes_bytes_written == stream->cur_pes_payload_size) {
420     TS_DEBUG ("Finished PES packet");
421     stream->state = TSMUX_STREAM_STATE_HEADER;
422     stream->pes_bytes_written = 0;
423   }
424
425   while (len > 0) {
426     guint32 avail;
427     guint8 *cur;
428
429     if (stream->cur_buffer == NULL) {
430       /* Start next packet */
431       if (stream->buffers == NULL)
432         return FALSE;
433       stream->cur_buffer = (TsMuxStreamBuffer *) (stream->buffers->data);
434       stream->cur_buffer_consumed = 0;
435     }
436
437     /* Take as much as we can from the current buffer */
438     avail = stream->cur_buffer->size - stream->cur_buffer_consumed;
439     cur = stream->cur_buffer->data + stream->cur_buffer_consumed;
440     if (avail < len) {
441       memcpy (buf, cur, avail);
442       tsmux_stream_consume (stream, avail);
443
444       buf += avail;
445       len -= avail;
446     } else {
447       memcpy (buf, cur, len);
448       tsmux_stream_consume (stream, len);
449
450       len = 0;
451     }
452   }
453
454   return TRUE;
455 }
456
457 static guint8
458 tsmux_stream_pes_header_length (TsMuxStream * stream)
459 {
460   guint8 packet_len;
461
462   /* Calculate the length of the header for this stream */
463
464   /* start_code prefix + stream_id + pes_packet_length = 6 bytes */
465   packet_len = 6;
466
467   if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_FULL_HEADER) {
468     /* For a PES 'full header' we have at least 3 more bytes, 
469      * and then more based on flags */
470     packet_len += 3;
471     if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) {
472       packet_len += 10;
473     } else if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS) {
474       packet_len += 5;
475     }
476     if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_EXT_STREAMID) {
477       /* Need basic extension flags (1 byte), plus 2 more bytes for the 
478        * length + extended stream id */
479       packet_len += 3;
480     }
481   }
482
483   return packet_len;
484 }
485
486 /* Find a PTS/DTS to write into the pes header within the next bound bytes
487  * of the data */
488 static void
489 tsmux_stream_find_pts_dts_within (TsMuxStream * stream, guint bound,
490     gint64 * pts, gint64 * dts)
491 {
492   GList *cur;
493
494   *pts = -1;
495   *dts = -1;
496
497   for (cur = g_list_first (stream->buffers); cur != NULL;
498       cur = g_list_next (cur)) {
499     TsMuxStreamBuffer *curbuf = cur->data;
500
501     /* FIXME: This isn't quite correct - if the 'bound' is within this
502      * buffer, we don't know if the timestamp is before or after the split
503      * so we shouldn't return it */
504     if (bound <= curbuf->size) {
505       *pts = curbuf->pts;
506       *dts = curbuf->dts;
507       return;
508     }
509
510     /* Have we found a buffer with pts/dts set? */
511     if (curbuf->pts != -1 || curbuf->dts != -1) {
512       *pts = curbuf->pts;
513       *dts = curbuf->dts;
514       return;
515     }
516
517     bound -= curbuf->size;
518   }
519 }
520
521 static void
522 tsmux_stream_write_pes_header (TsMuxStream * stream, guint8 * data)
523 {
524   guint16 length_to_write;
525   guint8 hdr_len = tsmux_stream_pes_header_length (stream);
526
527   /* start_code prefix + stream_id + pes_packet_length = 6 bytes */
528   data[0] = 0x00;
529   data[1] = 0x00;
530   data[2] = 0x01;
531   data[3] = stream->id;
532   data += 4;
533
534   /* Write 2 byte PES packet length here. 0 (unbounded) is only
535    * valid for video packets */
536   if (stream->cur_pes_payload_size != 0) {
537     length_to_write = hdr_len + stream->cur_pes_payload_size - 6;
538   } else {
539     length_to_write = 0;
540   }
541
542   tsmux_put16 (&data, length_to_write);
543
544   if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_FULL_HEADER) {
545     guint8 flags = 0;
546
547     /* Not scrambled, original, not-copyrighted, data_alignment not specified */
548     *data++ = 0x81;
549
550     /* Flags */
551     if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS)
552       flags |= 0xC0;
553     else if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS)
554       flags |= 0x80;
555     if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_EXT_STREAMID)
556       flags |= 0x01;            /* Enable PES_extension_flag */
557     *data++ = flags;
558
559     /* Header length is the total pes length, 
560      * minus the 9 bytes of start codes, flags + hdr_len */
561     g_return_if_fail (hdr_len >= 9);
562     *data++ = (hdr_len - 9);
563
564     if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) {
565       tsmux_put_ts (&data, 0x3, stream->pts);
566       tsmux_put_ts (&data, 0x1, stream->dts);
567     } else if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_WRITE_PTS) {
568       tsmux_put_ts (&data, 0x2, stream->pts);
569     }
570     if (stream->pi.flags & TSMUX_PACKET_FLAG_PES_EXT_STREAMID) {
571       guint8 ext_len;
572
573       flags = 0x0f;             /* (reserved bits) | PES_extension_flag_2 */
574       *data++ = flags;
575
576       ext_len = 1;              /* Only writing 1 byte into the extended fields */
577       *data++ = 0x80 | ext_len;
578       /* Write the extended streamID */
579       *data++ = stream->id_extended;
580     }
581   }
582 }
583
584 /**
585  * tsmux_stream_add_data:
586  * @stream: a #TsMuxStream
587  * @data: data to add
588  * @len: length of @data
589  * @user_data: user data to pass to release func
590  * @pts: PTS of access unit in @data
591  * @dts: DTS of access unit in @data
592  *
593  * Submit @len bytes of @data into @stream. @pts and @dts can be set to the
594  * timestamp (against a 90Hz clock) of the first access unit in @data. A
595  * timestamp of -1 for @pts or @dts means unknown.
596  *
597  * @user_data will be passed to the release function as set with
598  * tsmux_stream_set_buffer_release_func() when @data can be freed.
599  */
600 void
601 tsmux_stream_add_data (TsMuxStream * stream, guint8 * data, guint len,
602     void *user_data, gint64 pts, gint64 dts)
603 {
604   TsMuxStreamBuffer *packet;
605
606   g_return_if_fail (stream != NULL);
607
608   packet = g_slice_new (TsMuxStreamBuffer);
609   packet->data = data;
610   packet->size = len;
611   packet->user_data = user_data;
612
613   packet->pts = pts;
614   packet->dts = dts;
615
616   if (stream->bytes_avail == 0)
617     stream->last_pts = pts;
618
619   stream->bytes_avail += len;
620   stream->buffers = g_list_append (stream->buffers, packet);
621 }
622
623 /**
624  * tsmux_stream_get_es_descrs:
625  * @stream: a #TsMuxStream
626  * @buf: a buffer to hold the ES descriptor
627  * @len: the length used in @buf
628  *
629  * Write an Elementary Stream Descriptor for @stream into @buf. the number of
630  * bytes consumed in @buf will be updated in @len.
631  *
632  * @buf and @len must be at least #TSMUX_MIN_ES_DESC_LEN.
633  */
634 void
635 tsmux_stream_get_es_descrs (TsMuxStream * stream, guint8 * buf, guint16 * len)
636 {
637   guint8 *pos;
638
639   g_return_if_fail (stream != NULL);
640
641   if (buf == NULL) {
642     if (len != NULL)
643       *len = 0;
644     return;
645   }
646
647   /* Based on the stream type, write out any descriptors to go in the 
648    * PMT ES_info field */
649   pos = buf;
650
651   /* tag (registration_descriptor), length, format_identifier */
652   switch (stream->stream_type) {
653     case TSMUX_ST_AUDIO_AAC:
654       /* FIXME */
655       break;
656     case TSMUX_ST_VIDEO_MPEG4:
657       /* FIXME */
658       break;
659     case TSMUX_ST_VIDEO_H264:
660       *pos++ = 0x05;
661       *pos++ = 8;
662       *pos++ = 0x48;            /* 'H' */
663       *pos++ = 0x44;            /* 'D' */
664       *pos++ = 0x4D;            /* 'M' */
665       *pos++ = 0x56;            /* 'V' */
666       /* FIXME : Not sure about this additional_identification_info */
667       *pos++ = 0xFF;
668       *pos++ = 0x1B;
669       *pos++ = 0x44;
670       *pos++ = 0x3F;
671       break;
672     case TSMUX_ST_VIDEO_DIRAC:
673       *pos++ = 0x05;
674       *pos++ = 4;
675       *pos++ = 0x64;            /* 'd' */
676       *pos++ = 0x72;            /* 'r' */
677       *pos++ = 0x61;            /* 'a' */
678       *pos++ = 0x63;            /* 'c' */
679       break;
680     case TSMUX_ST_PS_AUDIO_AC3:
681     {
682       *pos++ = 0x05;
683       *pos++ = 4;
684       *pos++ = 0x41;            /* 'A' */
685       *pos++ = 0x43;            /* 'C' */
686       *pos++ = 0x2D;            /* '-' */
687       *pos++ = 0x33;            /* '3' */
688
689       /* audio_stream_descriptor () | ATSC A/52-2001 Annex A
690        *
691        * descriptor_tag       8 uimsbf
692        * descriptor_length    8 uimsbf
693        * sample_rate_code     3 bslbf
694        * bsid                 5 bslbf
695        * bit_rate_code        6 bslbf
696        * surround_mode        2 bslbf
697        * bsmod                3 bslbf
698        * num_channels         4 bslbf
699        * full_svc             1 bslbf
700        * langcod              8 bslbf
701        * [...]
702        */
703       *pos++ = 0x81;
704       *pos++ = 0x04;
705
706       /* 3 bits sample_rate_code, 5 bits hardcoded bsid (default ver 8) */
707       switch (stream->audio_sampling) {
708         case 48000:
709           *pos++ = 0x08;
710           break;
711         case 44100:
712           *pos++ = 0x28;
713           break;
714         case 32000:
715           *pos++ = 0x48;
716           break;
717         default:
718           *pos++ = 0xE8;
719           break;                /* 48, 44.1 or 32 Khz */
720       }
721
722       /* 1 bit bit_rate_limit, 5 bits bit_rate_code, 2 bits suround_mode */
723       switch (stream->audio_bitrate) {
724         case 32:
725           *pos++ = 0x00 << 2;
726           break;
727         case 40:
728           *pos++ = 0x01 << 2;
729           break;
730         case 48:
731           *pos++ = 0x02 << 2;
732           break;
733         case 56:
734           *pos++ = 0x03 << 2;
735           break;
736         case 64:
737           *pos++ = 0x04 << 2;
738           break;
739         case 80:
740           *pos++ = 0x05 << 2;
741           break;
742         case 96:
743           *pos++ = 0x06 << 2;
744           break;
745         case 112:
746           *pos++ = 0x07 << 2;
747           break;
748         case 128:
749           *pos++ = 0x08 << 2;
750           break;
751         case 160:
752           *pos++ = 0x09 << 2;
753           break;
754         case 192:
755           *pos++ = 0x0A << 2;
756           break;
757         case 224:
758           *pos++ = 0x0B << 2;
759           break;
760         case 256:
761           *pos++ = 0x0C << 2;
762           break;
763         case 320:
764           *pos++ = 0x0D << 2;
765           break;
766         case 384:
767           *pos++ = 0x0E << 2;
768           break;
769         case 448:
770           *pos++ = 0x0F << 2;
771           break;
772         case 512:
773           *pos++ = 0x10 << 2;
774           break;
775         case 576:
776           *pos++ = 0x11 << 2;
777           break;
778         case 640:
779           *pos++ = 0x12 << 2;
780           break;
781         default:
782           *pos++ = 0x32 << 2;
783           break;                /* 640 Kb/s upper limit */
784       }
785
786       /* 3 bits bsmod, 4 bits num_channels, 1 bit full_svc */
787       switch (stream->audio_channels) {
788         case 1:
789           *pos++ = 0x01 << 1;
790           break;                /* 1/0 */
791         case 2:
792           *pos++ = 0x02 << 1;
793           break;                /* 2/0 */
794         case 3:
795           *pos++ = 0x0A << 1;
796           break;                /* <= 3 */
797         case 4:
798           *pos++ = 0x0B << 1;
799           break;                /* <= 4 */
800         case 5:
801           *pos++ = 0x0C << 1;
802           break;                /* <= 5 */
803         case 6:
804         default:
805           *pos++ = 0x0D << 1;
806           break;                /* <= 6 */
807       }
808
809       *pos++ = 0x00;
810
811       break;
812     }
813     case TSMUX_ST_PS_AUDIO_DTS:
814       /* FIXME */
815       break;
816     case TSMUX_ST_PS_AUDIO_LPCM:
817       /* FIXME */
818       break;
819     default:
820       break;
821   }
822
823   if (len)
824     *len = (pos - buf);
825 }
826
827 /**
828  * tsmux_stream_pcr_ref:
829  * @stream: a #TsMuxStream
830  *
831  * Mark the stream as being used as the PCR for some program.
832  */
833 void
834 tsmux_stream_pcr_ref (TsMuxStream * stream)
835 {
836   g_return_if_fail (stream != NULL);
837
838   stream->pcr_ref++;
839 }
840
841 /**
842  * tsmux_stream_pcr_unref:
843  * @stream: a #TsMuxStream
844  *
845  * Mark the stream as no longer being used as the PCR for some program.
846  */
847 void
848 tsmux_stream_pcr_unref (TsMuxStream * stream)
849 {
850   g_return_if_fail (stream != NULL);
851
852   stream->pcr_ref--;
853 }
854
855 /**
856  * tsmux_stream_is_pcr:
857  * @stream: a #TsMuxStream
858  *
859  * Check if @stream is used as the PCR for some program.
860  *
861  * Returns: TRUE if the stream is in use as the PCR for some program.
862  */
863 gboolean
864 tsmux_stream_is_pcr (TsMuxStream * stream)
865 {
866   return stream->pcr_ref != 0;
867 }
868
869 /**
870  * tsmux_stream_get_pts:
871  * @stream: a #TsMuxStream
872  *
873  * Return the PTS of the last buffer that has had bytes written and
874  * which _had_ a PTS in @stream.
875  *
876  * Returns: the PTS of the last buffer in @stream.
877  */
878 guint64
879 tsmux_stream_get_pts (TsMuxStream * stream)
880 {
881   g_return_val_if_fail (stream != NULL, -1);
882
883   return stream->last_pts;
884 }