| /* |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /* |
| * Copyright (c) 2011-2016, Freescale Semiconductor, Inc. All rights reserved. |
| * Copyright 2017 NXP |
| * |
| */ |
| |
| |
| /* |
| * Module Name: beepdec.c |
| * |
| * Description: Implementation of unified audio decoder gstreamer plugin |
| * |
| * Portability: This code is written for Linux OS and Gstreamer |
| */ |
| |
| /* |
| * Changelog: |
| * |
| */ |
| |
| |
| #include <string.h> |
| #include <gst/tag/tag.h> |
| |
| #include "beepdec.h" |
| |
| GST_DEBUG_CATEGORY (beep_dec_debug); |
| |
| #define CORE_FATAL_ERROR_MASK ((uint32)0xff) |
| #define CORE_STATUS_MASK (~(uint32)0xff) |
| #define MAX_PROFILE_ERROR_COUNT 50 //about 1 second decoding time, 1 seconds' audio data length |
| #define VORBIS_HEADER_FRAME 3 |
| |
| #define GST_BUFFER_TIMESTAMP GST_BUFFER_PTS |
| |
| static gstsutils_property beep_property[]= |
| { |
| {"tolerance", G_TYPE_UINT64, gst_audio_decoder_set_tolerance}, |
| {"min-latency", G_TYPE_UINT64, gst_audio_decoder_set_min_latency}, |
| {"plc", G_TYPE_BOOLEAN, gst_audio_decoder_set_plc} |
| }; |
| |
| |
| #define BEEP_PCM_CAPS \ |
| "audio/x-raw, "\ |
| "format = (string){S8, S16LE, S24LE, S32LE}, "\ |
| "rate = (int){7350, 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000}, "\ |
| "channels = (int)[1, 8]" |
| |
| static GstStaticPadTemplate beep_src_template = |
| GST_STATIC_PAD_TEMPLATE ("src", |
| GST_PAD_SRC, |
| GST_PAD_ALWAYS, |
| GST_STATIC_CAPS (BEEP_PCM_CAPS) |
| ); |
| |
| static gint32 alsa_1channel_layout[] = { |
| /* FC */ |
| UA_CHANNEL_FRONT_CENTER, |
| }; |
| |
| static gint32 alsa_2channel_layout[] = { |
| /* FL,FR */ |
| UA_CHANNEL_FRONT_LEFT, |
| UA_CHANNEL_FRONT_RIGHT, |
| }; |
| |
| static gint32 alsa_3channel_layout[] = { |
| /* FL,FR,LFE */ |
| UA_CHANNEL_FRONT_LEFT, |
| UA_CHANNEL_FRONT_RIGHT, |
| UA_CHANNEL_FRONT_CENTER, |
| //UA_CHANNEL_LFE |
| }; |
| |
| static gint32 alsa_4channel_layout[] = { |
| /* FL,FR,BL,BR */ |
| UA_CHANNEL_FRONT_LEFT, |
| UA_CHANNEL_FRONT_RIGHT, |
| UA_CHANNEL_REAR_LEFT, |
| UA_CHANNEL_REAR_RIGHT, |
| }; |
| |
| static gint32 alsa_5channel_layout[] = { |
| /* FL,FR,BL,BR,FC*/ |
| UA_CHANNEL_FRONT_LEFT, |
| UA_CHANNEL_FRONT_RIGHT, |
| UA_CHANNEL_FRONT_CENTER, |
| UA_CHANNEL_REAR_LEFT, |
| UA_CHANNEL_REAR_RIGHT, |
| }; |
| |
| static gint32 alsa_6channel_layout[] = { |
| /* FL,FR,BL,BR,FC,LFE */ |
| UA_CHANNEL_FRONT_LEFT, |
| UA_CHANNEL_FRONT_RIGHT, |
| UA_CHANNEL_FRONT_CENTER, |
| UA_CHANNEL_LFE, |
| UA_CHANNEL_REAR_LEFT, |
| UA_CHANNEL_REAR_RIGHT, |
| }; |
| |
| static gint32 alsa_8channel_layout[] = { |
| /* FL,FR,BL,BR,FC,LFE,SL,SR */ |
| UA_CHANNEL_FRONT_LEFT, |
| UA_CHANNEL_FRONT_RIGHT, |
| UA_CHANNEL_REAR_LEFT, |
| UA_CHANNEL_REAR_RIGHT, |
| UA_CHANNEL_FRONT_CENTER, |
| UA_CHANNEL_LFE, |
| UA_CHANNEL_SIDE_LEFT, |
| UA_CHANNEL_SIDE_RIGHT, |
| }; |
| |
| |
| static gint32 *alsa_channel_layouts[] = { |
| NULL, |
| alsa_1channel_layout, // 1 |
| alsa_2channel_layout, // 2 |
| alsa_3channel_layout, |
| alsa_4channel_layout, |
| alsa_5channel_layout, |
| alsa_6channel_layout, |
| NULL, |
| alsa_8channel_layout, |
| }; |
| |
| #define gst_beep_dec_parent_class parent_class |
| G_DEFINE_TYPE (GstBeepDec, gst_beep_dec, GST_TYPE_AUDIO_DECODER); |
| |
| static void gst_beep_dec_finalize (GObject * object); |
| static gboolean beep_dec_set_format(GstAudioDecoder *dec, GstCaps *caps); |
| |
| static gboolean beep_dec_start (GstAudioDecoder * dec); |
| static gboolean beep_dec_stop (GstAudioDecoder * dec); |
| static GstFlowReturn beep_dec_parse_and_decode (GstAudioDecoder * dec, |
| GstAdapter *adapter,gint *offset, gint *length); |
| static GstFlowReturn beep_dec_handle_frame (GstAudioDecoder * dec, |
| GstBuffer * buffer); |
| static void beep_dec_flush (GstAudioDecoder * dec, gboolean hard); |
| |
| |
| static GstPadTemplate * |
| gst_beep_dec_sink_pad_template (void) |
| { |
| static GstPadTemplate *templ = NULL; |
| |
| if (!templ) { |
| GstCaps *caps = beep_core_get_caps (); |
| |
| if (caps) { |
| templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps); |
| gst_caps_unref(caps); |
| } |
| } |
| return templ; |
| } |
| |
| static void |
| gst_beep_dec_class_init (GstBeepDecClass * klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); |
| GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass); |
| |
| gobject_class->finalize = gst_beep_dec_finalize; |
| |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_beep_dec_sink_pad_template ()); |
| |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_static_pad_template_get (&beep_src_template)); |
| |
| base_class->set_format= GST_DEBUG_FUNCPTR (beep_dec_set_format); |
| base_class->start = GST_DEBUG_FUNCPTR (beep_dec_start); |
| base_class->stop = GST_DEBUG_FUNCPTR (beep_dec_stop); |
| base_class->handle_frame = GST_DEBUG_FUNCPTR (beep_dec_handle_frame); |
| //base_class->parse = GST_DEBUG_FUNCPTR (beep_dec_parse_and_decode); |
| base_class->flush = GST_DEBUG_FUNCPTR (beep_dec_flush); |
| |
| gst_element_class_set_static_metadata (gstelement_class, "Beep universal decoder", |
| "Codec/Decoder/Audio", |
| "Decode compressed audio to raw data", |
| "FreeScale Multimedia Team <shamm@freescale.com>"); |
| |
| GST_DEBUG_CATEGORY_INIT (beep_dec_debug, "beepdec", 0, "beepdec plugin"); |
| GST_LOG("gst_beep_dec_class_init \n"); |
| } |
| static void |
| gst_beep_dec_init (GstBeepDec * dec) |
| { |
| gst_audio_decoder_set_tolerance(GST_AUDIO_DECODER_CAST(dec),400000000); |
| gstsutils_load_default_property(beep_property,GST_AUDIO_DECODER_CAST(dec), |
| FSL_GST_CONF_DEFAULT_FILENAME,"beepdec"); |
| |
| GST_LOG("gst_beep_dec_init \n"); |
| } |
| static void gst_beep_dec_finalize (GObject * object) |
| { |
| GST_LOG("gst_beep_dec_finalize \n"); |
| |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| } |
| static void * |
| beepdec_core_mem_alloc (uint32 size) |
| { |
| void *ret = NULL; |
| if (size) { |
| ret = g_try_malloc (size); |
| } |
| return ret; |
| } |
| |
| static void |
| beepdec_core_mem_free (void *ptr) |
| { |
| g_free (ptr); |
| } |
| |
| static void * |
| beepdec_core_mem_calloc (uint32 numElements, uint32 size) |
| { |
| uint32 alloc_size = size * numElements; |
| void *ret = NULL; |
| if (alloc_size) { |
| ret = g_try_malloc (alloc_size); |
| if (ret) { |
| memset (ret, 0, alloc_size); |
| } |
| } |
| return ret; |
| } |
| |
| static void * |
| beepdec_core_mem_realloc (void *ptr, uint32 size) |
| { |
| void *ret = g_try_realloc (ptr, size); |
| return ret; |
| } |
| |
| static gboolean beep_dec_set_init_parameter(GstBeepDec * beep_dec, |
| GstCaps *caps) |
| { |
| GstStructure *structure; |
| UniACodecParameter parameter; |
| gint intvalue; |
| gboolean framed = FALSE; |
| gchar * stream_format; |
| gboolean ret = FALSE; |
| BeepCoreInterface *IDecoder = NULL; |
| UniACodec_Handle handle; |
| const GValue *value = NULL; |
| UA_ERROR_TYPE rc = ACODEC_SUCCESS; |
| |
| do{ |
| |
| if(!beep_dec || !caps) |
| break; |
| |
| IDecoder = beep_dec->beep_interface; |
| handle = beep_dec->handle; |
| |
| if(!IDecoder || !handle) |
| break; |
| |
| structure = gst_caps_get_structure (caps, 0); |
| |
| if (gst_structure_get_int (structure, "rate", &intvalue)) { |
| GST_INFO ("Set rate %d", intvalue); |
| parameter.samplerate = intvalue; |
| rc = IDecoder->setDecoderPara(handle, UNIA_SAMPLERATE, ¶meter); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| } |
| |
| if (gst_structure_get_int (structure, "bitrate", &intvalue)) { |
| GST_INFO ("Set bitrate %d", intvalue); |
| parameter.bitrate = intvalue; |
| rc = IDecoder->setDecoderPara(handle, UNIA_BITRATE, ¶meter); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| } |
| |
| if (gst_structure_get_int (structure, "channels", &intvalue)) { |
| GST_INFO ("Set channel %d", intvalue); |
| parameter.channels = intvalue; |
| rc = IDecoder->setDecoderPara(handle, UNIA_CHANNEL, ¶meter); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| } |
| |
| if (gst_structure_get_int (structure, "depth", &intvalue)) { |
| GST_INFO ("Set depth %d", intvalue); |
| parameter.depth = intvalue; |
| rc = IDecoder->setDecoderPara(handle, UNIA_DEPTH, ¶meter); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| } |
| |
| if (gst_structure_get_int (structure, "block_align", &intvalue)) { |
| GST_INFO ("Set block align %d", intvalue); |
| parameter.blockalign = intvalue; |
| rc = IDecoder->setDecoderPara(handle,UNIA_WMA_BlOCKALIGN, ¶meter); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| } |
| |
| if (gst_structure_get_int (structure, "frame_bit", &intvalue)) { |
| GST_INFO ("Set frame_bits %d", intvalue); |
| parameter.frame_bits= intvalue; |
| rc = IDecoder->setDecoderPara(handle,UNIA_RA_FRAME_BITS, ¶meter); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| } |
| |
| if (gst_structure_get_int (structure, "wmaversion", &intvalue)) { |
| GST_INFO ("Set wma version %d", intvalue); |
| parameter.version = intvalue; |
| rc = IDecoder->setDecoderPara(handle, UNIA_WMA_VERSION, ¶meter); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| } |
| |
| value = gst_structure_get_value (structure, "codec_data"); |
| if (value) { |
| GstBuffer *codec_data = gst_value_get_buffer (value); |
| GstMapInfo map; |
| |
| if ((codec_data) && gst_buffer_get_size (codec_data)) { |
| gst_buffer_map(codec_data, &map, GST_MAP_READ); |
| GST_INFO ("Set codec_data %" GST_PTR_FORMAT, codec_data); |
| parameter.codecData.size = map.size; |
| parameter.codecData.buf = map.data; |
| rc = IDecoder->setDecoderPara(handle,UNIA_CODEC_DATA, ¶meter); |
| gst_buffer_unmap(codec_data, &map); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| beep_dec->set_codec_data = TRUE; |
| } |
| } |
| |
| stream_format = gst_structure_get_string(structure, "stream-format"); |
| if (stream_format) { |
| GST_INFO ("Set stream_type %s", stream_format); |
| if(g_strcmp0(stream_format, "adts") == 0) { |
| parameter.stream_type = STREAM_ADTS; |
| } |
| else if(g_strcmp0(stream_format, "adif") == 0) { |
| parameter.stream_type = STREAM_ADIF; |
| } |
| else if(g_strcmp0(stream_format, "raw") == 0) { |
| parameter.stream_type = STREAM_RAW; |
| } |
| else { |
| parameter.stream_type = STREAM_UNKNOW; |
| } |
| |
| rc = IDecoder->setDecoderPara(handle,UNIA_STREAM_TYPE, ¶meter); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| } |
| |
| parameter.framed = FALSE; |
| |
| //set framed for all vorbis |
| if(!strcmp(IDecoder->name,"vorbis")) |
| parameter.framed = TRUE; |
| |
| if(gst_structure_get_boolean(structure, "framed",&framed)){ |
| parameter.framed |= framed; |
| } |
| |
| if(gst_structure_get_boolean(structure, "parsed",&framed)){ |
| parameter.framed |= framed; |
| } |
| |
| //beep_dec->framed = parameter.framed; |
| GST_INFO ("Set framed %s", ((parameter.framed) ? "true" : "false")); |
| rc = IDecoder->setDecoderPara(handle,UNIA_FRAMED, ¶meter); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| |
| { |
| CHAN_TABLE table; |
| memset(&table,0,sizeof(table)); |
| table.size = 8; |
| memcpy(&table.channel_table,alsa_channel_layouts,sizeof(alsa_channel_layouts)); |
| //set output format for channel layout |
| rc = IDecoder->setDecoderPara(handle,UNIA_CHAN_MAP_TABLE,(UniACodecParameter*)&table); |
| if (rc != ACODEC_SUCCESS) { |
| return ret; |
| } |
| } |
| |
| ret = TRUE; |
| }while(0); |
| |
| return ret; |
| } |
| |
| static gboolean beep_dec_set_format(GstAudioDecoder *dec, GstCaps *caps) |
| { |
| gboolean ret = FALSE; |
| GstBeepDec *beepdec; |
| UniACodecMemoryOps ops; |
| BeepCoreInterface *IDecoder = NULL; |
| |
| do{ |
| beepdec = GST_BEEP_DEC (dec); |
| |
| if (beepdec->beep_interface == NULL || beepdec->dsp_dec == TRUE) { |
| if (beepdec->beep_interface == NULL) { |
| beepdec->beep_interface = beep_core_create_interface_from_caps_dsp (caps); |
| } |
| if (beepdec->beep_interface) { |
| AUDIOFORMAT type = FORMAT_UNKNOW; |
| GST_INFO (" dsp wrapper interface created "); |
| IDecoder = beepdec->beep_interface; |
| ops.Malloc = beepdec_core_mem_alloc; |
| ops.Calloc = beepdec_core_mem_calloc; |
| ops.ReAlloc = beepdec_core_mem_realloc; |
| ops.Free = beepdec_core_mem_free; |
| GST_INFO (" audio type: %s ", IDecoder->name); |
| if (!strcmp (IDecoder->name, "aac")) { |
| type = AAC_PLUS; |
| } else if (!strcmp (IDecoder->name, "mp3")) { |
| type = MP3; |
| } else if (!strcmp (IDecoder->name, "bsac")) { |
| type = BSAC; |
| } else if (!strcmp (IDecoder->name, "dabplus")) { |
| type = DAB_PLUS; |
| } else if (!strcmp (IDecoder->name, "sbc")) { |
| type = SBCDEC; |
| } else { |
| goto dsp_fail; |
| } |
| beepdec->handle = IDecoder->createDecoderplus(&ops, type); |
| if (beepdec->handle == NULL) { |
| /* create fail, dsp not support */ |
| GST_INFO (" dsp create decoder fail "); |
| goto dsp_fail; |
| } |
| ret = beep_dec_set_init_parameter (beepdec,caps); |
| if (ret == FALSE) { |
| /* dsp not support parameter, try SW decoder*/ |
| GST_INFO (" dsp set parameter fail "); |
| goto dsp_fail; |
| } |
| beepdec->dsp_dec = TRUE; |
| break; |
| } |
| dsp_fail: |
| if (beepdec->handle && beepdec->beep_interface) { |
| beepdec->beep_interface->deleteDecoder (beepdec->handle); |
| beepdec->handle = NULL; |
| } |
| beepdec->beep_interface = NULL; |
| beepdec->dsp_dec = FALSE; |
| } |
| |
| GST_INFO ("normal create sw wrapper interface"); |
| |
| if(beepdec->beep_interface == NULL) |
| beepdec->beep_interface = beep_core_create_interface_from_caps (caps); |
| |
| if(beepdec->beep_interface == NULL) |
| break; |
| |
| IDecoder = beepdec->beep_interface; |
| |
| ops.Malloc = beepdec_core_mem_alloc; |
| ops.Calloc = beepdec_core_mem_calloc; |
| ops.ReAlloc = beepdec_core_mem_realloc; |
| ops.Free = beepdec_core_mem_free; |
| |
| beepdec->handle = IDecoder->createDecoder (&ops); |
| |
| if(beepdec->handle == NULL) |
| break; |
| |
| ret = beep_dec_set_init_parameter(beepdec,caps); |
| |
| }while(0); |
| |
| if (ret == FALSE && beepdec->handle && beepdec->beep_interface) { |
| beepdec->beep_interface->deleteDecoder (beepdec->handle); |
| beepdec->handle = NULL; |
| } |
| |
| |
| GST_LOG_OBJECT (beepdec,"beep_dec_set_format called ret=%d", ret); |
| return ret; |
| } |
| |
| static gboolean beep_dec_start (GstAudioDecoder * dec) |
| { |
| GstBeepDec *beepdec; |
| |
| beepdec = GST_BEEP_DEC (dec); |
| |
| beepdec->err_cnt = 0; |
| beepdec->not_enough_cnt = 0; |
| memset (&beepdec->outputformat, 0, sizeof(UniAcodecOutputPCMFormat)); |
| memset (&beepdec->audio_info, 0, sizeof(GstAudioInfo)); |
| beepdec->adapter = gst_adapter_new (); |
| beepdec->core_layout = NULL; |
| beepdec->out_layout = NULL; |
| beepdec->output_changed = FALSE; |
| beepdec->frame_cnt = 0; |
| beepdec->set_codec_data = FALSE; |
| beepdec->in_cnt = 0; |
| beepdec->eos_sent = FALSE; |
| beepdec->dsp_dec = FALSE; |
| |
| gst_audio_decoder_set_estimate_rate(dec, TRUE); |
| |
| |
| GST_LOG_OBJECT (beepdec,"beep_dec_start called "); |
| |
| return TRUE; |
| } |
| static gboolean beep_dec_stop (GstAudioDecoder * dec) |
| { |
| GstBeepDec *beepdec; |
| |
| beepdec = GST_BEEP_DEC (dec); |
| |
| if (beepdec->beep_interface && beepdec->handle) { |
| if (beepdec->beep_interface->deleteDecoder) { |
| beepdec->beep_interface->deleteDecoder (beepdec->handle); |
| beepdec->handle = NULL; |
| } |
| } |
| if (beepdec->adapter) { |
| gst_adapter_clear (beepdec->adapter); |
| g_object_unref (beepdec->adapter); |
| beepdec->adapter = NULL; |
| } |
| |
| if(beepdec->core_layout) |
| g_free(beepdec->core_layout); |
| |
| if(beepdec->out_layout) |
| g_free(beepdec->out_layout); |
| |
| |
| beep_core_destroy_interface (beepdec->beep_interface); |
| beepdec->beep_interface = NULL; |
| |
| GST_LOG_OBJECT (beepdec,"beep_dec_stop called "); |
| return TRUE; |
| } |
| static gboolean beep_dec_map_channel_layout(UniAcodecOutputPCMFormat * outputValue, |
| GstAudioChannelPosition *pos) |
| { |
| uint32 i = 0; |
| |
| uint32 nChannels; |
| |
| |
| if(outputValue == NULL || pos == NULL){ |
| return FALSE; |
| } |
| |
| nChannels = outputValue->channels; |
| |
| if(nChannels == 1){ |
| pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO; |
| return TRUE; |
| } |
| |
| if(nChannels == 2){ |
| pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; |
| pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; |
| return TRUE; |
| } |
| |
| for( i = 0; i < nChannels; i++){ |
| |
| switch(outputValue->layout[i]){ |
| case UA_CHANNEL_FRONT_LEFT: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; |
| break; |
| case UA_CHANNEL_FRONT_RIGHT: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; |
| break; |
| case UA_CHANNEL_REAR_CENTER: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_CENTER; |
| break; |
| case UA_CHANNEL_REAR_LEFT: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_LEFT; |
| break; |
| case UA_CHANNEL_REAR_RIGHT: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT; |
| break; |
| case UA_CHANNEL_LFE: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_LFE1; |
| break; |
| case UA_CHANNEL_FRONT_CENTER: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER; |
| break; |
| case UA_CHANNEL_FRONT_LEFT_CENTER: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT; |
| break; |
| case UA_CHANNEL_FRONT_RIGHT_CENTER: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT; |
| break; |
| case UA_CHANNEL_SIDE_LEFT: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT; |
| break; |
| case UA_CHANNEL_SIDE_RIGHT: |
| pos[i] = GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT; |
| break; |
| default: |
| break; |
| } |
| |
| } |
| |
| return TRUE; |
| } |
| |
| static void beep_dec_handle_output_changed(GstBeepDec *beepdec) |
| { |
| BeepCoreInterface *IDecoder = NULL; |
| UniACodec_Handle handle; |
| UniACodecParameter parameter = { 0 }; |
| GstAudioInfo info; |
| GstAudioChannelPosition *pos = NULL; |
| GstTagList *list = NULL; |
| |
| do{ |
| |
| if(beepdec == NULL) |
| break; |
| |
| IDecoder = beepdec->beep_interface; |
| handle = beepdec->handle; |
| |
| if(!IDecoder || !handle){ |
| break; |
| } |
| |
| IDecoder->getDecoderPara(handle,UNIA_OUTPUT_PCM_FORMAT,¶meter); |
| |
| if (!memcmp (¶meter.outputFormat, &beepdec->outputformat, |
| sizeof (UniAcodecOutputPCMFormat))) { |
| break; |
| } |
| |
| beepdec->outputformat = parameter.outputFormat; |
| |
| GST_DEBUG_OBJECT(beepdec,"output changed channel num=%d,core layout=%d,%d,%d,%d,%d,%d,%d,%d", |
| beepdec->outputformat.channels, |
| beepdec->outputformat.layout[0], beepdec->outputformat.layout[1], |
| beepdec->outputformat.layout[2], beepdec->outputformat.layout[3], |
| beepdec->outputformat.layout[4], beepdec->outputformat.layout[5], |
| beepdec->outputformat.layout[6], beepdec->outputformat.layout[7]); |
| |
| |
| if(beepdec->core_layout != NULL) |
| g_free(beepdec->core_layout); |
| |
| if(beepdec->out_layout != NULL) |
| g_free(beepdec->out_layout); |
| |
| if(beepdec->outputformat.channels > 0){ |
| beepdec->core_layout = g_malloc(sizeof(GstAudioChannelPosition) |
| * 64); |
| beepdec->out_layout = g_malloc(sizeof(GstAudioChannelPosition) |
| * 64); |
| |
| } |
| |
| if(beepdec->core_layout && beepdec->out_layout){ |
| memset(beepdec->core_layout,0,sizeof(GstAudioChannelPosition)* 64); |
| memset(beepdec->out_layout,0,sizeof(GstAudioChannelPosition)* 64); |
| beep_dec_map_channel_layout(&beepdec->outputformat,beepdec->core_layout); |
| memcpy (beepdec->out_layout,beepdec->core_layout, |
| sizeof (GstAudioChannelPosition) * beepdec->outputformat.channels); |
| } else { |
| break; /* core_layout or out_layout is NULL */ |
| } |
| |
| GST_DEBUG_OBJECT(beepdec,"output changed after convert num=%d,core layout=%d,%d,%d,%d,%d,%d,%d,%d", |
| beepdec->outputformat.channels, |
| beepdec->core_layout[0], beepdec->core_layout[1], |
| beepdec->core_layout[2], beepdec->core_layout[3], |
| beepdec->core_layout[4], beepdec->core_layout[5], |
| beepdec->core_layout[6], beepdec->core_layout[7]); |
| |
| gst_audio_channel_positions_to_valid_order (beepdec->out_layout, |
| beepdec->outputformat.channels); |
| |
| GST_DEBUG_OBJECT(beepdec,"output changed after map num=%d,gstreamer layout=%d,%d,%d,%d,%d,%d,%d,%d", |
| beepdec->outputformat.channels, |
| beepdec->out_layout[0], beepdec->out_layout[1], |
| beepdec->out_layout[2], beepdec->out_layout[3], |
| beepdec->out_layout[4], beepdec->out_layout[5], |
| beepdec->out_layout[6], beepdec->out_layout[7]); |
| |
| beepdec->audio_format = gst_audio_format_build_integer (TRUE, G_BYTE_ORDER, |
| beepdec->outputformat.width, beepdec->outputformat.depth); |
| |
| gst_audio_info_set_format (&info, |
| beepdec->audio_format, |
| beepdec->outputformat.samplerate, |
| beepdec->outputformat.channels, beepdec->out_layout); |
| |
| if (!memcmp (&beepdec->audio_info, &info, sizeof (GstAudioInfo))) { |
| break; |
| } |
| |
| beepdec->audio_info = info; |
| |
| beepdec->output_changed = TRUE; |
| gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (beepdec), &beepdec->audio_info); |
| |
| list = gst_tag_list_new_empty (); |
| |
| |
| IDecoder->getDecoderPara(handle,UNIA_BITRATE,¶meter); |
| if(parameter.bitrate > 0){ |
| gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, |
| parameter.bitrate, NULL); |
| } |
| |
| IDecoder->getDecoderPara(handle,UNIA_CODEC_DESCRIPTION,¶meter); |
| gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_AUDIO_CODEC, |
| *(parameter.codecDesc), NULL); |
| |
| gst_audio_decoder_merge_tags (GST_AUDIO_DECODER_CAST (beepdec), list, |
| GST_TAG_MERGE_REPLACE); |
| gst_tag_list_unref (list); |
| |
| }while(0); |
| |
| } |
| static GstFlowReturn beep_dec_handle_frame (GstAudioDecoder * dec, |
| GstBuffer * buffer) |
| { |
| GstBeepDec *beepdec; |
| BeepCoreInterface *IDecoder = NULL; |
| UniACodec_Handle handle; |
| GstFlowReturn ret = GST_FLOW_OK; |
| uint8 *inbuf = NULL; |
| uint32 inbuf_size = 0, offset = 0; |
| uint8 *outbuf = NULL; |
| uint32 out_size = 0; |
| uint32 status; |
| uint32 core_ret; |
| GstBuffer *out = NULL; |
| GstBuffer * temp_buffer = NULL; |
| uint32 adapter_size = 0; |
| GstMapInfo map; |
| gboolean twice = FALSE; |
| beepdec = GST_BEEP_DEC (dec); |
| gboolean sent = FALSE; |
| if(!beepdec) |
| goto bail; |
| |
| IDecoder = beepdec->beep_interface; |
| handle = beepdec->handle; |
| |
| if(!IDecoder || !handle){ |
| ret = GST_FLOW_FLUSHING; |
| goto bail; |
| } |
| |
| |
| if(!buffer){ |
| //audio decoder will send a null frame after 3 codec data buffer |
| //received when playing vorbis audio. |
| //return if in this case. |
| if(beepdec->in_cnt > 0) |
| goto begin; |
| else |
| goto bail; |
| } |
| |
| inbuf_size = gst_buffer_get_size(buffer); |
| |
| gst_buffer_map(buffer, &map, GST_MAP_READ); |
| inbuf = map.data; |
| gst_buffer_unmap(buffer, &map); |
| beepdec->in_cnt++; |
| |
| GST_LOG_OBJECT (beepdec,"handle_frame [%d] BEGIN size=%d",beepdec->in_cnt,inbuf_size); |
| |
| |
| if(!strcmp(IDecoder->name,"mp3")) |
| twice = TRUE; |
| |
| if(!strcmp(IDecoder->name,"vorbis") && !beepdec->set_codec_data){ |
| if(beepdec->frame_cnt < VORBIS_HEADER_FRAME){ |
| temp_buffer = gst_buffer_new_allocate (NULL, inbuf_size, NULL); |
| temp_buffer = gst_buffer_make_writable (temp_buffer); |
| gst_buffer_fill (temp_buffer, 0, inbuf, inbuf_size); |
| gst_adapter_push (beepdec->adapter, temp_buffer); |
| beepdec->frame_cnt ++; |
| buffer = NULL; |
| beepdec->in_cnt--; |
| sent=TRUE; |
| ret = gst_audio_decoder_finish_frame (dec, NULL, 1); |
| goto bail; |
| }else if(beepdec->frame_cnt == VORBIS_HEADER_FRAME){ |
| UniACodecParameter parameter; |
| GstBuffer *codec_data; |
| GstMapInfo map; |
| |
| adapter_size = gst_adapter_available(beepdec->adapter); |
| codec_data = gst_adapter_take_buffer (beepdec->adapter, adapter_size); |
| |
| if ((codec_data) && gst_buffer_get_size (codec_data)) { |
| gst_buffer_map(codec_data, &map, GST_MAP_READ); |
| GST_INFO ("Set codec_data %" GST_PTR_FORMAT, codec_data); |
| parameter.codecData.size = map.size; |
| parameter.codecData.buf = map.data; |
| IDecoder->setDecoderPara(handle,UNIA_CODEC_DATA, ¶meter); |
| gst_buffer_unmap(codec_data, &map); |
| beepdec->set_codec_data = TRUE; |
| } |
| gst_adapter_clear (beepdec->adapter); |
| } |
| } |
| |
| |
| begin: |
| |
| do{ |
| if (beepdec->eos_sent == TRUE) { |
| break; |
| } |
| outbuf = NULL; |
| out_size = 0; |
| core_ret = IDecoder->decode(handle,inbuf,inbuf_size,&offset,&outbuf,&out_size); |
| |
| GST_LOG_OBJECT (beepdec,"decode RET=%x input size=%d,used size=%d,output_size=%d" |
| ,core_ret,inbuf_size,offset,out_size); |
| |
| if (ACODEC_ERROR_STREAM == core_ret) { |
| GST_WARNING("decode END error = %x\n", core_ret); |
| IDecoder->resetDecoder(handle); |
| //send null frame to delete the timestamp |
| beepdec->err_cnt ++; |
| if (beepdec->in_cnt != 0) |
| ret = gst_audio_decoder_finish_frame (dec, NULL, beepdec->in_cnt); |
| beepdec->in_cnt = 0; |
| sent = TRUE; |
| break; |
| }else if(core_ret == ACODEC_PROFILE_NOT_SUPPORT){ |
| beepdec->err_cnt += 4; |
| break; |
| }else if(core_ret == ACODEC_NOT_ENOUGH_DATA){ |
| break; |
| } else if(core_ret==ACODEC_INIT_ERR){ |
| /* ACODEC_INIT_ERR is a fatal error, no need to try decoding again. */ |
| ret = GST_FLOW_EOS; |
| beepdec->eos_sent = TRUE; |
| gst_pad_push_event (dec->srcpad, gst_event_new_eos ()); |
| GST_ERROR("core ret = ACODEC_INIT_ERR\n", core_ret); |
| goto bail; |
| } |
| |
| status = core_ret & CORE_STATUS_MASK; |
| |
| if (status == ACODEC_CAPIBILITY_CHANGE) { |
| beep_dec_handle_output_changed(beepdec); |
| } |
| |
| if(outbuf && out_size > 0){ |
| |
| temp_buffer = gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER (dec),out_size); |
| temp_buffer = gst_buffer_make_writable (temp_buffer); |
| gst_buffer_fill (temp_buffer, 0, outbuf, out_size); |
| gst_audio_buffer_reorder_channels (temp_buffer, beepdec->audio_format, |
| beepdec->outputformat.channels, beepdec->core_layout, beepdec->out_layout); |
| |
| g_free(outbuf); |
| if(beepdec->in_cnt > 1 ) |
| { |
| beepdec->in_cnt--; |
| ret = gst_audio_decoder_finish_frame (dec, temp_buffer, 1); |
| sent = TRUE; |
| GST_LOG_OBJECT (beepdec,"output one frame[%d] size=%d",beepdec->in_cnt,out_size); |
| }else{ |
| gst_adapter_push (beepdec->adapter, temp_buffer); |
| } |
| beepdec->err_cnt = 0; |
| |
| temp_buffer = NULL; |
| } |
| |
| |
| |
| } while (((status != ACODEC_NOT_ENOUGH_DATA) |
| && (status != ACODEC_END_OF_STREAM) && (((inbuf_size) |
| && (offset < inbuf_size)) || (inbuf_size == 0)))); |
| |
| #if 0 |
| if(outbuf && out_size > 0 && twice){ |
| twice = FALSE; |
| goto begin; |
| } |
| #endif |
| |
| adapter_size = gst_adapter_available (beepdec->adapter); |
| |
| if(adapter_size > 0){ |
| out = gst_adapter_take_buffer (beepdec->adapter, adapter_size); |
| beepdec->in_cnt--; |
| sent=TRUE; |
| ret = gst_audio_decoder_finish_frame (dec, out, 1); |
| gst_adapter_clear (beepdec->adapter); |
| GST_LOG_OBJECT (beepdec,"output frames[%d] size=%d",beepdec->in_cnt,adapter_size); |
| }else if (sent == FALSE && (!strcmp(IDecoder->name,"wma") ) ){ |
| beepdec->in_cnt--; |
| gst_audio_decoder_finish_frame (dec, NULL, 1); |
| sent=TRUE; |
| GST_LOG_OBJECT (beepdec,"beep_dec_parse_and_decode ret=%x",ret); |
| } |
| |
| if(beepdec->err_cnt > MAX_PROFILE_ERROR_COUNT) { |
| gst_pad_push_event (dec->srcpad, gst_event_new_eos ()); |
| beepdec->eos_sent = TRUE; |
| ret = GST_FLOW_EOS; |
| } |
| |
| bail: |
| return ret; |
| |
| } |
| static void beep_dec_flush (GstAudioDecoder * dec, gboolean hard) |
| { |
| GstBeepDec *beepdec; |
| |
| beepdec = GST_BEEP_DEC (dec); |
| |
| if (beepdec->beep_interface && beepdec->handle) { |
| if (beepdec->beep_interface->resetDecoder) { |
| beepdec->beep_interface->resetDecoder (beepdec->handle); |
| } |
| } |
| beepdec->in_cnt = 0; |
| beepdec->eos_sent = FALSE; |
| |
| GST_LOG_OBJECT (beepdec,"beep_dec_flush called "); |
| } |
| |