psmux.c 13.6 KB
Newer Older
Lin Yang's avatar
Lin Yang committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* MPEG-PS muxer plugin for GStreamer
 * Copyright 2008 Lin YANG <oxcsnicho@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
Tim-Philipp Müller's avatar
Tim-Philipp Müller committed
16 17
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA 02110-1301, USA.
Lin Yang's avatar
Lin Yang committed
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
 */
/*
 * Unless otherwise indicated, Source Code is licensed under MIT license.
 * See further explanation attached in License Statement (distributed in the file
 * LICENSE).
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include <gst/gst.h>

#include "mpegpsmux.h"
#include "psmuxcommon.h"
#include "psmuxstream.h"
#include "psmux.h"
#include "crc.h"

static gboolean psmux_packet_out (PsMux * mux);
static gboolean psmux_write_pack_header (PsMux * mux);
static gboolean psmux_write_system_header (PsMux * mux);
static gboolean psmux_write_program_stream_map (PsMux * mux);

/**
 * psmux_new:
 *
 * Create a new muxer session.
 *
 * Returns: A new #PsMux object.
 */
PsMux *
Benjamin Otte's avatar
Benjamin Otte committed
70
psmux_new (void)
Lin Yang's avatar
Lin Yang committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
{
  PsMux *mux;

  mux = g_slice_new0 (PsMux);

  mux->pts = -1;                /* uninitialized values */
  mux->pack_hdr_pts = -1;
  mux->sys_hdr_pts = -1;
  mux->psm_pts = -1;

  mux->bit_pts = 0;

  mux->pes_max_payload = PSMUX_PES_MAX_PAYLOAD;
  mux->bit_rate = 400 * 1024;   /* XXX: better default values? */
  mux->rate_bound = 2 * 1024;   /* 2* bit_rate / (8*50). XXX: any better default? */

  mux->pack_hdr_freq = PSMUX_PACK_HDR_FREQ;
  mux->sys_hdr_freq = PSMUX_SYS_HDR_FREQ;
  mux->psm_freq = PSMUX_PSM_FREQ;

  psmux_stream_id_info_init (&mux->id_info);

  return mux;
}

/**
 * psmux_set_write_func:
 * @mux: a #PsMux
 * @func: a user callback function
 * @user_data: user data passed to @func
 *
 * Set the callback function and user data to be called when @mux has output to
 * produce. @user_data will be passed as user data in @func.
 */
void
psmux_set_write_func (PsMux * mux, PsMuxWriteFunc func, void *user_data)
{
  g_return_if_fail (mux != NULL);

  mux->write_func = func;
  mux->write_func_data = user_data;
}

gboolean
psmux_write_end_code (PsMux * mux)
{
  guint8 end_code[4] = { 0, 0, 1, PSMUX_PROGRAM_END };
  return mux->write_func (end_code, 4, mux->write_func_data);
}


/**
 * psmux_free:
 * @mux: a #PsMux
 *
 * Free all resources associated with @mux. After calling this function @mux can
 * not be used anymore.
 */
void
psmux_free (PsMux * mux)
{
  GList *cur;

  g_return_if_fail (mux != NULL);

  /* Free all streams */
  for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) {
    PsMuxStream *stream = (PsMuxStream *) cur->data;

    psmux_stream_free (stream);
  }
  g_list_free (mux->streams);

144 145 146 147 148 149
  if (mux->sys_header != NULL)
    gst_buffer_unref (mux->sys_header);

  if (mux->psm != NULL)
    gst_buffer_unref (mux->psm);

Lin Yang's avatar
Lin Yang committed
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
  g_slice_free (PsMux, mux);
}

/**
 * psmux_create_stream:
 * @mux: a #PsMux
 * @stream_type: a #PsMuxStreamType
 *
 * Create a new stream of @stream_type in the muxer session @mux.
 *
 * Returns: a new #PsMuxStream.
 */
