* Copyright (C) 2023 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 "audio_codec_adapter.h"
#include <malloc.h>
#include "avcodec_trace.h"
#include "avcodec_errors.h"
#include "avcodec_log.h"
#include "media_description.h"
namespace {
constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_AUDIO, "AvCodec-AudioCodecAdapter"};
constexpr uint8_t LOGD_FREQUENCY = 5;
}
namespace OHOS {
namespace MediaAVCodec {
AudioCodecAdapter::AudioCodecAdapter(const std::string &name) : state_(CodecState::RELEASED), name_(name) {}
AudioCodecAdapter::~AudioCodecAdapter()
{
if (worker_) {
worker_->Release();
worker_.reset();
worker_ = nullptr;
}
callback_ = nullptr;
if (audioCodec) {
audioCodec->Release();
audioCodec.reset();
audioCodec = nullptr;
}
state_ = CodecState::RELEASED;
(void)mallopt(M_FLUSH_THREAD_CACHE, 0);
}
int32_t AudioCodecAdapter::SetCallback(const std::shared_ptr<AVCodecCallback> &callback)
{
AVCODEC_SYNC_TRACE;
if (state_ != CodecState::RELEASED && state_ != CodecState::INITIALIZED && state_ != CodecState::INITIALIZING) {
AVCODEC_LOGE("SetCallback failed, state = %{public}s .", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
CHECK_AND_RETURN_RET_LOG(callback != nullptr, AVCodecServiceErrCode::AVCS_ERR_INVALID_VAL,
"SetCallback failed, callback is nullptr.");
callback_ = callback;
AVCODEC_LOGD("SetCallback success");
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::Init(Media::Meta &callerInfo)
{
if (state_ != CodecState::RELEASED) {
AVCODEC_LOGE("Init failed, state = %{public}s .", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
state_ = CodecState::INITIALIZING;
auto ret = doInit();
CHECK_AND_RETURN_RET_LOG(ret == AVCodecServiceErrCode::AVCS_ERR_OK, ret, "unknown error.");
if (state_ != CodecState::INITIALIZED) {
AVCODEC_LOGE("Init failed, state =%{public}s", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::Configure(const Format &format)
{
AVCODEC_SYNC_TRACE;
if (!format.ContainKey(MediaDescriptionKey::MD_KEY_CHANNEL_COUNT)) {
AVCODEC_LOGE("Configure failed, missing channel count key in format.");
return AVCodecServiceErrCode::AVCS_ERR_CONFIGURE_MISMATCH_CHANNEL_COUNT;
}
if (!format.ContainKey(MediaDescriptionKey::MD_KEY_SAMPLE_RATE)) {
AVCODEC_LOGE("Configure failed,missing sample rate key in format.");
return AVCodecServiceErrCode::AVCS_ERR_MISMATCH_SAMPLE_RATE;
}
int32_t ret = doConfigure(format);
AVCODEC_LOGD("Configure exit");
return ret;
}
int32_t AudioCodecAdapter::Start()
{
AVCODEC_LOGD("Start enter");
CHECK_AND_RETURN_RET_LOG(callback_ != nullptr, AVCodecServiceErrCode::AVCS_ERR_UNKNOWN,
"adapter start error, callback not initialized .");
if (!audioCodec) {
AVCODEC_LOGE("adapter start error, audio codec not initialized .");
return AVCodecServiceErrCode::AVCS_ERR_UNKNOWN;
}
if (state_ == CodecState::FLUSHED) {
AVCODEC_LOGI("Start, doResume");
return doResume();
}
if (state_ != CodecState::CONFIGURED) {
AVCODEC_LOGE("Start is incorrect, state = %{public}s .", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
AVCODEC_LOGI("state %{public}s to STARTING then RUNNING", stateToString(state_).data());
state_ = CodecState::STARTING;
auto ret = doStart();
return ret;
}
int32_t AudioCodecAdapter::Stop()
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGD("stop enter");
CHECK_AND_RETURN_RET_LOG(callback_ != nullptr, AVCodecServiceErrCode::AVCS_ERR_UNKNOWN,
"Stop failed, call back not initialized.");
if (state_ == CodecState::INITIALIZED || state_ == CodecState::RELEASED || state_ == CodecState::STOPPING ||
state_ == CodecState::RELEASING || state_ == CodecState::CONFIGURED) {
AVCODEC_LOGD("Stop, state_=%{public}s", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
state_ = CodecState::STOPPING;
auto ret = doStop();
AVCODEC_LOGI("state %{public}s to INITIALIZED", stateToString(state_).data());
state_ = CodecState::CONFIGURED;
return ret;
}
int32_t AudioCodecAdapter::Flush()
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGD("adapter Flush enter");
CHECK_AND_RETURN_RET_LOG(callback_ != nullptr, AVCodecServiceErrCode::AVCS_ERR_UNKNOWN,
"adapter flush error, call back not initialized .");
if (state_ == CodecState::FLUSHED) {
AVCODEC_LOGW("Flush, state is already flushed, state_=%{public}s .", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
if (state_ != CodecState::RUNNING) {
callback_->OnError(AVCodecErrorType::AVCODEC_ERROR_INTERNAL, AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE);
AVCODEC_LOGE("Flush failed, state =%{public}s", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
AVCODEC_LOGI("state %{public}s to FLUSHING then FLUSHED", stateToString(state_).data());
state_ = CodecState::FLUSHING;
auto ret = doFlush();
return ret;
}
int32_t AudioCodecAdapter::Reset()
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGD("adapter Reset enter");
int32_t status = AVCodecServiceErrCode::AVCS_ERR_OK;
if (worker_) {
worker_->Release();
worker_.reset();
worker_ = nullptr;
}
if (audioCodec) {
status = audioCodec->Reset();
AVCODEC_LOGI("state %{public}s to INITIALIZED", stateToString(state_).data());
state_ = CodecState::INITIALIZED;
} else {
auto ret = doInit();
CHECK_AND_RETURN_RET_LOG(ret == AVCodecServiceErrCode::AVCS_ERR_OK, ret, "unknown error.");
}
return status;
}
int32_t AudioCodecAdapter::Release()
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGD("adapter Release enter");
if (state_ == CodecState::RELEASED || state_ == CodecState::RELEASING) {
AVCODEC_LOGW("adapter Release, state isnot completely correct, state =%{public}s .",
stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
if (state_ == CodecState::INITIALIZING) {
AVCODEC_LOGW("adapter Release, state isnot completely correct, state =%{public}s .",
stateToString(state_).data());
state_ = CodecState::RELEASING;
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
if (state_ == CodecState::STARTING || state_ == CodecState::RUNNING || state_ == CodecState::STOPPING) {
AVCODEC_LOGE("adapter Release, state is running, state =%{public}s .", stateToString(state_).data());
}
AVCODEC_LOGI("state %{public}s to RELEASING then RELEASED", stateToString(state_).data());
state_ = CodecState::RELEASING;
auto ret = doRelease();
return ret;
}
int32_t AudioCodecAdapter::NotifyEos()
{
AVCODEC_SYNC_TRACE;
Flush();
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::SetParameter(const Format &format)
{
AVCODEC_SYNC_TRACE;
(void)format;
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::GetOutputFormat(Format &format)
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG(audioCodec != nullptr, AVCodecServiceErrCode::AVCS_ERR_NO_MEMORY,
"Codec not init or nullptr");
format = audioCodec->GetFormat();
if (!format.ContainKey(MediaDescriptionKey::MD_KEY_CODEC_NAME)) {
format.PutStringValue(MediaDescriptionKey::MD_KEY_CODEC_NAME, name_);
}
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::QueueInputBuffer(uint32_t index, const AVCodecBufferInfo &info, AVCodecBufferFlag flag)
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGD_LIMIT(LOGD_FREQUENCY, "adapter %{public}s queue buffer enter,index:%{public}u",
name_.data(), index);
if (!audioCodec) {
AVCODEC_LOGE("adapter QueueInputBuffer error, audio codec not initialized .");
return AVCodecServiceErrCode::AVCS_ERR_UNKNOWN;
}
if (!callback_) {
AVCODEC_LOGE("adapter queue input buffer error,index:%{public}u, call back not initialized .", index);
return AVCodecServiceErrCode::AVCS_ERR_UNKNOWN;
}
if (info.size < 0) {
AVCODEC_LOGE("size could not less than 0,size value:%{public}d.", info.size);
return AVCodecServiceErrCode::AVCS_ERR_INVALID_VAL;
}
if (info.offset < 0) {
AVCODEC_LOGE("offset could not less than 0,offset value:%{public}d.", info.offset);
return AVCodecServiceErrCode::AVCS_ERR_INVALID_VAL;
}
auto result = worker_->GetInputBufferInfo(index);
if (result == nullptr) {
AVCODEC_LOGE("getMemory failed");
return AVCodecServiceErrCode::AVCS_ERR_NO_MEMORY;
}
if (result->GetStatus() != BufferStatus::OWEN_BY_CLIENT) {
AVCODEC_LOGE("GetStatus failed");
return AVCodecServiceErrCode::AVCS_ERR_UNKNOWN;
}
if (result->CheckIsUsing()) {
AVCODEC_LOGE("input buffer now is already QueueInputBuffer,please don't do it again.");
return AVCodecServiceErrCode::AVCS_ERR_UNKNOWN;
}
if ((uint32_t) info.size > result->GetBufferSize()) {
AVCODEC_LOGE("Size could not lager than buffersize, please check input size %{public}d.", info.size);
return AVCodecServiceErrCode::AVCS_ERR_INVALID_VAL;
}
result->SetUsing();
result->SetBufferAttr(info);
if (flag == AVCodecBufferFlag::AVCODEC_BUFFER_FLAG_EOS) {
result->SetEos(true);
}
worker_->PushInputData(index);
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::ReleaseOutputBuffer(uint32_t index)
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGD_LIMIT(LOGD_FREQUENCY, "adapter %{public}s release buffer,index:%{public}u", name_.data(), index);
if (!callback_) {
AVCODEC_LOGE("adapter release buffer error,index:%{public}u, call back not initialized .", index);
return AVCodecServiceErrCode::AVCS_ERR_UNKNOWN;
}
CHECK_AND_RETURN_RET_LOG(audioCodec != nullptr, AVCodecServiceErrCode::AVCS_ERR_UNKNOWN,
"adapter release buffer error, audio codec not initialized .");
auto outBufferInfo = worker_->GetOutputBufferInfo(index);
if (outBufferInfo == nullptr) {
AVCODEC_LOGE("release buffer failed,index=%{public}u error", index);
return AVCodecServiceErrCode::AVCS_ERR_NO_MEMORY;
}
if (outBufferInfo->GetStatus() != BufferStatus::OWEN_BY_CLIENT) {
AVCODEC_LOGE("output buffer status now is IDLE, could not released");
return AVCodecServiceErrCode::AVCS_ERR_UNKNOWN;
}
auto outBuffer = worker_->GetOutputBuffer();
CHECK_AND_RETURN_RET_LOG(outBuffer != nullptr, AVCodecServiceErrCode::AVCS_ERR_NO_MEMORY,
"release buffer failed,index=%{public}u error", index);
bool result = outBuffer->ReleaseBuffer(index);
CHECK_AND_RETURN_RET_LOG(result == true, AVCodecServiceErrCode::AVCS_ERR_NO_MEMORY, "release buffer failed");
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::doInit()
{
AVCODEC_SYNC_TRACE;
if (name_.empty()) {
state_ = CodecState::RELEASED;
AVCODEC_LOGE("doInit failed, because name is empty");
return AVCodecServiceErrCode::AVCS_ERR_UNKNOWN;
}
AVCODEC_LOGD("adapter doInit, codec name:%{public}s", name_.data());
audioCodec = AudioBaseCodec::make_sharePtr(name_);
if (audioCodec == nullptr) {
state_ = CodecState::RELEASED;
AVCODEC_LOGE("Initlize failed, because create codec failed. name: %{public}s.", name_.data());
return AVCodecServiceErrCode::AVCS_ERR_UNKNOWN;
}
AVCODEC_LOGI("adapter doInit, state from %{public}s to INITIALIZED",
stateToString(state_).data());
state_ = CodecState::INITIALIZED;
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::doConfigure(const Format &format)
{
AVCODEC_SYNC_TRACE;
if (state_ != CodecState::INITIALIZED) {
AVCODEC_LOGE("adapter configure failed because state is incorrect,state:%{public}d.",
static_cast<int>(state_.load()));
state_ = CodecState::RELEASED;
AVCODEC_LOGE("state_=%{public}s", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
(void)mallopt(M_SET_THREAD_CACHE, M_THREAD_CACHE_DISABLE);
(void)mallopt(M_DELAYED_FREE, M_DELAYED_FREE_DISABLE);
int32_t ret = audioCodec->Init(format);
if (ret != AVCodecServiceErrCode::AVCS_ERR_OK) {
AVCODEC_LOGE("configure failed, because codec init failed,error:%{public}d.", static_cast<int>(ret));
return ret;
}
state_ = CodecState::CONFIGURED;
return ret;
}
int32_t AudioCodecAdapter::doStart()
{
AVCODEC_SYNC_TRACE;
if (state_ != CodecState::STARTING) {
AVCODEC_LOGE("doStart failed, state = %{public}s", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
AVCODEC_LOGD("adapter doStart, state from %{public}s to RUNNING", stateToString(state_).data());
state_ = CodecState::RUNNING;
worker_ = std::make_shared<AudioCodecWorker>(audioCodec, callback_);
worker_->Start();
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::doResume()
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGD("adapter doResume, state from %{public}s to RESUMING", stateToString(state_).data());
state_ = CodecState::RESUMING;
worker_->Resume();
AVCODEC_LOGD("adapter doResume, state from %{public}s to RUNNING", stateToString(state_).data());
state_ = CodecState::RUNNING;
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::doStop()
{
AVCODEC_SYNC_TRACE;
if (state_ == CodecState::RELEASING) {
AVCODEC_LOGW("adapter doStop, state is already release, state_=%{public}s .", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
if (state_ != CodecState::STOPPING) {
AVCODEC_LOGE("doStop failed, state =%{public}s", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
worker_->Stop();
int32_t status = audioCodec->Flush();
if (status != AVCodecServiceErrCode::AVCS_ERR_OK) {
AVCODEC_LOGE("flush status=%{public}d", static_cast<int>(status));
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::doFlush()
{
AVCODEC_SYNC_TRACE;
if (state_ != CodecState::FLUSHING) {
AVCODEC_LOGE("doFlush failed, state_=%{public}s", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
worker_->Pause();
int32_t status = audioCodec->Flush();
state_ = CodecState::FLUSHED;
if (status != AVCodecServiceErrCode::AVCS_ERR_OK) {
AVCODEC_LOGE("status=%{public}d", static_cast<int>(status));
return AVCodecServiceErrCode::AVCS_ERR_INVALID_STATE;
}
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
int32_t AudioCodecAdapter::doRelease()
{
AVCODEC_SYNC_TRACE;
if (state_ == CodecState::RELEASED) {
AVCODEC_LOGW("adapter doRelease, state already released, state_=%{public}s.", stateToString(state_).data());
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
if (worker_ != nullptr) {
worker_->Release();
worker_.reset();
worker_ = nullptr;
}
if (audioCodec != nullptr) {
audioCodec->Release();
audioCodec.reset();
audioCodec = nullptr;
}
AVCODEC_LOGD_LIMIT(LOGD_FREQUENCY, "adapter doRelease, state from %{public}s to RELEASED",
stateToString(state_).data());
state_ = CodecState::RELEASED;
return AVCodecServiceErrCode::AVCS_ERR_OK;
}
std::string_view AudioCodecAdapter::stateToString(CodecState state)
{
std::map<CodecState, std::string_view> stateStrMap = {
{CodecState::RELEASED, " RELEASED"}, {CodecState::INITIALIZED, " INITIALIZED"},
{CodecState::FLUSHED, " FLUSHED"}, {CodecState::RUNNING, " RUNNING"},
{CodecState::INITIALIZING, " INITIALIZING"}, {CodecState::STARTING, " STARTING"},
{CodecState::STOPPING, " STOPPING"}, {CodecState::FLUSHING, " FLUSHING"},
{CodecState::RESUMING, " RESUMING"}, {CodecState::RELEASING, " RELEASING"},
{CodecState::CONFIGURED, " CONFIGURED"},
};
return stateStrMap[state];
}
}
}