228 lines
9.3 KiB
C
228 lines
9.3 KiB
C
/*
|
|
* Copyright (C) 2025 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_parser"
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "aml_audio_parser.h"
|
|
#include "audio_parser.h"
|
|
#include "aml_audio_log.h"
|
|
#include "aml_dump_debug.h"
|
|
#include "audio_mpegh.h"
|
|
#include "audio_hw_utils.h"
|
|
|
|
int audio_parser_process(struct audio_stream_out *stream, struct audio_buffer *input, output_function next_func)
|
|
{
|
|
struct aml_stream_out *aml_out = (struct aml_stream_out *) stream;
|
|
int parser_count = 0;
|
|
struct audio_buffer parser_input = {0};
|
|
struct audio_buffer abuffer_out = {0};
|
|
void *parser_in_buf = NULL;
|
|
int32_t parser_in_size = 0;
|
|
int32_t total_used_size = 0;
|
|
int32_t current_used_size = 0;
|
|
int return_bytes = 0;
|
|
struct aml_audio_device *adev = (struct aml_audio_device *) adev_get_handle();
|
|
int debug_enable = get_debug_value(AML_DEBUG_AUDIOHAL_PARSER);
|
|
int ret = -1;
|
|
|
|
if (!stream) {
|
|
AM_LOGE("invalid stream %p", stream);
|
|
goto parser_error;
|
|
}
|
|
if (false == check_audio_buffer_valid(input)) {
|
|
AM_LOGE("audio buffer checked fail");
|
|
goto parser_error;
|
|
}
|
|
|
|
if (input->original_format == AUDIO_FORMAT_IEC61937) {
|
|
input->iec_data_buf = input->buffer;
|
|
input->iec_data_size = input->size;
|
|
}
|
|
|
|
memcpy(&abuffer_out, input, sizeof(abuffer_out));
|
|
abuffer_out.buffer = NULL;
|
|
abuffer_out.iec_data_buf = NULL;
|
|
|
|
memcpy(&parser_input, input, sizeof(parser_input));
|
|
parser_in_buf = (void *) input->buffer;
|
|
parser_in_size = input->size;
|
|
|
|
if (aml_out->next_parsered_pts == 0) {
|
|
if (!parser_input.b_pts_valid) {
|
|
AM_LOGE("first pts is invalid.");
|
|
}
|
|
aml_out->next_parsered_pts = parser_input.pts;
|
|
}
|
|
|
|
if (!parser_input.b_pts_valid) {
|
|
abuffer_out.pts = aml_out->next_parsered_pts;
|
|
abuffer_out.b_pts_valid = true;
|
|
}
|
|
|
|
AM_LOGI_IF(debug_enable, "======== parser_in_size(%d) total_used_size(%d) b_pts_valid(%d) pts(%"PRIu64"ms), last(%"PRIu64"ms)",
|
|
parser_in_size, total_used_size, input->b_pts_valid, input->pts/90, aml_out->last_pts/90);
|
|
|
|
if (!aml_out->aml_parser) {
|
|
ret = aml_audio_parser_open(&aml_out->aml_parser, input->format, aml_out->hal_internal_format);
|
|
if (ret != 0) {
|
|
AM_LOGE(" aml_spdif_decoder_open fail");
|
|
goto parser_error;
|
|
}
|
|
}
|
|
|
|
if (get_debug_value(AML_DUMP_AUDIOHAL_IN)) {
|
|
aml_dump_audio_bitstreams_with_id("input_before_parser.raw", input->buffer, input->size, aml_out->unique_ID);
|
|
}
|
|
|
|
|
|
while (parser_in_size > total_used_size) {
|
|
if (aml_out->fast_quit) {
|
|
AM_LOGI("fast quit, will break. total_used_size %d", total_used_size);
|
|
break;
|
|
}
|
|
|
|
parser_input.buffer = parser_in_buf + total_used_size;
|
|
parser_input.size = parser_in_size - total_used_size;
|
|
|
|
ret = aml_audio_parser_process(aml_out->aml_parser, &parser_input, &abuffer_out, ¤t_used_size);
|
|
total_used_size += current_used_size;
|
|
if (ret != 0) {
|
|
//AM_LOGW("aml_audio_parser_process_wrapper error");
|
|
break;
|
|
}
|
|
|
|
int bit_rate = 0;
|
|
if (input->pts > aml_out->last_pts && (input->pts - aml_out->last_pts)/90 > 1) {
|
|
bit_rate = input->size * 8 / ((input->pts - aml_out->last_pts)/90) * 1000;
|
|
}
|
|
// if audio stream is not AD and audio format is AAC or AC3 or EAC3, get audio info from parser
|
|
if (!(aml_out->flags & AUDIO_OUTPUT_FLAG_AD_STREAM) &&
|
|
(AUDIO_FORMAT_AAC == aml_out->hal_internal_format ||
|
|
AUDIO_FORMAT_AAC_LATM == aml_out->hal_internal_format ||
|
|
AUDIO_FORMAT_HE_AAC_V1 == aml_out->hal_internal_format ||
|
|
AUDIO_FORMAT_HE_AAC_V2 == aml_out->hal_internal_format ||
|
|
AUDIO_FORMAT_AC3 == aml_out->hal_internal_format ||
|
|
AUDIO_FORMAT_E_AC3 == aml_out->hal_internal_format)) {
|
|
struct audio_parser_info audio_info;
|
|
memset(&audio_info, 0x00, sizeof(audio_info));
|
|
if (!aml_audio_parser_get_info(aml_out->aml_parser, &audio_info)) {
|
|
if (!audio_info.bitrate) {
|
|
audio_info.bitrate = bit_rate;
|
|
}
|
|
if (audio_info.bitrate > 0) {
|
|
UpdateDecodedInfo_BitRate(audio_info.bitrate);
|
|
}
|
|
UpdateDecodedInfo_SampleRate_ChannelNum_ChannelConfiguration(audio_info.sample_rate, audio_info.channel_num, audio_info.ch_mask);
|
|
adev->audio_hal_info.channel_number = audio_info.channel_num;
|
|
}
|
|
} else if (aml_out->hal_format == AUDIO_FORMAT_AC4) {
|
|
if (bit_rate) {
|
|
UpdateDecodedInfo_BitRate(bit_rate);
|
|
}
|
|
}
|
|
|
|
//we don't trust the parser's sample rate
|
|
#if 0
|
|
if (aml_out->hal_rate != abuffer_out.sample_rate && abuffer_out.sample_rate != 0) {
|
|
aml_out->hal_rate = abuffer_out.sample_rate;
|
|
}
|
|
#endif
|
|
|
|
// add for aac
|
|
if (AUDIO_FORMAT_AAC == input->original_format ||
|
|
AUDIO_FORMAT_AAC_LATM == input->original_format ||
|
|
AUDIO_FORMAT_HE_AAC_V1 == input->original_format ||
|
|
AUDIO_FORMAT_HE_AAC_V2 == input->original_format) {
|
|
aml_out->hal_internal_format = input->format = abuffer_out.format;
|
|
aml_out->hal_format = abuffer_out.format;
|
|
}
|
|
|
|
if (get_debug_value(AML_DUMP_AUDIOHAL_IN)) {
|
|
aml_dump_audio_bitstreams_with_id("input_after_parser.raw", abuffer_out.buffer, abuffer_out.size, aml_out->unique_ID);
|
|
}
|
|
if (parser_count != 0) {
|
|
abuffer_out.b_pts_valid = false;
|
|
}
|
|
char *p = (char*)abuffer_out.buffer;
|
|
parser_count++;
|
|
AM_LOGI_IF(debug_enable, "After parser [%d]current_used_size %d, total_used_size %d, p %p", parser_count, current_used_size, total_used_size, p);
|
|
if (p != NULL && abuffer_out.size > 4) {
|
|
AM_LOGI_IF(debug_enable, "After parser in_buf(%p) out_buf(%p)(%x,%x,%x,%x) out_size(%"PRId32") b_pts_valid(%d) pts(%"PRIu64"ms) dur %d",
|
|
parser_in_buf, abuffer_out.buffer, *p, *(p+1),
|
|
*(p+2),*(p+3),abuffer_out.size, abuffer_out.b_pts_valid, abuffer_out.pts/90, abuffer_out.duration);
|
|
}
|
|
|
|
if (abuffer_out.buffer != NULL && abuffer_out.size != 0) {
|
|
aml_out->parser_frame_size = abuffer_out.size;
|
|
|
|
if (AUDIO_FORMAT_MPEGH == input->format) {
|
|
int asiupdate = 0;
|
|
//char *p = (char*)abuffer_out.buffer;
|
|
//AM_LOGI("[size:%d] data=%x,%x,%x,%x,%x,%x,%x,%x", abuffer_out.size, *p, *(p+1),*(p+2),*(p+3), *(p+4),*(p+5),*(p+6),*(p+7));
|
|
ret = aml_mpegh_process(&aml_out->mpegh_uimanager_handle, &abuffer_out, &asiupdate);
|
|
if (ret != 0) {
|
|
AM_LOGI_IF(debug_enable, "aml_mpegh_uimanager_process error, continue to decoder");
|
|
}
|
|
|
|
if (1 == asiupdate) {
|
|
struct aml_audio_device *adev = aml_out->dev;
|
|
/* send notify event */
|
|
aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_AUDIO_HAL_FORMAT, TYPE_MPEGH_ASI_UPDATE);
|
|
}
|
|
|
|
if (get_debug_value(AML_DUMP_AUDIOHAL_IN)) {
|
|
aml_dump_audio_bitstreams_with_id("input_main_after_uimanager.data", abuffer_out.buffer, abuffer_out.size, aml_out->unique_ID);
|
|
}
|
|
}
|
|
|
|
if (next_func) {
|
|
return_bytes = next_func(stream, &abuffer_out);
|
|
if (return_bytes != abuffer_out.size) {
|
|
AM_LOGE("return err return_bytes(%d) != write_size(%"PRId32"), need check!!", return_bytes, abuffer_out.size);
|
|
}
|
|
}
|
|
|
|
AM_LOGI_IF(debug_enable, "abuffer.pts: 0x%"PRIx64"(%"PRIu64") == > abuffer_out: 0x%"PRIx64"(%"PRIu64"), format:0x%x",
|
|
input->pts, input->pts, abuffer_out.pts, abuffer_out.pts, abuffer_out.format);
|
|
|
|
if (parser_input.b_pts_valid) {
|
|
aml_out->next_parsered_pts = parser_input.pts + abuffer_out.duration;
|
|
} else {
|
|
aml_out->next_parsered_pts += abuffer_out.duration;
|
|
}
|
|
|
|
AM_LOGI_IF(debug_enable, "abuffer_out: 0x%"PRIx64"(%"PRIu64"), format:0x%x valid:%d next_parsered_pts:(%"PRIu64"ms)",
|
|
abuffer_out.pts, abuffer_out.pts, abuffer_out.format, abuffer_out.b_pts_valid, aml_out->next_parsered_pts / 90);
|
|
|
|
}
|
|
}
|
|
|
|
return total_used_size;
|
|
|
|
parser_error:
|
|
if (next_func) {
|
|
return_bytes = next_func(stream, input);
|
|
if (return_bytes != input->size) {
|
|
AM_LOGE("return err return_bytes(%d) != write_size(%"PRId32"), need check!!", return_bytes, abuffer_out.size);
|
|
}
|
|
}
|
|
|
|
return input->size;
|
|
}
|