PsMuxStream *
psmux_create_stream (PsMux * mux, PsMuxStreamType stream_type)
{
  PsMuxStream *stream;
//  guint16 new_pid;

  g_return_val_if_fail (mux != NULL, NULL);

#if 0
  if (pid == PSMUX_PID_AUTO) {
    new_pid = psmux_get_new_pid (mux);
  } else {
    new_pid = pid & 0x1FFF;
  }

  /* Ensure we're not creating a PID collision */
  if (psmux_find_stream (mux, new_pid))
    return NULL;
#endif

  stream = psmux_stream_new (mux, stream_type);

  mux->streams = g_list_prepend (mux->streams, stream);
  if (stream->stream_id_ext) {
    if (!mux->nb_private_streams)
      mux->nb_streams++;
    mux->nb_private_streams++;
  } else
    mux->nb_streams++;

  if (stream->is_video_stream) {
    mux->video_bound++;
    if (mux->video_bound > 32)
      g_critical ("Number of video es exceeds upper limit");
  } else if (stream->is_audio_stream) {
    mux->audio_bound++;
    if (mux->audio_bound > 64)
      g_critical ("Number of audio es exceeds upper limit");
  }

  return stream;
}

static gboolean
psmux_packet_out (PsMux * mux)
{
  gboolean res;
  if (G_UNLIKELY (mux->write_func == NULL))
    return TRUE;

  res = mux->write_func (mux->packet_buf, mux->packet_bytes_written,
      mux->write_func_data);

  if (res) {
    mux->bit_size += mux->packet_bytes_written;
  }
  mux->packet_bytes_written = 0;
  return res;
}

/**
 * psmux_write_stream_packet:
 * @mux: a #PsMux
 * @stream: a #PsMuxStream
 *
 * Write a packet of @stream.
 *
 * Returns: TRUE if the packet could be written.
 */
gboolean
psmux_write_stream_packet (PsMux * mux, PsMuxStream * stream)
{
  gboolean res;

  g_return_val_if_fail (mux != NULL, FALSE);
  g_return_val_if_fail (stream != NULL, FALSE);


  {
    guint64 ts = psmux_stream_get_pts (stream);
    if (ts != -1)
      mux->pts = ts;
  }

  if (mux->pts - mux->pack_hdr_pts > PSMUX_PACK_HDR_INTERVAL
      || mux->pes_cnt % mux->pack_hdr_freq == 0) {
    /* Time to write pack header */
    /* FIXME: currently we write the mux rate of the PREVIOUS pack into the
     * pack header, because of the incapability to calculate the mux_rate
     * before outputing the pack. To calculate the mux_rate for the current
     * pack, we need to put the whole pack into buffer, calculate the
     * mux_rate, and then output the whole trunck.
     */
    if (mux->pts != -1 && mux->pts > mux->bit_pts
        && mux->pts - mux->bit_pts > PSMUX_BITRATE_CALC_INTERVAL) {
      /* XXX: smoothing the rate? */
      mux->bit_rate =
          gst_util_uint64_scale (mux->bit_size, 8 * CLOCKBASE,
          (mux->pts - mux->bit_pts));

      mux->bit_size = 0;
      mux->bit_pts = mux->pts;
    }

    psmux_write_pack_header (mux);
    mux->pack_hdr_pts = mux->pts;
  }

  if (mux->pes_cnt % mux->sys_hdr_freq == 0) {
    /* Time to write system header */
    psmux_write_system_header (mux);
    mux->sys_hdr_pts = mux->pts;
  }

  if (mux->pes_cnt % mux->psm_freq == 0) {
    /* Time to write program stream map (PSM) */
    psmux_write_program_stream_map (mux);
    mux->psm_pts = mux->pts;
  }

  /* Write the packet */
  if (!(mux->packet_bytes_written =
          psmux_stream_get_data (stream, mux->packet_buf,
              mux->pes_max_payload + PSMUX_PES_MAX_HDR_LEN))) {
    return FALSE;
  }

  res = psmux_packet_out (mux);
  if (!res) {
291
    GST_DEBUG_OBJECT (mux, "packet write false");
Lin Yang's avatar
Lin Yang committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
    return FALSE;
  }

  mux->pes_cnt += 1;

  return res;
}

