From ad6ea8fbe99b48463bb2cd615f2e4e2e50feb020 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= <tim.muller@collabora.co.uk>
Date: Sun, 25 Sep 2011 23:57:34 +0100
Subject: [PATCH] mpegpsmux: put stream headers into output caps

Basic version with only the system header and the program
stream map. An advanced version could include codec-specific
bits like SPS/PPS too. This is useful in connection with
e.g. multifilesink to make sure new files always start with
the stream headers.
---
 gst/mpegpsmux/mpegpsmux.c | 18 +++++++++
 gst/mpegpsmux/psmux.c     | 81 ++++++++++++++++++++++++++++++++++-----
 gst/mpegpsmux/psmux.h     |  6 +++
 3 files changed, 95 insertions(+), 10 deletions(-)

diff --git a/gst/mpegpsmux/mpegpsmux.c b/gst/mpegpsmux/mpegpsmux.c
index 6d07d023c0..3a86c0500e 100644
--- a/gst/mpegpsmux/mpegpsmux.c
+++ b/gst/mpegpsmux/mpegpsmux.c
@@ -709,6 +709,9 @@ new_packet_cb (guint8 * data, guint len, void *user_data)
 static gboolean
 mpegpsdemux_prepare_srcpad (MpegPsMux * mux)
 {
+  GValue val = { 0, };
+  GList *headers, *l;
+
   /* prepare the source pad for output */
 
   GstEvent *new_seg =
@@ -720,6 +723,21 @@ mpegpsdemux_prepare_srcpad (MpegPsMux * mux)
 
 /*      gst_static_pad_template_get_caps (&mpegpsmux_src_factory); */
 
+  headers = psmux_get_stream_headers (mux->psmux);
+  g_value_init (&val, GST_TYPE_ARRAY);
+  for (l = headers; l != NULL; l = l->next) {
+    GValue buf_val = { 0, };
+
+    g_value_init (&buf_val, GST_TYPE_BUFFER);
+    gst_value_take_buffer (&buf_val, GST_BUFFER (l->data));
+    l->data = NULL;
+    gst_value_array_append_value (&val, &buf_val);
+    g_value_unset (&buf_val);
+  }
+  gst_caps_set_value (caps, "streamheader", &val);
+  g_value_unset (&val);
+  g_list_free (headers);
+
   /* Set caps on src pad from our template and push new segment */
   gst_pad_set_caps (mux->srcpad, caps);
 
diff --git a/gst/mpegpsmux/psmux.c b/gst/mpegpsmux/psmux.c
index 0a714d038a..44e32faf5b 100644
--- a/gst/mpegpsmux/psmux.c
+++ b/gst/mpegpsmux/psmux.c
@@ -141,6 +141,12 @@ psmux_free (PsMux * mux)
   }
   g_list_free (mux->streams);
 
+  if (mux->sys_header != NULL)
+    gst_buffer_unref (mux->sys_header);
+
+  if (mux->psm != NULL)
+    gst_buffer_unref (mux->psm);
+
   g_slice_free (PsMux, mux);
 }
 
@@ -332,17 +338,23 @@ psmux_write_pack_header (PsMux * mux)
   return psmux_packet_out (mux);
 }
 
-static gboolean
-psmux_write_system_header (PsMux * mux)
+static void
+psmux_ensure_system_header (PsMux * mux)
 {
+  GstBuffer *buf;
   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;
 
+  if (mux->sys_header != NULL)
+    return;
+
+  buf = gst_buffer_new_and_alloc (len);
+
   /* system_header_start_code */
-  bits_initwrite (&bw, len, mux->packet_buf);
+  bits_initwrite (&bw, len, GST_BUFFER_DATA (buf));
 
   /* system_header start code */
   bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
@@ -378,19 +390,36 @@ psmux_write_system_header (PsMux * mux)
       private_hit = TRUE;
   }
 
-  mux->packet_bytes_written = len;
-  return psmux_packet_out (mux);
+  GST_MEMDUMP ("System Header", GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+
+  mux->sys_header = buf;
 }
 
 static gboolean
