/* * 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 #include #include #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; }