494 lines
16 KiB
C
494 lines
16 KiB
C
/*
|
|
* Copyright (C) 2021 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 "aml_dec_api"
|
|
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <fcntl.h>
|
|
#include <dlfcn.h>
|
|
#include <string.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/time.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <dlfcn.h>
|
|
#include "aml_audio_log.h"
|
|
|
|
#include "aml_dec_api.h"
|
|
#include "aml_ddp_dec_api.h"
|
|
#include "aml_dtsx_dec_api.h"
|
|
#include "aml_pcm_dec_api.h"
|
|
#include "aml_adpcm_dec_api.h"
|
|
#include "aml_mpeg_dec_api.h"
|
|
#include "aml_aac_dec_api.h"
|
|
#include "aml_flac_dec_api.h"
|
|
#include "aml_opus_dec_api.h"
|
|
#include "aml_vorbis_dec_api.h"
|
|
#include "aml_mpegh_dec_api.h"
|
|
#include "aml_iamf_dec_api.h"
|
|
#include "audio_hw_utils.h"
|
|
#include "aml_dump_debug.h"
|
|
|
|
#ifdef BUILD_LINUX
|
|
#define DTS_X_LIB_PATH_A "/vendor/lib/libHwAudio_dtsx.so"
|
|
#define DTS_HD_LIB_PATH_A "/usr/lib/libHwAudio_dtshd.so"
|
|
#else
|
|
#define DTS_X_LIB_PATH_A "/odm/lib/libHwAudio_dtsx.so"
|
|
#define DTS_HD_LIB_PATH_A "/odm/lib/libHwAudio_dtshd.so"
|
|
#endif
|
|
static const char* search_paths[] = {
|
|
"/usr/lib",
|
|
"/vendor/lib",
|
|
"/system/lib",
|
|
NULL
|
|
};
|
|
#define AML_DEC_FRAGMENT_FRAMES (512)
|
|
#define AML_DEC_MAX_FRAMES (AML_DEC_FRAGMENT_FRAMES * 4)
|
|
static eDTSLibType_t gDtsLibType = eDTSNull;
|
|
|
|
static aml_dec_func_t * get_decoder_function(audio_format_t format)
|
|
{
|
|
switch (format) {
|
|
case AUDIO_FORMAT_AC3:
|
|
case AUDIO_FORMAT_E_AC3: {
|
|
return &aml_dcv_func;
|
|
}
|
|
case AUDIO_FORMAT_DOLBY_TRUEHD:
|
|
case AUDIO_FORMAT_MAT:
|
|
return NULL;
|
|
case AUDIO_FORMAT_DTS:
|
|
case AUDIO_FORMAT_DTS_HD: {
|
|
if (gDtsLibType == eDTSXLib) {
|
|
return &aml_dtsx_func;
|
|
} else if (gDtsLibType == eDTSHDLib) {
|
|
return &aml_dca_func;
|
|
} else {
|
|
ALOGE("[%s:%d] Without any dts library", __func__, __LINE__);
|
|
return NULL;
|
|
}
|
|
}
|
|
case AUDIO_FORMAT_PCM_16_BIT:
|
|
case AUDIO_FORMAT_PCM_32_BIT:
|
|
case AUDIO_FORMAT_PCM_8_BIT:
|
|
case AUDIO_FORMAT_PCM_8_24_BIT:
|
|
case AUDIO_FORMAT_PCM_LPCM_DVD:
|
|
case AUDIO_FORMAT_PCM_LPCM_1394:
|
|
case AUDIO_FORMAT_PCM_LPCM_BLURAY: {
|
|
return &aml_pcm_func;
|
|
}
|
|
case AUDIO_FORMAT_MP3:
|
|
case AUDIO_FORMAT_MP2: {
|
|
return &aml_mad_func;
|
|
}
|
|
case AUDIO_FORMAT_AAC:
|
|
case AUDIO_FORMAT_AAC_LATM:
|
|
case AUDIO_FORMAT_HE_AAC_V1:
|
|
case AUDIO_FORMAT_HE_AAC_V2:{
|
|
return &aml_faad_func;
|
|
}
|
|
case AUDIO_FORMAT_FLAC: {
|
|
return &aml_flac_func;
|
|
}
|
|
case AUDIO_FORMAT_OPUS: {
|
|
return &aml_opus_func;
|
|
}
|
|
case AUDIO_FORMAT_VORBIS: {
|
|
return &aml_vorbis_func;
|
|
}
|
|
case AUDIO_FORMAT_PCM_ADPCM_IMA_WAV: {
|
|
return &aml_adpcm_func;
|
|
}
|
|
case AUDIO_FORMAT_MPEGH: {
|
|
return &aml_mpegh_func;
|
|
}
|
|
case AUDIO_FORMAT_IAMF: {
|
|
return &aml_iamf_func;
|
|
}
|
|
default:
|
|
ALOGE("[%s:%d] doesn't support decoder format:%#x", __func__, __LINE__, format);
|
|
return NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int aml_decoder_init(aml_dec_t **ppaml_dec, audio_format_t format, aml_dec_config_t * dec_config)
|
|
{
|
|
int ret = -1;
|
|
aml_dec_func_t *dec_fun = NULL;
|
|
dec_fun = get_decoder_function(format);
|
|
aml_dec_t *aml_dec_handel = NULL;
|
|
if (dec_fun == NULL) {
|
|
ALOGE("%s got dec_fun as NULL!\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
ALOGD("[%s:%d] dec_fun->f_init=%p, format:%#x", __func__, __LINE__, dec_fun->f_init, format);
|
|
if (dec_fun->f_init) {
|
|
ret = dec_fun->f_init(ppaml_dec, dec_config);
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
aml_dec_handel = *ppaml_dec;
|
|
aml_dec_handel->frame_cnt = 0;
|
|
aml_dec_handel->format = format;
|
|
aml_dec_handel->fragment_left_size = 0;
|
|
aml_dec_handel->out_frame_pts = -1;
|
|
aml_dec_handel->last_in_frame_pts = -1;
|
|
aml_dec_handel->debug_level = get_debug_value(AML_DEBUG_AUDIOHAL_DECODED_INFO);
|
|
return ret;
|
|
}
|
|
int aml_decoder_release(aml_dec_t *aml_dec)
|
|
{
|
|
int ret = -1;
|
|
aml_dec_func_t *dec_fun = NULL;
|
|
if (aml_dec == NULL) {
|
|
ALOGE("%s aml_dec is NULL\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
dec_fun = get_decoder_function(aml_dec->format);
|
|
if (dec_fun == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (dec_fun->f_release) {
|
|
dec_fun->f_release(aml_dec);
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
if (access(REPORT_DECODED_INFO, F_OK) == 0) {
|
|
char info_buf[MAX_BUFF_LEN] = {0};
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
int val = 0;
|
|
sprintf(info_buf, "bitrate %d", val);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "ch_num %d", val);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "samplerate %d", val);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "decoded_frames %d", val);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "decoded_err %d", val);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "decoded_drop %d", val);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf (info_buf, "ch_configuration %d", val);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
int aml_decoder_set_config(aml_dec_t *aml_dec, aml_dec_config_type_t config_type, aml_dec_config_t * dec_config)
|
|
{
|
|
int ret = -1;
|
|
aml_dec_func_t *dec_fun = NULL;
|
|
if (aml_dec == NULL) {
|
|
ALOGE("%s aml_dec is NULL\n", __func__);
|
|
return -1;
|
|
}
|
|
dec_fun = get_decoder_function(aml_dec->format);
|
|
if (dec_fun == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (dec_fun->f_config) {
|
|
ret = dec_fun->f_config(aml_dec, config_type, dec_config);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int aml_decoder_get_info(aml_dec_t *aml_dec, aml_dec_info_type_t info_type, aml_dec_info_t * dec_info)
|
|
{
|
|
int ret = -1;
|
|
aml_dec_func_t *dec_fun = NULL;
|
|
if (aml_dec == NULL) {
|
|
ALOGE("%s aml_dec is NULL\n", __func__);
|
|
return -1;
|
|
}
|
|
dec_fun = get_decoder_function(aml_dec->format);
|
|
if (dec_fun == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (dec_fun->f_info) {
|
|
ret = dec_fun->f_info(aml_dec, info_type, dec_info);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int aml_decoder_ad_process(struct audio_stream_out *stream, struct audio_buffer *abuffer, int *used_bytes)
|
|
{
|
|
int ret = -1;
|
|
aml_dec_func_t *dec_fun = NULL;
|
|
struct aml_stream_out *out = (struct aml_stream_out *)stream;
|
|
|
|
if (!stream) {
|
|
AM_LOGE("invalid stream:%p", stream);
|
|
return AML_DEC_RETURN_TYPE_FAIL;
|
|
}
|
|
if (false == check_audio_buffer_valid(abuffer)) {
|
|
AM_LOGE("audio buffer checked fail");
|
|
return AML_DEC_RETURN_TYPE_FAIL;
|
|
}
|
|
|
|
dec_fun = get_decoder_function(abuffer->original_format);
|
|
*used_bytes = 0;
|
|
if (dec_fun == NULL) {
|
|
ALOGW("[%s:%d] get_decoder_function format:%#x is null", __func__, __LINE__, abuffer->original_format);
|
|
return -1;
|
|
}
|
|
|
|
if (dec_fun->f_ad_process) {
|
|
ret = dec_fun->f_ad_process(out->aml_dec, abuffer);
|
|
} else {
|
|
ALOGE("[%s:%d] f_ad_process is null", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
if (ret >= 0 ) {
|
|
*used_bytes = ret;
|
|
return AML_DEC_RETURN_TYPE_OK;
|
|
} else {
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
|
|
int aml_system_sound_process(aml_dec_t *aml_dec, struct audio_buffer *abuffer, int *used_bytes)
|
|
{
|
|
int ret = -1;
|
|
aml_dec_func_t *dec_fun = NULL;
|
|
|
|
*used_bytes = 0;
|
|
if (aml_dec == NULL) {
|
|
ALOGE("[%s:%d] aml_dec is null", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
dec_fun = get_decoder_function(aml_dec->format);
|
|
if (dec_fun == NULL) {
|
|
ALOGW("[%s:%d] get_decoder_function format:%#x is null", __func__, __LINE__, aml_dec->format);
|
|
return -1;
|
|
}
|
|
|
|
if (dec_fun->f_system_sound_process) {
|
|
ret = dec_fun->f_system_sound_process(aml_dec, abuffer);
|
|
} else {
|
|
ALOGE("[%s:%d] f_system_sound_process is null", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
if (ret >= 0 ) {
|
|
*used_bytes = ret;
|
|
return AML_DEC_RETURN_TYPE_OK;
|
|
} else {
|
|
AM_LOGE("f_system_sound_process fail");
|
|
*used_bytes = abuffer->size;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
int aml_decoder_process(aml_dec_t *aml_dec, struct audio_buffer *abuffer, int *used_bytes)
|
|
{
|
|
int ret = -1;
|
|
aml_dec_func_t *dec_fun = NULL;
|
|
int frame_size = 0;
|
|
int fragment_size = 0;
|
|
dec_data_info_t * dec_pcm_data = NULL;
|
|
dec_data_info_t * dec_raw_data = NULL;
|
|
dec_data_info_t * raw_in_data = NULL;
|
|
|
|
if (aml_dec == NULL) {
|
|
ALOGE("[%s:%d] aml_dec is null", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
dec_pcm_data = &aml_dec->dec_pcm_data;
|
|
dec_raw_data = &aml_dec->dec_raw_data;
|
|
raw_in_data = &aml_dec->raw_in_data;
|
|
*used_bytes = 0;
|
|
|
|
dec_fun = get_decoder_function(aml_dec->format);
|
|
if (dec_fun == NULL) {
|
|
ALOGW("[%s:%d] get_decoder_function format:%#x is null", __func__, __LINE__, aml_dec->format);
|
|
return -1;
|
|
}
|
|
|
|
if ((dec_fun != &aml_dtsx_func) && (dec_fun != &aml_dca_func) && (false == check_audio_buffer_valid(abuffer))) {
|
|
AM_LOGE("audio buffer checked fail");
|
|
return -1;
|
|
}
|
|
/*if we have fragment size output*/
|
|
if (aml_dec->fragment_left_size > 0) {
|
|
ALOGI("[%s:%d] fragment_left_size=%d ", __func__, __LINE__, aml_dec->fragment_left_size);
|
|
frame_size = audio_bytes_per_sample(dec_pcm_data->data_format) * dec_pcm_data->data_ch;
|
|
fragment_size = AML_DEC_FRAGMENT_FRAMES * frame_size;
|
|
memmove(dec_pcm_data->buf, (unsigned char *)dec_pcm_data->buf + fragment_size, aml_dec->fragment_left_size);
|
|
memmove(dec_raw_data->buf, (unsigned char *)dec_raw_data->buf + fragment_size, aml_dec->fragment_left_size);
|
|
|
|
if (aml_dec->fragment_left_size >= fragment_size) {
|
|
dec_pcm_data->data_len = fragment_size;
|
|
dec_raw_data->data_len = fragment_size;
|
|
aml_dec->fragment_left_size -= fragment_size;
|
|
} else {
|
|
dec_pcm_data->data_len = aml_dec->fragment_left_size;
|
|
dec_raw_data->data_len = aml_dec->fragment_left_size;
|
|
aml_dec->fragment_left_size = 0;
|
|
}
|
|
*used_bytes = 0;
|
|
return 0;
|
|
}
|
|
|
|
dec_pcm_data->data_len = 0;
|
|
dec_raw_data->data_len = 0;
|
|
raw_in_data->data_len = 0;
|
|
if (dec_fun->f_process) {
|
|
ret = dec_fun->f_process(aml_dec, abuffer);
|
|
} else {
|
|
ALOGE("[%s:%d] f_process is null", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
if (access(REPORT_DECODED_INFO, F_OK) == 0) {
|
|
aml_dec_info_t dec_info;
|
|
memset(&dec_info, 0x00, sizeof(dec_info));
|
|
char info_buf[MAX_BUFF_LEN] = {0};
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
aml_decoder_get_info(aml_dec, AML_DEC_STREMAM_INFO, &dec_info);
|
|
|
|
sprintf(info_buf, "bitrate %d", dec_info.dec_info.stream_bitrate);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "ch_num %d", dec_info.dec_info.stream_ch);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "samplerate %d", dec_info.dec_info.stream_sr);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "decoded_frames %d", dec_info.dec_info.stream_decode_num);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "decoded_err %d", dec_info.dec_info.stream_error_num);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "decoded_drop %d", dec_info.dec_info.stream_drop_num);
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
memset(info_buf, 0x00, MAX_BUFF_LEN);
|
|
sprintf(info_buf, "ch_configuration %d", audio_channel_out_mask_from_count(dec_info.dec_info.stream_ch));
|
|
sysfs_set_sysfs_str(REPORT_DECODED_INFO, info_buf);
|
|
}
|
|
|
|
frame_size = audio_bytes_per_sample(dec_pcm_data->data_format) * dec_pcm_data->data_ch;
|
|
/*one decoded frame length is too big, we need separate it*/
|
|
if ((dec_pcm_data->data_len >= AML_DEC_MAX_FRAMES * frame_size) &&
|
|
(dec_raw_data->data_format == AUDIO_FORMAT_IEC61937) &&
|
|
(dec_raw_data->data_len == dec_pcm_data->data_len)) {
|
|
fragment_size = AML_DEC_FRAGMENT_FRAMES * frame_size;
|
|
aml_dec->fragment_left_size = dec_pcm_data->data_len - fragment_size;
|
|
dec_pcm_data->data_len = fragment_size;
|
|
dec_raw_data->data_len = fragment_size;
|
|
}
|
|
|
|
if (ret >= 0 ) {
|
|
*used_bytes = ret;
|
|
return AML_DEC_RETURN_TYPE_OK;
|
|
} else {
|
|
*used_bytes = abuffer->size;
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
|
|
eDTSLibType_t detect_dts_lib_type(bool Platform_support)
|
|
{
|
|
void *hDTSLibHanle = NULL;
|
|
|
|
// the priority would be "DTSX > DTSHD" lib
|
|
// DTSX is first priority
|
|
/*coverity[toctou]: DTS_X_LIB_PATH_A will not change after access*/
|
|
if (access(DTS_X_LIB_PATH_A, R_OK) == 0 && Platform_support) {
|
|
// try to open lib see if it's OK?
|
|
hDTSLibHanle = dlopen(DTS_X_LIB_PATH_A, RTLD_NOW);
|
|
if (hDTSLibHanle != NULL) {
|
|
dlclose(hDTSLibHanle);
|
|
hDTSLibHanle = NULL;
|
|
gDtsLibType = eDTSXLib;
|
|
ALOGI("[%s:%d] Found libHwAudio_dtsx lib", __func__, __LINE__);
|
|
return eDTSXLib;
|
|
}
|
|
}
|
|
|
|
// DTSHD is second priority
|
|
/*coverity[toctou]: DTS_X_LIB_PATH_A will not change after access*/
|
|
if (access(DTS_HD_LIB_PATH_A, R_OK) == 0) {
|
|
// try to open lib see if it's OK?
|
|
hDTSLibHanle = dlopen(DTS_HD_LIB_PATH_A, RTLD_NOW);
|
|
if (hDTSLibHanle != NULL) {
|
|
dlclose(hDTSLibHanle);
|
|
hDTSLibHanle = NULL;
|
|
gDtsLibType = eDTSHDLib;
|
|
ALOGI("[%s:%d] Found libHwAudio_dtshd lib", __func__, __LINE__);
|
|
return eDTSHDLib;
|
|
}
|
|
}
|
|
|
|
ALOGW("[%s:%d] Failed to find libHwAudio_dtsx.so and libHwAudio_dtshd.so, %s", __FUNCTION__, __LINE__, dlerror());
|
|
return eDTSNull;
|
|
}
|
|
eDTSLibType_t get_dts_lib_type(void)
|
|
{
|
|
return gDtsLibType;
|
|
}
|
|
void* aml_decoder_load(const char* lib_name) {
|
|
char path[128] = {0};
|
|
|
|
for (int i = 0; search_paths[i] != NULL; i++) {
|
|
snprintf(path, sizeof(path), "%s/%s", search_paths[i], lib_name);
|
|
if (access(path, F_OK) == 0) {
|
|
AM_LOGD("find %s in %s", lib_name, search_paths[i]);
|
|
void * handle = dlopen(path, RTLD_NOW);
|
|
if (handle) {
|
|
return handle;
|
|
} else {
|
|
AM_LOGE("failed to open (%s), %s", path, dlerror());
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|