blob: fbece935c7802ff1f236775ec19e600355b7c5bc [file] [log] [blame] [edit]
/*
* 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, &parameter);
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, &parameter);
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, &parameter);
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, &parameter);
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, &parameter);
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, &parameter);
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, &parameter);
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, &parameter);
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, &parameter);
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, &parameter);
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,&parameter);
if (!memcmp (&parameter.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,&parameter);
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,&parameter);
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, &parameter);
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 ");
}