static gboolean
psmux_write_pack_header (PsMux * mux)
{
  bits_buffer_t bw;
  guint64 scr = mux->pts;       /* XXX: is this correct? necessary to put any offset? */
  if (mux->pts == -1)
    scr = 0;

  /* pack_start_code */
  bits_initwrite (&bw, 14, mux->packet_buf);
  bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
  bits_write (&bw, 8, PSMUX_PACK_HEADER);

  /* scr */
  bits_write (&bw, 2, 0x1);
  bits_write (&bw, 3, (scr >> 30) & 0x07);
  bits_write (&bw, 1, 1);
  bits_write (&bw, 15, (scr >> 15) & 0x7fff);
  bits_write (&bw, 1, 1);
  bits_write (&bw, 15, scr & 0x7fff);
  bits_write (&bw, 1, 1);
  bits_write (&bw, 9, 0);       /* system_clock_reference_extension: set to 0 (like what VLC does) */
  bits_write (&bw, 1, 1);

  {
    /* Scale to get the mux_rate, rounding up */
    guint mux_rate =
        gst_util_uint64_scale (mux->bit_rate + 8 * 50 - 1, 1, 8 * 50);
    if (mux_rate > mux->rate_bound / 2)
      mux->rate_bound = mux_rate * 2;
    bits_write (&bw, 22, mux_rate);     /* program_mux_rate */
    bits_write (&bw, 2, 3);
  }

  bits_write (&bw, 5, 0x1f);
  bits_write (&bw, 3, 0);       /* pack_stuffing_length */

  mux->packet_bytes_written = 14;
  return psmux_packet_out (mux);
}

341 342
static void
psmux_ensure_system_header (PsMux * mux)
Lin Yang's avatar
Lin Yang committed
343 344 345 346 347 348
{
  bits_buffer_t bw;
  guint len = 12 + (mux->nb_streams +
      (mux->nb_private_streams > 1 ? mux->nb_private_streams - 1 : 0)) * 3;
  GList *cur;
  gboolean private_hit = FALSE;
349
  guint8 *data;
Lin Yang's avatar
Lin Yang committed
350

351 352 353
  if (mux->sys_header != NULL)
    return;

354
  data = g_malloc (len);
355

356
  bits_initwrite (&bw, len, data);
Lin Yang's avatar
Lin Yang committed
357 358 359 360 361

  /* system_header start code */
  bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
  bits_write (&bw, 8, PSMUX_SYSTEM_HEADER);

362
  bits_write (&bw, 16, len - 6);        /* header_length (bytes after this field) */
Lin Yang's avatar
Lin Yang committed
363 364 365 366 367 368 369 370 371 372 373 374 375
  bits_write (&bw, 1, 1);       /* marker */
  bits_write (&bw, 22, mux->rate_bound);        /* rate_bound */
  bits_write (&bw, 1, 1);       /* marker */
  bits_write (&bw, 6, mux->audio_bound);        /* audio_bound */
  bits_write (&bw, 1, 0);       /* fixed_flag */
  bits_write (&bw, 1, 0);       /* CSPS_flag */
  bits_write (&bw, 1, 0);       /* system_audio_lock_flag */
  bits_write (&bw, 1, 0);       /* system_video_lock_flag */
  bits_write (&bw, 1, 1);       /* marker */
  bits_write (&bw, 5, mux->video_bound);        /* video_bound */
  bits_write (&bw, 1, 0);       /* packet_rate_restriction_flag */
  bits_write (&bw, 7, 0x7f);    /* reserved_bits */

376
  for (cur = mux->streams, private_hit = FALSE; cur != NULL; cur = cur->next) {
Lin Yang's avatar
Lin Yang committed
377 378 379 380 381 382 383 384 385 386 387 388 389 390
    PsMuxStream *stream = (PsMuxStream *) cur->data;

    if (private_hit && stream->stream_id == PSMUX_EXTENDED_STREAM)
      continue;

    bits_write (&bw, 8, stream->stream_id);     /* stream_id */
    bits_write (&bw, 2, 0x3);   /* reserved */
    bits_write (&bw, 1, stream->is_video_stream);       /* buffer_bound_scale */
    bits_write (&bw, 13, stream->max_buffer_size / (stream->is_video_stream ? 1024 : 128));     /* buffer_size_bound */

    if (stream->stream_id == PSMUX_EXTENDED_STREAM)
      private_hit = TRUE;
  }

391
  GST_MEMDUMP ("System Header", data, len);
392

393
  mux->sys_header = gst_buffer_new_wrapped (data, len);
Lin Yang's avatar
Lin Yang committed
394 395 396
}

