/*
- * Copyright 2006, 2007, 2008 Fluendo S.A.
+ * Copyright 2006, 2007, 2008, 2009, 2010 Fluendo S.A.
* Authors: Jan Schmidt <jan@fluendo.com>
* Kapil Agrawal <kapil@fluendo.com>
* Julien Moutte <julien@fluendo.com>
GST_DEBUG_CATEGORY_EXTERN (mpegtsmux_debug);
#define GST_CAT_DEFAULT mpegtsmux_debug
-GstBuffer *
-mpegtsmux_prepare_h264 (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux)
+#define SPS_PPS_PERIOD GST_SECOND
+
+typedef struct PrivDataH264 PrivDataH264;
+
+struct PrivDataH264
{
- guint8 nal_length_size = 0;
- guint8 startcode[4] = { 0x00, 0x00, 0x00, 0x01 };
- GstBuffer *out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) * 2);
- gint offset = 4, i = 0, nb_sps = 0, nb_pps = 0;
- gsize out_offset = 0, in_offset = 0;
+ GstBuffer *last_codec_data;
+ GstClockTime last_resync_ts;
+ GstBuffer *cached_es;
+ guint8 nal_length_size;
+};
- GST_DEBUG_OBJECT (mux, "Preparing H264 buffer for output");
+void
+mpegtsmux_free_h264 (gpointer prepare_data)
+{
+ PrivDataH264 *h264_data = (PrivDataH264 *) prepare_data;
+ if (h264_data->cached_es) {
+ gst_buffer_unref (h264_data->cached_es);
+ h264_data->cached_es = NULL;
+ }
+ g_free (prepare_data);
+}
- /* We want the same metadata */
- gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL);
+static inline gboolean
+mpegtsmux_process_codec_data_h264 (MpegTsPadData * data, MpegTsMux * mux)
+{
+ PrivDataH264 *h264_data;
+ gboolean ret = FALSE;
- /* Get NAL length size */
- nal_length_size =
- (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x03) + 1;
- GST_LOG_OBJECT (mux, "NAL length will be coded on %u bytes", nal_length_size);
- offset++;
+ /* Initialize our private data structure for caching */
+ if (G_UNLIKELY (!data->prepare_data)) {
+ data->prepare_data = g_new0 (PrivDataH264, 1);
+ h264_data = (PrivDataH264 *) data->prepare_data;
+ h264_data->last_resync_ts = GST_CLOCK_TIME_NONE;
+ }
- /* Generate SPS */
- nb_sps = GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x1f;
- GST_DEBUG_OBJECT (mux, "we have %d Sequence Parameter Set", nb_sps);
- offset++;
+ h264_data = (PrivDataH264 *) data->prepare_data;
- /* For each SPS */
- for (i = 0; i < nb_sps; i++) {
- guint16 sps_size =
- GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset);
+ /* Detect a codec data change */
+ if (h264_data->last_codec_data != data->codec_data) {
+ gst_buffer_unref (h264_data->cached_es);
+ h264_data->cached_es = NULL;
+ ret = TRUE;
+ }
- GST_LOG_OBJECT (mux, "Sequence Parameter Set is %d bytes", sps_size);
+ /* Generate the SPS/PPS ES header that will be prepended regularly */
+ if (G_UNLIKELY (!h264_data->cached_es)) {
+ gint offset = 4, i = 0, nb_sps = 0, nb_pps = 0;
+ gsize out_offset = 0;
+ guint8 startcode[4] = { 0x00, 0x00, 0x00, 0x01 };
+ h264_data->last_codec_data = data->codec_data;
+ h264_data->cached_es =
+ gst_buffer_new_and_alloc (GST_BUFFER_SIZE (data->codec_data) * 10);
- /* Jump over SPS size */
- offset += 2;
+ /* Get NAL length size */
+ h264_data->nal_length_size =
+ (GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x03) +
+ 1;
+ GST_LOG_OBJECT (mux, "NAL length will be coded on %u bytes",
+ h264_data->nal_length_size);
+ offset++;
- /* Fake a start code */
- memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4);
- out_offset += 4;
- /* Now push the SPS */
- memcpy (GST_BUFFER_DATA (out_buf) + out_offset,
- GST_BUFFER_DATA (data->codec_data) + offset, sps_size);
+ /* How many SPS */
+ nb_sps =
+ GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset) & 0x1f;
+ GST_DEBUG_OBJECT (mux, "we have %d Sequence Parameter Set", nb_sps);
+ offset++;
- out_offset += sps_size;
- offset += sps_size;
- }
+ /* For each SPS */
+ for (i = 0; i < nb_sps; i++) {
+ guint16 sps_size =
+ GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset);
- nb_pps = GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset);
- GST_LOG_OBJECT (mux, "we have %d Picture Parameter Set", nb_sps);
- offset++;
+ GST_LOG_OBJECT (mux, "Sequence Parameter Set is %d bytes", sps_size);
- /* For each PPS */
- for (i = 0; i < nb_pps; i++) {
- gint pps_size =
- GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset);
+ /* Jump over SPS size */
+ offset += 2;
- GST_LOG_OBJECT (mux, "Picture Parameter Set is %d bytes", pps_size);
+ /* Fake a start code */
+ memcpy (GST_BUFFER_DATA (h264_data->cached_es) + out_offset,
+ startcode, 4);
+ out_offset += 4;
+ /* Now push the SPS */
+ memcpy (GST_BUFFER_DATA (h264_data->cached_es) + out_offset,
+ GST_BUFFER_DATA (data->codec_data) + offset, sps_size);
- /* Jump over PPS size */
- offset += 2;
+ out_offset += sps_size;
+ offset += sps_size;
+ }
- /* Fake a start code */
- memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4);
- out_offset += 4;
- /* Now push the PPS */
- memcpy (GST_BUFFER_DATA (out_buf) + out_offset,
- GST_BUFFER_DATA (data->codec_data) + offset, pps_size);
+ /* How many PPS */
+ nb_pps = GST_READ_UINT8 (GST_BUFFER_DATA (data->codec_data) + offset);
+ GST_LOG_OBJECT (mux, "we have %d Picture Parameter Set", nb_sps);
+ offset++;
- out_offset += pps_size;
- offset += pps_size;
+ /* For each PPS */
+ for (i = 0; i < nb_pps; i++) {
+ gint pps_size =
+ GST_READ_UINT16_BE (GST_BUFFER_DATA (data->codec_data) + offset);
+
+ GST_LOG_OBJECT (mux, "Picture Parameter Set is %d bytes", pps_size);
+
+ /* Jump over PPS size */
+ offset += 2;
+
+ /* Fake a start code */
+ memcpy (GST_BUFFER_DATA (h264_data->cached_es) + out_offset,
+ startcode, 4);
+ out_offset += 4;
+ /* Now push the PPS */
+ memcpy (GST_BUFFER_DATA (h264_data->cached_es) + out_offset,
+ GST_BUFFER_DATA (data->codec_data) + offset, pps_size);
+
+ out_offset += pps_size;
+ offset += pps_size;
+ }
+ GST_BUFFER_SIZE (h264_data->cached_es) = out_offset;
+ GST_DEBUG_OBJECT (mux, "generated a %" G_GSIZE_FORMAT
+ " bytes SPS/PPS header", out_offset);
+ }
+ return ret;
+}
+
+GstBuffer *
+mpegtsmux_prepare_h264 (GstBuffer * buf, MpegTsPadData * data, MpegTsMux * mux)
+{
+ guint8 startcode[4] = { 0x00, 0x00, 0x00, 0x01 };
+ gsize out_offset = 0, in_offset = 0;
+ GstBuffer *out_buf;
+ gboolean changed;
+ PrivDataH264 *h264_data;
+ GstClockTimeDiff diff = GST_CLOCK_TIME_NONE;
+
+ GST_DEBUG_OBJECT (mux, "Preparing H264 buffer for output");
+
+ changed = mpegtsmux_process_codec_data_h264 (data, mux);
+ h264_data = (PrivDataH264 *) data->prepare_data;
+
+ if (GST_CLOCK_TIME_IS_VALID (h264_data->last_resync_ts) &&
+ GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (buf))) {
+ diff = GST_CLOCK_DIFF (h264_data->last_resync_ts,
+ GST_BUFFER_TIMESTAMP (buf));
}
+ if (changed || (GST_CLOCK_TIME_IS_VALID (diff) && diff > SPS_PPS_PERIOD)) {
+ out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) * 2 +
+ GST_BUFFER_SIZE (h264_data->cached_es));
+ h264_data->last_resync_ts = GST_BUFFER_TIMESTAMP (buf);
+ memcpy (GST_BUFFER_DATA (out_buf), GST_BUFFER_DATA (h264_data->cached_es),
+ GST_BUFFER_SIZE (h264_data->cached_es));
+ out_offset = GST_BUFFER_SIZE (h264_data->cached_es);
+ GST_DEBUG_OBJECT (mux, "prepending SPS/PPS information to that packet");
+ } else {
+ out_buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) * 2);
+ }
+
+ /* We want the same metadata */
+ gst_buffer_copy_metadata (out_buf, buf, GST_BUFFER_COPY_ALL);
+
while (in_offset < GST_BUFFER_SIZE (buf) &&
out_offset < GST_BUFFER_SIZE (out_buf) - 4) {
guint32 nal_size = 0;
- switch (nal_length_size) {
+ switch (h264_data->nal_length_size) {
case 1:
nal_size = GST_READ_UINT8 (GST_BUFFER_DATA (buf) + in_offset);
break;
break;
default:
GST_WARNING_OBJECT (mux, "unsupported NAL length size %u",
- nal_length_size);
+ h264_data->nal_length_size);
}
- in_offset += nal_length_size;
+ in_offset += h264_data->nal_length_size;
/* Generate an Elementary stream buffer by inserting a startcode */
memcpy (GST_BUFFER_DATA (out_buf) + out_offset, startcode, 4);