/* * 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_hal_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 "aml_malloc_debug.h" #include "dtv_patch_utils.h" #include "audio_hw_utils.h" int dtv_package_list_flush(package_list *list) { pthread_mutex_lock(&(list->tslock)); struct package * dtv_package = NULL; while (list->pack_num && list->first) { dtv_package = list->first; list->first = list->first->next; if (dtv_package->data) { aml_audio_free(dtv_package->data); dtv_package->data = NULL; } aml_audio_free(dtv_package); /*coverity[unused_value]:set to NULL after free*/ dtv_package = NULL; list->pack_num--; } pthread_mutex_unlock(&(list->tslock)); return 0; } int dtv_package_list_init(package_list *list) { list->first = NULL; list->pack_num = 0; list->current = NULL; pthread_mutex_init(&list->tslock, NULL); return 0; } int dtv_package_add(package_list *list, struct package *p) { pthread_mutex_lock(&list->tslock); if (list->pack_num == INPUT_PACKAGE_MAXCOUNT) { //enough AM_LOGI("list->pack_num %d",list->pack_num); pthread_mutex_unlock(&(list->tslock)); return -2; } if (list->pack_num == 0) { //first package list->first = p; list->current = p; list->pack_num = 1; } else { list->current->next = p; list->current = p; list->pack_num++; } pthread_mutex_unlock(&list->tslock); return 0; } struct package * dtv_package_get(package_list *list) { pthread_mutex_lock(&(list->tslock)); if (list->pack_num == 0) { pthread_mutex_unlock(&list->tslock); return NULL; } struct package *p = list->first; if (list->pack_num == 1) { list->first = NULL; list->pack_num = 0; list->current = NULL; } else if (list->pack_num > 1) { list->first = list->first->next; list->pack_num--; } pthread_mutex_unlock(&list->tslock); return p; } bool dtv_package_is_full(package_list *list) { bool ret = false; pthread_mutex_lock(&list->tslock); ret = list->pack_num == INPUT_PACKAGE_MAXCOUNT; pthread_mutex_unlock(&list->tslock); return ret; } bool dtv_package_is_empty(package_list *list) { bool ret = false; pthread_mutex_lock(&list->tslock); ret = list->pack_num == 0; pthread_mutex_unlock(&list->tslock); return ret; } void init_cmd_list(struct cmd_node *dtv_cmd_list) { dtv_cmd_list->next = NULL; dtv_cmd_list->cmd = -1; dtv_cmd_list->cmd_num = 0; dtv_cmd_list->used = 0; dtv_cmd_list->initd = 1; pthread_mutex_init(&dtv_cmd_list->dtv_cmd_mutex, NULL); } void deinit_cmd_list(struct cmd_node *dtv_cmd_list) { struct cmd_node *cmd_list = dtv_cmd_list; pthread_mutex_destroy(&dtv_cmd_list->dtv_cmd_mutex); while (cmd_list != NULL) { dtv_cmd_list = dtv_cmd_list->next; aml_audio_free(cmd_list); cmd_list = dtv_cmd_list; } } int dtv_cmdlist_add_cmd(struct cmd_node *dtv_cmd_list,int cmd, int path_id) { struct cmd_node *list = NULL; struct cmd_node *new_cmd_node = NULL; if (!dtv_cmd_list || dtv_cmd_list->initd == 0) { return 0; } pthread_mutex_lock(&dtv_cmd_list->dtv_cmd_mutex); new_cmd_node = aml_audio_malloc(sizeof(struct cmd_node)); if (!new_cmd_node ) { ALOGE("new_cmd_node aml_audio_malloc failed"); pthread_mutex_unlock(&dtv_cmd_list->dtv_cmd_mutex); return -1; } new_cmd_node->cmd = cmd; new_cmd_node->path_id = path_id; new_cmd_node->next = NULL; new_cmd_node->used = 1; list = dtv_cmd_list; while (list->next != NULL) { list = list->next; } list->next = new_cmd_node; dtv_cmd_list->cmd_num++; pthread_mutex_unlock(&dtv_cmd_list->dtv_cmd_mutex); AM_LOGI("add by live dtv_patch_add_cmd the cmd is %d \n", cmd); return 0; } int dtv_cmdlist_get_cmd(struct cmd_node *dtv_cmd_list,int *cmd, int *path_id) { struct cmd_node *dtv_cmd = NULL; AM_LOGV("enter dtv_patch_get_cmd function now\n"); pthread_mutex_lock(&dtv_cmd_list->dtv_cmd_mutex); dtv_cmd = dtv_cmd_list->next; if (dtv_cmd != NULL) { dtv_cmd_list->next = dtv_cmd->next; dtv_cmd_list->cmd_num--; } else { pthread_mutex_unlock(&dtv_cmd_list->dtv_cmd_mutex); return -1; } dtv_cmd->used = 0; *cmd = dtv_cmd->cmd; *path_id = dtv_cmd->path_id; aml_audio_free(dtv_cmd); pthread_mutex_unlock(&dtv_cmd_list->dtv_cmd_mutex); AM_LOGV("leave dtv_patch_get_cmd the cmd is %d path %d\n", *cmd, *path_id); return 0; } int dtv_cmdlist_is_empty(struct cmd_node *dtv_cmd_list) { pthread_mutex_lock(&dtv_cmd_list->dtv_cmd_mutex); if (dtv_cmd_list->next == NULL) { pthread_mutex_unlock(&dtv_cmd_list->dtv_cmd_mutex); return 1; } pthread_mutex_unlock(&dtv_cmd_list->dtv_cmd_mutex); return 0; } int dtv_get_demuxidbase() { return DVB_DEMUX_ID_BASE; } audio_format_t dmx_fmt_convert2_hal_fmt (int input_fmt) { struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); if (!aml_dev) { AM_LOGE("get adev error"); return -1; } if (input_fmt == ACODEC_FMT_AC3) { return AUDIO_FORMAT_AC3; } else if (input_fmt == ACODEC_FMT_EAC3) { return AUDIO_FORMAT_E_AC3; } else if (input_fmt == ACODEC_FMT_DTS) { return AUDIO_FORMAT_DTS; } else if (input_fmt == ACODEC_FMT_AAC ) { if (eDolbyMS12Lib == aml_dev->dolby_lib_type_last) return AUDIO_FORMAT_HE_AAC_V2; else return AUDIO_FORMAT_AAC; } else if (input_fmt == ACODEC_FMT_AAC_LATM) { if (eDolbyMS12Lib == aml_dev->dolby_lib_type_last) return AUDIO_FORMAT_HE_AAC_V1; else return AUDIO_FORMAT_AAC_LATM; } else if (input_fmt == ACODEC_FMT_MPEG ) { return AUDIO_FORMAT_MP3; } else if (input_fmt == ACODEC_FMT_MPEG1) { return AUDIO_FORMAT_MP2;/* audio-base.h no define mpeg1 format */ } else if (input_fmt == ACODEC_FMT_MPEG2) { return AUDIO_FORMAT_MP2; } else if (input_fmt == ACODEC_FMT_AC4) { return AUDIO_FORMAT_AC4; } else if (input_fmt == ACODEC_FMT_MHAS) { return AUDIO_FORMAT_MPEGH; } else { return AUDIO_FORMAT_PCM_16_BIT; } } void dtv_do_ease_out(struct aml_audio_device *aml_dev) { if (eDolbyMS12Lib != aml_dev->dolby_lib_type) { AM_LOGE("%s(), TODO: need do ease out here", __func__); if (is_TV(aml_dev)) usleep(AUDIO_FADEOUT_TV_DURATION_US); else usleep(AUDIO_FADEOUT_STB_DURATION_US); } } float dtv_ad_mixlevel_vol_convert_to_gain(struct aml_stream_out *aml_out) { struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle(); float gain = 1.0; if (aml_out->stream_type == STREAM_MAIN_INPUT) { if (adev->mixing_level == 32) { gain = 0.0; } else if (adev->mixing_level > 0) { gain = DbToAmpl((-1.0) * adev->mixing_level); } } else if (aml_out->stream_type == STREAM_AD_INPUT) { if (adev->mixing_level == -32) { gain = 0.0; } else if (adev->mixing_level < 0) { gain = DbToAmpl(adev->mixing_level); } gain *= (adev->advol_level / 100.0f); } AM_LOGI("aout %p, mix level:%d, vol %d, gain %f", aml_out, adev->mixing_level, adev->advol_level, gain); return gain; } void dtv_es_data_free(struct mAudioEsDataInfo *mEsData) { if (mEsData) { if (mEsData->data) { aml_audio_free(mEsData->data); mEsData->data = NULL; } aml_audio_free(mEsData); } } void dmx_info_clean(aml_demux_audiopara_t *demux_info) { memset(demux_info, 0, sizeof(aml_demux_audiopara_t)); demux_info->main_pid = -1; demux_info->media_presentation_id = -1; demux_info->media_first_lang = -1; demux_info->media_second_lang = -1; demux_info->volume = 1.0; demux_info->mute = false; } AD_PACK_STATUS_T check_ad_package_status(int64_t main_pts, int64_t ad_pts, aml_demux_audiopara_t *demux_info) { struct aml_audio_device *aml_dev = (struct aml_audio_device *)adev_get_handle(); if (!aml_dev) { AM_LOGE("get adev error"); return -1; } int timems_diff = llabs(main_pts - ad_pts) / 90; if (timems_diff >= AD_PACK_STATUS_FREERUN_THRESHOLD_MS) { AM_LOGI("main_pts %"PRId64" ad_pts %"PRId64" timems_diff %dms is impossible so freerun ad data ", main_pts, ad_pts, timems_diff); return AD_PACK_STATUS_NORMAL; } if (timems_diff > AD_PACK_STATUS_UNNORMAL_THRESHOLD_MS) { AM_LOGI("main_pts %"PRId64" ad_pts %"PRId64" timems_diff %d it is impossible so drop ad data ", main_pts, ad_pts, timems_diff); return AD_PACK_STATUS_DROP; } AD_PACK_STATUS_T ad_status = demux_info->ad_package_status; int drop_threshold_ms,drop_start_threshold_ms,hold_start_threshold_ms,hold_threshold_ms; drop_threshold_ms = AD_PACK_STATUS_DROP_THRESHOLD_MS; drop_start_threshold_ms = AD_PACK_STATUS_DROP_START_THRESHOLD_MS; hold_threshold_ms = AD_PACK_STATUS_HOLD_THRESHOLD_MS; hold_start_threshold_ms = AD_PACK_STATUS_HOLD_START_THRESHOLD_MS; switch (ad_status) { case AD_PACK_STATUS_NORMAL: if (main_pts >= ad_pts) { timems_diff = (main_pts - ad_pts) / 90; if ( timems_diff > drop_threshold_ms) { AM_LOGI_IF(aml_dev->debug_flag, "main and ad timems_diff %d ms need drop ", timems_diff); ad_status = AD_PACK_STATUS_DROP; } else if (timems_diff < hold_start_threshold_ms) { AM_LOGI_IF(aml_dev->debug_flag, "main and ad timems_diff %d ms need hold ", timems_diff); ad_status = AD_PACK_STATUS_HOLD; } } else { timems_diff = (ad_pts - main_pts) / 90; AM_LOGI_IF(aml_dev->debug_flag, "ad ahead of main timems_diff %d ", timems_diff); ad_status = AD_PACK_STATUS_HOLD; } break; case AD_PACK_STATUS_DROP: if (main_pts > ad_pts) { timems_diff = (main_pts - ad_pts) / 90; if (timems_diff > drop_start_threshold_ms) { AM_LOGI_IF(aml_dev->debug_flag, "main and ad timems_diff %d ms need drop ", timems_diff); ad_status = AD_PACK_STATUS_DROP; } else { ad_status = AD_PACK_STATUS_NORMAL; } } else { ad_status = AD_PACK_STATUS_HOLD; } break; case AD_PACK_STATUS_HOLD: if (main_pts < ad_pts) { timems_diff = (ad_pts - main_pts) / 90; AM_LOGI_IF(aml_dev->debug_flag, "ad ahead of main timems_diff %d ", timems_diff); ad_status = AD_PACK_STATUS_HOLD; } else { timems_diff = (main_pts - ad_pts) / 90; if (timems_diff > hold_start_threshold_ms && timems_diff < hold_threshold_ms) { ad_status = AD_PACK_STATUS_NORMAL; } else if (timems_diff >= hold_threshold_ms) { ad_status = AD_PACK_STATUS_DROP; AM_LOGI_IF(aml_dev->debug_flag, "timems_diff %d, drop!", timems_diff); } else { ad_status = AD_PACK_STATUS_NORMAL; } } break; default: AM_LOGI("invalid status %d ", ad_status); } AM_LOGI_IF(aml_dev->debug_flag, "main_pts %"PRId64" ad_pts %"PRId64" ad_status %d timems_diff %d", main_pts, ad_pts, ad_status, timems_diff); return ad_status; } bool check_ad_buffer_available(struct audio_stream_out *stream) { struct aml_stream_out *aml_out = (struct aml_stream_out *) stream; if (aml_out && aml_out->source_type == DTV_SRC && aml_out->mix_buf && aml_out->mix_buf->mix_buffer.buffer && aml_out->mix_buf->consumed == false) { return true; } else { return false; } }