From 108774781d629c85244f6b976ea21d93021afe70 Mon Sep 17 00:00:00 2001 From: Sebastian Dröge Date: Mon, 13 Apr 2009 14:03:03 +0200 Subject: Add initial support for muxing/demuxing Speex audio Note: This is not in the Matroska spec yet Fixes bug #578310. --- gst/matroska/matroska-demux.c | 43 ++++++++++++++++++++++ gst/matroska/matroska-ids.h | 7 ++++ gst/matroska/matroska-mux.c | 84 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) (limited to 'gst/matroska') diff --git a/gst/matroska/matroska-demux.c b/gst/matroska/matroska-demux.c index 6b9c377b..518b48b6 100644 --- a/gst/matroska/matroska-demux.c +++ b/gst/matroska/matroska-demux.c @@ -3498,6 +3498,39 @@ gst_matroska_demux_push_flac_codec_priv_data (GstMatroskaDemux * demux, return GST_FLOW_OK; } +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; diff --git a/gst/matroska/matroska-ids.h b/gst/matroska/matroska-ids.h index 73cc2e21..0ea880af 100644 --- a/gst/matroska/matroska-ids.h +++ b/gst/matroska/matroska-ids.h @@ -351,6 +351,8 @@ #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 */ diff --git a/gst/matroska/matroska-mux.c b/gst/matroska/matroska-mux.c index 87ad4772..ee4b45cd 100644 --- a/gst/matroska/matroska-mux.c +++ b/gst/matroska/matroska-mux.c @@ -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); -- cgit