Add -Wold-style-definition
[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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, 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   g_slice_free (PsMux, mux);
145 }
146
147 /**
148  * psmux_create_stream:
149  * @mux: a #PsMux
150  * @stream_type: a #PsMuxStreamType
151  *
152  * Create a new stream of @stream_type in the muxer session @mux.
153  *
154  * Returns: a new #PsMuxStream.
155  */
156 PsMuxStream *
157 psmux_create_stream (PsMux * mux, PsMuxStreamType stream_type)
158 {
159   PsMuxStream *stream;
160 //  guint16 new_pid;
161
162   g_return_val_if_fail (mux != NULL, NULL);
163
164 #if 0
165   if (pid == PSMUX_PID_AUTO) {
166     new_pid = psmux_get_new_pid (mux);
167   } else {
168     new_pid = pid & 0x1FFF;
169   }
170
171   /* Ensure we're not creating a PID collision */
172   if (psmux_find_stream (mux, new_pid))
173     return NULL;
174 #endif
175
176   stream = psmux_stream_new (mux, stream_type);
177
178   mux->streams = g_list_prepend (mux->streams, stream);
179   if (stream->stream_id_ext) {
180     if (!mux->nb_private_streams)
181       mux->nb_streams++;
182     mux->nb_private_streams++;
183   } else
184     mux->nb_streams++;
185
186   if (stream->is_video_stream) {
187     mux->video_bound++;
188     if (mux->video_bound > 32)
189       g_critical ("Number of video es exceeds upper limit");
190   } else if (stream->is_audio_stream) {
191     mux->audio_bound++;
192     if (mux->audio_bound > 64)
193       g_critical ("Number of audio es exceeds upper limit");
194   }
195
196   return stream;
197 }
198
199 static gboolean
200 psmux_packet_out (PsMux * mux)
201 {
202   gboolean res;
203   if (G_UNLIKELY (mux->write_func == NULL))
204     return TRUE;
205
206   res = mux->write_func (mux->packet_buf, mux->packet_bytes_written,
207       mux->write_func_data);
208
209   if (res) {
210     mux->bit_size += mux->packet_bytes_written;
211   }
212   mux->packet_bytes_written = 0;
213   return res;
214 }
215
216 /**
217  * psmux_write_stream_packet:
218  * @mux: a #PsMux
219  * @stream: a #PsMuxStream
220  *
221  * Write a packet of @stream.
222  *
223  * Returns: TRUE if the packet could be written.
224  */
225 gboolean
226 psmux_write_stream_packet (PsMux * mux, PsMuxStream * stream)
227 {
228   gboolean res;
229
230   g_return_val_if_fail (mux != NULL, FALSE);
231   g_return_val_if_fail (stream != NULL, FALSE);
232
233
234   {
235     guint64 ts = psmux_stream_get_pts (stream);
236     if (ts != -1)
237       mux->pts = ts;
238   }
239
240   if (mux->pts - mux->pack_hdr_pts > PSMUX_PACK_HDR_INTERVAL
241       || mux->pes_cnt % mux->pack_hdr_freq == 0) {
242     /* Time to write pack header */
243     /* FIXME: currently we write the mux rate of the PREVIOUS pack into the
244      * pack header, because of the incapability to calculate the mux_rate
245      * before outputing the pack. To calculate the mux_rate for the current
246      * pack, we need to put the whole pack into buffer, calculate the
247      * mux_rate, and then output the whole trunck.
248      */
249     if (mux->pts != -1 && mux->pts > mux->bit_pts
250         && mux->pts - mux->bit_pts > PSMUX_BITRATE_CALC_INTERVAL) {
251       /* XXX: smoothing the rate? */
252       mux->bit_rate =
253           gst_util_uint64_scale (mux->bit_size, 8 * CLOCKBASE,
254           (mux->pts - mux->bit_pts));
255
256       mux->bit_size = 0;
257       mux->bit_pts = mux->pts;
258     }
259
260     psmux_write_pack_header (mux);
261     mux->pack_hdr_pts = mux->pts;
262   }
263
264   if (mux->pes_cnt % mux->sys_hdr_freq == 0) {
265     /* Time to write system header */
266     psmux_write_system_header (mux);
267     mux->sys_hdr_pts = mux->pts;
268   }
269
270   if (mux->pes_cnt % mux->psm_freq == 0) {
271     /* Time to write program stream map (PSM) */
272     psmux_write_program_stream_map (mux);
273     mux->psm_pts = mux->pts;
274   }
275
276   /* Write the packet */
277   if (!(mux->packet_bytes_written =
278           psmux_stream_get_data (stream, mux->packet_buf,
279               mux->pes_max_payload + PSMUX_PES_MAX_HDR_LEN))) {
280     return FALSE;
281   }
282
283   res = psmux_packet_out (mux);
284   if (!res) {
285     PS_DEBUG ("packet write false");
286     return FALSE;
287   }
288
289   mux->pes_cnt += 1;
290
291   return res;
292 }
293
294 static gboolean
295 psmux_write_pack_header (PsMux * mux)
296 {
297   bits_buffer_t bw;
298   guint64 scr = mux->pts;       /* XXX: is this correct? necessary to put any offset? */
299   if (mux->pts == -1)
300     scr = 0;
301
302   /* pack_start_code */
303   bits_initwrite (&bw, 14, mux->packet_buf);
304   bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
305   bits_write (&bw, 8, PSMUX_PACK_HEADER);
306
307   /* scr */
308   bits_write (&bw, 2, 0x1);
309   bits_write (&bw, 3, (scr >> 30) & 0x07);
310   bits_write (&bw, 1, 1);
311   bits_write (&bw, 15, (scr >> 15) & 0x7fff);
312   bits_write (&bw, 1, 1);
313   bits_write (&bw, 15, scr & 0x7fff);
314   bits_write (&bw, 1, 1);
315   bits_write (&bw, 9, 0);       /* system_clock_reference_extension: set to 0 (like what VLC does) */
316   bits_write (&bw, 1, 1);
317
318   {
319     /* Scale to get the mux_rate, rounding up */
320     guint mux_rate =
321         gst_util_uint64_scale (mux->bit_rate + 8 * 50 - 1, 1, 8 * 50);
322     if (mux_rate > mux->rate_bound / 2)
323       mux->rate_bound = mux_rate * 2;
324     bits_write (&bw, 22, mux_rate);     /* program_mux_rate */
325     bits_write (&bw, 2, 3);
326   }
327
328   bits_write (&bw, 5, 0x1f);
329   bits_write (&bw, 3, 0);       /* pack_stuffing_length */
330
331   mux->packet_bytes_written = 14;
332   return psmux_packet_out (mux);
333 }
334
335 static gboolean
336 psmux_write_system_header (PsMux * mux)
337 {
338   bits_buffer_t bw;
339   guint len = 12 + (mux->nb_streams +
340       (mux->nb_private_streams > 1 ? mux->nb_private_streams - 1 : 0)) * 3;
341   GList *cur;
342   gboolean private_hit = FALSE;
343
344   /* system_header_start_code */
345   bits_initwrite (&bw, len, mux->packet_buf);
346
347   /* system_header start code */
348   bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
349   bits_write (&bw, 8, PSMUX_SYSTEM_HEADER);
350
351   bits_write (&bw, 16, len);    /* header_length */
352   bits_write (&bw, 1, 1);       /* marker */
353   bits_write (&bw, 22, mux->rate_bound);        /* rate_bound */
354   bits_write (&bw, 1, 1);       /* marker */
355   bits_write (&bw, 6, mux->audio_bound);        /* audio_bound */
356   bits_write (&bw, 1, 0);       /* fixed_flag */
357   bits_write (&bw, 1, 0);       /* CSPS_flag */
358   bits_write (&bw, 1, 0);       /* system_audio_lock_flag */
359   bits_write (&bw, 1, 0);       /* system_video_lock_flag */
360   bits_write (&bw, 1, 1);       /* marker */
361   bits_write (&bw, 5, mux->video_bound);        /* video_bound */
362   bits_write (&bw, 1, 0);       /* packet_rate_restriction_flag */
363   bits_write (&bw, 7, 0x7f);    /* reserved_bits */
364
365   for (cur = g_list_first (mux->streams), private_hit = FALSE; cur != NULL;
366       cur = g_list_next (cur)) {
367     PsMuxStream *stream = (PsMuxStream *) cur->data;
368
369     if (private_hit && stream->stream_id == PSMUX_EXTENDED_STREAM)
370       continue;
371
372     bits_write (&bw, 8, stream->stream_id);     /* stream_id */
373     bits_write (&bw, 2, 0x3);   /* reserved */
374     bits_write (&bw, 1, stream->is_video_stream);       /* buffer_bound_scale */
375     bits_write (&bw, 13, stream->max_buffer_size / (stream->is_video_stream ? 1024 : 128));     /* buffer_size_bound */
376
377     if (stream->stream_id == PSMUX_EXTENDED_STREAM)
378       private_hit = TRUE;
379   }
380
381   mux->packet_bytes_written = len;
382   return psmux_packet_out (mux);
383 }
384
385 static gboolean
386 psmux_write_program_stream_map (PsMux * mux)
387 {
388   gint psm_size = 16, es_map_size = 0;
389   bits_buffer_t bw;
390   GList *cur;
391   guint16 len;
392   guint8 *pos;
393
394   /* pre-write the descriptor loop */
395   pos = mux->es_info_buf;
396   for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) {
397     PsMuxStream *stream = (PsMuxStream *) cur->data;
398     len = 0;
399
400     *pos++ = stream->stream_type;
401     *pos++ = stream->stream_id;
402
403     psmux_stream_get_es_descrs (stream, pos + 2, &len);
404     psmux_put16 (&pos, len);
405
406     es_map_size += len + 4;
407     pos += len;
408 #if 0
409     if (stream->lang[0] != 0)
410       es_map_size += 6;
411 #endif
412   }
413
414   psm_size += es_map_size;
415   bits_initwrite (&bw, psm_size, mux->packet_buf);
416
417   /* psm start code */
418   bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
419   bits_write (&bw, 8, PSMUX_PROGRAM_STREAM_MAP);
420
421   bits_write (&bw, 16, psm_size - 6);   /* psm_length */
422   bits_write (&bw, 1, 1);       /* current_next_indicator */
423   bits_write (&bw, 2, 0xF);     /* reserved */
424   bits_write (&bw, 5, 0x1);     /* psm_version = 1 */
425   bits_write (&bw, 7, 0xFF);    /* reserved */
426   bits_write (&bw, 1, 1);       /* marker */
427
428   bits_write (&bw, 16, 0);      /* program_stream_info_length */
429   /* program_stream_info empty */
430
431   bits_write (&bw, 16, es_map_size);    /* elementary_stream_map_length */
432   memcpy (bw.p_data + bw.i_data, mux->es_info_buf, es_map_size);
433
434   /* CRC32 */
435   {
436     guint32 crc = calc_crc32 (mux->packet_buf, psm_size - 4);
437     guint8 *pos = mux->packet_buf + psm_size - 4;
438     psmux_put32 (&pos, crc);
439   }
440
441   mux->packet_bytes_written = psm_size;
442   return psmux_packet_out (mux);
443 }