Add initial support for muxing/demuxing Speex audio
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Mon, 13 Apr 2009 12:03:03 +0000 (14:03 +0200)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Mon, 13 Apr 2009 12:03:03 +0000 (14:03 +0200)
Note: This is not in the Matroska spec yet
Fixes bug #578310.

gst/matroska/matroska-demux.c
gst/matroska/matroska-ids.h
gst/matroska/matroska-mux.c

index 6b9c377..518b48b 100644 (file)
@@ -3499,6 +3499,39 @@ gst_matroska_demux_push_flac_codec_priv_data (GstMatroskaDemux * demux,
 }
 
 static GstFlowReturn
+gst_matroska_demux_push_speex_codec_priv_data (GstMatroskaDemux * demux,
+    GstMatroskaTrackContext * stream)
+{
+  GstFlowReturn ret;
+  guint8 *pdata;
+
+  GST_LOG_OBJECT (demux, "priv data size = %u", stream->codec_priv_size);
+
+  pdata = (guint8 *) stream->codec_priv;
+
+  /* need at least 'fLaC' marker + STREAMINFO metadata block */
+  if (stream->codec_priv_size < 80) {
+    GST_WARNING_OBJECT (demux, "not enough codec priv data for speex headers");
+    return GST_FLOW_ERROR;
+  }
+
+  if (memcmp (pdata, "Speex   ", 8) != 0) {
+    GST_WARNING_OBJECT (demux, "no Speex marker at start of stream headers");
+    return GST_FLOW_ERROR;
+  }
+
+  ret = gst_matroska_demux_push_hdr_buf (demux, stream, pdata, 80);
+  if (ret != GST_FLOW_OK)
+    return ret;
+
+  if (stream->codec_priv_size == 80)
+    return ret;
+  else
+    return gst_matroska_demux_push_hdr_buf (demux, stream, pdata + 80,
+        stream->codec_priv_size - 80);
+}
+
+static GstFlowReturn
 gst_matroska_demux_push_xiph_codec_priv_data (GstMatroskaDemux * demux,
     GstMatroskaTrackContext * stream)
 {
@@ -4047,6 +4080,11 @@ gst_matroska_demux_parse_blockgroup_or_simpleblock (GstMatroskaDemux * demux,
           stream->send_flac_headers = FALSE;
         }
 
+        if (stream->send_speex_headers) {
+          ret = gst_matroska_demux_push_speex_codec_priv_data (demux, stream);
+          stream->send_speex_headers = FALSE;
+        }
+
         if (stream->send_dvd_event) {
           gst_matroska_demux_push_dvd_clut_change_event (demux, stream);
           /* FIXME: should we send this event again after (flushing) seek ? */
@@ -4958,6 +4996,7 @@ gst_matroska_demux_video_caps (GstMatroskaTrackVideoContext *
 
   context->send_xiph_headers = FALSE;
   context->send_flac_headers = FALSE;
+  context->send_speex_headers = FALSE;
 
   /* TODO: check if we have all codec types from matroska-ids.h
    *       check if we have to do more special things with codec_private
@@ -5308,6 +5347,7 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext *
 
   context->send_xiph_headers = FALSE;
   context->send_flac_headers = FALSE;
+  context->send_speex_headers = FALSE;
 
   /* TODO: check if we have all codec types from matroska-ids.h
    *       check if we have to do more special things with codec_private
@@ -5380,6 +5420,9 @@ gst_matroska_demux_audio_caps (GstMatroskaTrackAudioContext *
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_FLAC)) {
     caps = gst_caps_new_simple ("audio/x-flac", NULL);
     context->send_flac_headers = TRUE;
+  } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_SPEEX)) {
+    caps = gst_caps_new_simple ("audio/x-speex", NULL);
+    context->send_speex_headers = TRUE;
   } else if (!strcmp (codec_id, GST_MATROSKA_CODEC_ID_AUDIO_ACM)) {
     gst_riff_strf_auds *auds = NULL;
 
index 73cc2e2..0ea880a 100644 (file)
 #define GST_MATROSKA_CODEC_ID_AUDIO_DTS            "A_DTS"
 #define GST_MATROSKA_CODEC_ID_AUDIO_VORBIS         "A_VORBIS"
 #define GST_MATROSKA_CODEC_ID_AUDIO_FLAC           "A_FLAC"
+/* FIXME: not yet in the spec */
+#define GST_MATROSKA_CODEC_ID_AUDIO_SPEEX          "A_SPEEX"
 #define GST_MATROSKA_CODEC_ID_AUDIO_ACM            "A_MS/ACM"
 #define GST_MATROSKA_CODEC_ID_AUDIO_TTA            "A_TTA1"
 #define GST_MATROSKA_CODEC_ID_AUDIO_WAVPACK4       "A_WAVPACK4"
@@ -488,6 +490,11 @@ struct _GstMatroskaTrackContext {
    * testing for time == 0 is not enough to detect that. Used by demuxer */
   gboolean      send_flac_headers;
 
+  /* Special flag for Speex, for which we need to reconstruct the header
+   * buffer from the codec_priv data before sending any data, and just
+   * testing for time == 0 is not enough to detect that. Used by demuxer */
+  gboolean      send_speex_headers;
+
   /* Special flag for VobSub, for which we have to send colour table info
    * (if available) first before sending any data, and just testing
    * for time == 0 is not enough to detect that. Used by demuxer */
index 87ad477..ee4b45c 100644 (file)
@@ -142,6 +142,8 @@ static GstStaticPadTemplate audiosink_templ =
         COMMON_AUDIO_CAPS "; "
         "audio/x-flac, "
         COMMON_AUDIO_CAPS "; "
+        "audio/x-speex, "
+        COMMON_AUDIO_CAPS "; "
         "audio/x-raw-int, "
         "width = (int) 8, "
         "depth = (int) 8, "
@@ -224,6 +226,8 @@ static gboolean theora_streamheader_to_codecdata (const GValue * streamheader,
     GstMatroskaTrackContext * context);
 static gboolean vorbis_streamheader_to_codecdata (const GValue * streamheader,
     GstMatroskaTrackContext * context);
+static gboolean speex_streamheader_to_codecdata (const GValue * streamheader,
+    GstMatroskaTrackContext * context);
 static gboolean flac_streamheader_to_codecdata (const GValue * streamheader,
     GstMatroskaTrackContext * context);
 
@@ -1140,6 +1144,69 @@ flac_streamheader_to_codecdata (const GValue * streamheader,
   return TRUE;
 }
 
+static gboolean
+speex_streamheader_to_codecdata (const GValue * streamheader,
+    GstMatroskaTrackContext * context)
+{
+  GArray *bufarr;
+  GValue *bufval;
+  GstBuffer *buffer;
+
+  if (streamheader == NULL || G_VALUE_TYPE (streamheader) != GST_TYPE_ARRAY) {
+    GST_WARNING ("No or invalid streamheader field in the caps");
+    return FALSE;
+  }
+
+  bufarr = g_value_peek_pointer (streamheader);
+  if (bufarr->len != 2) {
+    GST_WARNING ("Too few headers in streamheader field");
+    return FALSE;
+  }
+
+  context->xiph_headers_to_skip = bufarr->len + 1;
+
+  bufval = &g_array_index (bufarr, GValue, 0);
+  if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
+    GST_WARNING ("streamheaders array does not contain GstBuffers");
+    return FALSE;
+  }
+
+  buffer = g_value_peek_pointer (bufval);
+
+  if (GST_BUFFER_SIZE (buffer) < 80
+      || memcmp (GST_BUFFER_DATA (buffer), "Speex   ", 8) != 0) {
+    GST_WARNING ("Invalid streamheader for Speex");
+    return FALSE;
+  }
+
+  context->codec_priv = g_malloc (GST_BUFFER_SIZE (buffer));
+  context->codec_priv_size = GST_BUFFER_SIZE (buffer);
+  memcpy (context->codec_priv, GST_BUFFER_DATA (buffer),
+      GST_BUFFER_SIZE (buffer));
+
+  bufval = &g_array_index (bufarr, GValue, 1);
+
+  if (G_VALUE_TYPE (bufval) != GST_TYPE_BUFFER) {
+    g_free (context->codec_priv);
+    context->codec_priv = NULL;
+    context->codec_priv_size = 0;
+    GST_WARNING ("streamheaders array does not contain GstBuffers");
+    return FALSE;
+  }
+
+  buffer = g_value_peek_pointer (bufval);
+
+  context->codec_priv =
+      g_realloc (context->codec_priv,
+      context->codec_priv_size + GST_BUFFER_SIZE (buffer));
+  memcpy ((guint8 *) context->codec_priv + context->codec_priv_size,
+      GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
+  context->codec_priv_size =
+      context->codec_priv_size + GST_BUFFER_SIZE (buffer);
+
+  return TRUE;
+}
+
 static gchar *
 aac_codec_data_to_codec_id (const GstBuffer * buf)
 {
@@ -1382,6 +1449,23 @@ gst_matroska_mux_audio_pad_setcaps (GstPad * pad, GstCaps * caps)
       return FALSE;
     }
     return TRUE;
+  } else if (!strcmp (mimetype, "audio/x-speex")) {
+    const GValue *streamheader;
+
+    context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_SPEEX);
+    if (context->codec_priv != NULL) {
+      g_free (context->codec_priv);
+      context->codec_priv = NULL;
+      context->codec_priv_size = 0;
+    }
+
+    streamheader = gst_structure_get_value (structure, "streamheader");
+    if (!speex_streamheader_to_codecdata (streamheader, context)) {
+      GST_ELEMENT_ERROR (mux, STREAM, MUX, (NULL),
+          ("speex stream headers missing or malformed"));
+      return FALSE;
+    }
+    return TRUE;
   } else if (!strcmp (mimetype, "audio/x-ac3")) {
     context->codec_id = g_strdup (GST_MATROSKA_CODEC_ID_AUDIO_AC3);