| /* |
| * 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 <cutils/atomic.h> |
| #include <cutils/log.h> |
| #include <cutils/properties.h> |
| #include <cutils/str_parms.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <hardware/hardware.h> |
| #include <inttypes.h> |
| #include <linux/ioctl.h> |
| #include <math.h> |
| #include <pthread.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <sys/prctl.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/system_properties.h> |
| #include <system/audio.h> |
| #include <time.h> |
| #include <utils/Timers.h> |
| #include <sys/ioctl.h> |
| |
| #if ANDROID_PLATFORM_SDK_VERSION >= 25 // 8.0 |
| #include <system/audio-base.h> |
| #endif |
| |
| #include <hardware/audio.h> |
| |
| |
| #include <aml_data_utils.h> |
| |
| #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) || defined(MS12_V26_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" |
| |
| |
| #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 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 && 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 (AM_AUDIO_Dmx_SUCCESS == ad_start_flag && path->ad_output_thread_created == 0) { |
| create_dtv_ad_output_stream_thread(path); |
| } |
| } |
| |
| 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); |
| } |
| } |
| 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); |
| dtv_check_audio_reset(); |
| } |
| |
| 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(adev); |
| struct dolby_ms12_desc *ms12 = &(adev->ms12); |
| |
| 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; |
| void *demux_handle = demux_info->demux_handle; |
| |
| 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: |
| demux_info->media_sync_id = val; |
| 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); |
| // adev->tv_mute = val; |
| adev->decoder_mute = val; |
| adev_update_decoder_mute_state(dev); |
| 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 (get_dtv_volume(adev) != dtv_volume_switch && dtv_volume_switch >= 0.0f && dtv_volume_switch <= 1.0f) { |
| set_dtv_volume(adev, dtv_volume_switch); |
| AM_LOGI ("dtv set volume:%f(%f)", get_dtv_volume(adev), dtv_volume_switch); |
| if (path->dtv_aml_out) { |
| struct audio_stream_out *stream_out = (struct audio_stream_out *)path->dtv_aml_out; |
| stream_out->set_volume(stream_out, dtv_volume_switch, 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; |
| } |
| demux_info->advol_level = val; |
| AM_LOGI("advol set to %d", demux_info->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; |
| } |
| demux_info->mixing_level = (val * 64 - 32 * 100) / 100; //[0,100] mapping to [-32,32] |
| AM_LOGI("mixing_level set to %d", demux_info->mixing_level); |
| if (path->dtv_aml_out) { |
| set_ms12_ad_mixing_level((struct audio_stream_out *)path->dtv_aml_out, demux_info->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) { |
| 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) { |
| 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) { |
| 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; |
| 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 }; |
| |
| //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()); |
| |
| //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; |
| |
| //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 = demux_info->advol_level; |
| aml_out->dec_param.mixing_level = demux_info->mixing_level; |
| 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 (path->dtv_decoder_state == AUDIO_DTV_PATCH_DECODER_STATE_PAUSE) { |
| usleep(1000); |
| continue; |
| } |
| //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 = path->last_valid_main_pts; |
| } |
| if (get_debug_value(AML_DUMP_AUDIOHAL_DTV)) { |
| aml_dump_audio_bitstreams("dtv_main_audio_dmx.es", mEsData->data, mEsData->size); |
| } |
| } |
| |
| //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); |
| } |
| |
| //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); |
| 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, main_size_in_queue = 0, ad_size_in_queue = 0; |
| int associate_audio_mixing_enable = -1, mixing_level = 0, advol_level = 100; |
| struct timespec ts, watch_start_ts; |
| bool watch_flag = 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, // devices_t |
| 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; |
| 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) { |
| 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 || |
| (aml_dev->dolby_lib_type == eDolbyMS12Lib && main_aml_out && !main_aml_out->ms12_dec_handle)) { |
| usleep(20000); |
| continue; |
| } |
| |
| if (aml_dev->dolby_lib_type == eDolbyMS12Lib) { |
| ad_aml_out->ms12_dec_handle = main_aml_out->ms12_dec_handle; |
| } |
| #if QUEUE_AD_DATA_WHEN_START |
| /* as for DD_Disappearing-AA_ddp_DVB_h264_29fps.trp: first 10s has ad, medium 10s has no ad data, then last 10s has ad again. |
| from no_ad to has_ad, need to use start threshold to queue some data, to make sure ad has data continuously with main in mixer process*/ |
| if (Get_Audio_AD_ES_Queue_Size(demux_handle, &ad_size_in_queue) == AM_AUDIO_Dmx_SUCCESS |
| && ad_size_in_queue == 0 && !watch_flag) {//1.save the ts when there's no ad data. |
| clock_gettime(CLOCK_MONOTONIC, &watch_start_ts); |
| watch_flag = true; |
| } |
| Get_Audio_Main_ES_Queue_Size(demux_handle, &main_size_in_queue); |
| AM_LOGI_IF(aml_dev->debug_flag, "ad_num %d, main_num %d, watch_flag %d", ad_size_in_queue, main_size_in_queue, watch_flag); |
| if (watch_flag && ad_size_in_queue != 0) { |
| struct timespec cur_ts; |
| clock_gettime(CLOCK_MONOTONIC, &cur_ts); |
| int has_data_cost_ms = calc_time_interval_us(&watch_start_ts, &cur_ts) / 1000; |
| if (has_data_cost_ms > 1000) {//2.if the period time from no_data to has_data > 1s, need queue |
| need_hold_data = true; |
| } |
| watch_flag = false; |
| } |
| AM_LOGI_IF(aml_dev->debug_flag, "ad_num %d, main_num %d, watch_flag %d, need_hold_data %d", ad_size_in_queue, main_size_in_queue, watch_flag, need_hold_data); |
| |
| //start threshold to make ad has enough data, avoid no ad when mix main data in ms12. |
| #define AD_START_THRESHOLD 2 |
| if (ad_size_in_queue > AD_START_THRESHOLD && need_hold_data) { |
| need_hold_data = false; |
| } else if (need_hold_data) { |
| usleep(10000); |
| continue; |
| } |
| #endif |
| |
| if (associate_audio_mixing_enable != demux_info->associate_audio_mixing_enable |
| || advol_level != demux_info->advol_level |
| || mixing_level != demux_info->mixing_level) { |
| associate_audio_mixing_enable = demux_info->associate_audio_mixing_enable; |
| advol_level = demux_info->advol_level; |
| mixing_level = demux_info->mixing_level; |
| if (eDolbyMS12Lib == aml_dev->dolby_lib_type) { |
| set_ms12_ad_mixing_enable(ad_stream_out, associate_audio_mixing_enable); |
| set_ms12_ad_mixing_level(ad_stream_out, mixing_level); |
| set_ms12_ad_vol(ad_stream_out, advol_level); |
| AM_LOGI("associate_audio_mixing_enable:%d, advol_level:%d, mixing_level:%d", |
| associate_audio_mixing_enable, advol_level, mixing_level); |
| } |
| if (false == associate_audio_mixing_enable) { |
| usleep(20000); |
| continue; |
| } |
| } |
| if (mAdEsData == NULL) { |
| ret_ad = Get_ADAudio_Es(demux_handle, &mAdEsData); |
| if (ret_ad != AM_AUDIO_Dmx_SUCCESS || !mAdEsData) { |
| AM_LOGE("Get_ADAudio_Es failed, ret %d, mAdEsData %p", ret_ad, mAdEsData); |
| usleep(10000); |
| goto free_data; |
| } else { |
| AM_LOGI_IF(aml_dev->debug_flag, "ad size %d pts %"PRIx64"(%"PRIx64"), diff %"PRId64"ms. ms12_dec %p", mAdEsData->size, mAdEsData->pts, path->last_valid_ad_pts, (mAdEsData->pts-path->last_valid_ad_pts)/90, ad_aml_out->ms12_dec_handle); |
| 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; |
| } else { |
| mAdEsData->pts = path->last_valid_ad_pts; |
| } |
| if (get_debug_value(AML_DUMP_AUDIOHAL_DTV)) { |
| aml_dump_audio_bitstreams("dtv_ad_audio_dmx.es", mAdEsData->data, mAdEsData->size); |
| } |
| } |
| } |
| /* align ad and main data by pts compare */ |
| pthread_mutex_lock(&path->apts_cal_mutex); |
| 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("drop ad data"); |
| goto free_data; |
| } else if (demux_info->ad_package_status == AD_PACK_STATUS_HOLD) { |
| 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; |
| |
| 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, demux_info->mixing_level, demux_info->advol_level, ret_ad); |
| |
| 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)) { |
| struct dolby_ms12_desc *ms12 = &(aml_dev->ms12); |
| set_ms12_fade_pan(ad_stream_out, mAdEsData->adfade /* fade byte*/ |
| , 0 /*gain_byte_center*/, 0 /*gain_byte_front */ |
| , 0 /*gain_byte_surround*/, mAdEsData->adpan /*pan byte*/); |
| } |
| 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; |
| } |
| 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); |
| 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); |
| dtv_check_audio_reset(); |
| 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) |
| { |
| int ret = 0; |
| 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); |
| 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 audio_hw_device *dev, audio_devices_t input, |
| audio_devices_t output __unused) |
| { |
| struct aml_audio_patch *patch; |
| struct aml_audio_device *aml_dev = (struct aml_audio_device *)dev; |
| int ret = 0; |
| |
| if (get_dev_patch(aml_dev)) { |
| AM_LOGD("patch exists, first release it"); |
| if (get_dev_patch(aml_dev)->is_dtv_src) { |
| return ret; |
| //release_dtv_patch_l(aml_dev); |
| } else { |
| release_patch_l(aml_dev); |
| } |
| } |
| patch = aml_audio_calloc(1, sizeof(*patch)); |
| if (!patch) { |
| ret = -1; |
| goto err; |
| } |
| memset(patch, 0, sizeof(*patch)); |
| |
| // save dev to patch |
| patch->dev = dev; |
| patch->input_src = input; |
| patch->is_dtv_src = true; |
| 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); |
| set_dev_patch(aml_dev, patch); |
| pthread_mutex_unlock(&aml_dev->dtv_patch_lock); |
| /* Use flag to indicate that patch struct is ready. TBD */ |
| validate_dev_patch(aml_dev); |
| |
| 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); |
| } |
| set_dev_patch(aml_dev, NULL); |
| |
| 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); |
| } |
| } |
| static int release_dtv_patch_l(struct aml_audio_device *aml_dev) |
| { |
| if (aml_dev == NULL) { |
| AM_LOGE("aml_dev == NULL"); |
| return 0; |
| } |
| struct aml_audio_patch *patch = get_dev_patch(aml_dev); |
| struct audio_hw_device *dev = (struct audio_hw_device *)aml_dev; |
| 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); |
| set_dev_patch(aml_dev, NULL); |
| pthread_mutex_unlock(&aml_dev->dtv_patch_lock); |
| |
| AM_LOGI("-- Exit"); |
| |
| return 0; |
| } |
| |
| int create_dtv_patch(struct audio_hw_device *dev, audio_devices_t input, |
| audio_devices_t output) |
| { |
| 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, input, output); |
| pthread_mutex_unlock(&aml_dev->patch_lock); |
| |
| return ret; |
| } |
| |
| int release_dtv_patch(struct aml_audio_device *aml_dev) |
| { |
| int ret = 0; |
| pthread_mutex_lock(&aml_dev->patch_lock); |
| if (is_same_patch_src(aml_dev, SRC_DTV)) { |
| ret = release_dtv_patch_l(aml_dev); |
| } |
| 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(aml_dev); |
| 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_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; |
| } |