22026a1e5186f6704fda9a240fa65f08aabd71a9
[platform/upstream/gstreamer.git] / gst / mpegpsmux / psmuxstream.c
1 /* MPEG-PS muxer plugin for GStreamer
2  * Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /*
20  * Unless otherwise indicated, Source Code is licensed under MIT license.
21  * See further explanation attached in License Statement (distributed in the file
22  * LICENSE).
23  *
24  * Permission is hereby granted, free of charge, to any person obtaining a copy of
25  * this software and associated documentation files (the "Software"), to deal in
26  * the Software without restriction, including without limitation the rights to
27  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
28  * of the Software, and to permit persons to whom the Software is furnished to do
29  * so, subject to the following conditions:
30  *
31  * The above copyright notice and this permission notice shall be included in all
32  * copies or substantial portions of the Software.
33  *
34  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
40  * SOFTWARE.
41  */
42
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include <string.h>
48
49 #include "psmuxcommon.h"
50 #include "psmuxstream.h"
51 #include "psmux.h"
52
53 static guint8 psmux_stream_pes_header_length (PsMuxStream * stream);
54 static void psmux_stream_write_pes_header (PsMuxStream * stream, guint8 * data);
55 static void psmux_stream_find_pts_dts_within (PsMuxStream * stream, guint bound,
56     gint64 * pts, gint64 * dts);
57
58 /**
59  * psmux_stream_new:
60  * @pid: a PID
61  * @stream_type: the stream type
62  *
63  * Create a new stream with type = @stream_type, assign stream id accordingly
64  *
65  * Returns: a new #PsMuxStream.
66  */
67 PsMuxStream *
68 psmux_stream_new (PsMux * mux, PsMuxStreamType stream_type)
69 {
70   PsMuxStream *stream = g_slice_new0 (PsMuxStream);
71   PsMuxStreamIdInfo *info = &(mux->id_info);
72
73   stream->stream_type = stream_type;
74   stream->is_audio_stream = FALSE;
75   stream->is_video_stream = FALSE;
76   stream->stream_id = 0;
77   stream->max_buffer_size = 0;
78
79   switch (stream_type) {
80       /* MPEG AUDIO */
81     case PSMUX_ST_AUDIO_MPEG1:
82     case PSMUX_ST_AUDIO_MPEG2:
83       stream->max_buffer_size = 2484;   /* ISO/IEC 13818 2.5.2.4 */
84     case PSMUX_ST_AUDIO_AAC:
85       if (info->id_mpga > PSMUX_STREAM_ID_MPGA_MAX)
86         break;
87       stream->stream_id = info->id_mpga++;
88       stream->stream_id_ext = 0;
89       stream->is_audio_stream = TRUE;
90       break;
91       /* MPEG VIDEO */
92     case PSMUX_ST_VIDEO_MPEG1:
93     case PSMUX_ST_VIDEO_MPEG2:
94     case PSMUX_ST_VIDEO_MPEG4:
95     case PSMUX_ST_VIDEO_H264:
96       if (info->id_mpgv > PSMUX_STREAM_ID_MPGV_MAX)
97         break;
98       stream->stream_id = info->id_mpgv++;
99       stream->stream_id_ext = 0;
100       stream->is_video_stream = TRUE;
101       break;
102       /* AC3 / A52 */
103     case PSMUX_ST_PS_AUDIO_AC3:
104       if (info->id_ac3 > PSMUX_STREAM_ID_AC3_MAX)
105         break;
106       stream->stream_id = PSMUX_PRIVATE_STREAM_1;
107       stream->stream_id_ext = info->id_ac3++;
108       stream->is_audio_stream = TRUE;
109       /* AC3 requires data alignment */
110       stream->pi.flags |= PSMUX_PACKET_FLAG_PES_DATA_ALIGN;
111       break;
112       /* TODO: SPU missing */
113 #if 0
114     case spu:
115       if (info->id_spu > PSMUX_STREAM_ID_SPU_MAX)
116         break;
117       return info->id_spu++;
118 #endif
119       /* DTS */
120     case PSMUX_ST_PS_AUDIO_DTS:
121       if (info->id_dts > PSMUX_STREAM_ID_DTS_MAX)
122         break;
123       stream->stream_id = PSMUX_PRIVATE_STREAM_1;
124       stream->stream_id_ext = info->id_dts++;
125       stream->is_audio_stream = TRUE;
126       break;
127       /* LPCM */
128     case PSMUX_ST_PS_AUDIO_LPCM:
129       if (info->id_lpcm > PSMUX_STREAM_ID_LPCM_MAX)
130         break;
131       stream->stream_id = PSMUX_PRIVATE_STREAM_1;
132       stream->stream_id_ext = info->id_lpcm++;
133       stream->is_audio_stream = TRUE;
134       break;
135     case PSMUX_ST_VIDEO_DIRAC:
136       if (info->id_dirac > PSMUX_STREAM_ID_DIRAC_MAX)
137         break;
138       stream->stream_id = PSMUX_EXTENDED_STREAM;
139       stream->stream_id_ext = info->id_dirac++;
140       stream->is_video_stream = TRUE;
141       break;
142     default:
143       g_critical ("Stream type 0x%0x not yet implemented", stream_type);
144       break;
145   }
146
147   if (stream->stream_id == 0) {
148     g_critical ("Number of elementary streams of type %04x exceeds maximum",
149         stream->stream_type);
150     g_slice_free (PsMuxStream, stream);
151     return NULL;
152   }
153
154   /* XXX: Are private streams also using stream_id_ext? */
155   if (stream->stream_id == PSMUX_EXTENDED_STREAM)
156     stream->pi.flags |= PSMUX_PACKET_FLAG_PES_EXT_STREAMID;
157
158   /* Are these useful at all? */
159   if (stream->stream_id == PSMUX_PROGRAM_STREAM_MAP ||
160       stream->stream_id == PSMUX_PADDING_STREAM ||
161       stream->stream_id == PSMUX_PRIVATE_STREAM_2 ||
162       stream->stream_id == PSMUX_ECM ||
163       stream->stream_id == PSMUX_EMM ||
164       stream->stream_id == PSMUX_PROGRAM_STREAM_DIRECTORY ||
165       stream->stream_id == PSMUX_DSMCC_STREAM ||
166       stream->stream_id == PSMUX_ITU_T_H222_1_TYPE_E)
167     stream->pi.flags &= ~PSMUX_PACKET_FLAG_PES_FULL_HEADER;
168   else
169     stream->pi.flags |= PSMUX_PACKET_FLAG_PES_FULL_HEADER;
170
171   stream->buffers = NULL;
172   stream->bytes_avail = 0;
173   stream->cur_buffer = NULL;
174   stream->cur_buffer_consumed = 0;
175
176   stream->cur_pes_payload_size = 0;
177
178   stream->pts = -1;
179   stream->dts = -1;
180   stream->last_pts = -1;
181
182   /* These fields are set by gstreamer */
183   stream->audio_sampling = 0;
184   stream->audio_channels = 0;
185   stream->audio_bitrate = 0;
186
187   if (stream->max_buffer_size == 0) {
188     /* XXX: VLC'S VALUE. Better default? */
189     if (stream->is_video_stream)
190       stream->max_buffer_size = 400 * 1024;
191     else if (stream->is_audio_stream)
192       stream->max_buffer_size = 4 * 1024;
193     else
194       g_assert_not_reached ();
195   }
196
197   return stream;
198 }
199
200 /**
201  * psmux_stream_free:
202  * @stream: a #PsMuxStream
203  *
204  * Free the resources of @stream.
205  */
206 void
207 psmux_stream_free (PsMuxStream * stream)
208 {
209   g_return_if_fail (stream != NULL);
210
211   if (psmux_stream_bytes_in_buffer (stream)) {
212     g_warning ("Freeing stream with data not yet processed");
213   }
214   g_slice_free (PsMuxStream, stream);
215 }
216
217 /* Advance the current packet stream position by len bytes.
218  * Mustn't consume more than available in the current packet */
219 static void
220 psmux_stream_consume (PsMuxStream * stream, guint len)
221 {
222   g_assert (stream->cur_buffer != NULL);
223   g_assert (len <= stream->cur_buffer->map.size - stream->cur_buffer_consumed);
224
225   stream->cur_buffer_consumed += len;
226   stream->bytes_avail -= len;
227
228   if (stream->cur_buffer_consumed == 0)
229     return;
230
231   if (stream->cur_buffer->pts != -1)
232     stream->last_pts = stream->cur_buffer->pts;
233
234   if (stream->cur_buffer_consumed == stream->cur_buffer->map.size) {
235     /* Current packet is completed, move along */
236     stream->buffers = g_list_delete_link (stream->buffers, stream->buffers);
237
238     gst_buffer_unmap (stream->cur_buffer->buf, &stream->cur_buffer->map);
239     gst_buffer_unref (stream->cur_buffer->buf);
240     g_slice_free (PsMuxStreamBuffer, stream->cur_buffer);
241     stream->cur_buffer = NULL;
242   }
243 }
244
245
246 /**
247  * psmux_stream_bytes_in_buffer:
248  * @stream: a #PsMuxStream
249  *
250  * Calculate how much bytes are in the buffer.
251  *
252  * Returns: The number of bytes in the buffer.
253  */
254 gint
255 psmux_stream_bytes_in_buffer (PsMuxStream * stream)
256 {
257   g_return_val_if_fail (stream != NULL, 0);
258
259   return stream->bytes_avail;
260 }
261
262 /**
263  * psmux_stream_get_data:
264  * @stream: a #PsMuxStream
265  * @buf: a buffer to hold the result
266  * @len: the length of @buf
267  *
268  * Write a PES packet to @buf, up to @len bytes
269  *
270  * Returns: number of bytes having been written, 0 if error
271  */
272 guint
273 psmux_stream_get_data (PsMuxStream * stream, guint8 * buf, guint len)
274 {
275   guint8 pes_hdr_length;
276   guint w;
277
278   g_return_val_if_fail (stream != NULL, FALSE);
279   g_return_val_if_fail (buf != NULL, FALSE);
280   g_return_val_if_fail (len >= PSMUX_PES_MAX_HDR_LEN, FALSE);
281
282   stream->cur_pes_payload_size =
283       MIN (psmux_stream_bytes_in_buffer (stream), len - PSMUX_PES_MAX_HDR_LEN);
284   /* Note that we cannot make a better estimation of the header length for the
285    * time being; because the header length is dependent on whether we can find a
286    * timestamp in the upcomming buffers, which in turn depends on
287    * cur_pes_payload_size, which is exactly what we want to decide.
288    */
289
290   psmux_stream_find_pts_dts_within (stream, stream->cur_pes_payload_size,
291       &stream->pts, &stream->dts);
292
293   /* clear pts/dts flag */
294   stream->pi.flags &= ~(PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS |
295       PSMUX_PACKET_FLAG_PES_WRITE_PTS);
296   /* update pts/dts flag */
297   if (stream->pts != -1 && stream->dts != -1)
298     stream->pi.flags |= PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS;
299   else {
300     if (stream->pts != -1)
301       stream->pi.flags |= PSMUX_PACKET_FLAG_PES_WRITE_PTS;
302   }
303
304   pes_hdr_length = psmux_stream_pes_header_length (stream);
305
306   /* write pes header */
307   GST_LOG ("Writing PES header of length %u and payload %d",
308       pes_hdr_length, stream->cur_pes_payload_size);
309   psmux_stream_write_pes_header (stream, buf);
310
311   buf += pes_hdr_length;
312   w = stream->cur_pes_payload_size;     /* number of bytes of payload to write */
313
314   while (w > 0) {
315     guint32 avail;
316     guint8 *cur;
317
318     if (stream->cur_buffer == NULL) {
319       /* Start next packet */
320       if (stream->buffers == NULL)
321         return FALSE;
322       stream->cur_buffer = (PsMuxStreamBuffer *) (stream->buffers->data);
323       stream->cur_buffer_consumed = 0;
324     }
325
326     /* Take as much as we can from the current buffer */
327     avail = stream->cur_buffer->map.size - stream->cur_buffer_consumed;
328     cur = stream->cur_buffer->map.data + stream->cur_buffer_consumed;
329     if (avail < w) {
330       memcpy (buf, cur, avail);
331       psmux_stream_consume (stream, avail);
332
333       buf += avail;
334       w -= avail;
335     } else {
336       memcpy (buf, cur, w);
337       psmux_stream_consume (stream, w);
338
339       w = 0;
340     }
341   }
342
343   return pes_hdr_length + stream->cur_pes_payload_size;
344 }
345
346 static guint8
347 psmux_stream_pes_header_length (PsMuxStream * stream)
348 {
349   guint8 packet_len;
350
351   /* Calculate the length of the header for this stream */
352
353   /* start_code prefix + stream_id + pes_packet_length = 6 bytes */
354   packet_len = 6;
355
356   if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_FULL_HEADER) {
357     /* For a PES 'full header' we have at least 3 more bytes, 
358      * and then more based on flags */
359     packet_len += 3;
360     if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) {
361       packet_len += 10;
362     } else if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS) {
363       packet_len += 5;
364     }
365     if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_EXT_STREAMID) {
366       /* Need basic extension flags (1 byte), plus 2 more bytes for the 
367        * length + extended stream id */
368       packet_len += 3;
369     }
370   }
371
372   return packet_len;
373 }
374
375 /* Find a PTS/DTS to write into the pes header within the next bound bytes
376  * of the data */
377 static void
378 psmux_stream_find_pts_dts_within (PsMuxStream * stream, guint bound,
379     gint64 * pts, gint64 * dts)
380 {
381   /* 1. When there are at least one buffer then output the first buffer with
382    * pts/dts
383    * 2. If the bound is too small to include even one buffer, output the pts/dts
384    * of that buffer.
385    */
386   GList *cur;
387
388   *pts = -1;
389   *dts = -1;
390
391   for (cur = g_list_first (stream->buffers); cur != NULL;
392       cur = g_list_next (cur)) {
393     PsMuxStreamBuffer *curbuf = cur->data;
394
395     /* FIXME: This isn't quite correct - if the 'bound' is within this
396      * buffer, we don't know if the timestamp is before or after the split
397      * so we shouldn't return it */
398     if (bound <= curbuf->map.size) {
399       *pts = curbuf->pts;
400       *dts = curbuf->dts;
401       return;
402     }
403
404     /* Have we found a buffer with pts/dts set? */
405     if (curbuf->pts != -1 || curbuf->dts != -1) {
406       *pts = curbuf->pts;
407       *dts = curbuf->dts;
408       return;
409     }
410
411     bound -= curbuf->map.size;
412   }
413 }
414
415 static void
416 psmux_stream_write_pes_header (PsMuxStream * stream, guint8 * data)
417 {
418   guint16 length_to_write;
419   guint8 hdr_len = psmux_stream_pes_header_length (stream);
420
421   /* start_code prefix + stream_id + pes_packet_length = 6 bytes */
422   data[0] = 0x00;
423   data[1] = 0x00;
424   data[2] = 0x01;
425   data[3] = stream->stream_id;
426   data += 4;
427
428   length_to_write = hdr_len - 6 + stream->cur_pes_payload_size;
429
430   psmux_put16 (&data, length_to_write);
431
432   if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_FULL_HEADER) {
433     guint8 flags = 0;
434
435     /* Not scrambled, original, not-copyrighted, data_alignment specified by flag */
436     flags = 0x81;
437     if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_DATA_ALIGN)
438       flags |= 0x04;            /* Enable data_alignment_indicator */
439     *data++ = flags;
440
441     /* Flags */
442     flags = 0;
443     if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS)
444       flags |= 0xC0;
445     else if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS)
446       flags |= 0x80;
447     if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_EXT_STREAMID)
448       flags |= 0x01;            /* Enable PES_extension_flag */
449     *data++ = flags;
450
451     /* Header length is the total pes length, 
452      * minus the 9 bytes of start codes, flags + hdr_len */
453     g_return_if_fail (hdr_len >= 9);
454     *data++ = (hdr_len - 9);
455
456     if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS_DTS) {
457       psmux_put_ts (&data, 0x3, stream->pts);
458       psmux_put_ts (&data, 0x1, stream->dts);
459     } else if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_WRITE_PTS) {
460       psmux_put_ts (&data, 0x2, stream->pts);
461     }
462
463     if (stream->pi.flags & PSMUX_PACKET_FLAG_PES_EXT_STREAMID) {
464       guint8 ext_len;
465
466       flags = 0x0f;             /* preceeding flags all 0 | (reserved bits) | PES_extension_flag_2 */
467       *data++ = flags;
468
469       ext_len = 1;              /* Only writing 1 byte into the extended fields */
470       *data++ = 0x80 | ext_len; /* marker | PES_extension_field_length */
471       *data++ = 0x80 | stream->stream_id_ext;   /* stream_id_extension_flag | extended_stream_id */
472     }
473   }
474 }
475
476 /**
477  * psmux_stream_add_data:
478  * @stream: a #PsMuxStream
479  * @buffer: (transfer full): buffer with data to add
480  * @pts: PTS of access unit in @data
481  * @dts: DTS of access unit in @data
482  *
483  * Submit @len bytes of @data into @stream. @pts and @dts can be set to the
484  * timestamp (against a 90Hz clock) of the first access unit in @data. A
485  * timestamp of -1 for @pts or @dts means unknown.
486  *
487  * This function takes ownership of @buffer.
488  */
489 void
490 psmux_stream_add_data (PsMuxStream * stream, GstBuffer * buffer,
491     gint64 pts, gint64 dts, gboolean keyunit)
492 {
493   PsMuxStreamBuffer *packet;
494
495   g_return_if_fail (stream != NULL);
496
497   packet = g_slice_new (PsMuxStreamBuffer);
498   packet->buf = buffer;
499
500   if (!gst_buffer_map (packet->buf, &packet->map, GST_MAP_READ)) {
501     GST_ERROR ("Failed to map buffer for reading");
502     gst_buffer_unref (packet->buf);
503     g_slice_free (PsMuxStreamBuffer, packet);
504     return;
505   }
506
507   packet->keyunit = keyunit;
508   packet->pts = pts;
509   packet->dts = dts;
510
511   if (stream->bytes_avail == 0)
512     stream->last_pts = pts;
513
514   stream->bytes_avail += packet->map.size;
515   /* FIXME: perhaps use GstQueueArray instead? */
516   stream->buffers = g_list_append (stream->buffers, packet);
517
518 }
519
520 /**
521  * psmux_stream_get_es_descrs:
522  * @stream: a #PsMuxStream
523  * @buf: a buffer to hold the ES descriptor
524  * @len: the length used in @buf
525  *
526  * Write an Elementary Stream Descriptor for @stream into @buf. the number of
527  * bytes consumed in @buf will be updated in @len.
528  *
529  * @buf and @len must be at least #PSMUX_MIN_ES_DESC_LEN.
530  */
531 void
532 psmux_stream_get_es_descrs (PsMuxStream * stream, guint8 * buf, guint16 * len)
533 {
534   guint8 *pos;
535
536   g_return_if_fail (stream != NULL);
537
538   if (buf == NULL) {
539     if (len != NULL)
540       *len = 0;
541     return;
542   }
543
544   /* Based on the stream type, write out any descriptors to go in the 
545    * PMT ES_info field */
546   pos = buf;
547
548   /* tag (registration_descriptor), length, format_identifier */
549   switch (stream->stream_type) {
550     case PSMUX_ST_AUDIO_AAC:
551       /* FIXME */
552       break;
553     case PSMUX_ST_VIDEO_MPEG4:
554       /* FIXME */
555       break;
556     case PSMUX_ST_VIDEO_H264:
557       *pos++ = 0x05;
558       *pos++ = 8;
559       *pos++ = 0x48;            /* 'H' */
560       *pos++ = 0x44;            /* 'D' */
561       *pos++ = 0x4D;            /* 'M' */
562       *pos++ = 0x56;            /* 'V' */
563       /* FIXME : Not sure about this additional_identification_info */
564       *pos++ = 0xFF;
565       *pos++ = 0x1B;
566       *pos++ = 0x44;
567       *pos++ = 0x3F;
568       break;
569     case PSMUX_ST_VIDEO_DIRAC:
570       *pos++ = 0x05;
571       *pos++ = 4;
572       *pos++ = 0x64;            /* 'd' */
573       *pos++ = 0x72;            /* 'r' */
574       *pos++ = 0x61;            /* 'a' */
575       *pos++ = 0x63;            /* 'c' */
576       break;
577     case PSMUX_ST_PS_AUDIO_AC3:
578     {
579       *pos++ = 0x05;
580       *pos++ = 4;
581       *pos++ = 0x41;            /* 'A' */
582       *pos++ = 0x43;            /* 'C' */
583       *pos++ = 0x2D;            /* '-' */
584       *pos++ = 0x33;            /* '3' */
585
586       /* audio_stream_descriptor () | ATSC A/52-2001 Annex A
587        *
588        * descriptor_tag       8 uimsbf
589        * descriptor_length    8 uimsbf
590        * sample_rate_code     3 bslbf
591        * bsid                 5 bslbf
592        * bit_rate_code        6 bslbf
593        * surround_mode        2 bslbf
594        * bsmod                3 bslbf
595        * num_channels         4 bslbf
596        * full_svc             1 bslbf
597        * langcod              8 bslbf
598        * [...]
599        */
600       *pos++ = 0x81;
601       *pos++ = 0x04;
602
603       /* 3 bits sample_rate_code, 5 bits hardcoded bsid (default ver 8) */
604       switch (stream->audio_sampling) {
605         case 48000:
606           *pos++ = 0x08;
607           break;
608         case 44100:
609           *pos++ = 0x28;
610           break;
611         case 32000:
612           *pos++ = 0x48;
613           break;
614         default:
615           *pos++ = 0xE8;
616           break;                /* 48, 44.1 or 32 Khz */
617       }
618
619       /* 1 bit bit_rate_limit, 5 bits bit_rate_code, 2 bits suround_mode */
620       switch (stream->audio_bitrate) {
621         case 32:
622           *pos++ = 0x00 << 2;
623           break;
624         case 40:
625           *pos++ = 0x01 << 2;
626           break;
627         case 48:
628           *pos++ = 0x02 << 2;
629           break;
630         case 56:
631           *pos++ = 0x03 << 2;
632           break;
633         case 64:
634           *pos++ = 0x04 << 2;
635           break;
636         case 80:
637           *pos++ = 0x05 << 2;
638           break;
639         case 96:
640           *pos++ = 0x06 << 2;
641           break;
642         case 112:
643           *pos++ = 0x07 << 2;
644           break;
645         case 128:
646           *pos++ = 0x08 << 2;
647           break;
648         case 160:
649           *pos++ = 0x09 << 2;
650           break;
651         case 192:
652           *pos++ = 0x0A << 2;
653           break;
654         case 224:
655           *pos++ = 0x0B << 2;
656           break;
657         case 256:
658           *pos++ = 0x0C << 2;
659           break;
660         case 320:
661           *pos++ = 0x0D << 2;
662           break;
663         case 384:
664           *pos++ = 0x0E << 2;
665           break;
666         case 448:
667           *pos++ = 0x0F << 2;
668           break;
669         case 512:
670           *pos++ = 0x10 << 2;
671           break;
672         case 576:
673           *pos++ = 0x11 << 2;
674           break;
675         case 640:
676           *pos++ = 0x12 << 2;
677           break;
678         default:
679           *pos++ = 0x32 << 2;
680           break;                /* 640 Kb/s upper limit */
681       }
682
683       /* 3 bits bsmod, 4 bits num_channels, 1 bit full_svc */
684       switch (stream->audio_channels) {
685         case 1:
686           *pos++ = 0x01 << 1;
687           break;                /* 1/0 */
688         case 2:
689           *pos++ = 0x02 << 1;
690           break;                /* 2/0 */
691         case 3:
692           *pos++ = 0x0A << 1;
693           break;                /* <= 3 */
694         case 4:
695           *pos++ = 0x0B << 1;
696           break;                /* <= 4 */
697         case 5:
698           *pos++ = 0x0C << 1;
699           break;                /* <= 5 */
700         case 6:
701         default:
702           *pos++ = 0x0D << 1;
703           break;                /* <= 6 */
704       }
705
706       *pos++ = 0x00;
707
708       break;
709     }
710     case PSMUX_ST_PS_AUDIO_DTS:
711       /* FIXME */
712       break;
713     case PSMUX_ST_PS_AUDIO_LPCM:
714       /* FIXME */
715       break;
716     default:
717       break;
718   }
719
720   if (len)
721     *len = (pos - buf);
722 }
723
724 /**
725  * psmux_stream_get_pts:
726  * @stream: a #PsMuxStream
727  *
728  * Return the PTS of the last buffer that has had bytes written and
729  * which _had_ a PTS in @stream.
730  *
731  * Returns: the PTS of the last buffer in @stream.
732  */
733 guint64
734 psmux_stream_get_pts (PsMuxStream * stream)
735 {
736   g_return_val_if_fail (stream != NULL, -1);
737
738   return stream->last_pts;
739 }