Files
BPI-CM9-Linux/aml-comp/multimedia/aml_audio_hal/audio_hal/audio_decoder.c
T

524 lines
19 KiB
C

/*
* Copyright (C) 2021 Amlogic Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "audio_hw_hal_decoder"
#include <string.h>
#include <inttypes.h>
#include <stdint.h>
#include <pthread.h>
#include "aml_audio_log.h"
#include "audio_hw.h"
#include "audio_hw_utils.h"
#include "audio_hwsync.h"
#include "aml_dec_api.h"
#include "aml_ddp_dec_api.h"
#include "aml_dtshd_dec_api.h"
#include "aml_dtsx_dec_api.h"
#include "audio_hw_resource_mgr.h"
#include "hdmirx_utils.h"
#include "aml_audio_output.h"
#include "aml_hw_mixer.h"
#include "audio_decoder.h"
static void ddp_decoder_config_prepare(struct audio_stream_out *stream, aml_dcv_config_t *ddp_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
struct aml_audio_device *adev = aml_out->dev;
adev->dcvlib_bypass_enable = 0;
ddp_config->digital_raw = AML_DEC_CONTROL_CONVERT;
if (aml_out->ad_substream_supported) {
ddp_config->decoding_mode = DDP_DECODE_MODE_AD_SUBSTREAM;
} else if (aml_out->dec_param.ad_mix_enable) {
ddp_config->decoding_mode = DDP_DECODE_MODE_AD_INDEPENDENT;
} else {
ddp_config->decoding_mode = DDP_DECODE_MODE_SINGLE;
}
if (aml_out->hal_internal_format == AUDIO_FORMAT_E_AC3) {
ddp_config->nIsEc3 = 1;
} else if (aml_out->hal_internal_format == AUDIO_FORMAT_AC3) {
ddp_config->nIsEc3 = 0;
}
ddp_config->is_pcmout_32bits = (adev->audio_process_bitwidth == 32);
AM_LOGI("digital_raw:%d, dual_output_flag:%d, IsEc3:%d, mode %d, %d. is_pcmout_32bits %d",
ddp_config->digital_raw, aml_out->dual_output_flag, ddp_config->nIsEc3, ddp_config->decoding_mode, aml_out->ad_substream_supported, ddp_config->is_pcmout_32bits);
}
static void dts_decoder_config_prepare(struct audio_stream_out *stream, aml_dec_config_t *dec_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
struct aml_audio_device *adev = aml_out->dev;
struct aml_arc_hdmi_desc *p_hdmi_descs = get_arc_hdmi_cap(adev);
int output_bitwidth = 16;
adev->dtslib_bypass_enable = 0;
{
int dts_out_ch;
bool non_spk = (adev->active_outport == OUTPORT_HEADPHONE) ||
(adev->active_outport == OUTPORT_A2DP) ||
(adev->active_outport == OUTPORT_HDMI_ARC);
bool vx_want_auto = adev->native_postprocess.libvx_exist && adev->effect_ctrl.vx_enable;
AM_LOGI("dts out-ch decide: outport=%s(%d) non_spk=%d libvx=%d vx_enable=%d dap_enable=%d "
"effect_mode=%d(1=OFF,2=DAP,3=VX,4=AUTO) virtualXlib1=%d vx_force_stereo=%d ms12_en=%d",
outputPort2Str(adev->active_outport), adev->active_outport, non_spk,
adev->native_postprocess.libvx_exist, adev->effect_ctrl.vx_enable,
adev->effect_ctrl.dap_enable, adev->effect_ctrl.effect_mode,
adev->effect_ctrl.virtualXlib1_state, adev->native_postprocess.vx_force_stereo,
(eDolbyMS12Lib == adev->dolby_lib_type && adev->ms12.dolby_ms12_enable));
if (non_spk) {
dts_out_ch = 2;
AM_LOGI("set dts output mode to stereo(2): reason=non-speaker outport");
} else if (vx_want_auto) {
dts_out_ch = 0;
AM_LOGI("set dts output mode to auto(0): reason=speaker + libvx && vx_enable");
} else {
dts_out_ch = 2;
AM_LOGI("set dts output mode to stereo(2): reason=speaker but libvx=%d vx_enable=%d",
adev->native_postprocess.libvx_exist, adev->effect_ctrl.vx_enable);
}
dca_set_out_ch_internal(dts_out_ch);
}
if (32 == adev->audio_process_bitwidth) {
output_bitwidth = 24; // DTS:X just support 16/24 bit pcm output.
} else {
output_bitwidth = 16;
}
if (adev->dts_lib_type == eDTSXLib) {
dec_config->dtsx_config.digital_raw = AML_DEC_CONTROL_CONVERT;
dec_config->dtsx_config.is_dtscd = aml_out->is_dtscd;
dec_config->dtsx_config.dev = (void *)adev;
dec_config->dtsx_config.stream = (void*)aml_out;
if (adev->hdmi_format == BYPASS) {
dec_config->dtsx_config.passthroug_enable = 1;
} else {
dec_config->dtsx_config.passthroug_enable = 0;
}
if ((adev->active_outport & OUTPORT_HDMI_ARC) != 0 || (adev->active_outport & AUDIO_DEVICE_OUT_HDMI) != 0) {
dec_config->dtsx_config.is_hdmi_output = 1;
} else {
dec_config->dtsx_config.is_hdmi_output = 0;
}
if (p_hdmi_descs->dtshd_fmt.is_support) {
dec_config->dtsx_config.sink_dev_type = p_hdmi_descs->dtshd_fmt.dts_vsdb_byte3;
} else {
dec_config->dtsx_config.sink_dev_type = 0; //CA(0),MA(1),P1(2),P2(4)
}
dec_config->dtsx_config.output_bw = output_bitwidth;
if (is_STB(adev)) {
dec_config->dtsx_config.device_type = STB;
if (p_hdmi_descs->pcm_fmt.max_channels == 8 && adev->hdmi_format == PCM) {
ALOGI("%s sink support multi-ch pcm, and dtsx decoder bus0 output multi-ch pcm when stream channel != 2", __func__);
dec_config->dtsx_config.sink_support_multich_pcm = true;
}
} else {
dec_config->dtsx_config.device_type = TV;
}
ALOGI("[%s:%d] digital_raw:%d dual_output:%d is_dtscd:%d bypass:%d hdmi_out:%d support_mch_pcm:%d output_bw:%d", __func__, __LINE__,
dec_config->dtsx_config.digital_raw, aml_out->dual_output_flag, dec_config->dtsx_config.is_dtscd,
dec_config->dtsx_config.passthroug_enable, dec_config->dtsx_config.is_hdmi_output,
dec_config->dtsx_config.sink_support_multich_pcm, dec_config->dtsx_config.output_bw);
} else if (adev->dts_lib_type == eDTSHDLib) {
dec_config->dca_config.digital_raw = AML_DEC_CONTROL_CONVERT;
dec_config->dca_config.dev = (void *)adev;
ALOGI("[%s:%d] digital_raw:%d, dual_output_flag:%d, is_dtscd:%d", __func__, __LINE__,
dec_config->dca_config.digital_raw, aml_out->dual_output_flag, dec_config->dca_config.is_dtscd);
} else {
ALOGE("[%s:%d] Without any dts library", __func__, __LINE__);
}
}
static void mad_decoder_config_prepare(struct audio_stream_out *stream, aml_mad_config_t *mad_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
mad_config->channel = aml_out->hal_ch;
mad_config->samplerate = aml_out->hal_rate;
mad_config->mpeg_format = aml_out->hal_format;
}
static void faad_decoder_config_prepare(struct audio_stream_out *stream, aml_faad_config_t *faad_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
faad_config->channel = aml_out->hal_ch;
faad_config->samplerate = aml_out->hal_rate;
faad_config->aac_format = aml_out->hal_format;
}
static void pcm_decoder_config_prepare(struct audio_stream_out *stream, aml_pcm_config_t *pcm_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
pcm_config->channel = aml_out->hal_ch;
pcm_config->samplerate = aml_out->hal_rate;
pcm_config->pcm_format = aml_out->hal_format;
}
static void adpcm_decoder_config_prepare(struct audio_stream_out *stream, aml_adpcm_config_t *adpcm_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
adpcm_config->channel = aml_out->hal_ch;
adpcm_config->samplerate = aml_out->hal_rate;
adpcm_config->pcm_format = aml_out->hal_format;
adpcm_config->block_size = aml_out->dec_param.hal_decode_block_size;
}
static void flac_decoder_config_prepare(struct audio_stream_out *stream, aml_flac_config_t *flac_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
flac_config->channel = aml_out->hal_ch;
flac_config->samplerate = aml_out->hal_rate;
flac_config->flac_format = aml_out->hal_format;
}
static void vorbis_decoder_config_prepare(struct audio_stream_out *stream, aml_vorbis_config_t *vorbis_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
vorbis_config->channel = aml_out->hal_ch;
vorbis_config->samplerate = aml_out->hal_rate;
vorbis_config->vorbis_format = aml_out->hal_format;
}
static void opus_decoder_config_prepare(struct audio_stream_out *stream, aml_opus_config_t *opus_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
opus_config->channel = aml_out->hal_ch;
opus_config->samplerate = aml_out->hal_rate;
opus_config->opus_format = aml_out->hal_format;
}
static void mpegh_decoder_config_prepare(struct audio_stream_out *stream, aml_mpegh_config_t *mpegh_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
struct aml_audio_device *adev = aml_out->dev;
if (eDolbyMS12Lib == adev->dolby_lib_type) {
mpegh_config->channel = 6;
} else {
mpegh_config->channel = 2;
}
mpegh_config->samplerate = aml_out->hal_rate;
mpegh_config->mpegh_format = aml_out->hal_internal_format;
}
static int aml_decoder_config_prepare(struct audio_stream_out *stream, audio_format_t format, aml_dec_config_t *dec_config)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
struct aml_audio_device *adev = aml_out->dev;
if (eDolbyMS12Lib != adev->dolby_lib_type && aml_out->source_type == DTV_SRC) {
dec_config->dec_left_gain = dec_config->dec_right_gain = aml_out->dec_param.dec_gain;
dec_config->ad_mixing_enable = aml_out->dec_param.ad_mix_enable;
dec_config->mixer_level = aml_out->dec_param.mixing_level;
dec_config->advol_level = aml_out->dec_param.advol_level;
AM_LOGI("out %p, mixer_level %d ad_mixing_enable %d advol %d. dec gain %f", aml_out, dec_config->mixer_level, dec_config->ad_mixing_enable, dec_config->advol_level, dec_config->dec_left_gain);
} else {
dec_config->dec_left_gain = dec_config->dec_right_gain = 1.0;
}
#ifdef NO_SERVER
adev->decoder_drc_control = aml_audio_get_drc_control(&adev->alsa_mixer);
#endif
dec_config->drc_control = adev->decoder_drc_control;
dec_config->downmix_type = adev->downmix_type;
switch (format) {
case AUDIO_FORMAT_AC3:
case AUDIO_FORMAT_E_AC3: {
ddp_decoder_config_prepare(stream, &dec_config->dcv_config);
break;
}
case AUDIO_FORMAT_DTS:
case AUDIO_FORMAT_DTS_HD: {
dts_decoder_config_prepare(stream, dec_config);
break;
}
case AUDIO_FORMAT_PCM_16_BIT:
case AUDIO_FORMAT_PCM_32_BIT:
case AUDIO_FORMAT_PCM_8_BIT:
case AUDIO_FORMAT_PCM_8_24_BIT:
case AUDIO_FORMAT_PCM_LPCM_DVD:
case AUDIO_FORMAT_PCM_LPCM_1394:
case AUDIO_FORMAT_PCM_LPCM_BLURAY: {
pcm_decoder_config_prepare(stream, &dec_config->pcm_config);
break;
}
case AUDIO_FORMAT_PCM_ADPCM_IMA_WAV: {
adpcm_decoder_config_prepare(stream, &dec_config->adpcm_config);
break;
}
case AUDIO_FORMAT_MP3:
case AUDIO_FORMAT_MP2: {
mad_decoder_config_prepare(stream, &dec_config->mad_config);
break;
}
case AUDIO_FORMAT_AAC:
case AUDIO_FORMAT_AAC_LATM:
case AUDIO_FORMAT_HE_AAC_V1:
case AUDIO_FORMAT_HE_AAC_V2:{
faad_decoder_config_prepare(stream, &dec_config->faad_config);
break;
}
case AUDIO_FORMAT_FLAC: {
flac_decoder_config_prepare(stream, &dec_config->flac_config);
break;
}
case AUDIO_FORMAT_OPUS: {
opus_decoder_config_prepare(stream, &dec_config->opus_config);
break;
}
case AUDIO_FORMAT_VORBIS: {
vorbis_decoder_config_prepare(stream, &dec_config->vorbis_config);
break;
}
case AUDIO_FORMAT_MPEGH:
case AUDIO_FORMAT_MPEGH_BL_L3:
case AUDIO_FORMAT_MPEGH_BL_L4:
case AUDIO_FORMAT_MPEGH_LC_L3:
case AUDIO_FORMAT_MPEGH_LC_L4: {
mpegh_decoder_config_prepare(stream, &dec_config->mpegh_config);
break;
}
default:
break;
}
return 0;
}
int dca_get_out_ch_internal(void)
{
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
if (!adev)
return -1;
if (adev->dts_lib_type == eDTSXLib) {
return dtsx_get_out_ch_internal();
} else {
return dtshd_get_out_ch_internal();
}
}
uint32_t dca_get_out_chmask_internal(void)
{
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
if (!adev)
return -1;
if (adev->dts_lib_type == eDTSXLib) {
return dtsx_get_out_chmask_internal();
} else {
return dtshd_get_out_chmask_internal();
}
}
int dca_set_out_ch_internal(int ch_num)
{
struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle();
if (!adev)
return -1;
AM_LOGI("dca_set_out_ch_internal ch=%d(%s) dts_lib=%d vx_enable=%d effect_mode=%d",
ch_num, (ch_num == 0) ? "auto" : (ch_num == 2) ? "stereo" : "other",
adev->dts_lib_type, adev->effect_ctrl.vx_enable, adev->effect_ctrl.effect_mode);
if (adev->dts_lib_type == eDTSXLib) {
return dtsx_set_out_ch_internal(ch_num);
} else {
return dtshd_set_out_ch_internal(ch_num);
}
}
int audio_decoder_release(struct audio_stream_out *stream)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
if (!aml_out || !aml_out->aml_dec) {
return 0;
}
aml_decoder_release(aml_out->aml_dec);
aml_out->aml_dec = NULL;
return 0;
}
int audio_decoder_init(struct audio_stream_out *stream)
{
struct aml_stream_out *aml_out = NULL;
struct aml_audio_device *adev = NULL;
int ret = -1;
if (!stream) {
AM_LOGE("invalid stream %p", stream);
return -1;
}
aml_out = (struct aml_stream_out *)stream;
adev = aml_out->dev;
memset(&aml_out->dec_config, 0, sizeof(aml_dec_config_t));
AM_LOGI("+<in>");
ret = aml_decoder_config_prepare(stream, aml_out->hal_internal_format, &aml_out->dec_config);
if (ret < 0) {
AM_LOGE("config decoder error");
return ret;
}
ret = aml_decoder_init(&aml_out->aml_dec, aml_out->hal_internal_format, (aml_dec_config_t *)&aml_out->dec_config);
if (ret < 0) {
AM_LOGE("aml_decoder_init failed");
return ret;
}
if (eDolbyMS12Lib != adev->dolby_lib_type) {
pthread_mutex_lock(&adev->lock);
/*coverity[missing_lock]:adev->hw_mixer.start_buf does not need to be lock*/
if (!adev->hw_mixer.start_buf) {
aml_hw_mixer_init(&adev->hw_mixer);
} else {
aml_hw_mixer_reset(&adev->hw_mixer);
}
pthread_mutex_unlock(&adev->lock);
}
AM_LOGI("-<out> init success! ret=%d", ret);
return ret;
}
int audio_decoder_reset(struct audio_stream_out *stream)
{
int ret = -1;
audio_decoder_release(stream);
aml_audio_spdif_output_close_all();
ret = audio_decoder_init(stream);
AM_LOGI("ret=%d", ret);
return ret;
}
int audio_decoder_process(struct audio_stream_out *stream,
struct audio_buffer *abuffer,
audio_decoder_frame_cb cb)
{
struct aml_stream_out *aml_out = (struct aml_stream_out *)stream;
struct aml_audio_device *adev = NULL;
aml_dec_t *aml_dec = NULL;
struct audio_buffer ainput;
int decoder_ret = 0;
int used_size = 0;
int left_bytes = 0;
bool try_again = false;
int cb_ret = 0;
if (!stream) {
AM_LOGE("invalid stream %p", stream);
return -1;
}
if (false == check_audio_buffer_valid(abuffer)) {
AM_LOGE("audio buffer checked fail");
return -1;
}
adev = aml_out->dev;
if (NULL == aml_out->aml_dec) {
decoder_ret = audio_decoder_init(stream);
if (decoder_ret < 0) {
AM_LOGE("audio_decoder_init fail");
return decoder_ret;
}
}
aml_dec = aml_out->aml_dec;
if (!aml_dec) {
return -1;
}
memcpy(&ainput, abuffer, sizeof(ainput));
if (!ainput.b_pts_valid ||
(ainput.pts == aml_dec->last_in_frame_pts) ||
(ainput.pts == NULL_INT64) ||
(ainput.pts == HWSYNC_PTS_NA)) {
AM_LOGI_IF(adev->debug_flag, "pts_valid:%d ainput.pts(0x%"PRIx64")->out_frame_pts(0x%"PRIx64")",
abuffer->b_pts_valid, ainput.pts, aml_dec->out_frame_pts);
ainput.pts = aml_dec->out_frame_pts;
ainput.b_pts_valid = false;
}
aml_dec->last_in_frame_pts = abuffer->pts;
left_bytes = ainput.size;
do {
try_again = false;
ainput.buffer = (const uint8_t *)ainput.buffer + used_size;
ainput.size = left_bytes;
used_size = 0;
AM_LOGI_IF(adev->debug_flag, "dec: %p, in pts:0x%"PRIx64" (%"PRIu64" ms) size: %"PRId32"",
aml_dec, ainput.pts, ainput.pts / 90, ainput.size);
if (cb) {
cb_ret = cb(stream, aml_dec, &ainput, AUDIO_DECODER_PRE);
if (cb_ret < 0) {
break;
}
}
decoder_ret = aml_decoder_process(aml_dec, &ainput, &used_size);
if (decoder_ret == AML_DEC_RETURN_TYPE_CACHE_DATA) {
AM_LOGI_IF(adev->debug_flag, "cache the data to decode");
return abuffer->size;
} else if (decoder_ret == AML_DEC_RETURN_TYPE_FAIL) {
AM_LOGI_IF(adev->debug_flag, "aml_decoder_process error, ret:%d", decoder_ret);
break;
}
left_bytes -= used_size;
aml_out->consumedsize += used_size;
ainput.b_pts_valid = false;
aml_dec->out_frame_pts = ainput.pts;
AM_LOGI_IF(adev->debug_flag, "out pts:0x%"PRIx64" (%"PRIu64" ms) pcm len=%d raw len=%d used_size %d left_bytes=%d",
aml_dec->dec_pcm_data.pts, aml_dec->dec_pcm_data.pts / 90,
aml_dec->dec_pcm_data.data_len, aml_dec->dec_raw_data.data_len,
used_size, left_bytes);
if (cb) {
cb_ret = cb(stream, aml_dec, &ainput, decoder_ret);
if (cb_ret < 0) {
break;
}
}
if ((abuffer->format == AUDIO_FORMAT_DTS ||
abuffer->format == AUDIO_FORMAT_DTS_HD) &&
decoder_ret == AML_DEC_RETURN_TYPE_NEED_DEC_AGAIN) {
try_again = true;
}
} while (((left_bytes > 0) || aml_dec->fragment_left_size || try_again) && !aml_out->fast_quit);
return abuffer->size;
}