* 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 "AudioVividPlaybackManager.h"
#include "AudioVividWrapper.h"
#include <cstring>
#include <chrono>
AudioVividPlaybackManager::AudioVividPlaybackManager()
{}
AudioVividPlaybackManager::~AudioVividPlaybackManager()
{
Release();
}
int32_t AudioVividPlaybackManager::Initialize()
{
int32_t ret = pcmReader_.Open(
"/data/storage/el2/base/haps/entry/files/resfile/2ch_2obj_48k_24bit.pcm", SAMPLE_RATE, PCM_CHANNEL_COUNT, 24);
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to open PCM file");
return -1;
}
ret = encoder_.Create();
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to create encoder");
return -1;
}
ret = encoder_.Config(SAMPLE_RATE, PCM_CHANNEL_COUNT, CHANNEL_LAYOUT, BITRATE);
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to config encoder");
return -1;
}
ret = decoder_.Create();
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to create decoder");
return -1;
}
ret = decoder_.Config(SAMPLE_RATE, PCM_CHANNEL_COUNT, CHANNEL_LAYOUT);
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to config decoder");
return -1;
}
ret = renderer_.Create(SAMPLE_RATE, PCM_CHANNEL_COUNT, CHANNEL_LAYOUT);
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to create renderer");
return -1;
}
renderer_.SetPlaybackManager(this);
AVCODEC_SAMPLE_LOGI("AudioVividPlaybackManager initialized successfully");
return 0;
}
int32_t AudioVividPlaybackManager::Start()
{
if (isRunning_.load()) {
AVCODEC_SAMPLE_LOGW("Playback is already running");
return 0;
}
shouldStop_.store(false);
presentationTimeUs_.store(0);
GenerateInitialMetadata();
int32_t ret = renderer_.Start();
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to start renderer");
return -1;
}
ret = encoder_.Start();
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to start encoder");
return -1;
}
ret = decoder_.Start();
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to start decoder");
return -1;
}
encoderInputThread_ = std::thread(&AudioVividPlaybackManager::EncoderInputThread, this);
encoderOutputThread_ = std::thread(&AudioVividPlaybackManager::EncoderOutputThread, this);
decoderOutputThread_ = std::thread(&AudioVividPlaybackManager::DecoderOutputThread, this);
isRunning_.store(true);
AVCODEC_SAMPLE_LOGI("AudioVividPlaybackManager started successfully");
return 0;
}
int32_t AudioVividPlaybackManager::Stop()
{
if (!isRunning_.load()) {
AVCODEC_SAMPLE_LOGW("Playback is not running");
return 0;
}
shouldStop_.store(true);
encoder_.GetContext()->inputCond.notify_all();
encoder_.GetContext()->outputCond.notify_all();
decoder_.GetContext()->inputCond.notify_all();
decoder_.GetContext()->outputCond.notify_all();
decodedQueueCond_.notify_all();
if (encoderInputThread_.joinable()) {
encoderInputThread_.join();
}
if (encoderOutputThread_.joinable()) {
encoderOutputThread_.join();
}
if (decoderOutputThread_.joinable()) {
decoderOutputThread_.join();
}
decoder_.Stop();
encoder_.Stop();
renderer_.Stop();
isRunning_.store(false);
AVCODEC_SAMPLE_LOGI("AudioVividPlaybackManager stopped successfully");
return 0;
}
int32_t AudioVividPlaybackManager::Release()
{
Stop();
encoder_.Release();
decoder_.Release();
renderer_.Release();
pcmReader_.Close();
if (currentMetadata_) {
delete[] currentMetadata_;
currentMetadata_ = nullptr;
}
currentMetadataSize_ = 0;
std::lock_guard<std::mutex> lock(decodedQueueMutex_);
while (!decodedDataQueue_.empty()) {
AudioVividDecoderData *data = decodedDataQueue_.front();
decodedDataQueue_.pop();
if (data) {
if (data->pcmData) {
delete[] data->pcmData;
}
if (data->metadata) {
delete[] data->metadata;
}
delete data;
}
}
AVCODEC_SAMPLE_LOGI("AudioVividPlaybackManager released successfully");
return 0;
}
void AudioVividPlaybackManager::GenerateInitialMetadata()
{
vividWrapper_.Initialize();
vividWrapper_.UpdateObjectPosition(0, 1.0f, 0.0f, 0.0f);
vividWrapper_.UpdateObjectPosition(1, -1.0f, 0.0f, 0.0f);
for (int i = 0; i < 2; i++) {
vividWrapper_.UpdateObjectGain(i, 1.0f);
}
std::vector<uint8_t> metadata = vividWrapper_.GetMetadata(true);
{
std::lock_guard<std::mutex> lock(metadataMutex_);
if (currentMetadata_) {
delete[] currentMetadata_;
}
currentMetadata_ = new uint8_t[metadata.size()];
memcpy(currentMetadata_, metadata.data(), metadata.size());
currentMetadataSize_ = metadata.size();
}
encoder_.UpdateMetadata(currentMetadata_, currentMetadataSize_);
}
void AudioVividPlaybackManager::UpdateMetadata(uint8_t *metadata, int32_t metadataSize)
{
std::lock_guard<std::mutex> lock(metadataMutex_);
if (currentMetadata_) {
delete[] currentMetadata_;
}
if (metadataSize > 10485760) {
return;
}
currentMetadata_ = new uint8_t[metadataSize];
memcpy(currentMetadata_, metadata, metadataSize);
currentMetadataSize_ = metadataSize;
encoder_.UpdateMetadata(metadata, metadataSize);
}
int32_t AudioVividPlaybackManager::GetDecodedDataToRender(
uint8_t **pcmData, int32_t *pcmSize, uint8_t **metadata, int32_t *metadataSize)
{
std::unique_lock<std::mutex> lock(decodedQueueMutex_);
decodedQueueCond_.wait(lock, [this] { return !decodedDataQueue_.empty() || shouldStop_.load(); });
if (shouldStop_.load() && decodedDataQueue_.empty()) {
return -1;
}
AudioVividDecoderData *data = decodedDataQueue_.front();
decodedDataQueue_.pop();
*pcmData = data->pcmData;
*pcmSize = data->pcmSize;
*metadata = data->metadata;
*metadataSize = data->metadataSize;
data->pcmData = nullptr;
data->metadata = nullptr;
delete data;
return 0;
}
void AudioVividPlaybackManager::EncoderInputThread()
{
uint8_t *pcmBuffer = new uint8_t[BYTES_PER_PCM_FRAME];
uint8_t *encoderBuffer = new uint8_t[BYTES_PER_FRAME];
while (!shouldStop_.load()) {
size_t queueSize = 0;
{
std::lock_guard<std::mutex> lock(decodedQueueMutex_);
queueSize = decodedDataQueue_.size();
}
if (queueSize >= MAX_DECODED_QUEUE_SIZE) {
std::this_thread::sleep_for(std::chrono::milliseconds(20));
continue;
}
int32_t bytesRead = pcmReader_.Read(pcmBuffer, BYTES_PER_PCM_FRAME);
if (bytesRead <= 0) {
if (pcmReader_.IsEOF()) {
pcmReader_.Reset();
AVCODEC_SAMPLE_LOGI("PCM file looped, restarting from beginning");
}
continue;
}
memset(encoderBuffer, 0, BYTES_PER_FRAME);
memcpy(encoderBuffer, pcmBuffer, bytesRead);
int64_t pts = presentationTimeUs_.fetch_add(FRAME_SIZE * 1000000 / SAMPLE_RATE);
uint8_t *metadata = nullptr;
int32_t metadataSize = 0;
{
std::lock_guard<std::mutex> lock(metadataMutex_);
if (currentMetadata_ && currentMetadataSize_ > 0) {
metadata = currentMetadata_;
metadataSize = currentMetadataSize_;
}
}
int32_t ret = encoder_.PushInputBuffer(encoderBuffer, BYTES_PER_FRAME, metadata, metadataSize, pts);
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to push input buffer to encoder");
}
}
delete[] pcmBuffer;
delete[] encoderBuffer;
AVCODEC_SAMPLE_LOGI("EncoderInputThread exited");
}
void AudioVividPlaybackManager::EncoderOutputThread()
{
while (!shouldStop_.load()) {
AudioVividEncoderOutputData *outputData = encoder_.GetOutputBuffer();
if (outputData == nullptr) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
if (outputData->eos) {
delete outputData;
break;
}
int32_t ret = decoder_.PushInputBuffer(outputData->encodedData, outputData->encodedSize);
if (ret != 0) {
AVCODEC_SAMPLE_LOGE("Failed to push input buffer to decoder");
}
delete outputData;
encoder_.FreeOutputBuffer();
}
AVCODEC_SAMPLE_LOGI("EncoderOutputThread exited");
}
void AudioVividPlaybackManager::DecoderOutputThread()
{
while (!shouldStop_.load()) {
AudioVividDecoderData *outputData = decoder_.GetOutputBuffer();
if (outputData == nullptr) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
continue;
}
if (outputData->pcmData && outputData->pcmSize > 0) {
std::lock_guard<std::mutex> lock(decodedQueueMutex_);
decodedDataQueue_.push(outputData);
decodedQueueCond_.notify_one();
} else {
if (outputData->pcmData) {
delete[] outputData->pcmData;
}
if (outputData->metadata) {
delete[] outputData->metadata;
}
delete outputData;
}
decoder_.FreeOutputBuffer();
}
AVCODEC_SAMPLE_LOGI("DecoderOutputThread exited");
}