-psmux_write_program_stream_map (PsMux * mux)
+psmux_write_system_header (PsMux * mux)
 {
+  psmux_ensure_system_header (mux);
+
+  memcpy (mux->packet_buf, GST_BUFFER_DATA (mux->sys_header),
+      GST_BUFFER_SIZE (mux->sys_header));
+  mux->packet_bytes_written = GST_BUFFER_SIZE (mux->sys_header);
+
+  return psmux_packet_out (mux);
+}
+
+static void
+psmux_ensure_program_stream_map (PsMux * mux)
+{
+  GstBuffer *buf;
   gint psm_size = 16, es_map_size = 0;
   bits_buffer_t bw;
   GList *cur;
   guint16 len;
   guint8 *pos;
 
+  if (mux->psm != NULL)
+    return;
+
   /* pre-write the descriptor loop */
   pos = mux->es_info_buf;
   for (cur = g_list_first (mux->streams); cur != NULL; cur = g_list_next (cur)) {
@@ -412,7 +441,10 @@ psmux_write_program_stream_map (PsMux * mux)
   }
 
   psm_size += es_map_size;
-  bits_initwrite (&bw, psm_size, mux->packet_buf);
+
+  buf = gst_buffer_new_and_alloc (psm_size);
+
+  bits_initwrite (&bw, psm_size, GST_BUFFER_DATA (buf));
 
   /* psm start code */
   bits_write (&bw, 24, PSMUX_START_CODE_PREFIX);
@@ -429,15 +461,44 @@ psmux_write_program_stream_map (PsMux * mux)
   /* program_stream_info empty */
 
   bits_write (&bw, 16, es_map_size);    /* elementary_stream_map_length */
+
   memcpy (bw.p_data + bw.i_data, mux->es_info_buf, es_map_size);
 
   /* CRC32 */
   {
-    guint32 crc = calc_crc32 (mux->packet_buf, psm_size - 4);
-    guint8 *pos = mux->packet_buf + psm_size - 4;
+    guint32 crc = calc_crc32 (bw.p_data, psm_size - 4);
+    guint8 *pos = bw.p_data + psm_size - 4;
     psmux_put32 (&pos, crc);
   }
 
-  mux->packet_bytes_written = psm_size;
+  GST_MEMDUMP ("Program Stream Map", GST_BUFFER_DATA (buf),
+      GST_BUFFER_SIZE (buf));
+
+  mux->psm = buf;
+}
+
+static gboolean
+psmux_write_program_stream_map (PsMux * mux)
+{
+  psmux_ensure_program_stream_map (mux);
+
+  memcpy (mux->packet_buf, GST_BUFFER_DATA (mux->psm),
+      GST_BUFFER_SIZE (mux->psm));
+  mux->packet_bytes_written = GST_BUFFER_SIZE (mux->psm);
+
   return psmux_packet_out (mux);
 }
+
+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;
+}
diff --git a/gst/mpegpsmux/psmux.h b/gst/mpegpsmux/psmux.h
index 5aacf94cec..3daa90af7e 100644
--- a/gst/mpegpsmux/psmux.h
+++ b/gst/mpegpsmux/psmux.h
@@ -93,6 +93,10 @@ struct PsMux {
   guint8 audio_bound;
   guint8 video_bound;
   guint32 rate_bound;
+
+  /* stream headers */
+  GstBuffer *sys_header;
+  GstBuffer *psm;
 };
 
 /* create/free new muxer session */
@@ -109,6 +113,8 @@ PsMuxStream *	psmux_create_stream 		(PsMux *mux, PsMuxStreamType stream_type);
 gboolean 	psmux_write_stream_packet 	(PsMux *mux, PsMuxStream *stream); 
 gboolean	psmux_write_end_code		(PsMux *mux);
 
+GList *		psmux_get_stream_headers	(PsMux *mux);
+
 G_END_DECLS
 
 #endif