static gboolean
397 398
psmux_write_system_header (PsMux * mux)
{
399 400
  GstMapInfo map;

401 402
  psmux_ensure_system_header (mux);

403 404 405 406
  gst_buffer_map (mux->sys_header, &map, GST_MAP_READ);
  memcpy (mux->packet_buf, map.data, map.size);
  mux->packet_bytes_written = map.size;
  gst_buffer_unmap (mux->sys_header, &map);
407 408 409 410 411 412

  return psmux_packet_out (mux);
}

static void
psmux_ensure_program_stream_map (PsMux * mux)
Lin Yang's avatar
Lin Yang committed
413 414 415 416 417 418
{
  gint psm_size = 16, es_map_size = 0;
  bits_buffer_t bw;
  GList *cur;
  guint16 len;
  guint8 *pos;
419
  guint8 *data;
Lin Yang's avatar
Lin Yang committed
420

421 422 423
  if (mux->psm != NULL)
    return;

Lin Yang's avatar
Lin Yang committed
424 425
  /* pre-write the descriptor loop */
  pos = mux->es_info_buf;
426
  for (cur = mux->streams; cur != NULL; cur = cur->next) {
Lin Yang's avatar
Lin Yang committed
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
    PsMuxStream *stream = (PsMuxStream *) cur->data;
    len = 0;

    *pos++ = stream->stream_type;
    *pos++ = stream->stream_id;

    psmux_stream_get_es_descrs (stream, pos + 2, &len);
    psmux_put16 (&pos, len);

    es_map_size += len + 4;
    pos += len;
#if 0
    if (stream->lang[0] != 0)
      es_map_size += 6;
#endif
  }

  psm_size += es_map_size;
445

446
  data = g_malloc (psm_size);
447

448
  bits_initwrite (&bw, psm_size, data);
Lin Yang's avatar
Lin Yang committed
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

  /* psm start code */
  bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
  bits_write (&bw, 8, PSMUX_PROGRAM_STREAM_MAP);

  bits_write (&bw, 16, psm_size - 6);   /* psm_length */
  bits_write (&bw, 1, 1);       /* current_next_indicator */
  bits_write (&bw, 2, 0xF);     /* reserved */
  bits_write (&bw, 5, 0x1);     /* psm_version = 1 */
  bits_write (&bw, 7, 0xFF);    /* reserved */
  bits_write (&bw, 1, 1);       /* marker */

  bits_write (&bw, 16, 0);      /* program_stream_info_length */
  /* program_stream_info empty */

  bits_write (&bw, 16, es_map_size);    /* elementary_stream_map_length */
465

Lin Yang's avatar
Lin Yang committed
466 467 468 469
  memcpy (bw.p_data + bw.i_data, mux->es_info_buf, es_map_size);

  /* CRC32 */
  {
470 471
    guint32 crc = calc_crc32 (bw.p_data, psm_size - 4);
    guint8 *pos = bw.p_data + psm_size - 4;
Lin Yang's avatar
Lin Yang committed
472 473 474
    psmux_put32 (&pos, crc);
  }

475
  GST_MEMDUMP ("Program Stream Map", data, psm_size);
476

477
  mux->psm = gst_buffer_new_wrapped (data, psm_size);
478 479 480 481 482
}

static gboolean
psmux_write_program_stream_map (PsMux * mux)
{
483 484
  GstMapInfo map;

485 486
  psmux_ensure_program_stream_map (mux);

487 488 489 490
  gst_buffer_map (mux->psm, &map, GST_MAP_READ);
  memcpy (mux->packet_buf, map.data, map.size);
  mux->packet_bytes_written = map.size;
  gst_buffer_unmap (mux->psm, &map);
491

Lin Yang's avatar
Lin Yang committed
492 493
  return psmux_packet_out (mux);
}
494 495 496 497 498 499 500 501 502 503 504 505 506 507

GList *
psmux_get_stream_headers (PsMux * mux)
{
  GList *list;

  psmux_ensure_system_header (mux);
  psmux_ensure_program_stream_map (mux);

  list = g_list_append (NULL, gst_buffer_ref (mux->sys_header));
  list = g_list_append (list, gst_buffer_ref (mux->psm));

  return list;
}