Files

406 lines
13 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_hal_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 <unistd.h>
#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;
}
}