| /* |
| * Siren Decoder Gst Element |
| * |
| * @author: Youness Alaoui <kakaroto@kakaroto.homelinux.net> |
| * |
| * 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 |
| * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| /** |
| * SECTION:element-sirendec |
| * |
| * This decodes audio buffers from the Siren 16 codec (a 16khz extension of |
| * G.722.1) that is meant to be compatible with the Microsoft Windows Live |
| * Messenger(tm) implementation. |
| * |
| * Ref: http://www.polycom.com/company/about_us/technology/siren_g7221/index.html |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include "gstsirendec.h" |
| |
| #include <string.h> |
| |
| GST_DEBUG_CATEGORY (sirendec_debug); |
| #define GST_CAT_DEFAULT (sirendec_debug) |
| |
| #define FRAME_DURATION (20 * GST_MSECOND) |
| |
| static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", |
| GST_PAD_SINK, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("audio/x-siren, " "dct-length = (int) 320")); |
| |
| static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS ("audio/x-raw, format = (string) \"S16LE\", " |
| "rate = (int) 16000, " "channels = (int) 1")); |
| |
| static gboolean gst_siren_dec_start (GstAudioDecoder * dec); |
| static gboolean gst_siren_dec_stop (GstAudioDecoder * dec); |
| static gboolean gst_siren_dec_set_format (GstAudioDecoder * dec, |
| GstCaps * caps); |
| static gboolean gst_siren_dec_parse (GstAudioDecoder * dec, |
| GstAdapter * adapter, gint * offset, gint * length); |
| static GstFlowReturn gst_siren_dec_handle_frame (GstAudioDecoder * dec, |
| GstBuffer * buffer); |
| |
| |
| G_DEFINE_TYPE (GstSirenDec, gst_siren_dec, GST_TYPE_AUDIO_DECODER); |
| |
| static void |
| gst_siren_dec_class_init (GstSirenDecClass * klass) |
| { |
| GstElementClass *element_class = GST_ELEMENT_CLASS (klass); |
| GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass); |
| |
| GST_DEBUG_CATEGORY_INIT (sirendec_debug, "sirendec", 0, "sirendec"); |
| |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&srctemplate)); |
| gst_element_class_add_pad_template (element_class, |
| gst_static_pad_template_get (&sinktemplate)); |
| |
| gst_element_class_set_static_metadata (element_class, "Siren Decoder element", |
| "Codec/Decoder/Audio ", |
| "Decode streams encoded with the Siren7 codec into 16bit PCM", |
| "Youness Alaoui <kakaroto@kakaroto.homelinux.net>"); |
| |
| base_class->start = GST_DEBUG_FUNCPTR (gst_siren_dec_start); |
| base_class->stop = GST_DEBUG_FUNCPTR (gst_siren_dec_stop); |
| base_class->set_format = GST_DEBUG_FUNCPTR (gst_siren_dec_set_format); |
| base_class->parse = GST_DEBUG_FUNCPTR (gst_siren_dec_parse); |
| base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_siren_dec_handle_frame); |
| |
| GST_DEBUG ("Class Init done"); |
| } |
| |
| static void |
| gst_siren_dec_init (GstSirenDec * dec) |
| { |
| gst_audio_decoder_set_needs_format (GST_AUDIO_DECODER (dec), TRUE); |
| gst_audio_decoder_set_use_default_pad_acceptcaps (GST_AUDIO_DECODER_CAST |
| (dec), TRUE); |
| GST_PAD_SET_ACCEPT_TEMPLATE (GST_AUDIO_DECODER_SINK_PAD (dec)); |
| } |
| |
| static gboolean |
| gst_siren_dec_start (GstAudioDecoder * dec) |
| { |
| GstSirenDec *sdec = GST_SIREN_DEC (dec); |
| |
| GST_DEBUG_OBJECT (dec, "start"); |
| |
| sdec->decoder = Siren7_NewDecoder (16000); |
| |
| /* no flushing please */ |
| gst_audio_decoder_set_drainable (dec, FALSE); |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| gst_siren_dec_stop (GstAudioDecoder * dec) |
| { |
| GstSirenDec *sdec = GST_SIREN_DEC (dec); |
| |
| GST_DEBUG_OBJECT (dec, "stop"); |
| |
| Siren7_CloseDecoder (sdec->decoder); |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| gst_siren_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) |
| { |
| GstAudioInfo info; |
| |
| gst_audio_info_init (&info); |
| gst_audio_info_set_format (&info, GST_AUDIO_FORMAT_S16LE, 16000, 1, NULL); |
| return gst_audio_decoder_set_output_format (bdec, &info); |
| } |
| |
| static GstFlowReturn |
| gst_siren_dec_parse (GstAudioDecoder * dec, GstAdapter * adapter, |
| gint * offset, gint * length) |
| { |
| gint size; |
| GstFlowReturn ret; |
| |
| size = gst_adapter_available (adapter); |
| g_return_val_if_fail (size > 0, GST_FLOW_ERROR); |
| |
| /* accept any multiple of frames */ |
| if (size > 40) { |
| ret = GST_FLOW_OK; |
| *offset = 0; |
| *length = size - (size % 40); |
| } else { |
| ret = GST_FLOW_EOS; |
| } |
| |
| return ret; |
| } |
| |
| static GstFlowReturn |
| gst_siren_dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buf) |
| { |
| GstSirenDec *dec; |
| GstFlowReturn ret = GST_FLOW_OK; |
| GstBuffer *out_buf; |
| guint8 *in_data, *out_data; |
| guint i, size, num_frames; |
| gint out_size; |
| #ifndef GST_DISABLE_GST_DEBUG |
| gint in_size; |
| #endif |
| gint decode_ret; |
| GstMapInfo inmap, outmap; |
| |
| dec = GST_SIREN_DEC (bdec); |
| |
| size = gst_buffer_get_size (buf); |
| |
| GST_LOG_OBJECT (dec, "Received buffer of size %u", size); |
| |
| g_return_val_if_fail (size % 40 == 0, GST_FLOW_ERROR); |
| g_return_val_if_fail (size > 0, GST_FLOW_ERROR); |
| |
| /* process 40 input bytes into 640 output bytes */ |
| num_frames = size / 40; |
| |
| /* this is the input/output size */ |
| #ifndef GST_DISABLE_GST_DEBUG |
| in_size = num_frames * 40; |
| #endif |
| out_size = num_frames * 640; |
| |
| GST_LOG_OBJECT (dec, "we have %u frames, %u in, %u out", num_frames, in_size, |
| out_size); |
| |
| out_buf = gst_audio_decoder_allocate_output_buffer (bdec, out_size); |
| if (out_buf == NULL) |
| goto alloc_failed; |
| |
| /* get the input data for all the frames */ |
| gst_buffer_map (buf, &inmap, GST_MAP_READ); |
| gst_buffer_map (out_buf, &outmap, GST_MAP_WRITE); |
| |
| in_data = inmap.data; |
| out_data = outmap.data; |
| |
| for (i = 0; i < num_frames; i++) { |
| GST_LOG_OBJECT (dec, "Decoding frame %u/%u", i, num_frames); |
| |
| /* decode 40 input bytes to 640 output bytes */ |
| decode_ret = Siren7_DecodeFrame (dec->decoder, in_data, out_data); |
| if (decode_ret != 0) |
| goto decode_error; |
| |
| /* move to next frame */ |
| out_data += 640; |
| in_data += 40; |
| } |
| |
| gst_buffer_unmap (buf, &inmap); |
| gst_buffer_unmap (out_buf, &outmap); |
| |
| GST_LOG_OBJECT (dec, "Finished decoding"); |
| |
| /* might really be multiple frames, |
| * but was treated as one for all purposes here */ |
| ret = gst_audio_decoder_finish_frame (bdec, out_buf, 1); |
| |
| done: |
| return ret; |
| |
| /* ERRORS */ |
| alloc_failed: |
| { |
| GST_DEBUG_OBJECT (dec, "failed to pad_alloc buffer: %d (%s)", ret, |
| gst_flow_get_name (ret)); |
| goto done; |
| } |
| decode_error: |
| { |
| GST_AUDIO_DECODER_ERROR (bdec, 1, STREAM, DECODE, (NULL), |
| ("Error decoding frame: %d", decode_ret), ret); |
| if (ret == GST_FLOW_OK) |
| gst_audio_decoder_finish_frame (bdec, NULL, 1); |
| gst_buffer_unref (out_buf); |
| goto done; |
| } |
| } |
| |
| gboolean |
| gst_siren_dec_plugin_init (GstPlugin * plugin) |
| { |
| return gst_element_register (plugin, "sirendec", |
| GST_RANK_MARGINAL, GST_TYPE_SIREN_DEC); |
| } |