/* * Copyright (C) 2018 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_input_dtv" //#define LOG_NDEBUG 0 #include #include "aml_audio_log.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if ANDROID_PLATFORM_SDK_VERSION >= 25 // 8.0 #include #endif #include #include #include "aml_config_data.h" #include "aml_audio_stream.h" #include "dtv_patch.h" #include "audio_hw_profile.h" #include "audio_hw_utils.h" //#include "dtv_patch_out.h" #include "aml_audio_resampler.h" #if defined(MS12_V24_ENABLE) #include "audio_hw_ms12_v2.h" #endif #include "alsa_config_parameters.h" #include "alsa_device_parser.h" #include "dtv_patch_hal_avsync.h" #include "aml_audio_spdifout.h" #include "aml_audio_timer.h" #include "aml_volume_utils.h" #include "dmx_audio_es.h" #include "aml_ddp_dec_api.h" #include "dtv_patch_utils.h" #include "audio_hw_ms12_common.h" #include "audio_hw_resource_mgr.h" #include "tv_patch.h" #include "dtv_private_object.h" #include "aml_dump_debug.h" #include "audio_mediasync.h" #include "device_patch.h" #include "aml_audio_property.h" #include "aml_audio_parser.h" #define cmd_str(cmd) dtvAudioCmd2Str(cmd) #define state_str(state) dtvAudioState2Str(state) static int create_dtv_output_stream_thread(dtv_path_t *path); static int create_dtv_ad_output_stream_thread(dtv_path_t *path); static int release_dtv_output_stream_thread(dtv_path_t *path); static int dtv_path_cmd_process_thread(void *data); static void dtv_update_ad_vol_parameter(dtv_path_t *path); static void dtv_update_pan_fade_parameter(dtv_path_t *path); static void dtv_update_decoder_params(dtv_path_t *path); static int dtv_create_ad_mixbuf(struct aml_stream_out *aml_out); static void dtv_release_ad_mixbuf(struct aml_stream_out *aml_out); static void dmx_enable_main(aml_demux_audiopara_t *demux_info, bool enable) { if (enable) { struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); if (!aml_dev) { AM_LOGE("get adev error"); return; } demux_info->is_multi_dmx = is_dtv_multi_demux(aml_dev); Open_Dmx_Audio(&demux_info->demux_handle, demux_info, dtv_queue_es_info, aml_audio_get_debug_flag()); Init_Dmx_Main_Audio(demux_info->demux_handle, demux_info->main_fmt, demux_info->main_pid); Start_Dmx_Main_Audio(demux_info->demux_handle); } else { Stop_Dmx_Main_Audio(demux_info->demux_handle); Destroy_Dmx_Main_Audio(demux_info->demux_handle); if (demux_info->ad_dmx_init) { Stop_Dmx_AD_Audio(demux_info->demux_handle); Destroy_Dmx_AD_Audio(demux_info->demux_handle); demux_info->ad_dmx_init = false; } Close_Dmx_Audio(demux_info->demux_handle); } } static int dmx_enable_ad(aml_demux_audiopara_t *demux_info) { if (demux_info && demux_info->demux_handle && VALID_PID(demux_info->ad_pid)) { if (demux_info->is_multi_dmx) { if (aml_audio_property_get_bool("vendor.media.dtv.pesmode", true)) { if (VALID_AD_FMT_UK(demux_info->ad_fmt)) { Init_Dmx_AD_Audio(demux_info->demux_handle, demux_info->ad_fmt, demux_info->ad_pid, 1); } else { Init_Dmx_AD_Audio(demux_info->demux_handle, demux_info->ad_fmt, demux_info->ad_pid, 0); } } else { Init_Dmx_AD_Audio(demux_info->demux_handle, demux_info->ad_fmt, demux_info->ad_pid, 0); } } else { Init_Dmx_AD_Audio(demux_info->demux_handle, demux_info->ad_fmt, demux_info->ad_pid, 1); } demux_info->ad_dmx_init = true; return Start_Dmx_AD_Audio(demux_info->demux_handle); } return -1; } static bool check_ad_enable(dtv_path_t * path) { aml_demux_audiopara_t *demux_info = &path->demux_info; if (demux_info && VALID_PID(demux_info->ad_pid) && demux_info->associate_audio_mixing_enable) { return true; } else { return false; } } static int audio_dtv_ad_start_dynamic(dtv_path_t *path) { aml_demux_audiopara_t *demux_info = &path->demux_info; int ad_start_flag = -1; if (demux_info->main_fmt == ACODEC_FMT_AC4 || demux_info->ad_fmt == ACODEC_FMT_AC4) { return -1; } if (demux_info && demux_info->demux_handle && (false == demux_info->ad_dmx_init) && check_ad_enable(path)) { ad_start_flag = dmx_enable_ad(demux_info); /*coverity[ATOMICITY]: just ALOGI print*/ AM_LOGI("[] dmx_handle %p, ad_pid:%d, ad_start_flag %d", demux_info->demux_handle, demux_info->ad_pid, ad_start_flag); } if (demux_info->ad_dmx_init && path->ad_output_thread_created == 0) { create_dtv_ad_output_stream_thread(path); } return 0; } static int audio_dtv_output_start(dtv_path_t *path) { if (path && path->demux_info.demux_handle) { create_dtv_output_stream_thread(path); audio_dtv_ad_start_dynamic(path); } return 0; } static int audio_dtv_output_pause(dtv_path_t *path, int cmd) { if (path) { struct aml_stream_out *aml_out = path->dtv_aml_out; if (aml_out && aml_out->avsync_ctx && (AVSYNC_TYPE_MEDIASYNC == aml_out->avsync_type) && aml_out->avsync_ctx->mediasync_ctx) { if (cmd == AUDIO_DTV_PATCH_CMD_PAUSE) { mediasync_wrap_setPause(aml_out->avsync_ctx->mediasync_ctx->handle, true); AM_LOGI("now end pause the audio decoder"); } else if (cmd == AUDIO_DTV_PATCH_CMD_RESUME) { mediasync_wrap_setPause(aml_out->avsync_ctx->mediasync_ctx->handle, false); AM_LOGI("now end resume the audio decoder"); } return 0; } AM_LOGE("aml_out is %p", aml_out); return -1; } AM_LOGE("path is %p", path); return -1; } static int audio_dtv_output_stop(dtv_path_t *path) { struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); if (!aml_dev || !path) { AM_LOGE("get adev(%p) or path(%p) error", aml_dev, path); return -1; } /*coverity[sleep]*/ dtv_do_ease_out(aml_dev); release_dtv_output_stream_thread(path); return 0; } static int path_thread_get_cmd(struct dtv_path *path, int *cmd, int *path_id) { if (path == NULL || path->dtv_cmd_list->initd == 0) { return -1; } if (dtv_cmdlist_is_empty(path->dtv_cmd_list) == 1) { return -1; } else { return dtv_cmdlist_get_cmd(path->dtv_cmd_list, cmd, path_id); } } int dtv_patch_handle_event(struct audio_hw_device *dev,int cmd, int val) { struct aml_audio_device *adev = (struct aml_audio_device *) dev; struct aml_audio_patch *patch = get_dev_patch_by_source(adev, SRC_DTV); float dtv_volume_switch = 1.0; unsigned int path_id = val >> DVB_DEMUX_ID_BASE; if (NULL == patch) { AM_LOGE("patch NULL error, need create patch first!!"); goto exit; } if (path_id >= DVB_DEMUX_SUPPORT_MAX_NUM) { AM_LOGW("path_id %d is invalid !",path_id); goto exit; } dtv_path_t *path = &patch->dtv_path[path_id]; aml_demux_audiopara_t *demux_info = &path->demux_info; val = val & ((1 << DVB_DEMUX_ID_BASE) - 1); AM_LOGI("patch %p, path %p, path_id %d, cmd %d(%s), val %d", patch,path, path_id, cmd, cmd_str(cmd), val); switch (cmd) { case AUDIO_DTV_PATCH_CMD_SET_MEDIA_SYNC_ID: adev->hbbtvsync_info.sync_id = demux_info->media_sync_id = val; for (int i = 0; i < STREAM_NUM_MAX; i++) { struct aml_stream_out *aml_out = adev->active_outputs[i]; if (aml_out && aml_out->flags & AUDIO_OUTPUT_FLAG_HBBTV && aml_out->avsync_ctx) { if (aml_out->avsync_ctx->mediasync_ctx && val > 0 && (val != aml_out->avsync_ctx->mediasync_ctx->mediasync_id || !aml_out->avsync_ctx->mediasync_ctx->handle)) { int sync_enable = 0; AM_LOGI("hbbtv out %p, old id %d", aml_out, aml_out->avsync_ctx->mediasync_ctx->mediasync_id); hwsync_mediasync_outset(aml_out, &sync_enable, val, aml_out->hal_format); } } } AM_LOGI("demux_info->media_sync_id %d", demux_info->media_sync_id); break; case AUDIO_DTV_PATCH_CMD_SET_OUTPUT_MODE: AM_LOGI("DTV sound mode %d ", val); adev->sound_track_mode = val; break; case AUDIO_DTV_PATCH_CMD_SET_MUTE: AM_LOGE ("TV-Mute:%d.", val); demux_info->mute = val; break; case AUDIO_DTV_PATCH_CMD_SET_VOLUME: dtv_volume_switch = (float)val / 100; // val range is [0, 100], conversion range is [0, 1] if (dtv_volume_switch >= 0.0f && dtv_volume_switch <= 1.0f) { demux_info->volume = dtv_volume_switch; AM_LOGI ("dtv set volume:%f", dtv_volume_switch); } else { AM_LOGE("dtv set volume error! volume:%f", dtv_volume_switch); } break; case AUDIO_DTV_PATCH_CMD_SET_HAS_VIDEO: demux_info->has_video = val; AM_LOGI("has_video %d",demux_info->has_video); break; case AUDIO_DTV_PATCH_CMD_SET_DEMUX_INFO: demux_info->demux_id = val; AM_LOGI("demux_id %d",demux_info->demux_id); break; case AUDIO_DTV_PATCH_CMD_SET_SECURITY_MEM_LEVEL: demux_info->security_mem_level = val; AM_LOGI("security_mem_level set to %d", demux_info->security_mem_level); break; case AUDIO_DTV_PATCH_CMD_SET_PID: demux_info->main_pid = val; AM_LOGI("main_pid %d(0x%x)",demux_info->main_pid, val); break; case AUDIO_DTV_PATCH_CMD_SET_FMT: demux_info->main_fmt = val; AM_LOGI("main_fmt %d",demux_info->main_fmt); break; case AUDIO_DTV_PATCH_CMD_SET_AD_FMT: demux_info->ad_fmt = val; AM_LOGI("ad_fmt %d",demux_info->ad_fmt); break; case AUDIO_DTV_PATCH_CMD_SET_AD_PID: demux_info->ad_pid = val; AM_LOGI("ad_pid %d(0x%x)",demux_info->ad_pid, val); break; case AUDIO_DTV_PATCH_CMD_SET_AD_ENABLE: demux_info->associate_audio_mixing_enable = val; AM_LOGI("associate_audio_mixing_enable set to %d", demux_info->associate_audio_mixing_enable); pthread_mutex_lock(&path->dtv_cmd_process_mutex); dtv_cmdlist_add_cmd(path->dtv_cmd_list, AUDIO_DTV_PATCH_CMD_SET_AD_ENABLE, path_id); pthread_cond_signal(&path->dtv_cmd_process_cond); pthread_mutex_unlock(&path->dtv_cmd_process_mutex); break; case AUDIO_DTV_PATCH_CMD_SET_AD_VOL_LEVEL: if (val < 0) { /* coverity[deadcode]:this is just error checking and should not be executed here */ val = 0; } else if (val > 100) { val = 100; } adev->advol_level = val; AM_LOGI("advol set to %d", adev->advol_level); break; case AUDIO_DTV_PATCH_CMD_SET_AD_MIX_LEVEL: if (val < 0) { /* coverity[deadcode]:this is just error checking and should not be executed here */ val = 0; } else if (val > 100) { val = 100; } adev->mixing_level = (val * 64 - 32 * 100) / 100; //[0,100] mapping to [-32,32] AM_LOGI("mixing_level set to %d", adev->mixing_level); break; case AUDIO_DTV_PATCH_CMD_SET_MEDIA_PRESENTATION_ID: demux_info->media_presentation_id = val; AM_LOGI("media_presentation_id %d", demux_info->media_presentation_id); if (path->dtv_aml_out && eDolbyMS12Lib == adev->dolby_lib_type) { set_ms12_ac4_presentation_group_index((struct audio_stream_out *)path->dtv_aml_out, demux_info->media_presentation_id); } break; case AUDIO_DTV_PATCH_CMD_SET_MEDIA_FIRST_LANG: demux_info->media_first_lang = val; char first_lang[4] = {0}; language_code_convert_to_string(demux_info->media_first_lang, first_lang); AM_LOGI("media_first_lang %s,%x,%d",first_lang,val,val); if (path->dtv_aml_out && eDolbyMS12Lib == adev->dolby_lib_type) { set_ms12_ac4_1st_preferred_language_code((struct audio_stream_out *)path->dtv_aml_out, first_lang); } break; case AUDIO_DTV_PATCH_CMD_SET_MEDIA_SECOND_LANG: demux_info->media_second_lang = val; char second_lang[4] = {0}; language_code_convert_to_string(demux_info->media_second_lang, second_lang); AM_LOGI("media_second_lang %s,%x,%d",second_lang, val, val); if (path->dtv_aml_out && eDolbyMS12Lib == adev->dolby_lib_type) { set_ms12_ac4_2nd_preferred_language_code((struct audio_stream_out *)path->dtv_aml_out, second_lang); } break; case AUDIO_DTV_PATCH_CMD_CONTROL: if (val <= AUDIO_DTV_PATCH_CMD_NULL || val > AUDIO_DTV_PATCH_CMD_NUM) { AM_LOGW("Unsupported dtv cmd:%d", val); break; } AM_LOGI("Send dtv path(%d) cmd:%s, cmd_process create %d", path_id, cmd_str(val), path->cmd_process_thread_created); if (val == AUDIO_DTV_PATCH_CMD_OPEN) { if (!path->cmd_process_thread_created) { path->cmd_process_thread_exit = 0; int ret = pthread_create(&(path->audio_cmd_process_threadID), NULL, (void *)dtv_path_cmd_process_thread, path); if (ret != 0) { AM_LOGE("Create process thread fail!"); goto exit; } path->cmd_process_thread_created = 1; } else { AM_LOGW("cmd_process already create %d!", path->cmd_process_thread_created); } } if (path->cmd_process_thread_created) { pthread_mutex_lock(&path->dtv_cmd_process_mutex); dtv_cmdlist_add_cmd(path->dtv_cmd_list, val, path_id); pthread_cond_signal(&path->dtv_cmd_process_cond); pthread_mutex_unlock(&path->dtv_cmd_process_mutex); } else { AM_LOGW("no cmd_process thread %d, exit %d!", path->cmd_process_thread_created, path->cmd_process_thread_exit); } if (val == AUDIO_DTV_PATCH_CMD_CLOSE && path->cmd_process_thread_created) { pthread_join(path->audio_cmd_process_threadID, NULL); path->cmd_process_thread_created = 0; } break; case AUDIO_DTV_PATCH_CMD_SET_DECODER_ID: path->dtv_path_decoder_id = val; if (path->dtv_aml_out) { path->dtv_aml_out->decoder_id = val; AM_LOGI ("set audio stream(%p) decoder_id:%d ", path->dtv_aml_out, path->dtv_aml_out->decoder_id); } else { path->pending_decoder_id = true; AM_LOGI ("stream is not open, set decoder_id after stream is opened."); } break; default: AM_LOGI("invalid cmd %d", cmd); } return 0; exit: AM_LOGI("failed "); return -1; } static void *audio_dtv_path_output_threadloop(void *data) { //0.prepare variable dtv_path_t *path = (dtv_path_t *)data; struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); if (!aml_dev || !path) { AM_LOGE("get adev(%p) or path(%p) error", aml_dev, path); goto exit_open; } struct audio_stream_out *stream_out = NULL; struct aml_stream_out *aml_out = NULL; struct audio_config stream_config; int ret = -1, nRet = 0, sync_enable = 0; struct mAudioEsDataInfo *mEsData = NULL; aml_demux_audiopara_t *demux_info = (aml_demux_audiopara_t *)(&path->demux_info); void *demux_handle = demux_info->demux_handle; char thread_name[128] = { 0 }; struct aml_hw_mixer mixer_handle = {0}; struct mixer_buf mixer_buffer = {0}; //set name for this thread snprintf(thread_name, 128, "dtv_output_data_%d", demux_info->main_pid); prctl(PR_SET_NAME, (unsigned long)thread_name); AM_LOGI("created. path %p, path_id %d, pid %d(0x%x), demux_handle %p, demux_info %p, thread id:%ld", path, path->path_id, demux_info->main_pid, demux_info->main_pid, demux_handle, demux_info, pthread_self()); aml_set_thread_priority(thread_name, path->audio_output_threadID, 3); //1.prepare a output stream stream_config.sample_rate = 48000; stream_config.channel_mask = AUDIO_CHANNEL_OUT_STEREO; stream_config.format = dmx_fmt_convert2_hal_fmt(demux_info->main_fmt); if (aml_dev->hw_device.open_output_stream) { ret = aml_dev->hw_device.open_output_stream((struct audio_hw_device *)aml_dev, 0, AUDIO_DEVICE_OUT_SPEAKER, // devices_t AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_HW_AV_SYNC, // flags &stream_config, &stream_out, "AML_DTV_SOURCE"); } if (ret < 0 || !stream_out) { AM_LOGE("open output stream fail, ret = %d, out = %p. open func: %p", ret, stream_out, aml_dev->hw_device.open_output_stream); goto exit_open; } aml_out = (struct aml_stream_out *)stream_out; path->dtv_aml_out = aml_out; if (path->pending_decoder_id) { aml_out->decoder_id = path->dtv_path_decoder_id; path->pending_decoder_id = false; AM_LOGI("set stream(%p) deocer_id:%d", aml_out, path->dtv_path_decoder_id); } //init decoder-related param, they would be used when config dec later aml_out->dec_param.ad_mix_enable = demux_info->associate_audio_mixing_enable; aml_out->dec_param.advol_level = aml_dev->advol_level;//to init ms12 and dcv decoder aml_out->dec_param.mixing_level = aml_dev->mixing_level; if (eDolbyMS12Lib != aml_dev->dolby_lib_type && aml_out->hal_format != AUDIO_FORMAT_AC3 && aml_out->hal_format != AUDIO_FORMAT_E_AC3) { aml_out->dec_param.dec_gain = demux_info->associate_audio_mixing_enable ? dtv_ad_mixlevel_vol_convert_to_gain(aml_out) : 1.0;//to init mp3/aac decoder gain } if (aml_out->hal_format == AUDIO_FORMAT_AC4) { aml_out->dec_param.ac4_presentation_id = demux_info->media_presentation_id; language_code_convert_to_string(demux_info->media_first_lang, aml_out->dec_param.ac4_1st_lang); language_code_convert_to_string(demux_info->media_second_lang, aml_out->dec_param.ac4_2nd_lang); } //init mediasync for this output stream if (aml_out->avsync_ctx) { aml_out->avsync_type = AVSYNC_TYPE_MEDIASYNC; hwsync_mediasync_outset(aml_out, &sync_enable, demux_info->media_sync_id, demux_info->main_fmt); if (NULL == aml_out->avsync_ctx->mediasync_ctx || NULL == aml_out->avsync_ctx->mediasync_ctx->handle) { goto exit_sync; } } else { AM_LOGE("aml_out->avsync_ctx NULL error!"); goto exit_sync; } AM_LOGI("create output stream(%p) success, thread_exit %d. sync_enable %d.avsync ctx %p, mediasync ctx %p, handle %p", aml_out, path->output_thread_exit, sync_enable, aml_out->avsync_ctx, aml_out->avsync_ctx->mediasync_ctx, aml_out->avsync_ctx->mediasync_ctx->handle); while (!path->output_thread_exit) { if (aml_out->external_mute != aml_dev->dtv_mute) { out_set_stream_mute((struct audio_stream_out *)aml_out, aml_dev->patch_mute, true); } if (path->dtv_decoder_state == AUDIO_DTV_PATCH_DECODER_STATE_PAUSE) { usleep(1000); continue; } if (demux_info->associate_audio_mixing_enable && VALID_AD_FMT(demux_info->main_fmt)) { if (aml_out->mix_buf == NULL) { aml_out->mix_buf = &mixer_buffer; dtv_create_ad_mixbuf(aml_out); } if (eDolbyMS12Lib != aml_dev->dolby_lib_type && aml_out->hal_format != AUDIO_FORMAT_AC3 && aml_out->hal_format != AUDIO_FORMAT_E_AC3) { if (aml_out->mixer_handle == NULL) { aml_out->mixer_handle = &mixer_handle; aml_hw_mixer_init(aml_out->mixer_handle); } } } //get data from es queue nRet = Get_MainAudio_Es(demux_handle, &mEsData); if (nRet != AM_AUDIO_Dmx_SUCCESS || !mEsData) { AM_LOGI_IF(aml_dev->debug_flag, "nRet %d, mEsData %p", nRet, mEsData); if (mEsData) { path->pts_dts_flag = mEsData->pts_dts_flag; } usleep(5000); goto free_data; } else { AM_LOGI_IF(aml_dev->debug_flag, "%p, main size %d, pts %"PRIx64"(%"PRIx64"), diff %"PRId64"ms, pts flag %d", mEsData, mEsData->size, mEsData->pts, path->last_valid_main_pts, (mEsData->pts - path->last_valid_main_pts)/90, mEsData->pts_dts_flag); path->pts_dts_flag = mEsData->pts_dts_flag; // if first package pts is invalid, we need drop it if ((!path->dtv_first_apts_flag) && (NULL_INT64 == mEsData->pts)) { AM_LOGE("first data pts is NULL_INT64"); goto free_data; } if ((!mEsData->data || !mEsData->size)) { AM_LOGE("current data invalid"); goto free_data; } path->dtv_first_apts_flag = 1; if (NULL_INT64 != mEsData->pts) { path->last_valid_main_pts = mEsData->pts; pthread_mutex_lock(&path->apts_cal_mutex); pthread_cond_signal(&path->apts_cond); pthread_mutex_unlock(&path->apts_cal_mutex); } else { mEsData->pts = aml_out->next_parsered_pts; } if (get_debug_value(AML_DUMP_AUDIOHAL_DTV)) { aml_dump_audio_bitstreams_with_id("dtv_main_audio_dmx.es", mEsData->data, mEsData->size, aml_out->unique_ID); } } //apply pts if ((aml_out->avsync_ctx) && (aml_out->avsync_ctx->mediasync_ctx)) { pthread_mutex_lock(&(aml_out->avsync_ctx->lock)); aml_out->avsync_ctx->mediasync_ctx->in_apts = mEsData->pts; pthread_mutex_unlock(&(aml_out->avsync_ctx->lock)); } if (!aml_out->aml_dec && aml_out->hal_format == AUDIO_FORMAT_E_AC3 && aml_dev->dolby_lib_type == eDolbyDcvLib) { aml_out->ad_substream_supported = is_ad_substream_supported((unsigned char *)mEsData->data, mEsData->size); } dtv_update_decoder_params(path); //decode data ret = stream_out->write(stream_out, mEsData->data, mEsData->size); if (ret < 0) { AM_LOGE("stream_out->write ret %d, need check!!", ret); } if (aml_out->parser_frame_size) { Set_Audio_ES_Frame_Size(demux_handle, aml_out->parser_frame_size); } free_data: dtv_es_data_free(mEsData); mEsData = NULL; } exit_sync: AM_LOGI("path(%d) exit %d", path->path_id, path->output_thread_exit); if (aml_out->mixer_handle) { aml_hw_mixer_deinit(aml_out->mixer_handle); aml_out->mixer_handle = NULL; } if (aml_out->mix_buf) { dtv_release_ad_mixbuf(aml_out); aml_out->mix_buf = NULL; } pthread_mutex_lock(&path->dtv_path_lock); if (aml_out && aml_dev->hw_device.close_output_stream) { aml_dev->hw_device.close_output_stream((struct audio_hw_device *)aml_dev, stream_out); } path->dtv_aml_out = NULL; pthread_mutex_unlock(&path->dtv_path_lock);//as es read thread would also use out, so use lock. exit_open: AM_LOGI("--"); return ((void *)0); } #define QUEUE_AD_DATA_WHEN_START 1 static void *audio_dtv_path_ad_output_threadloop(void *data) { //0.prepare variable dtv_path_t *path = (dtv_path_t *)data; struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); if (!aml_dev || !path) { AM_LOGE("get adev(%p) or path(%p) error", aml_dev, path); goto exit_open; } struct audio_stream_out *ad_stream_out = NULL; struct aml_stream_out *ad_aml_out = NULL; struct aml_stream_out *main_aml_out = path->dtv_aml_out; struct mAudioEsDataInfo *mAdEsData = NULL; struct audio_config stream_config; int ret = -1, ret_ad = 0, ad_size_in_queue = 0, ad_render_start = 0; struct timespec last_getdata_ts, ts; bool data_come_soon = false, first_ad_flag = false, ad_need_sync = 0, addata_timeout = false, need_hold_data = false; aml_demux_audiopara_t *demux_info = (aml_demux_audiopara_t *)(&path->demux_info); void *demux_handle = demux_info->demux_handle; char thread_name[32] = { 0 }; //set name for this thread snprintf(thread_name, 32, "dtv_ad_output_data_%d", demux_info->ad_pid); prctl(PR_SET_NAME, (unsigned long)thread_name); AM_LOGI("created. pid %d, demux_handle %p, demux_info %p, thread id:%lu", demux_info->ad_pid, demux_handle, demux_info, pthread_self()); //1.prepare a output stream stream_config.sample_rate = 48000; stream_config.channel_mask = AUDIO_CHANNEL_OUT_STEREO; stream_config.format = dmx_fmt_convert2_hal_fmt(demux_info->ad_fmt); if (aml_dev->hw_device.open_output_stream) { ret = aml_dev->hw_device.open_output_stream((struct audio_hw_device *)aml_dev, 0, AUDIO_DEVICE_OUT_SPEAKER, AUDIO_OUTPUT_FLAG_DIRECT + AUDIO_OUTPUT_FLAG_AD_STREAM, // flags &stream_config, &ad_stream_out, "AML_DTV_SOURCE"); } if (ret < 0 || !ad_stream_out) { AM_LOGE("live open ad output stream fail, ret = %d. open func: %p", ret, aml_dev->hw_device.open_output_stream); goto exit_open; } ad_aml_out = (struct aml_stream_out *)ad_stream_out; path->dtv_ad_aml_out = ad_aml_out; if (eDolbyMS12Lib != aml_dev->dolby_lib_type && ad_aml_out->hal_format != AUDIO_FORMAT_AC3 && ad_aml_out->hal_format != AUDIO_FORMAT_E_AC3) { ad_aml_out->dec_param.dec_gain = dtv_ad_mixlevel_vol_convert_to_gain(ad_aml_out);//to init mp3/aac decoder gain } AM_LOGI("create a AD output stream(%p) success now, ad_output_thread_exit %d. main stream %p", ad_aml_out, path->ad_output_thread_exit, main_aml_out); while (!path->ad_output_thread_exit) { if (ad_aml_out->external_mute != aml_dev->dtv_mute) { out_set_stream_mute((struct audio_stream_out *)ad_aml_out, aml_dev->patch_mute, true); } main_aml_out = (struct aml_stream_out *)path->dtv_aml_out; if (path->dtv_decoder_state == AUDIO_DTV_PATCH_DECODER_STATE_PAUSE || !main_aml_out || !main_aml_out->mix_buf || !main_aml_out->mix_buf->mix_buffer.buffer || (!path->dtv_first_apts_flag)) { usleep(10000); continue; } if (ad_aml_out->mix_buf == NULL) { ad_aml_out->mix_buf = main_aml_out->mix_buf; } #define AD_START_THRESHOLD 2 Get_Audio_AD_ES_Queue_Size(demux_handle, &ad_size_in_queue); if (ad_size_in_queue > AD_START_THRESHOLD && need_hold_data) { need_hold_data = false; } else if (need_hold_data) { usleep(10000); continue; } dtv_update_ad_vol_parameter(path); if (0 == demux_info->associate_audio_mixing_enable) { usleep(20000); ad_render_start = 0; continue; } if (mAdEsData == NULL) { ret_ad = Get_ADAudio_Es(demux_handle, &mAdEsData); if (ret_ad != AM_AUDIO_Dmx_SUCCESS || !mAdEsData) { AM_LOGI_IF(aml_dev->debug_flag, "Get_ADAudio_Es failed, ret %d, mAdEsData %p", ret_ad, mAdEsData); usleep(5000); if (first_ad_flag) { data_come_soon = Stop_watch(last_getdata_ts, DATA_TIMEOUT); if (!data_come_soon) { addata_timeout = true; need_hold_data = true; ad_render_start = 0; AM_LOGI_IF(aml_dev->debug_flag, "no data for %dms!!", DATA_TIMEOUT); } } goto free_data; } else { clock_gettime(CLOCK_MONOTONIC, &last_getdata_ts); AM_LOGI_IF(aml_dev->debug_flag, "ad size %d pts %"PRIx64"(%"PRIx64"), diff %"PRId64"ms. ms12_dec %p. timeout %d, first_flag %d", mAdEsData->size, mAdEsData->pts, path->last_valid_ad_pts, (mAdEsData->pts-path->last_valid_ad_pts)/90, ad_aml_out->ms12_dec_handle, addata_timeout, first_ad_flag); if (!mAdEsData->data || !mAdEsData->size) { AM_LOGE("current data invalid"); goto free_data; } if (NULL_INT64 != mAdEsData->pts) { path->last_valid_ad_pts = mAdEsData->pts; ad_need_sync = 1; addata_timeout = false; first_ad_flag = true; } else { if (addata_timeout || !first_ad_flag) {// if first pts is invalid, we need drop it aml_audio_parser_reset(ad_aml_out->aml_parser);// if first pts after timeout is invalid, we need drop it and flush the remain data in parser goto free_data; } mAdEsData->pts = path->last_valid_ad_pts; ad_need_sync = 0; } if (get_debug_value(AML_DUMP_AUDIOHAL_DTV)) { aml_dump_audio_bitstreams_with_id("dtv_ad_audio_dmx.es", mAdEsData->data, mAdEsData->size, ad_aml_out->unique_ID); } } } /* align ad and main data by pts compare */ pthread_mutex_lock(&path->apts_cal_mutex); if (ad_need_sync) { demux_info->ad_package_status = check_ad_package_status(path->last_valid_main_pts, mAdEsData->pts, demux_info); } if (demux_info->ad_package_status == AD_PACK_STATUS_DROP) { pthread_mutex_unlock(&path->apts_cal_mutex); AM_LOGI_IF(aml_dev->debug_flag, "drop ad data"); goto free_data; } else if (demux_info->ad_package_status == AD_PACK_STATUS_HOLD && ad_render_start == 0) {//after render, not hold ad here ts_wait_time(&ts, 100000); ret = pthread_cond_timedwait(&path->apts_cond, &path->apts_cal_mutex, &ts); pthread_mutex_unlock(&path->apts_cal_mutex); if (path->ad_output_thread_exit) { goto free_data; } continue; } pthread_mutex_unlock(&path->apts_cal_mutex); struct audio_buffer abuffer_out = {0}; abuffer_out.buffer = mAdEsData->data; abuffer_out.size = mAdEsData->size; abuffer_out.pts = mAdEsData->pts; abuffer_out.channel = 2; abuffer_out.sample_rate = 48000; abuffer_out.format = abuffer_out.original_format = stream_config.format; demux_info->ad_fade = mAdEsData->adfade; demux_info->ad_pan = mAdEsData->adpan; ad_render_start = 1; ret_ad = ad_aml_out->write(ad_stream_out, &abuffer_out); AM_LOGI_IF((aml_dev->debug_flag > 1), "fade %d, pan %d, mixing_level %d, advol %d, ret_ad %d", mAdEsData->adfade, mAdEsData->adpan, aml_dev->mixing_level, aml_dev->advol_level, ret_ad); dtv_update_pan_fade_parameter(path); free_data: dtv_es_data_free(mAdEsData); mAdEsData = NULL; } AM_LOGI("path(%d) exit %d", path->path_id, path->ad_output_thread_exit); if (ad_aml_out && aml_dev->hw_device.close_output_stream) { aml_dev->hw_device.close_output_stream((struct audio_hw_device *)aml_dev, ad_stream_out); path->dtv_ad_aml_out = NULL; path->ad_fade = path->ad_pan = 0; } dtv_es_data_free(mAdEsData); exit_open: AM_LOGI("--"); return ((void *)0); } static int dtv_path_cmd_process_thread(void *data) { dtv_path_t *path = (dtv_path_t *)data; struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); if (!aml_dev || !path) { AM_LOGE("get adev(%p) or path(%p) error", aml_dev, path); return -1; } aml_demux_audiopara_t *demux_info = &path->demux_info; int cmd = AUDIO_DTV_PATCH_CMD_NUM, path_id = 0, cur_path_id = path->path_id; path->dtv_decoder_state = AUDIO_DTV_PATCH_DECODER_STATE_NULL; AM_LOGI("++ Enter, path(%p) id %d initial state: %s", path, cur_path_id, state_str(path->dtv_decoder_state)); prctl(PR_SET_NAME, (unsigned long)"dtv_process_thread"); while (!path->cmd_process_thread_exit ) { pthread_mutex_lock(&path->dtv_cmd_process_mutex); if (path_thread_get_cmd(path, &cmd, &path_id) != 0) { pthread_cond_wait(&path->dtv_cmd_process_cond, &path->dtv_cmd_process_mutex); pthread_mutex_unlock(&path->dtv_cmd_process_mutex); continue; } switch (path->dtv_decoder_state) { case AUDIO_DTV_PATCH_DECODER_STATE_NULL: { if (cmd == AUDIO_DTV_PATCH_CMD_OPEN) { path->dtv_decoder_state = AUDIO_DTV_PATCH_DECODER_STATE_INIT;/*** null to init ***/ AM_LOGI("[cmd_path %d, cur path %d] dmx_hanle %p, dmx_info %p. %s : %s", path_id, cur_path_id, demux_info->demux_handle, demux_info, cmd_str(cmd), state_str(path->dtv_decoder_state)); demux_info->priv_data = path; dmx_enable_main(demux_info, true); } else { AM_LOGW("[cmd_path %d, cur path %d] state %s, unsupport cmd %s !", path_id, cur_path_id, state_str(path->dtv_decoder_state), cmd_str(cmd)); } } break; case AUDIO_DTV_PATCH_DECODER_STATE_INIT: if (cmd == AUDIO_DTV_PATCH_CMD_START) { path->dtv_decoder_state = AUDIO_DTV_PATCH_DECODER_STATE_RUNNING;/*** init to running ***/ AM_LOGI("[cmd_path %d, cur path %d]dmx_handle:%p, %s : %s", path_id, cur_path_id, demux_info->demux_handle, cmd_str(cmd), state_str(path->dtv_decoder_state)); audio_dtv_output_start(path); } else if (cmd == AUDIO_DTV_PATCH_CMD_CLOSE) { path->dtv_decoder_state = AUDIO_DTV_PATCH_DECODER_STATE_NULL;/*** init to null ***/ AM_LOGI("[cmd_path %d, cur path %d] handle %p, dmx_info %p. %s : %s", path_id, cur_path_id, demux_info->demux_handle, demux_info, cmd_str(cmd), state_str(path->dtv_decoder_state)); dmx_enable_main(demux_info, false); dmx_info_clean(demux_info); /*coverity[LOCK_EVASION]: There will be no competition here*/ path->cmd_process_thread_exit = 1; } else { AM_LOGW("[cmd_path %d, cur path %d] state %s, unsupport cmd %s !", path_id, cur_path_id, state_str(path->dtv_decoder_state), cmd_str(cmd)); } break; case AUDIO_DTV_PATCH_DECODER_STATE_RUNNING: if (cmd == AUDIO_DTV_PATCH_CMD_PAUSE) { path->dtv_decoder_state = AUDIO_DTV_PATCH_DECODER_STATE_PAUSE;/*** running to pause ***/ AM_LOGI("[cmd_path %d, cur path %d] aml_out %p. %s : %s", path_id, cur_path_id, path->dtv_aml_out, cmd_str(cmd), state_str(path->dtv_decoder_state)); audio_dtv_output_pause(path, cmd); } else if (cmd == AUDIO_DTV_PATCH_CMD_STOP) { path->dtv_decoder_state = AUDIO_DTV_PATCH_DECODER_STATE_INIT;/*** running to init ***/ AM_LOGI("[cmd_path %d, cur path %d] %s : %s", path_id, cur_path_id, cmd_str(cmd), state_str(path->dtv_decoder_state)); audio_dtv_output_stop(path); } else if (cmd == AUDIO_DTV_PATCH_CMD_SET_AD_ENABLE) { AM_LOGI("[cmd_path %d, cur path %d] %s : %s", path_id, cur_path_id, cmd_str(cmd), state_str(path->dtv_decoder_state)); audio_dtv_ad_start_dynamic(path); } else { //AM_LOGW("[cmd_path %d, cur path %d] state %s, unsupport cmd %s !", path_id, cur_path_id, state_str(path->dtv_decoder_state), cmd_str(cmd)); } break; case AUDIO_DTV_PATCH_DECODER_STATE_PAUSE: if (cmd == AUDIO_DTV_PATCH_CMD_RESUME) { path->dtv_decoder_state = AUDIO_DTV_PATCH_DECODER_STATE_RUNNING;/*** pause to running ***/ AM_LOGI("[cmd_path %d, cur path %d] aml_out %p. %s : %s", path_id, cur_path_id, path->dtv_aml_out, cmd_str(cmd), state_str(path->dtv_decoder_state)); audio_dtv_output_pause(path, cmd); } else if (cmd == AUDIO_DTV_PATCH_CMD_STOP) { path->dtv_decoder_state = AUDIO_DTV_PATCH_DECODER_STATE_INIT;/*** pause to init ***/ AM_LOGI("[cmd_path %d, cur path %d] %s : %s", path_id, cur_path_id, cmd_str(cmd), state_str(path->dtv_decoder_state)); audio_dtv_output_stop(path); } else { AM_LOGW("[cmd_path %d, cur path %d] state %s, unsupport cmd %s !", path_id, cur_path_id, state_str(path->dtv_decoder_state), cmd_str(cmd)); } break; default: AM_LOGW("[cmd_path %d, cur path %d] state %s", path_id, cur_path_id, state_str(path->dtv_decoder_state)); break; } pthread_mutex_unlock(&path->dtv_cmd_process_mutex); } release_dtv_output_stream_thread(path); AM_LOGI("-- Exit, path(%p) id %d final state: %s", path, cur_path_id, state_str(path->dtv_decoder_state)); pthread_exit(NULL); } static int create_dtv_ad_output_stream_thread(dtv_path_t *path) { int ret = 0; AM_LOGI("++ path(%d) %p, ad created:%u", path->path_id, path, path->ad_output_thread_created); if (path->ad_output_thread_created == 0) { path->ad_output_thread_exit = 0; ret = pthread_create(&(path->audio_ad_output_threadID), NULL, audio_dtv_path_ad_output_threadloop, path); if (ret != 0) { AM_LOGE("Create ad output thread fail!\n"); return -1; } path->ad_output_thread_created = 1; } AM_LOGI("--, threadID %ld", path->audio_ad_output_threadID); return 0; } static int create_dtv_output_stream_thread(dtv_path_t *path) { int ret = 0; AM_LOGI("++ path(%d) %p, main created:%u", path->path_id, path, path->output_thread_created); if (path->output_thread_created == 0) { path->output_thread_exit = 0; ret = pthread_create(&(path->audio_output_threadID), NULL, audio_dtv_path_output_threadloop, path); if (ret != 0) { AM_LOGE("Create main output thread fail!\n"); return -1; } path->output_thread_created = 1; } AM_LOGI("--, threadID %ld", path->audio_output_threadID); return 0; } static int release_dtv_output_stream_thread(dtv_path_t *path) { AM_LOGI("++ path(%d) %p, main:%u, ad:%u\n", path->path_id, path, path->output_thread_created, path->ad_output_thread_created); if (path->dtv_aml_out) { path->dtv_aml_out->fast_quit = true; AM_LOGI("aml_out:%p, set fast_quit:%d", path->dtv_aml_out, path->dtv_aml_out->fast_quit); } if (path->dtv_ad_aml_out) { path->dtv_ad_aml_out->fast_quit = true; AM_LOGI("aml_ad_out:%p, set fast_quit:%d", path->dtv_ad_aml_out, path->dtv_ad_aml_out->fast_quit); } if (path->ad_output_thread_created == 1) { path->ad_output_thread_exit = 1; pthread_cond_signal(&path->apts_cond); if (path->dtv_ad_aml_out && path->dtv_ad_aml_out->mix_buf) pthread_cond_signal(&path->dtv_ad_aml_out->mix_buf->cond); pthread_join(path->audio_ad_output_threadID, NULL); path->ad_output_thread_created = 0; } if (path->output_thread_created == 1) { path->output_thread_exit = 1; pthread_join(path->audio_output_threadID, NULL); path->output_thread_created = 0; } AM_LOGI("--"); return 0; } static int create_dtv_patch_l(struct aml_audio_device *aml_dev, struct aml_audio_patch ** apatch) { struct aml_audio_patch *patch; int ret = 0; AM_LOGI("++"); patch = aml_audio_calloc(1, sizeof(*patch)); if (!patch) { ret = -1; goto err; } memset(patch, 0, sizeof(*patch)); // save dev to patch patch->dev = aml_dev; patch->patch_src = SRC_DTV; patch->input_device = AUDIO_DEVICE_IN_TV_TUNER; patch->aformat = AUDIO_FORMAT_PCM_16_BIT; for (int i = 0; i < DVB_DEMUX_SUPPORT_MAX_NUM; i++) { patch->dtv_path[i].dtv_cmd_list = aml_audio_calloc(1, sizeof(struct cmd_node)); if (!patch->dtv_path[i].dtv_cmd_list) { ret = -1; goto err; } init_cmd_list(patch->dtv_path[i].dtv_cmd_list); dmx_info_clean(&(patch->dtv_path[i].demux_info)); patch->dtv_path[i].path_id = i; pthread_cond_init(&patch->dtv_path[i].apts_cond, NULL); pthread_cond_init(&patch->dtv_path[i].dtv_cmd_process_cond, NULL); pthread_mutex_init(&patch->dtv_path[i].dtv_cmd_process_mutex, NULL); pthread_mutex_init(&patch->dtv_path[i].apts_cal_mutex, NULL); pthread_mutex_init(&patch->dtv_path[i].dtv_path_lock, NULL); } pthread_mutex_lock(&aml_dev->dtv_patch_lock); pthread_mutex_unlock(&aml_dev->dtv_patch_lock); /* Use flag to indicate that patch struct is ready. TBD */ //validate_dev_patch(aml_dev); *apatch = patch; AM_LOGI("-- patch %p", patch); return 0; err: if (patch) { for (int i = 0; i < DVB_DEMUX_SUPPORT_MAX_NUM; i++) { if (patch->dtv_path[i].dtv_cmd_list) { deinit_cmd_list(patch->dtv_path[i].dtv_cmd_list); } } aml_audio_free(patch); } return ret; } static int _dtv_patch_release_resource(struct aml_audio_patch *patch) { for (int i = 0; i < DVB_DEMUX_SUPPORT_MAX_NUM; i++) { dtv_path_t *path = &(patch->dtv_path[i]); if (path->cmd_process_thread_created) { pthread_mutex_lock(&path->dtv_cmd_process_mutex); dtv_cmdlist_add_cmd(path->dtv_cmd_list, AUDIO_DTV_PATCH_CMD_STOP, path->path_id); pthread_cond_signal(&path->dtv_cmd_process_cond); pthread_mutex_unlock(&path->dtv_cmd_process_mutex); pthread_mutex_lock(&path->dtv_cmd_process_mutex); dtv_cmdlist_add_cmd(path->dtv_cmd_list, AUDIO_DTV_PATCH_CMD_CLOSE, path->path_id); pthread_cond_signal(&path->dtv_cmd_process_cond); pthread_mutex_unlock(&path->dtv_cmd_process_mutex); pthread_join(path->audio_cmd_process_threadID, NULL); path->cmd_process_thread_created = 0; } pthread_mutex_destroy(&path->dtv_path_lock); pthread_mutex_destroy(&path->dtv_cmd_process_mutex); pthread_cond_destroy(&path->dtv_cmd_process_cond); pthread_cond_destroy(&path->apts_cond); pthread_mutex_destroy(&path->apts_cal_mutex); deinit_cmd_list(path->dtv_cmd_list); } return 0; } static int release_dtv_patch_l(struct aml_audio_device *aml_dev, struct aml_audio_patch *apatch) { if (aml_dev == NULL || apatch == NULL) { AM_LOGE("aml_dev == NULL"); return 0; } struct aml_audio_patch *patch = apatch; AM_LOGI("++ Enter, patch %p", patch); if (patch == NULL) { AM_LOGE("patch == NULL"); return 0; } /* Use flag to indicate that it will start to free patch struct. TBD */ //invalidate_dev_patch(aml_dev); _dtv_patch_release_resource(patch); pthread_mutex_lock(&aml_dev->dtv_patch_lock); aml_audio_free(patch); pthread_mutex_unlock(&aml_dev->dtv_patch_lock); AM_LOGI("-- Exit"); return 0; } int create_dtv_patch(struct aml_audio_device *dev, void ** apatch) { struct aml_audio_device *aml_dev = (struct aml_audio_device *)dev; int ret = 0; pthread_mutex_lock(&aml_dev->patch_lock); ret = create_dtv_patch_l(dev, (struct aml_audio_patch **)apatch); pthread_mutex_unlock(&aml_dev->patch_lock); return ret; } int release_dtv_patch(struct aml_audio_device *aml_dev, void *apatch) { int ret = 0; pthread_mutex_lock(&aml_dev->patch_lock); ret = release_dtv_patch_l(aml_dev, (struct aml_audio_patch *)apatch); pthread_mutex_unlock(&aml_dev->patch_lock); return ret; } int dtv_path_get_es_pts_dts_flag(int path_id) { struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); if (!aml_dev) { AM_LOGE("get adev(%p) error", aml_dev); return 0; } struct aml_audio_patch *patch = get_dev_patch_by_source(aml_dev, SRC_DTV); if (patch == NULL) { AM_LOGE("dtv patch == NULL"); return 0; } AM_LOGI("path_id %d pts_dts_flag %d", path_id, patch->dtv_path[path_id].pts_dts_flag); return patch->dtv_path[path_id].pts_dts_flag; } int dtv_path_get_mediasync_id(int path_id) { struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); if (!aml_dev) { AM_LOGE("get adev(%p) error", aml_dev); return 0; } struct aml_audio_patch *patch = get_dev_patch_by_source(aml_dev, SRC_DTV); if (patch == NULL) { AM_LOGE("dtv patch == NULL"); return 0; } AM_LOGI("path_id %d pts_dts_flag %d", path_id, patch->dtv_path[path_id].media_sync_id); return patch->dtv_path[path_id].media_sync_id; } int dtv_queue_es_info(void *info) { int ret = -1; R_CHECK_POINTER_LEGAL(ret, info, "info"); struct es_queue_info *queue_info = (struct es_queue_info *) info; struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle(); dtv_path_t *dtv_path = (dtv_path_t *)queue_info->priv_data; if (!adev || !dtv_path) { AM_LOGE("get adev(%p) or path(%p) error", adev, dtv_path); return ret; } pthread_mutex_lock(&dtv_path->dtv_path_lock);//lock because output thread may close output stream if (dtv_path->dtv_aml_out) { struct aml_stream_out *aml_out = dtv_path->dtv_aml_out; if (aml_out->avsync_ctx) { if (aml_out->avsync_ctx->mediasync_ctx) { if (aml_out->avsync_ctx->mediasync_ctx->handle) { struct mediasync_audio_queue_info audio_queue_info; audio_queue_info.apts = queue_info->pts; audio_queue_info.duration = 0; audio_queue_info.size = queue_info->size; audio_queue_info.isneedupdate = false; audio_queue_info.isworkingchannel = true; audio_queue_info.tunit = MEDIASYNC_UNIT_PTS; mediasync_wrap_queueAudioFrame(aml_out->avsync_ctx->mediasync_ctx->handle, &audio_queue_info); AM_LOGI_IF(adev->debug_flag, "path %p, aout %p, avsync_ctx %p, mediasync ctx %p, handle %p. pid %d, dmx_id %d:%d, pts:0x%"PRIx64", size:%d", dtv_path, aml_out, aml_out->avsync_ctx, aml_out->avsync_ctx->mediasync_ctx, aml_out->avsync_ctx->mediasync_ctx->handle, dtv_path->demux_info.main_pid, dtv_path->demux_info.demux_id, queue_info->demux_id, queue_info->pts, queue_info->size); ret = 0; } else { AM_LOGI_IF(adev->debug_flag, "path %p(pid %d, dmx_id %d:%d), aml_out %p, avsync_ctx %p, mediasync ctx %p, handle %p", dtv_path, dtv_path->demux_info.main_pid, dtv_path->demux_info.demux_id, queue_info->demux_id, aml_out, aml_out->avsync_ctx, aml_out->avsync_ctx->mediasync_ctx, aml_out->avsync_ctx->mediasync_ctx->handle); } } else { AM_LOGI_IF(adev->debug_flag, "path %p(pid %d, dmx_id %d:%d), aml_out %p, avsync_ctx %p, mediasync ctx %p", dtv_path, dtv_path->demux_info.main_pid, dtv_path->demux_info.demux_id, queue_info->demux_id, aml_out, aml_out->avsync_ctx, aml_out->avsync_ctx->mediasync_ctx); } } else { AM_LOGI_IF(adev->debug_flag, "path %p(pid %d, dmx_id %d:%d), aml_out %p, avsync_ctx %p", dtv_path, dtv_path->demux_info.main_pid, dtv_path->demux_info.demux_id, queue_info->demux_id, aml_out, aml_out->avsync_ctx); } } else { AM_LOGI_IF(adev->debug_flag, "path %p(pid %d, dmx_id %d:%d), aml_out %p", dtv_path, dtv_path->demux_info.main_pid, dtv_path->demux_info.demux_id, queue_info->demux_id, dtv_path->dtv_aml_out); } pthread_mutex_unlock(&dtv_path->dtv_path_lock); return ret; } static void dtv_set_decoder_gain(dtv_path_t *path, struct audio_stream_out *stream) { R_CHECK_POINTER_LEGAL(, stream, ""); struct aml_stream_out *aml_out = (struct aml_stream_out *) stream; struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle(); float gain = 1.0; if (eDolbyMS12Lib != adev->dolby_lib_type) {//when ms12, ad vol & mix level would be set to ms12 if ((aml_out->stream_type == STREAM_MAIN_INPUT && aml_out->dec_param.ad_mix_enable) || aml_out->stream_type == STREAM_AD_INPUT) gain = dtv_ad_mixlevel_vol_convert_to_gain(aml_out); } if (aml_out->stream_type == STREAM_MAIN_INPUT) { float fade_coef = 1.0; if (path->ad_fade && aml_out->dec_param.ad_mix_enable) fade_coef = DbToAmpl(path->ad_fade * (-0.3f)); AM_LOGI("[main]fade %d fade_coef %f; mix level: %d, gain: %f", path->ad_fade, fade_coef, adev->mixing_level, gain); aml_out->dec_config.dec_left_gain = gain * fade_coef; aml_out->dec_config.dec_right_gain = aml_out->dec_config.dec_left_gain; } else if (aml_out->stream_type == STREAM_AD_INPUT) { float left_vol = 1.0, right_vol = 1.0; if (path->ad_pan > 0 && path->ad_pan < 0xFF) { get_left_right_volume(path->ad_pan, &left_vol, &right_vol); } AM_LOGI("[ad]pan %d(L %f, R %f); ad vol %d, mix level: %d, gain: %f", path->ad_pan, left_vol, right_vol, adev->advol_level, adev->mixing_level, gain); aml_out->dec_config.dec_left_gain = left_vol * gain; aml_out->dec_config.dec_right_gain = right_vol * gain; } aml_decoder_set_config(aml_out->aml_dec, AML_DEC_CONFIG_LEFT_GAIN, &aml_out->dec_config); aml_decoder_set_config(aml_out->aml_dec, AML_DEC_CONFIG_RIGHT_GAIN, &aml_out->dec_config); return; } static void dtv_update_decoder_params(dtv_path_t *path) { struct aml_stream_out *main_aml_out = path->dtv_aml_out; aml_demux_audiopara_t *demux_info = (aml_demux_audiopara_t *)(&path->demux_info); static float vol = 1.0; if (main_aml_out->hal_format == AUDIO_FORMAT_AC4 || main_aml_out->ad_substream_supported) { dtv_update_ad_vol_parameter(path); } if (main_aml_out->internal_mute != demux_info->mute) { main_aml_out->internal_mute = demux_info->mute; if (main_aml_out->internal_mute) { vol = main_aml_out->volume_l_org; main_aml_out->stream.set_volume(&main_aml_out->stream, 0, 0); } else { main_aml_out->stream.set_volume(&main_aml_out->stream, vol, vol); } } if (main_aml_out->volume_l_org != demux_info->volume) { vol = main_aml_out->volume_l_org = demux_info->volume; if (!main_aml_out->internal_mute) { main_aml_out->stream.set_volume(&main_aml_out->stream, vol, vol); } } } static void dtv_update_ad_vol_parameter(dtv_path_t *path) { struct aml_stream_out *main_aml_out = path->dtv_aml_out; struct aml_stream_out *ad_aml_out = path->dtv_ad_aml_out; struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); aml_demux_audiopara_t *demux_info = (aml_demux_audiopara_t *)(&path->demux_info); if (main_aml_out->dec_param.ad_mix_enable != demux_info->associate_audio_mixing_enable) { main_aml_out->dec_param.ad_mix_enable = demux_info->associate_audio_mixing_enable; if (eDolbyMS12Lib == aml_dev->dolby_lib_type) { set_ms12_ad_mixing_enable((struct audio_stream_out *)main_aml_out, demux_info->associate_audio_mixing_enable);//set to ms12 } else if (main_aml_out->hal_format == AUDIO_FORMAT_AC3 || main_aml_out->hal_format == AUDIO_FORMAT_E_AC3) { main_aml_out->dec_config.ad_mixing_enable = demux_info->associate_audio_mixing_enable; aml_decoder_set_config(main_aml_out->aml_dec, AML_DEC_CONFIG_MIXING_ENABLE, &main_aml_out->dec_config);//set to dcv } else { dtv_set_decoder_gain(path, (struct audio_stream_out *)main_aml_out);//mp3/aac use decoder gain } } if (main_aml_out->dec_param.advol_level != aml_dev->advol_level) { main_aml_out->dec_param.advol_level = aml_dev->advol_level; if (eDolbyMS12Lib == aml_dev->dolby_lib_type) { set_ms12_ad_vol((struct audio_stream_out *)main_aml_out, aml_dev->advol_level);//set to ms12 } else if (main_aml_out->hal_format == AUDIO_FORMAT_AC3 || main_aml_out->hal_format == AUDIO_FORMAT_E_AC3) { main_aml_out->dec_config.advol_level = aml_dev->advol_level; aml_decoder_set_config(main_aml_out->aml_dec, AML_DEC_CONFIG_AD_VOL, &main_aml_out->dec_config);//set to dcv } else { dtv_set_decoder_gain(path, (struct audio_stream_out *)ad_aml_out);//mp3/aac use decoder gain } } if (main_aml_out->dec_param.mixing_level != aml_dev->mixing_level) { main_aml_out->dec_param.mixing_level = aml_dev->mixing_level; if (eDolbyMS12Lib == aml_dev->dolby_lib_type) {////set to ms12 set_ms12_ad_mixing_level((struct audio_stream_out *)main_aml_out, aml_dev->mixing_level); } else if (main_aml_out->hal_format == AUDIO_FORMAT_AC3 || main_aml_out->hal_format == AUDIO_FORMAT_E_AC3) { main_aml_out->dec_config.mixer_level = aml_dev->mixing_level; aml_decoder_set_config(main_aml_out->aml_dec, AML_DEC_CONFIG_MIXER_LEVEL, &main_aml_out->dec_config);//set to dcv } else { dtv_set_decoder_gain(path, (struct audio_stream_out *)ad_aml_out);//mp3/aac use decoder gain dtv_set_decoder_gain(path, (struct audio_stream_out *)main_aml_out); } } } static void dtv_update_pan_fade_parameter(dtv_path_t *path) { struct aml_stream_out *main_aml_out = path->dtv_aml_out; struct aml_stream_out *ad_aml_out = path->dtv_ad_aml_out; struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); aml_demux_audiopara_t *demux_info = (aml_demux_audiopara_t *)(&path->demux_info); if (eDolbyMS12Lib == aml_dev->dolby_lib_type && (ad_aml_out->hal_internal_format == AUDIO_FORMAT_HE_AAC_V1 || ad_aml_out->hal_internal_format == AUDIO_FORMAT_HE_AAC_V2)) { set_ms12_fade_pan((struct audio_stream_out *)main_aml_out, demux_info->ad_fade, 0, 0, 0, demux_info->ad_pan);//set to ms12 } else if (demux_info->ad_fade != path->ad_fade && main_aml_out->aml_dec) { path->ad_fade = demux_info->ad_fade; dtv_set_decoder_gain(path, (struct audio_stream_out *)main_aml_out);//mp3/aac use decoder gain } else if (demux_info->ad_pan != path->ad_pan && ad_aml_out->aml_dec) { path->ad_pan = demux_info->ad_pan; dtv_set_decoder_gain(path, (struct audio_stream_out *)ad_aml_out);//mp3/aac use decoder gain } } static int dtv_create_ad_mixbuf(struct aml_stream_out *aml_out) { if (!aml_out || !aml_out->mix_buf) { return -1; } aml_out->mix_buf->consumed = true; int ret = pthread_mutex_init(&aml_out->mix_buf->lock, NULL); if (ret != 0) { AM_LOGE("pthread_mutex_init err %d", ret); return -1; } ret = pthread_cond_init(&aml_out->mix_buf->cond, NULL); if (ret != 0) { AM_LOGE("pthread_cond_init err %d", ret); return -1; } aml_out->mix_buf->buf_size = 4096; aml_out->mix_buf->mix_buffer.buffer = (char *)aml_audio_calloc(1, aml_out->mix_buf->buf_size); if (!aml_out->mix_buf->mix_buffer.buffer) { AM_LOGE("malloc mixed_buffer buf error"); goto malloc_fail; } else { AM_LOGI("buffer:%p create success", aml_out->mix_buf->mix_buffer.buffer); } return 0; malloc_fail: if (aml_out->mix_buf) { pthread_mutex_destroy(&aml_out->mix_buf->lock); pthread_cond_destroy(&aml_out->mix_buf->cond); } return -1; } static void dtv_release_ad_mixbuf(struct aml_stream_out *aml_out) { if (aml_out->mix_buf) { if (aml_out->mix_buf->mix_buffer.buffer) { AM_LOGD("free buffer %p", aml_out->mix_buf->mix_buffer.buffer); aml_audio_free(aml_out->mix_buf->mix_buffer.buffer); aml_out->mix_buf->mix_buffer.buffer = NULL; } pthread_mutex_destroy(&aml_out->mix_buf->lock); pthread_cond_destroy(&aml_out->mix_buf->cond); } }