/* * 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 #include #include #include #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("+"); 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("- 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; }