1 /* MPEG-PS muxer plugin for GStreamer
2 * Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
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.
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.
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.
20 * Unless otherwise indicated, Source Code is licensed under MIT license.
21 * See further explanation attached in License Statement (distributed in the file
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:
31 * The above copyright notice and this permission notice shall be included in all
32 * copies or substantial portions of the Software.
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
51 #include "mpegpsmux.h"
52 #include "psmuxcommon.h"
53 #include "psmuxstream.h"
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);
65 * Create a new muxer session.
67 * Returns: A new #PsMux object.
74 mux = g_slice_new0 (PsMux);
76 mux->pts = -1; /* uninitialized values */
77 mux->pack_hdr_pts = -1;
78 mux->sys_hdr_pts = -1;
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? */
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;
91 psmux_stream_id_info_init (&mux->id_info);
97 * psmux_set_write_func:
99 * @func: a user callback function
100 * @user_data: user data passed to @func
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.
106 psmux_set_write_func (PsMux * mux, PsMuxWriteFunc func, void *user_data)
108 g_return_if_fail (mux != NULL);
110 mux->write_func = func;
111 mux->write_func_data = user_data;
115 psmux_write_end_code (PsMux * mux)
117 guint8 end_code[4] = { 0, 0, 1, PSMUX_PROGRAM_END };
118 return mux->write_func (end_code, 4, mux->write_func_data);
126 * Free all resources associated with @mux. After calling this function @mux can
127 * not be used anymore.
130 psmux_free (PsMux * mux)
134 g_return_if_fail (mux != NULL);
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;
140 psmux_stream_free (stream);
142 g_list_free (mux->streams);
144 if (mux->sys_header != NULL)
145 gst_buffer_unref (mux->sys_header);
147 if (mux->psm != NULL)
148 gst_buffer_unref (mux->psm);
150 g_slice_free (PsMux, mux);
154 * psmux_create_stream:
156 * @stream_type: a #PsMuxStreamType
158 * Create a new stream of @stream_type in the muxer session @mux.
160 * Returns: a new #PsMuxStream.
163 psmux_create_stream (PsMux * mux, PsMuxStreamType stream_type)
168 g_return_val_if_fail (mux != NULL, NULL);
171 if (pid == PSMUX_PID_AUTO) {
172 new_pid = psmux_get_new_pid (mux);
174 new_pid = pid & 0x1FFF;
177 /* Ensure we're not creating a PID collision */
178 if (psmux_find_stream (mux, new_pid))
182 stream = psmux_stream_new (mux, stream_type);
184 mux->streams = g_list_prepend (mux->streams, stream);
185 if (stream->stream_id_ext) {
186 if (!mux->nb_private_streams)
188 mux->nb_private_streams++;
192 if (stream->is_video_stream) {
194 if (mux->video_bound > 32)
195 g_critical ("Number of video es exceeds upper limit");
196 } else if (stream->is_audio_stream) {
198 if (mux->audio_bound > 64)
199 g_critical ("Number of audio es exceeds upper limit");
206 psmux_packet_out (PsMux * mux)
209 if (G_UNLIKELY (mux->write_func == NULL))
212 res = mux->write_func (mux->packet_buf, mux->packet_bytes_written,
213 mux->write_func_data);
216 mux->bit_size += mux->packet_bytes_written;
218 mux->packet_bytes_written = 0;
223 * psmux_write_stream_packet:
225 * @stream: a #PsMuxStream
227 * Write a packet of @stream.
229 * Returns: TRUE if the packet could be written.
232 psmux_write_stream_packet (PsMux * mux, PsMuxStream * stream)
236 g_return_val_if_fail (mux != NULL, FALSE);
237 g_return_val_if_fail (stream != NULL, FALSE);
241 guint64 ts = psmux_stream_get_pts (stream);
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.
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? */
259 gst_util_uint64_scale (mux->bit_size, 8 * CLOCKBASE,
260 (mux->pts - mux->bit_pts));
263 mux->bit_pts = mux->pts;
266 psmux_write_pack_header (mux);
267 mux->pack_hdr_pts = mux->pts;
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;
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;
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))) {
289 res = psmux_packet_out (mux);
291 GST_DEBUG_OBJECT (mux, "packet write false");
301 psmux_write_pack_header (PsMux * mux)
304 guint64 scr = mux->pts; /* XXX: is this correct? necessary to put any offset? */
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);
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);
325 /* Scale to get the mux_rate, rounding up */
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);
334 bits_write (&bw, 5, 0x1f);
335 bits_write (&bw, 3, 0); /* pack_stuffing_length */
337 mux->packet_bytes_written = 14;
338 return psmux_packet_out (mux);
342 psmux_ensure_system_header (PsMux * mux)
345 guint len = 12 + (mux->nb_streams +
346 (mux->nb_private_streams > 1 ? mux->nb_private_streams - 1 : 0)) * 3;
348 gboolean private_hit = FALSE;
351 if (mux->sys_header != NULL)
354 data = g_malloc (len);
356 bits_initwrite (&bw, len, data);
358 /* system_header start code */
359 bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
360 bits_write (&bw, 8, PSMUX_SYSTEM_HEADER);
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 */
376 for (cur = mux->streams, private_hit = FALSE; cur != NULL; cur = cur->next) {
377 PsMuxStream *stream = (PsMuxStream *) cur->data;
379 if (private_hit && stream->stream_id == PSMUX_EXTENDED_STREAM)
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 */
387 if (stream->stream_id == PSMUX_EXTENDED_STREAM)
391 GST_MEMDUMP ("System Header", data, len);
393 mux->sys_header = gst_buffer_new_wrapped (data, len);
397 psmux_write_system_header (PsMux * mux)
401 psmux_ensure_system_header (mux);
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);
408 return psmux_packet_out (mux);
412 psmux_ensure_program_stream_map (PsMux * mux)
414 gint psm_size = 16, es_map_size = 0;
421 if (mux->psm != NULL)
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;
430 *pos++ = stream->stream_type;
431 *pos++ = stream->stream_id;
433 psmux_stream_get_es_descrs (stream, pos + 2, &len);
434 psmux_put16 (&pos, len);
436 es_map_size += len + 4;
439 if (stream->lang[0] != 0)
444 psm_size += es_map_size;
446 data = g_malloc (psm_size);
448 bits_initwrite (&bw, psm_size, data);
451 bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
452 bits_write (&bw, 8, PSMUX_PROGRAM_STREAM_MAP);
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 */
461 bits_write (&bw, 16, 0); /* program_stream_info_length */
462 /* program_stream_info empty */
464 bits_write (&bw, 16, es_map_size); /* elementary_stream_map_length */
466 memcpy (bw.p_data + bw.i_data, mux->es_info_buf, es_map_size);
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);
475 GST_MEMDUMP ("Program Stream Map", data, psm_size);
477 mux->psm = gst_buffer_new_wrapped (data, psm_size);
481 psmux_write_program_stream_map (PsMux * mux)
485 psmux_ensure_program_stream_map (mux);
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);
492 return psmux_packet_out (mux);
496 psmux_get_stream_headers (PsMux * mux)
500 psmux_ensure_system_header (mux);
501 psmux_ensure_program_stream_map (mux);
503 list = g_list_append (NULL, gst_buffer_ref (mux->sys_header));
504 list = g_list_append (list, gst_buffer_ref (mux->psm));