Files
2026-08-24 10:03:24 +08:00

1309 lines
57 KiB
C

/*
* 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 "aml_audio_log.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 <system/audio.h>
#include <time.h>
#include <sys/ioctl.h>
#include <unistd.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)
#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);
}
}