documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / gst / mpegpsmux / psmux.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
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
48 #include <string.h>
49 #include <gst/gst.h>
50
51 #include "mpegpsmux.h"
52 #include "psmuxcommon.h"
53 #include "psmuxstream.h"
54 #include "psmux.h"
55 #include "crc.h"
56
57 static gboolean psmux_packet_out (PsMux * mux);
58 static gboolean psmux_write_pack_header (PsMux * mux);
59 static gboolean psmux_write_system_header (PsMux * mux);
60 static gboolean psmux_write_program_stream_map (PsMux * mux);
61
62 /**
63  * psmux_new:
64  *
65  * Create a new muxer session.
66  *
67  * Returns: A new #PsMux object.
68  */
69 PsMux *
70 psmux_new (void)
71 {
72   PsMux *mux;
73
74   mux = g_slice_new0 (PsMux);
75
76   mux->pts = -1;                /* uninitialized values */
77   mux->pack_hdr_pts = -1;
78   mux->sys_hdr_pts = -1;
79   mux->psm_pts = -1;
80
81   mux->bit_pts = 0;
82
83   mux->pes_max_payload = PSMUX_PES_MAX_PAYLOAD;
84   mux->bit_rate = 400 * 1024;   /* XXX: better default values? */
85   mux->rate_bound = 2 * 1024;   /* 2* bit_rate / (8*50). XXX: any better default? */
86
87   mux->pack_hdr_freq = PSMUX_PACK_HDR_FREQ;
88   mux->sys_hdr_freq = PSMUX_SYS_HDR_FREQ;
89   mux->psm_freq = PSMUX_PSM_FREQ;
90
91   psmux_stream_id_info_init (&mux->id_info);
92
93   return mux;
94 }
95
96 /**
97  * psmux_set_write_func:
98  * @mux: a #PsMux
99  * @func: a user callback function
100  * @user_data: user data passed to @func
101  *
102  * Set the callback function and user data to be called when @mux has output to
103  * produce. @user_data will be passed as user data in @func.
104  */
105 void
106 psmux_set_write_func (PsMux * mux, PsMuxWriteFunc func, void *user_data)
107 {
108   g_return_if_fail (mux != NULL);
109
110   mux->write_func = func;
111   mux->write_func_data = user_data;
112 }
113
114 gboolean
115 psmux_write_end_code (PsMux * mux)
116 {
117   guint8 end_code[4] = { 0, 0, 1, PSMUX_PROGRAM_END };
118   return mux->write_func (end_code, 4, mux->write_func_data);
119 }
120
121
122 /**
123  * psmux_free:
124  * @mux: a #PsMux
125  *
126  * Free all resources associated with @mux. After calling this function @mux can
127  * not be used anymore.
128  */
129 void
130 psmux_free (PsMux * mux)
131 {
132   GList *cur;
133
134   g_return_if_fail (mux != NULL);
135
136   /* Free all streams */
137   for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) {
138     PsMuxStream *stream = (PsMuxStream *) cur->data;
139
140     psmux_stream_free (stream);
141   }
142   g_list_free (mux->streams);
143
144   if (mux->sys_header != NULL)
145     gst_buffer_unref (mux->sys_header);
146
147   if (mux->psm != NULL)
148     gst_buffer_unref (mux->psm);
149
150   g_slice_free (PsMux, mux);
151 }
152
153 /**
154  * psmux_create_stream:
155  * @mux: a #PsMux
156  * @stream_type: a #PsMuxStreamType
157  *
158  * Create a new stream of @stream_type in the muxer session @mux.
159  *
160  * Returns: a new #PsMuxStream.
161  */
162 PsMuxStream *
163 psmux_create_stream (PsMux * mux, PsMuxStreamType stream_type)
164 {
165   PsMuxStream *stream;
166 //  guint16 new_pid;
167
168   g_return_val_if_fail (mux != NULL, NULL);
169
170 #if 0
171   if (pid == PSMUX_PID_AUTO) {
172     new_pid = psmux_get_new_pid (mux);
173   } else {
174     new_pid = pid & 0x1FFF;
175   }
176
177   /* Ensure we're not creating a PID collision */
178   if (psmux_find_stream (mux, new_pid))
179     return NULL;
180 #endif
181
182   stream = psmux_stream_new (mux, stream_type);
183
184   mux->streams = g_list_prepend (mux->streams, stream);
185   if (stream->stream_id_ext) {
186     if (!mux->nb_private_streams)
187       mux->nb_streams++;
188     mux->nb_private_streams++;
189   } else
190     mux->nb_streams++;
191
192   if (stream->is_video_stream) {
193     mux->video_bound++;
194     if (mux->video_bound > 32)
195       g_critical ("Number of video es exceeds upper limit");
196   } else if (stream->is_audio_stream) {
197     mux->audio_bound++;
198     if (mux->audio_bound > 64)
199       g_critical ("Number of audio es exceeds upper limit");
200   }
201
202   return stream;
203 }
204
205 static gboolean
206 psmux_packet_out (PsMux * mux)
207 {
208   gboolean res;
209   if (G_UNLIKELY (mux->write_func == NULL))
210     return TRUE;
211
212   res = mux->write_func (mux->packet_buf, mux->packet_bytes_written,
213       mux->write_func_data);
214
215   if (res) {
216     mux->bit_size += mux->packet_bytes_written;
217   }
218   mux->packet_bytes_written = 0;
219   return res;
220 }
221
222 /**
223  * psmux_write_stream_packet:
224  * @mux: a #PsMux
225  * @stream: a #PsMuxStream
226  *
227  * Write a packet of @stream.
228  *
229  * Returns: TRUE if the packet could be written.
230  */
231 gboolean
232 psmux_write_stream_packet (PsMux * mux, PsMuxStream * stream)
233 {
234   gboolean res;
235
236   g_return_val_if_fail (mux != NULL, FALSE);
237   g_return_val_if_fail (stream != NULL, FALSE);
238
239
240   {
241     guint64 ts = psmux_stream_get_pts (stream);
242     if (ts != -1)
243       mux->pts = ts;
244   }
245
246   if (mux->pts - mux->pack_hdr_pts > PSMUX_PACK_HDR_INTERVAL
247       || mux->pes_cnt % mux->pack_hdr_freq == 0) {
248     /* Time to write pack header */
249     /* FIXME: currently we write the mux rate of the PREVIOUS pack into the
250      * pack header, because of the incapability to calculate the mux_rate
251      * before outputting the pack. To calculate the mux_rate for the current
252      * pack, we need to put the whole pack into buffer, calculate the
253      * mux_rate, and then output the whole trunck.
254      */
255     if (mux->pts != -1 && mux->pts > mux->bit_pts
256         && mux->pts - mux->bit_pts > PSMUX_BITRATE_CALC_INTERVAL) {
257       /* XXX: smoothing the rate? */
258       mux->bit_rate =
259           gst_util_uint64_scale (mux->bit_size, 8 * CLOCKBASE,
260           (mux->pts - mux->bit_pts));
261
262       mux->bit_size = 0;
263       mux->bit_pts = mux->pts;
264     }
265
266     psmux_write_pack_header (mux);
267     mux->pack_hdr_pts = mux->pts;
268   }
269
270   if (mux->pes_cnt % mux->sys_hdr_freq == 0) {
271     /* Time to write system header */
272     psmux_write_system_header (mux);
273     mux->sys_hdr_pts = mux->pts;
274   }
275
276   if (mux->pes_cnt % mux->psm_freq == 0) {
277     /* Time to write program stream map (PSM) */
278     psmux_write_program_stream_map (mux);
279     mux->psm_pts = mux->pts;
280   }
281
282   /* Write the packet */
283   if (!(mux->packet_bytes_written =
284           psmux_stream_get_data (stream, mux->packet_buf,
285               mux->pes_max_payload + PSMUX_PES_MAX_HDR_LEN))) {
286     return FALSE;
287   }
288
289   res = psmux_packet_out (mux);
290   if (!res) {
291     GST_DEBUG_OBJECT (mux, "packet write false");
292     return FALSE;
293   }
294
295   mux->pes_cnt += 1;
296
297   return res;
298 }
299
300 static gboolean
301 psmux_write_pack_header (PsMux * mux)
302 {
303   bits_buffer_t bw;
304   guint64 scr = mux->pts;       /* XXX: is this correct? necessary to put any offset? */
305   if (mux->pts == -1)
306     scr = 0;
307
308   /* pack_start_code */
309   bits_initwrite (&bw, 14, mux->packet_buf);
310   bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
311   bits_write (&bw, 8, PSMUX_PACK_HEADER);
312
313   /* scr */
314   bits_write (&bw, 2, 0x1);
315   bits_write (&bw, 3, (scr >> 30) & 0x07);
316   bits_write (&bw, 1, 1);
317   bits_write (&bw, 15, (scr >> 15) & 0x7fff);
318   bits_write (&bw, 1, 1);
319   bits_write (&bw, 15, scr & 0x7fff);
320   bits_write (&bw, 1, 1);
321   bits_write (&bw, 9, 0);       /* system_clock_reference_extension: set to 0 (like what VLC does) */
322   bits_write (&bw, 1, 1);
323
324   {
325     /* Scale to get the mux_rate, rounding up */
326     guint mux_rate =
327         gst_util_uint64_scale (mux->bit_rate + 8 * 50 - 1, 1, 8 * 50);
328     if (mux_rate > mux->rate_bound / 2)
329       mux->rate_bound = mux_rate * 2;
330     bits_write (&bw, 22, mux_rate);     /* program_mux_rate */
331     bits_write (&bw, 2, 3);
332   }
333
334   bits_write (&bw, 5, 0x1f);
335   bits_write (&bw, 3, 0);       /* pack_stuffing_length */
336
337   mux->packet_bytes_written = 14;
338   return psmux_packet_out (mux);
339 }
340
341 static void
342 psmux_ensure_system_header (PsMux * mux)
343 {
344   bits_buffer_t bw;
345   guint len = 12 + (mux->nb_streams +
346       (mux->nb_private_streams > 1 ? mux->nb_private_streams - 1 : 0)) * 3;
347   GList *cur;
348   gboolean private_hit = FALSE;
349   guint8 *data;
350
351   if (mux->sys_header != NULL)
352     return;
353
354   data = g_malloc (len);
355
356   bits_initwrite (&bw, len, data);
357
358   /* system_header start code */
359   bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
360   bits_write (&bw, 8, PSMUX_SYSTEM_HEADER);
361
362   bits_write (&bw, 16, len - 6);        /* header_length (bytes after this field) */
363   bits_write (&bw, 1, 1);       /* marker */
364   bits_write (&bw, 22, mux->rate_bound);        /* rate_bound */
365   bits_write (&bw, 1, 1);       /* marker */
366   bits_write (&bw, 6, mux->audio_bound);        /* audio_bound */
367   bits_write (&bw, 1, 0);       /* fixed_flag */
368   bits_write (&bw, 1, 0);       /* CSPS_flag */
369   bits_write (&bw, 1, 0);       /* system_audio_lock_flag */
370   bits_write (&bw, 1, 0);       /* system_video_lock_flag */
371   bits_write (&bw, 1, 1);       /* marker */
372   bits_write (&bw, 5, mux->video_bound);        /* video_bound */
373   bits_write (&bw, 1, 0);       /* packet_rate_restriction_flag */
374   bits_write (&bw, 7, 0x7f);    /* reserved_bits */
375
376   for (cur = mux->streams, private_hit = FALSE; cur != NULL; cur = cur->next) {
377     PsMuxStream *stream = (PsMuxStream *) cur->data;
378
379     if (private_hit && stream->stream_id == PSMUX_EXTENDED_STREAM)
380       continue;
381
382     bits_write (&bw, 8, stream->stream_id);     /* stream_id */
383     bits_write (&bw, 2, 0x3);   /* reserved */
384     bits_write (&bw, 1, stream->is_video_stream);       /* buffer_bound_scale */
385     bits_write (&bw, 13, stream->max_buffer_size / (stream->is_video_stream ? 1024 : 128));     /* buffer_size_bound */
386
387     if (stream->stream_id == PSMUX_EXTENDED_STREAM)
388       private_hit = TRUE;
389   }
390
391   GST_MEMDUMP ("System Header", data, len);
392
393   mux->sys_header = gst_buffer_new_wrapped (data, len);
394 }
395
396 static gboolean
397 psmux_write_system_header (PsMux * mux)
398 {
399   GstMapInfo map;
400
401   psmux_ensure_system_header (mux);
402
403   gst_buffer_map (mux->sys_header, &map, GST_MAP_READ);
404   memcpy (mux->packet_buf, map.data, map.size);
405   mux->packet_bytes_written = map.size;
406   gst_buffer_unmap (mux->sys_header, &map);
407
408   return psmux_packet_out (mux);
409 }
410
411 static void
412 psmux_ensure_program_stream_map (PsMux * mux)
413 {
414   gint psm_size = 16, es_map_size = 0;
415   bits_buffer_t bw;
416   GList *cur;
417   guint16 len;
418   guint8 *pos;
419   guint8 *data;
420
421   if (mux->psm != NULL)
422     return;
423
424   /* pre-write the descriptor loop */
425   pos = mux->es_info_buf;
426   for (cur = mux->streams; cur != NULL; cur = cur->next) {
427     PsMuxStream *stream = (PsMuxStream *) cur->data;
428     len = 0;
429
430     *pos++ = stream->stream_type;
431     *pos++ = stream->stream_id;
432
433     psmux_stream_get_es_descrs (stream, pos + 2, &len);
434     psmux_put16 (&pos, len);
435
436     es_map_size += len + 4;
437     pos += len;
438 #if 0
439     if (stream->lang[0] != 0)
440       es_map_size += 6;
441 #endif
442   }
443
444   psm_size += es_map_size;
445
446   data = g_malloc (psm_size);
447
448   bits_initwrite (&bw, psm_size, data);
449
450   /* psm start code */
451   bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
452   bits_write (&bw, 8, PSMUX_PROGRAM_STREAM_MAP);
453
454   bits_write (&bw, 16, psm_size - 6);   /* psm_length */
455   bits_write (&bw, 1, 1);       /* current_next_indicator */
456   bits_write (&bw, 2, 0xF);     /* reserved */
457   bits_write (&bw, 5, 0x1);     /* psm_version = 1 */
458   bits_write (&bw, 7, 0xFF);    /* reserved */
459   bits_write (&bw, 1, 1);       /* marker */
460
461   bits_write (&bw, 16, 0);      /* program_stream_info_length */
462   /* program_stream_info empty */
463
464   bits_write (&bw, 16, es_map_size);    /* elementary_stream_map_length */
465
466   memcpy (bw.p_data + bw.i_data, mux->es_info_buf, es_map_size);
467
468   /* CRC32 */
469   {
470     guint32 crc = calc_crc32 (bw.p_data, psm_size - 4);
471     guint8 *pos = bw.p_data + psm_size - 4;
472     psmux_put32 (&pos, crc);
473   }
474
475   GST_MEMDUMP ("Program Stream Map", data, psm_size);
476
477   mux->psm = gst_buffer_new_wrapped (data, psm_size);
478 }
479
480 static gboolean
481 psmux_write_program_stream_map (PsMux * mux)
482 {
483   GstMapInfo map;
484
485   psmux_ensure_program_stream_map (mux);
486
487   gst_buffer_map (mux->psm, &map, GST_MAP_READ);
488   memcpy (mux->packet_buf, map.data, map.size);
489   mux->packet_bytes_written = map.size;
490   gst_buffer_unmap (mux->psm, &map);
491
492   return psmux_packet_out (mux);
493 }
494
495 GList *
496 psmux_get_stream_headers (PsMux * mux)
497 {
498   GList *list;
499
500   psmux_ensure_system_header (mux);
501   psmux_ensure_program_stream_map (mux);
502
503   list = g_list_append (NULL, gst_buffer_ref (mux->sys_header));
504   list = g_list_append (list, gst_buffer_ref (mux->psm));
505
506   return list;
507 }