* Copyright (c) 2026 Huawei Device Co., Ltd.
* 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.
*/
#include "AudioVividDecoder.h"
#include <multimedia/player_framework/native_avbuffer.h>
#include <cstring>
AudioVividDecoder::~AudioVividDecoder()
{
Release();
}
int32_t AudioVividDecoder::Create()
{
decoder_ = OH_AudioCodec_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_VIVID, false);
if (decoder_ == nullptr) {
AVCODEC_SAMPLE_LOGE("Create AudioVivid decoder failed");
return -1;
}
AVCODEC_SAMPLE_LOGI("Create AudioVivid decoder success");
return 0;
}
int32_t AudioVividDecoder::Config(int32_t sampleRate, int32_t channelCount, int64_t channelLayout)
{
sampleRate_ = sampleRate;
channelCount_ = channelCount;
channelLayout_ = channelLayout;
int32_t ret = Configure();
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Configure failed");
return ret;
}
ret = SetCallback();
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("SetCallback failed");
return ret;
}
ret = OH_AudioCodec_Prepare(decoder_);
if (ret != AV_ERR_OK) {
AVCODEC_SAMPLE_LOGE("Prepare failed, ret: %{public}d", ret);
return -1;
}
AVCODEC_SAMPLE_LOGI(
"Config AudioVivid decoder: sampleRate=%{public}d, channelCount=%{public}d", sampleRate_, channelCount_);
return 0;
}
int32_t AudioVividDecoder::Configure()
{
OH_AVFormat *format = OH_AVFormat_Create();
if (format == nullptr) {
AVCODEC_SAMPLE_LOGE("AVFormat create failed");
return -1;
}
OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUDIO_SAMPLE_FORMAT, SAMPLE_S24LE);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_CHANNEL_COUNT, channelCount_);
OH_AVFormat_SetIntValue(format, OH_MD_KEY_AUD_SAMPLE_RATE, sampleRate_);
OH_AVFormat_SetLongValue(format, OH_MD_KEY_CHANNEL_LAYOUT, channelLayout_);
int32_t ret = OH_AudioCodec_Configure(decoder_, format);
OH_AVFormat_Destroy(format);
if (ret != AV_ERR_OK) {
AVCODEC_SAMPLE_LOGE("Configure decoder failed, ret: %{public}d", ret);
return -1;
}
return 0;
}
int32_t AudioVividDecoder::SetCallback()
{
auto onError = [](OH_AVCodec *codec, int32_t errorCode, void *userData) {
AVCODEC_SAMPLE_LOGE("Decoder error: %{public}d", errorCode);
};
auto onFormatChange = [](OH_AVCodec *codec, OH_AVFormat *format, void *userData) {
AVCODEC_SAMPLE_LOGI("Decoder format change");
};
auto onNeedInputBuffer = [](OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
AudioVividDecoderContext *context = static_cast<AudioVividDecoderContext *>(userData);
std::unique_lock<std::mutex> lock(context->inputMutex);
context->inputBufferIndices.push(index);
context->inputBufferQueue.push(buffer);
context->inputCond.notify_all();
};
auto onNewOutputBuffer = [](OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) {
AudioVividDecoderContext *context = static_cast<AudioVividDecoderContext *>(userData);
AudioVividDecoderData outputData;
OH_AVCodecBufferAttr attr;
OH_AVBuffer_GetBufferAttr(buffer, &attr);
outputData.pcmSize = attr.size;
outputData.presentationTimeUs = attr.pts;
uint8_t *pcmAddr = OH_AVBuffer_GetAddr(buffer);
if (pcmAddr && attr.size > 0) {
outputData.pcmData = new uint8_t[attr.size];
memcpy(outputData.pcmData, pcmAddr, attr.size);
}
OH_AVFormat *format = OH_AVBuffer_GetParameter(buffer);
uint8_t *metadata = nullptr;
size_t metadataSize = 0;
OH_AVFormat_GetBuffer(format, OH_MD_KEY_AUDIO_VIVID_METADATA, &metadata, &metadataSize);
if (metadata && metadataSize > 0) {
outputData.metadataSize = metadataSize;
outputData.metadata = new uint8_t[metadataSize];
memcpy(outputData.metadata, metadata, metadataSize);
}
{
std::unique_lock<std::mutex> lock(context->outputMutex);
context->outputQueue.push(outputData);
context->outputCond.notify_all();
}
OH_AudioCodec_FreeOutputBuffer(codec, index);
};
int32_t ret = OH_AudioCodec_RegisterCallback(
decoder_, {onError, onFormatChange, onNeedInputBuffer, onNewOutputBuffer}, &context_);
if (ret != AV_ERR_OK) {
AVCODEC_SAMPLE_LOGE("RegisterCallback failed, ret: %{public}d", ret);
return -1;
}
return 0;
}
int32_t AudioVividDecoder::Start()
{
if (decoder_ == nullptr) {
AVCODEC_SAMPLE_LOGE("Decoder is null");
return -1;
}
int32_t ret = OH_AudioCodec_Start(decoder_);
if (ret != AV_ERR_OK) {
AVCODEC_SAMPLE_LOGE("Start decoder failed, ret: %{public}d", ret);
return -1;
}
AVCODEC_SAMPLE_LOGI("AudioVivid decoder started");
return 0;
}
int32_t AudioVividDecoder::PushInputBuffer(uint8_t *data, int32_t size)
{
if (decoder_ == nullptr || data == nullptr || size <= 0) {
AVCODEC_SAMPLE_LOGE("Invalid parameters");
return -1;
}
std::unique_lock<std::mutex> lock(context_.inputMutex);
if (context_.inputBufferIndices.empty()) {
context_.inputCond.wait_for(lock, std::chrono::milliseconds(100), [this] {
return !context_.inputBufferIndices.empty() || context_.eos;
});
}
if (context_.inputBufferIndices.empty()) {
AVCODEC_SAMPLE_LOGE("No input buffer available");
return -1;
}
uint32_t index = context_.inputBufferIndices.front();
context_.inputBufferIndices.pop();
OH_AVBuffer *buffer = context_.inputBufferQueue.front();
context_.inputBufferQueue.pop();
lock.unlock();
uint8_t *bufferAddr = OH_AVBuffer_GetAddr(buffer);
if (bufferAddr == nullptr) {
AVCODEC_SAMPLE_LOGE("input buffer nullptr");
return -1;
}
int32_t capacity = OH_AVBuffer_GetCapacity(buffer);
if (size > capacity) {
size = capacity;
}
memcpy(bufferAddr, data, size);
OH_AVCodecBufferAttr attr = {0, size, 0, AVCODEC_BUFFER_FLAGS_NONE};
OH_AVBuffer_SetBufferAttr(buffer, &attr);
int32_t ret = OH_AudioCodec_PushInputBuffer(decoder_, index);
if (ret != AV_ERR_OK) {
AVCODEC_SAMPLE_LOGE("Push input buffer failed, ret: %{public}d", ret);
return -1;
}
return 0;
}
AudioVividDecoderData *AudioVividDecoder::GetOutputBuffer()
{
std::unique_lock<std::mutex> lock(context_.outputMutex);
context_.outputCond.wait_for(lock, std::chrono::milliseconds(100),
[this] { return !context_.outputQueue.empty() || context_.eos; });
if (context_.outputQueue.empty()) {
return nullptr;
}
AudioVividDecoderData &frontData = context_.outputQueue.front();
AudioVividDecoderData *data = new AudioVividDecoderData();
data->pcmData = frontData.pcmData;
data->pcmSize = frontData.pcmSize;
data->metadata = frontData.metadata;
data->metadataSize = frontData.metadataSize;
data->presentationTimeUs = frontData.presentationTimeUs;
frontData.pcmData = nullptr;
frontData.metadata = nullptr;
return data;
}
void AudioVividDecoder::FreeOutputBuffer()
{
std::unique_lock<std::mutex> lock(context_.outputMutex);
if (!context_.outputQueue.empty()) {
context_.outputQueue.pop();
}
}
int32_t AudioVividDecoder::Stop()
{
if (decoder_ == nullptr) {
return -1;
}
context_.eos = true;
context_.inputCond.notify_all();
context_.outputCond.notify_all();
int32_t ret = OH_AudioCodec_Stop(decoder_);
if (ret != AV_ERR_OK) {
AVCODEC_SAMPLE_LOGE("Stop decoder failed, ret: %{public}d", ret);
return -1;
}
context_.inputBufferIndices = std::queue<uint32_t>();
context_.inputBufferQueue = std::queue<OH_AVBuffer *>();
AVCODEC_SAMPLE_LOGI("AudioVivid decoder stopped");
return 0;
}
int32_t AudioVividDecoder::Release()
{
if (decoder_ != nullptr) {
OH_AudioCodec_Flush(decoder_);
OH_AudioCodec_Destroy(decoder_);
decoder_ = nullptr;
}
{
std::unique_lock<std::mutex> lock(context_.outputMutex);
while (!context_.outputQueue.empty()) {
AudioVividDecoderData &data = context_.outputQueue.front();
if (data.pcmData) {
delete[] data.pcmData;
}
if (data.metadata) {
delete[] data.metadata;
}
context_.outputQueue.pop();
}
}
context_.eos = false;
context_.errorCode = 0;
return 0;
}