* Copyright (C) 2025 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 "syspara/parameters.h"
#include "avcodec_trace.h"
#include "avcodec_log.h"
#include "avcodec_codec_name.h"
#include "video_decoder.h"
#include <sstream>
#include <sys/ioctl.h>
#include <linux/dma-buf.h>
#include "v1_0/cm_color_space.h"
#include "v1_0/hdr_static_metadata.h"
#include "v1_0/buffer_handle_meta_key_type.h"
namespace OHOS {
namespace MediaAVCodec {
namespace Codec {
namespace {
#define DMA_BUF_SET_LEAK_TYPE _IOW(DMA_BUF_BASE, 5, const char *)
constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_FRAMEWORK, "VideoDecoder"};
constexpr int32_t DEFAULT_OUT_SURFACE_CNT = 4;
constexpr int32_t VIDEO_PLANE_COUNT_YUV = 3;
constexpr int32_t VIDEO_PLANE_SIZE_YUVP10 = 10;
constexpr int32_t VIDEO_MIN_BUFFER_SIZE = 1474560;
constexpr int32_t DEFAULT_MIN_BUFFER_CNT = 2;
constexpr uint32_t INDEX_INPUT = 0;
constexpr uint32_t INDEX_OUTPUT = 1;
constexpr int32_t DEFAULT_VIDEO_WIDTH = 1920;
constexpr int32_t DEFAULT_VIDEO_HEIGHT = 1080;
constexpr int32_t DEFAULT_OUT_BUFFER_CNT = 3;
constexpr int32_t DEFAULT_IN_BUFFER_CNT = 4;
constexpr int32_t DEFAULT_MAX_BUFFER_CNT = 10;
constexpr uint32_t LOG_LOW_FREQUENCY = 100;
#ifdef BUILD_ENG_VERSION
constexpr uint32_t PATH_MAX_LEN = 128;
constexpr char DUMP_PATH[] = "/data/misc/VideoDecoderdump";
#endif
}
using namespace OHOS::Media;
VideoDecoder::VideoDecoder(const std::string &name) : codecName_(name) {}
int32_t VideoDecoder::Init(Meta &callerInfo)
{
if (callerInfo.GetData(Tag::AV_CODEC_FORWARD_CALLER_PID, decInfo_.pid) &&
callerInfo.GetData(Tag::AV_CODEC_FORWARD_CALLER_PROCESS_NAME, decInfo_.processName)) {
decInfo_.calledByAvcodec = false;
} else if (callerInfo.GetData(Tag::AV_CODEC_CALLER_PID, decInfo_.pid) &&
callerInfo.GetData(Tag::AV_CODEC_CALLER_PROCESS_NAME, decInfo_.processName)) {
decInfo_.calledByAvcodec = true;
}
callerInfo.GetData("av_codec_event_info_instance_id", instanceId_);
int32_t ret = Initialize();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Failed to initialize");
decInfo_.instanceId = std::to_string(instanceId_);
AVCODEC_LOGI("Num %{public}u Decoder codec name: %{public}s", decInstanceID_, decName_.c_str());
return AVCS_ERR_OK;
}
#ifdef BUILD_ENG_VERSION
void VideoDecoder::OpenDumpFile()
{
std::string dumpModeStr = OHOS::system::GetParameter("decoder.dump", "0");
AVCODEC_LOGI("dumpModeStr %{public}s", dumpModeStr.c_str());
char fileName[PATH_MAX_LEN] = {0};
if (dumpModeStr[0] == '1') {
int ret = sprintf_s(fileName, sizeof(fileName), "%s/input_%p.dat", DUMP_PATH, this);
if (ret > 0) {
dumpInFile_ = std::make_shared<std::ofstream>();
dumpInFile_->open(fileName, std::ios::out | std::ios::binary);
if (!dumpInFile_->is_open()) {
AVCODEC_LOGW("fail open file %{public}s", fileName);
dumpInFile_ = nullptr;
}
}
}
if (dumpModeStr[1] == '1') {
int ret = sprintf_s(fileName, sizeof(fileName), "%s/output_%p.yuv", DUMP_PATH, this);
if (ret > 0) {
dumpOutFile_ = std::make_shared<std::ofstream>();
dumpOutFile_->open(fileName, std::ios::out | std::ios::binary);
if (!dumpOutFile_->is_open()) {
AVCODEC_LOGW("fail open file %{public}s", fileName);
dumpOutFile_ = nullptr;
}
}
ret = sprintf_s(fileName, sizeof(fileName), "%s/outConvert_%p.data", DUMP_PATH, this);
if (ret > 0) {
dumpConvertFile_ = std::make_shared<std::ofstream>();
dumpConvertFile_->open(fileName, std::ios::out | std::ios::binary);
if (!dumpConvertFile_->is_open()) {
AVCODEC_LOGW("fail open file %{public}s", fileName);
dumpConvertFile_ = nullptr;
}
}
}
}
#endif
void VideoDecoder::ConfigureDefaultVal(const Format &format, const std::string_view &formatKey, int32_t minVal,
int32_t maxVal)
{
int32_t val32 = 0;
if (format.GetIntValue(formatKey, val32) && val32 >= minVal && val32 <= maxVal) {
format_.PutIntValue(formatKey, val32);
} else {
AVCODEC_LOGW("Set parameter failed: %{public}s, which minimum threshold=%{public}d, "
"maximum threshold=%{public}d",
formatKey.data(), minVal, maxVal);
}
}
void VideoDecoder::GetSurfaceCfgFromFmt(const Format &format)
{
int32_t val = -1;
if (format.GetIntValue(MediaDescriptionKey::MD_KEY_PIXEL_FORMAT, val)) {
if (val == static_cast<int32_t>(VideoPixelFormat::NV12) ||
val == static_cast<int32_t>(VideoPixelFormat::NV21)) {
std::lock_guard<std::mutex> runlock(decRunMutex_);
outputPixelFmt_ = static_cast<VideoPixelFormat>(val);
format_.PutIntValue(MediaDescriptionKey::MD_KEY_PIXEL_FORMAT, val);
} else {
AVCODEC_LOGI("Invalid pixel_format: %{public}d.", val);
}
}
if (format.GetIntValue(MediaDescriptionKey::MD_KEY_SCALE_TYPE, val)) {
if (IsValidScaleType(val)) {
format_.PutIntValue(MediaDescriptionKey::MD_KEY_SCALE_TYPE, val);
AVCODEC_LOGI("Set parameter scale_type: %{public}d success.", val);
} else {
AVCODEC_LOGE("Invalid scale_type: %{public}d.", val);
}
}
std::optional<int32_t> orientation = std::nullopt;
if (format.GetIntValue(OHOS::Media::Tag::VIDEO_ORIENTATION_TYPE, val)) {
orientation = val;
AVCODEC_LOGI("Set parameter video_orientation_type: %{public}d success.", orientation.value());
}
if (!orientation.has_value() && format.GetIntValue(MediaDescriptionKey::MD_KEY_ROTATION_ANGLE, val)) {
if (IsValidRotation(val)) {
orientation = static_cast<int32_t>(TranslateSurfaceRotation(static_cast<VideoRotation>(val)));
AVCODEC_LOGI("Set parameter rotation_angle: %{public}d success.", orientation.value());
} else {
AVCODEC_LOGE("Invalid rotation_angle: %{public}d.", val);
}
}
if (orientation) {
format_.PutIntValue(OHOS::Media::Tag::VIDEO_ORIENTATION_TYPE, orientation.value());
}
}
bool VideoDecoder::IsActive() const
{
return state_ == State::RUNNING || state_ == State::FLUSHED || state_ == State::EOS;
}
int32_t VideoDecoder::Start()
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG(callback_ != nullptr, AVCS_ERR_INVALID_OPERATION, "Start codec failed: callback is null");
CHECK_AND_RETURN_RET_LOG((state_ == State::CONFIGURED || state_ == State::FLUSHED), AVCS_ERR_INVALID_STATE,
"Start codec failed: not in Configured or Flushed state");
int32_t ret = CreateDecoder();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, AVCS_ERR_INVALID_OPERATION, "decoder create failed");
if (!isBufferAllocated_) {
for (int32_t i = 0; i < AV_NUM_DATA_POINTERS; i++) {
std::lock_guard<std::mutex> convertLock(convertDataMutex_);
scaleData_[i] = nullptr;
scaleLineSize_[i] = 0;
}
isConverted_ = false;
int32_t allocateResult = AllocateBuffers();
CHECK_AND_RETURN_RET_LOG(allocateResult == AVCS_ERR_OK, allocateResult,
"Start codec failed: cannot allocate buffers");
isBufferAllocated_ = true;
}
InitBuffers();
isSendEos_ = false;
sendTask_->Start();
state_ = State::RUNNING;
AVCODEC_LOGI("Num %{public}u %{public}s Start codec successful, state: Running", decInstanceID_, decName_.c_str());
return AVCS_ERR_OK;
}
void VideoDecoder::ConfigureHdrMetadata(const Format &format)
{
(void)format;
AVCODEC_LOGI("Decoder does not support HDR metadata configuration.");
}
void VideoDecoder::ConfigureFormatHdr(const Format &format)
{
if (format.ContainKey(MediaDescriptionKey::MD_KEY_VIDEO_HDR_METADATA)) {
ConfigureHdrMetadata(format);
}
if (format.ContainKey(MediaDescriptionKey::MD_KEY_TRANSFER_CHARACTERISTICS)) {
ConfigureHdrColorSpaceIno(format);
}
if (format.ContainKey(OHOS::Media::Tag::VIDEO_STATIC_METADATA_SMPT2086)) {
ConfigureHdrStaticMetadata(format);
}
}
int32_t VideoDecoder::Configure(const Format &format)
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG((state_ == State::INITIALIZED), AVCS_ERR_INVALID_STATE,
"Configure codec failed: not in Initialized state");
format_.PutIntValue(MediaDescriptionKey::MD_KEY_WIDTH, DEFAULT_VIDEO_WIDTH);
format_.PutIntValue(MediaDescriptionKey::MD_KEY_HEIGHT, DEFAULT_VIDEO_HEIGHT);
format_.PutIntValue(MediaDescriptionKey::MD_KEY_MAX_OUTPUT_BUFFER_COUNT, DEFAULT_OUT_BUFFER_CNT);
format_.PutIntValue(MediaDescriptionKey::MD_KEY_MAX_INPUT_BUFFER_COUNT, DEFAULT_IN_BUFFER_CNT);
isValidHdrSttMd_.store(false);
int32_t val32;
for (auto &it : format.GetFormatMap()) {
if (it.first == MediaDescriptionKey::MD_KEY_MAX_OUTPUT_BUFFER_COUNT) {
isOutBufSetted_ = true;
ConfigureDefaultVal(format, it.first, DEFAULT_MIN_BUFFER_CNT, DEFAULT_MAX_BUFFER_CNT);
} else if (it.first == MediaDescriptionKey::MD_KEY_MAX_INPUT_BUFFER_COUNT) {
ConfigureDefaultVal(format, it.first, DEFAULT_MIN_BUFFER_CNT, DEFAULT_MAX_BUFFER_CNT);
} else if (it.first == MediaDescriptionKey::MD_KEY_WIDTH) {
ConfigurelWidthAndHeight(format, it.first, true);
} else if (it.first == MediaDescriptionKey::MD_KEY_HEIGHT) {
ConfigurelWidthAndHeight(format, it.first, false);
} else if (it.first == MediaDescriptionKey::MD_KEY_PIXEL_FORMAT ||
it.first == MediaDescriptionKey::MD_KEY_ROTATION_ANGLE ||
it.first == MediaDescriptionKey::MD_KEY_SCALE_TYPE ||
it.first == OHOS::Media::Tag::VIDEO_ORIENTATION_TYPE) {
continue;
} else if (it.first == Tag::VIDEO_DECODER_OUTPUT_IN_DECODING_ORDER) {
CHECK_AND_CONTINUE_LOG(format.GetIntValue(it.first, val32),
"not find valid int value for key %{public}s", it.first.data());
format_.PutIntValue(it.first, val32);
ConfigureOutPutOrder(val32 == 1 ? false : true);
} else {
AVCODEC_LOGW("Set parameter failed: size:%{public}s, unsupport key", it.first.data());
}
}
GetSurfaceCfgFromFmt(format);
ConfigureFormatHdr(format);
format_.GetIntValue(MediaDescriptionKey::MD_KEY_WIDTH, width_);
format_.GetIntValue(MediaDescriptionKey::MD_KEY_HEIGHT, height_);
state_ = State::CONFIGURED;
return AVCS_ERR_OK;
}
void VideoDecoder::InitBuffers()
{
inputAvailQue_->SetActive(true);
codecAvailQue_->SetActive(true);
if (sInfo_.surface != nullptr) {
renderAvailQue_->SetActive(true);
requestSurfaceBufferQue_->SetActive(true);
}
CHECK_AND_RETURN_LOG(buffers_[INDEX_INPUT].size() > 0, "Input buffers is null!");
if (buffers_[INDEX_INPUT].size() > 0) {
for (uint32_t i = 0; i < buffers_[INDEX_INPUT].size(); i++) {
buffers_[INDEX_INPUT][i]->owner_ = Owner::OWNED_BY_USER;
callback_->OnInputBufferAvailable(i, buffers_[INDEX_INPUT][i]->avBuffer);
AVCODEC_LOGI("%{public}s OnInputBufferAvailable frame index = %{public}u, owner = %{public}d",
decName_.c_str(), i, buffers_[INDEX_INPUT][i]->owner_.load());
}
}
CHECK_AND_RETURN_LOG(buffers_[INDEX_OUTPUT].size() > 0, "Output buffers is null!");
InitParams();
if (sInfo_.surface == nullptr || state_ == State::CONFIGURED) {
for (uint32_t i = 0u; i < buffers_[INDEX_OUTPUT].size(); i++) {
buffers_[INDEX_OUTPUT][i]->owner_ = Owner::OWNED_BY_CODEC;
codecAvailQue_->Push(i);
}
return;
}
std::lock_guard<std::mutex> sLock(surfaceMutex_);
for (uint32_t i = 0u; i < buffers_[INDEX_OUTPUT].size(); i++) {
if (buffers_[INDEX_OUTPUT][i]->owner_ != Owner::OWNED_BY_SURFACE) {
buffers_[INDEX_OUTPUT][i]->owner_ = Owner::OWNED_BY_CODEC;
codecAvailQue_->Push(i);
}
}
}
void VideoDecoder::ResetData()
{
std::lock_guard<std::mutex> convertLock(convertDataMutex_);
if (scaleData_[0] != nullptr) {
if (isConverted_) {
av_free(scaleData_[0]);
isConverted_ = false;
scale_.reset();
}
for (int32_t i = 0; i < AV_NUM_DATA_POINTERS; i++) {
scaleData_[i] = nullptr;
scaleLineSize_[i] = 0;
}
}
}
void VideoDecoder::ResetBuffers()
{
inputAvailQue_->Clear();
codecAvailQue_->Clear();
ResetData();
}
void VideoDecoder::StopThread()
{
if (inputAvailQue_ != nullptr) {
inputAvailQue_->SetActive(false, false);
}
if (codecAvailQue_ != nullptr) {
codecAvailQue_->SetActive(false, false);
}
if (sendTask_ != nullptr) {
sendTask_->Stop();
}
if (sInfo_.surface != nullptr) {
if (renderAvailQue_ != nullptr) {
renderAvailQue_->SetActive(false, false);
}
if (requestSurfaceBufferQue_ != nullptr) {
requestSurfaceBufferQue_->SetActive(false, false);
}
}
}
int32_t VideoDecoder::Stop()
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG((IsActive()), AVCS_ERR_INVALID_STATE, "Stop codec failed: not in executing state");
state_ = State::STOPPING;
inputAvailQue_->SetActive(false, false);
codecAvailQue_->SetActive(false, false);
sendTask_->Stop();
if (sInfo_.surface != nullptr) {
renderAvailQue_->SetActive(false, false);
requestSurfaceBufferQue_->SetActive(false, false);
}
DeleteDecoder();
ReleaseBuffers();
state_ = State::CONFIGURED;
AVCODEC_LOGI("Num %{public}u Stop codec successful, state: Configured", decInstanceID_);
return AVCS_ERR_OK;
}
int32_t VideoDecoder::Flush()
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG((state_ == State::RUNNING || state_ == State::EOS), AVCS_ERR_INVALID_STATE,
"%{public}s Flush codec failed: not in running or Eos state", decName_.c_str());
state_ = State::FLUSHING;
AVCODEC_LOGI("%{public}s step into FLUSHING status", decName_.c_str());
inputAvailQue_->SetActive(false, false);
codecAvailQue_->SetActive(false, false);
sendTask_->Pause();
ResetBuffers();
FlushAllFrames();
state_ = State::FLUSHED;
AVCODEC_LOGI("Num %{public}u %{public}s Flush codec successful, state: Flushed", decInstanceID_, decName_.c_str());
return AVCS_ERR_OK;
}
int32_t VideoDecoder::Reset()
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGI("Num %{public}u Reset codec called", decInstanceID_);
int32_t ret = Release();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Reset codec failed: cannot release codec");
ret = Initialize();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Reset codec failed: cannot init codec");
AVCODEC_LOGI("Reset codec successful, state: Initialized");
return AVCS_ERR_OK;
}
void VideoDecoder::ReleaseResource()
{
StopThread();
ReleaseBuffers();
format_ = Format();
if (sInfo_.surface != nullptr) {
transform_.store(GraphicTransformType::GRAPHIC_ROTATE_NONE);
sInfo_.surface->SetTransform(transform_.load());
UnRegisterListenerToSurface(sInfo_.surface);
StopRequestSurfaceBufferThread();
}
sInfo_.surface = nullptr;
DeleteDecoder();
}
int32_t VideoDecoder::Release()
{
AVCODEC_SYNC_TRACE;
state_ = State::STOPPING;
ReleaseResource();
state_ = State::UNINITIALIZED;
AVCODEC_LOGI("Num %{public}u Release codec successful, state: Uninitialized", decInstanceID_);
return AVCS_ERR_OK;
}
int32_t VideoDecoder::SetParameter(const Format &format)
{
AVCODEC_SYNC_TRACE;
if (sInfo_.surface != nullptr) {
GetSurfaceCfgFromFmt(format);
SetSurfaceParameter();
}
AVCODEC_LOGI("Num %{public}u Set parameter successful", decInstanceID_);
return AVCS_ERR_OK;
}
void VideoDecoder::CalculateBufferSize()
{
if (codecName_ == AVCodecCodecName::VIDEO_DECODER_VP8_NAME) {
inputBufferSize_ = static_cast<UINT32>(width_ * height_ * VIDEO_PLANE_COUNT_YUV) >> 1;
} else if (codecName_ == AVCodecCodecName::VIDEO_DECODER_VP9_NAME ||
codecName_ == AVCodecCodecName::VIDEO_DECODER_AV1_NAME) {
inputBufferSize_ =
static_cast<UINT32>(width_ * height_ * VIDEO_PLANE_COUNT_YUV * VIDEO_PLANE_SIZE_YUVP10) >> 3;
}
if (inputBufferSize_ <= VIDEO_MIN_BUFFER_SIZE) {
inputBufferSize_ = VIDEO_MIN_BUFFER_SIZE;
}
format_.PutIntValue(MediaDescriptionKey::MD_KEY_MAX_INPUT_SIZE, inputBufferSize_);
AVCODEC_LOGI("width = %{public}d, height = %{public}d, Input buffer size = %{public}d",
width_, height_, inputBufferSize_);
}
int32_t VideoDecoder::AllocateInputBuffer(int32_t bufferCnt, int32_t inBufferSize)
{
int32_t valBufferCnt = 0;
for (int32_t i = 0; i < bufferCnt; i++) {
std::shared_ptr<CodecBuffer> buf = std::make_shared<CodecBuffer>();
std::shared_ptr<AVAllocator> allocator =
AVAllocatorFactory::CreateSharedAllocator(MemoryFlag::MEMORY_READ_WRITE);
CHECK_AND_CONTINUE_LOG(allocator != nullptr, "input buffer %{public}d allocator is nullptr", i);
buf->avBuffer = AVBuffer::CreateAVBuffer(allocator, inBufferSize);
CHECK_AND_CONTINUE_LOG(buf->avBuffer != nullptr, "Allocate input buffer failed, index=%{public}d", i);
AVCODEC_LOGI("Allocate input buffer success: index=%{public}d, size=%{public}d", i,
buf->avBuffer->memory_->GetCapacity());
buffers_[INDEX_INPUT].emplace_back(buf);
valBufferCnt++;
}
if (valBufferCnt < DEFAULT_MIN_BUFFER_CNT) {
AVCODEC_LOGE("Allocate input buffer failed: only %{public}d buffer is allocated, no memory", valBufferCnt);
buffers_[INDEX_INPUT].clear();
return AVCS_ERR_NO_MEMORY;
}
return AVCS_ERR_OK;
}
int32_t VideoDecoder::AllocateOutputBuffer(int32_t bufferCnt)
{
CHECK_AND_RETURN_RET_LOG(sInfo_.surface == nullptr, AVCS_ERR_UNKNOWN, "Not in buffer mode!");
int32_t ret = SetSurfaceCfg();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Set surface cfg failed!");
int32_t valBufferCnt = 0;
for (int i = 0; i < bufferCnt; i++) {
std::shared_ptr<CodecBuffer> buf = std::make_shared<CodecBuffer>();
buf->width = width_;
buf->height = height_;
std::shared_ptr<AVAllocator> allocator =
AVAllocatorFactory::CreateSurfaceAllocator(sInfo_.requestConfig);
CHECK_AND_CONTINUE_LOG(allocator != nullptr, "output buffer %{public}d allocator is nullptr", i);
buf->avBuffer = AVBuffer::CreateAVBuffer(allocator, 0);
if (buf->avBuffer != nullptr) {
AVCODEC_LOGI("Allocate output share buffer success: index=%{public}d, size=%{public}d", i,
buf->avBuffer->memory_->GetCapacity());
}
CHECK_AND_CONTINUE_LOG(buf->avBuffer != nullptr, "Allocate output buffer failed, index=%{public}d", i);
SetCallerToBuffer(buf->avBuffer->memory_->GetSurfaceBuffer());
buffers_[INDEX_OUTPUT].emplace_back(buf);
valBufferCnt++;
}
if (valBufferCnt < DEFAULT_MIN_BUFFER_CNT) {
AVCODEC_LOGE("Allocate output buffer failed: only %{public}d buffer is allocated, no memory", valBufferCnt);
buffers_[INDEX_INPUT].clear();
buffers_[INDEX_OUTPUT].clear();
return AVCS_ERR_NO_MEMORY;
}
return AVCS_ERR_OK;
}
int32_t VideoDecoder::AllocateOutputBuffersFromSurface(int32_t bufferCnt)
{
CHECK_AND_RETURN_RET_LOG(sInfo_.surface != nullptr, AVCS_ERR_UNKNOWN, "Not in surface mode!");
int32_t ret = ClearSurfaceAndSetQueueSize(sInfo_.surface, bufferCnt);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Clean surface and set queue size failed!");
requestSurfaceBufferQue_->Clear();
requestSurfaceBufferQue_->SetActive(true);
StartRequestSurfaceBufferThread();
for (int32_t i = 0; i < bufferCnt; i++) {
std::shared_ptr<FSurfaceMemory> surfaceMemory =
std::make_shared<FSurfaceMemory>(&sInfo_, decInfo_);
CHECK_AND_RETURN_RET_LOG(surfaceMemory != nullptr, AVCS_ERR_UNKNOWN, "Creata surface memory failed!");
ret = surfaceMemory->AllocSurfaceBuffer(width_, height_);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Alloc surface buffer failed!");
sptr<SurfaceBuffer> surfaceBuffer = surfaceMemory->GetSurfaceBuffer();
CHECK_AND_RETURN_RET_LOG(surfaceBuffer != nullptr, AVCS_ERR_UNKNOWN, "surface buf(%{public}u) is null.", i);
ret = Attach(surfaceBuffer);
CHECK_AND_CONTINUE_LOG(ret == AVCS_ERR_OK, "surface buf(%{public}u) attach to surface failed.", i);
surfaceMemory->isAttached = true;
std::shared_ptr<CodecBuffer> buf = std::make_shared<CodecBuffer>();
CHECK_AND_RETURN_RET_LOG(buf != nullptr, AVCS_ERR_UNKNOWN, "Creata output buffer failed!");
buf->sMemory = surfaceMemory;
buf->height = height_;
buf->width = width_;
outAVBuffer4Surface_.emplace_back(AVBuffer::CreateAVBuffer());
buf->avBuffer = AVBuffer::CreateAVBuffer(buf->sMemory->GetBase(), buf->sMemory->GetSize());
CHECK_AND_RETURN_RET_LOG(buf->avBuffer != nullptr && outAVBuffer4Surface_.back() != nullptr, AVCS_ERR_UNKNOWN,
"Num %{public}u Allocate output buffer failed, index=%{public}d", decInstanceID_, i);
AVCODEC_LOGI("Allocate output surface buffer success, index=%{public}d, size=%{public}d, stride=%{public}d", i,
buf->sMemory->GetSize(), buf->sMemory->GetSurfaceBufferStride());
buffers_[INDEX_OUTPUT].emplace_back(buf);
}
int32_t outputBufferNum = static_cast<int32_t>(buffers_[INDEX_OUTPUT].size());
CHECK_AND_RETURN_RET_LOG(outputBufferNum == bufferCnt, AVCS_ERR_UNKNOWN,
"Only alloc %{public}d buffers, less %{public}d", outputBufferNum, bufferCnt);
return AVCS_ERR_OK;
}
int32_t VideoDecoder::SetOutputSurface(sptr<Surface> surface)
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG(state_ != State::UNINITIALIZED, AV_ERR_INVALID_VAL,
"set output surface fail: not initialized or configured");
CHECK_AND_RETURN_RET_LOG((state_ == State::CONFIGURED || state_ == State::FLUSHED ||
state_ == State::RUNNING || state_ == State::EOS), AVCS_ERR_INVALID_STATE,
"set output surface fail: state_ %{public}d not support set output surface",
static_cast<int32_t>(state_.load()));
if (surface == nullptr || surface->IsConsumer()) {
AVCODEC_LOGE("Set surface fail");
return AVCS_ERR_INVALID_VAL;
}
if (state_ == State::FLUSHED || state_ == State::RUNNING ||
state_ == State::EOS) {
return ReplaceOutputSurfaceWhenRunning(surface);
}
UnRegisterListenerToSurface(sInfo_.surface);
uint64_t surfaceId = surface->GetUniqueId();
sInfo_.surface = surface;
CombineConsumerUsage();
int32_t ret = RegisterListenerToSurface(sInfo_.surface);
CHECK_AND_RETURN_RET_LOG(ret == GSERROR_OK, ret,
"surface(%{public}" PRIu64 ") register listener to surface failed, GSError=%{public}d",
sInfo_.surface->GetUniqueId(), ret);
AVCODEC_LOGI("Num %{public}u Set surface(%{public}" PRIu64 ") success.", decInstanceID_, surfaceId);
return AVCS_ERR_OK;
}
int32_t VideoDecoder::RenderOutputBuffer(uint32_t index)
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG(sInfo_.surface != nullptr, AVCS_ERR_UNSUPPORT,
"Render output buffer failed, surface is nullptr!");
AVCODEC_LOGI_LIMIT(LOG_LOW_FREQUENCY, "Num %{public}u render output buffer with index=%{public}u",
decInstanceID_, index);
std::unique_lock<std::mutex> oLock(outputMutex_);
CHECK_AND_RETURN_RET_LOG(index < buffers_[INDEX_OUTPUT].size(), AVCS_ERR_INVALID_VAL,
"Failed to render output buffer: invalid index");
std::shared_ptr<CodecBuffer> frameBuffer = buffers_[INDEX_OUTPUT][index];
if (frameBuffer->owner_ == Owner::OWNED_BY_USER) {
frameBuffer->owner_ = Owner::OWNED_BY_SURFACE;
oLock.unlock();
std::shared_ptr<FSurfaceMemory> surfaceMemory = frameBuffer->sMemory;
int32_t ret = FlushSurfaceMemory(surfaceMemory, index);
if (ret != AVCS_ERR_OK) {
AVCODEC_LOGW("Flush surface memory(index=%{public}u) failed: %{public}d", index, ret);
} else {
AVCODEC_LOGD("Flush surface memory(index=%{public}u) successful.", index);
}
AVCODEC_LOGD("render output buffer with index, index=%{public}u", index);
return AVCS_ERR_OK;
} else {
AVCODEC_LOGE("Failed to render output buffer with bad index, index=%{public}u, owner=%{public}d",
index, frameBuffer->owner_.load());
return AVCS_ERR_INVALID_VAL;
}
}
int32_t VideoDecoder::GetOutputFormat(Format &format)
{
AVCODEC_SYNC_TRACE;
if (!format_.ContainKey(OHOS::Media::Tag::VIDEO_SLICE_HEIGHT)) {
format_.PutIntValue(OHOS::Media::Tag::VIDEO_SLICE_HEIGHT, height_);
}
if (!format_.ContainKey(OHOS::Media::Tag::VIDEO_PIC_WIDTH) ||
!format_.ContainKey(OHOS::Media::Tag::VIDEO_PIC_HEIGHT)) {
format_.PutIntValue(OHOS::Media::Tag::VIDEO_PIC_WIDTH, width_);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_PIC_HEIGHT, height_);
}
if (!format_.ContainKey(OHOS::Media::Tag::VIDEO_CROP_RIGHT) ||
!format_.ContainKey(OHOS::Media::Tag::VIDEO_CROP_BOTTOM)) {
format_.PutIntValue(OHOS::Media::Tag::VIDEO_CROP_RIGHT, width_-1);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_CROP_BOTTOM, height_-1);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_CROP_LEFT, 0);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_CROP_TOP, 0);
}
format = format_;
AVCODEC_LOGI("Get outputFormat successful");
return AVCS_ERR_OK;
}
void VideoDecoder::SetSurfaceParameter()
{
int32_t val = -1;
bool setSurfacePixelFormat = false;
std::optional<ScalingMode> scaling = std::nullopt;
std::optional<GraphicTransformType> orientation = std::nullopt;
if (format_.GetIntValue(MediaDescriptionKey::MD_KEY_PIXEL_FORMAT, val)) {
setSurfacePixelFormat = true;
}
if (format_.GetIntValue(MediaDescriptionKey::MD_KEY_SCALE_TYPE, val)) {
scaling = static_cast<ScalingMode>(val);
}
if (format_.GetIntValue(OHOS::Media::Tag::VIDEO_ORIENTATION_TYPE, val)) {
orientation = static_cast<GraphicTransformType>(val);
}
std::lock_guard<std::mutex> sLock(surfaceMutex_);
if (setSurfacePixelFormat) {
CHECK_AND_RETURN_LOG(SetSurfaceFormat() == AVCS_ERR_OK,
"set surface format failed: unsupported surface format");
}
if (scaling) {
sInfo_.scalingMode = scaling.value();
sInfo_.surface->SetScalingMode(sInfo_.scalingMode.value());
}
if (orientation) {
transform_.store(orientation.value());
AVCODEC_LOGI("Set surface video_orientation_type: %{public}d success.",
static_cast<int32_t>(transform_.load()));
sInfo_.surface->SetTransform(transform_.load());
}
}
int32_t VideoDecoder::CheckFormatChange(uint32_t index, int width, int height, int bitDepth)
{
bool formatChanged = false;
if (width_ != width || height_ != height || bitDepth_ != bitDepth) {
AVCODEC_LOGI("format change, width: %{public}d->%{public}d, height: %{public}d->%{public}d, "
"bitDepth: %{public}d->%{public}d", width_, width, height_, height, bitDepth_, bitDepth);
width_ = width;
height_ = height;
bitDepth_ = bitDepth;
ResetData();
std::unique_lock<std::mutex> convertLock(convertDataMutex_);
scale_ = nullptr;
convertLock.unlock();
std::unique_lock<std::mutex> sLock(surfaceMutex_);
sInfo_.requestConfig.width = width_;
sInfo_.requestConfig.height = height_;
CHECK_AND_RETURN_RET_LOG(SetSurfaceFormat() == AVCS_ERR_OK, AVCS_ERR_UNSUPPORT,
"set surface format failed: unsupported surface format");
sLock.unlock();
formatChanged = true;
}
if (sInfo_.surface == nullptr) {
std::lock_guard<std::mutex> oLock(outputMutex_);
CHECK_AND_RETURN_RET_LOG((UpdateOutputBuffer(index) == AVCS_ERR_OK), AVCS_ERR_NO_MEMORY,
"Update output buffer failed, index=%{public}u", index);
} else {
CHECK_AND_RETURN_RET_LOG((UpdateSurfaceMemory(index) == AVCS_ERR_OK), AVCS_ERR_NO_MEMORY,
"Update buffer failed");
}
if (!format_.ContainKey(OHOS::Media::Tag::VIDEO_STRIDE) || formatChanged) {
int32_t stride = GetSurfaceBufferStride(buffers_[INDEX_OUTPUT][index]);
CHECK_AND_RETURN_RET_LOG(stride > 0, AVCS_ERR_NO_MEMORY, "get GetSurfaceBufferStride failed");
format_.PutIntValue(OHOS::Media::Tag::VIDEO_STRIDE, stride);
format_.PutIntValue(MediaDescriptionKey::MD_KEY_WIDTH, width_);
format_.PutIntValue(MediaDescriptionKey::MD_KEY_HEIGHT, height_);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_SLICE_HEIGHT, height_);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_PIC_WIDTH, width_);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_PIC_HEIGHT, height_);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_CROP_RIGHT, width_-1);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_CROP_BOTTOM, height_-1);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_CROP_LEFT, 0);
format_.PutIntValue(OHOS::Media::Tag::VIDEO_CROP_TOP, 0);
callback_->OnOutputFormatChanged(format_);
}
return AVCS_ERR_OK;
}
int32_t VideoDecoder::AllocateBuffers()
{
AVCODEC_SYNC_TRACE;
CalculateBufferSize();
CHECK_AND_RETURN_RET_LOG(inputBufferSize_ > 0, AVCS_ERR_INVALID_VAL,
"Allocate buffer with input size=%{public}d failed", inputBufferSize_);
if (sInfo_.surface != nullptr && isOutBufSetted_ == false) {
format_.PutIntValue(MediaDescriptionKey::MD_KEY_MAX_OUTPUT_BUFFER_COUNT, DEFAULT_OUT_SURFACE_CNT);
}
format_.GetIntValue(MediaDescriptionKey::MD_KEY_MAX_INPUT_BUFFER_COUNT, inputBufferCnt_);
format_.GetIntValue(MediaDescriptionKey::MD_KEY_MAX_OUTPUT_BUFFER_COUNT, outputBufferCnt_);
inputAvailQue_ = std::make_shared<BlockQueue<uint32_t>>("inputAvailQue", inputBufferCnt_);
codecAvailQue_ = std::make_shared<BlockQueue<uint32_t>>("codecAvailQue", outputBufferCnt_);
if (sInfo_.surface != nullptr) {
renderAvailQue_ = std::make_shared<BlockQueue<uint32_t>>("renderAvailQue", outputBufferCnt_);
requestSurfaceBufferQue_ = std::make_shared<BlockQueue<uint32_t>>("requestSurfaceBufferQue", outputBufferCnt_);
}
int32_t ret = AllocateInputBuffer(inputBufferCnt_, inputBufferSize_);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Allocate input buffers failed!");
ret = sInfo_.surface ? AllocateOutputBuffersFromSurface(outputBufferCnt_) :
AllocateOutputBuffer(outputBufferCnt_);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Allocate output buffers failed!");
return AVCS_ERR_OK;
}
int32_t VideoDecoder::UpdateOutputBuffer(uint32_t index)
{
std::shared_ptr<CodecBuffer> outputBuffer = buffers_[INDEX_OUTPUT][index];
if (width_ != outputBuffer->width || height_ != outputBuffer->height || bitDepth_ != outputBuffer->bitDepth) {
std::shared_ptr<AVAllocator> allocator =
AVAllocatorFactory::CreateSurfaceAllocator(sInfo_.requestConfig);
CHECK_AND_RETURN_RET_LOG(allocator != nullptr, AVCS_ERR_NO_MEMORY, "buffer %{public}d allocator is nullptr",
index);
outputBuffer->avBuffer = AVBuffer::CreateAVBuffer(allocator, 0);
CHECK_AND_RETURN_RET_LOG(outputBuffer->avBuffer != nullptr, AVCS_ERR_NO_MEMORY,
"Buffer allocate failed, index=%{public}d", index);
AVCODEC_LOGI("update output buffer success: index=%{public}d, size=%{public}d", index,
outputBuffer->avBuffer->memory_->GetCapacity());
SetCallerToBuffer(outputBuffer->avBuffer->memory_->GetSurfaceBuffer());
outputBuffer->owner_ = Owner::OWNED_BY_CODEC;
outputBuffer->width = width_;
outputBuffer->height = height_;
outputBuffer->bitDepth = bitDepth_;
}
return AVCS_ERR_OK;
}
int32_t VideoDecoder::UpdateSurfaceMemory(uint32_t index)
{
AVCODEC_SYNC_TRACE;
std::unique_lock<std::mutex> oLock(outputMutex_);
std::shared_ptr<CodecBuffer> outputBuffer = buffers_[INDEX_OUTPUT][index];
oLock.unlock();
if (width_ != outputBuffer->width || height_ != outputBuffer->height || bitDepth_ != outputBuffer->bitDepth) {
std::shared_ptr<FSurfaceMemory> surfaceMemory = outputBuffer->sMemory;
CHECK_AND_RETURN_RET_LOG(surfaceMemory != nullptr, AVCS_ERR_UNKNOWN, "Surface memory is nullptr!");
AVCODEC_LOGI("Update surface memory, width=%{public}d, height=%{public}d", width_, height_);
std::lock_guard<std::mutex> sLock(surfaceMutex_);
if (surfaceMemory->isAttached) {
sptr<SurfaceBuffer> surfaceBuffer = surfaceMemory->GetSurfaceBuffer();
CHECK_AND_RETURN_RET_LOG(surfaceBuffer != nullptr, AVCS_ERR_UNKNOWN, "Get surface buffer failed!");
int32_t ret = Detach(surfaceBuffer);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Surface buffer detach failed!");
surfaceMemory->isAttached = false;
}
surfaceMemory->ReleaseSurfaceBuffer();
int32_t ret = surfaceMemory->AllocSurfaceBuffer(width_, height_);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Alloc surface buffer failed!");
sptr<SurfaceBuffer> newSurfaceBuffer = surfaceMemory->GetSurfaceBuffer();
CHECK_AND_RETURN_RET_LOG(newSurfaceBuffer != nullptr, AVCS_ERR_UNKNOWN, "Alloc surface buffer failed!");
ret = Attach(newSurfaceBuffer);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Surface buffer attach failed!");
surfaceMemory->isAttached = true;
outputBuffer->avBuffer =
AVBuffer::CreateAVBuffer(outputBuffer->sMemory->GetBase(), outputBuffer->sMemory->GetSize());
CHECK_AND_RETURN_RET_LOG(outputBuffer->avBuffer != nullptr, AVCS_ERR_NO_MEMORY,
"Num %{public}u Buffer allocate failed, index=%{public}d", decInstanceID_, index);
outputBuffer->width = width_;
outputBuffer->height = height_;
outputBuffer->bitDepth = bitDepth_;
}
return AVCS_ERR_OK;
}
int32_t VideoDecoder::GetSurfaceBufferStride(const std::shared_ptr<CodecBuffer> &frameBuffer)
{
int32_t surfaceBufferStride = 0;
if (sInfo_.surface == nullptr) {
auto surfaceBuffer = frameBuffer->avBuffer->memory_->GetSurfaceBuffer();
CHECK_AND_RETURN_RET_LOG(surfaceBuffer != nullptr, -1, "surfaceBuffer is nullptr");
auto bufferHandle = surfaceBuffer->GetBufferHandle();
CHECK_AND_RETURN_RET_LOG(bufferHandle != nullptr, -1, "fail to get bufferHandle");
surfaceBufferStride = bufferHandle->stride;
} else {
surfaceBufferStride = frameBuffer->sMemory->GetSurfaceBufferStride();
}
return surfaceBufferStride;
}
void VideoDecoder::ReleaseBuffers()
{
ResetData();
if (!isBufferAllocated_) {
return;
}
inputAvailQue_->Clear();
buffers_[INDEX_INPUT].clear();
inputBufferCnt_ = 0;
std::unique_lock<std::mutex> oLock(outputMutex_);
codecAvailQue_->Clear();
if (sInfo_.surface != nullptr) {
{
std::lock_guard<std::mutex> sLock(surfaceMutex_);
StopRequestSurfaceBufferThread();
sInfo_.surface->CleanCache();
}
renderAvailQue_->Clear();
requestSurfaceBufferQue_->Clear();
{
std::lock_guard<std::mutex> mLock(renderBufferMapMutex_);
renderSurfaceBufferMap_.clear();
}
for (uint32_t i = 0; i < buffers_[INDEX_OUTPUT].size(); i++) {
std::shared_ptr<CodecBuffer> outputBuffer = buffers_[INDEX_OUTPUT][i];
if (outputBuffer->owner_ == Owner::OWNED_BY_CODEC) {
std::shared_ptr<FSurfaceMemory> surfaceMemory = outputBuffer->sMemory;
surfaceMemory->ReleaseSurfaceBuffer();
outputBuffer->owner_ = Owner::OWNED_BY_SURFACE;
}
}
AVCODEC_LOGI("Num %{public}u surface cleancache success", decInstanceID_);
}
buffers_[INDEX_OUTPUT].clear();
outputBufferCnt_ = 0;
outAVBuffer4Surface_.clear();
oLock.unlock();
isBufferAllocated_ = false;
}
int32_t VideoDecoder::QueueInputBuffer(uint32_t index)
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG(state_ == State::RUNNING, AVCS_ERR_INVALID_STATE,
"Queue input buffer failed: not in Running state");
CHECK_AND_RETURN_RET_LOG(index < buffers_[INDEX_INPUT].size(), AVCS_ERR_INVALID_VAL,
"Queue input buffer failed with bad index, index=%{public}u, buffer_size=%{public}zu",
index, buffers_[INDEX_INPUT].size());
std::shared_ptr<CodecBuffer> inputBuffer = buffers_[INDEX_INPUT][index];
CHECK_AND_RETURN_RET_LOG(inputBuffer->owner_ == Owner::OWNED_BY_USER, AVCS_ERR_INVALID_OPERATION,
"Queue input buffer failed: buffer with index=%{public}u is not available", index);
inputBuffer->owner_ = Owner::OWNED_BY_CODEC;
inputAvailQue_->Push(index);
return AVCS_ERR_OK;
}
int32_t VideoDecoder::FillFrameBuffer(const std::shared_ptr<CodecBuffer> &frameBuffer)
{
VideoPixelFormat targetPixelFmt = outputPixelFmt_;
AVPixelFormat ffmpegFormat;
if (bitDepth_ == BITS_PER_PIXEL_COMPONENT_10) {
ffmpegFormat = AVPixelFormat::AV_PIX_FMT_P010LE;
} else {
ffmpegFormat = ConvertPixelFormatToFFmpeg(targetPixelFmt);
}
std::lock_guard<std::mutex> convertLock(convertDataMutex_);
int32_t ret = ConvertVideoFrame(&scale_, cachedFrame_, scaleData_, scaleLineSize_, ffmpegFormat);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Scale video frame failed: %{public}d", ret);
isConverted_ = true;
std::shared_ptr<AVMemory> &bufferMemory = frameBuffer->avBuffer->memory_;
CHECK_AND_RETURN_RET_LOG(bufferMemory != nullptr, AVCS_ERR_INVALID_VAL, "bufferMemory is nullptr");
sptr<SurfaceBuffer> surfaceBuffer = sInfo_.surface ? frameBuffer->sMemory->GetSurfaceBuffer() :
frameBuffer->avBuffer->memory_->GetSurfaceBuffer();
CHECK_AND_RETURN_RET_LOG(surfaceBuffer != nullptr, AVCS_ERR_INVALID_VAL, "surfaceBuffer is nullptr");
CHECK_AND_RETURN_RET_LOG(surfaceBuffer->GetVirAddr() == bufferMemory->GetAddr() &&
surfaceBuffer->GetSize() == static_cast<uint32_t>(bufferMemory->GetCapacity()),
AVCS_ERR_INVALID_VAL, "surfaceBuffer and bufferMemory not match");
CHECK_AND_RETURN_RET_LOG(surfaceBuffer->GetWidth() == cachedFrame_->width &&
surfaceBuffer->GetHeight() == cachedFrame_->height, AVCS_ERR_INVALID_VAL,
"surfaceBuffer not match current cachedFrame_");
bufferMemory->SetSize(0);
struct SurfaceInfo surfaceInfo;
surfaceInfo.scaleData = scaleData_;
surfaceInfo.scaleLineSize = scaleLineSize_;
int32_t surfaceStride = GetSurfaceBufferStride(frameBuffer);
CHECK_AND_RETURN_RET_LOG(surfaceStride > 0, AVCS_ERR_INVALID_VAL, "get GetSurfaceBufferStride failed");
surfaceInfo.surfaceStride = static_cast<uint32_t>(surfaceStride);
Format bufferFormat;
bufferFormat.PutIntValue(MediaDescriptionKey::MD_KEY_HEIGHT, cachedFrame_->height);
bufferFormat.PutIntValue(MediaDescriptionKey::MD_KEY_WIDTH, surfaceStride);
bufferFormat.PutIntValue(MediaDescriptionKey::MD_KEY_PIXEL_FORMAT, static_cast<int32_t>(targetPixelFmt));
if (sInfo_.surface) {
surfaceInfo.surfaceFence = frameBuffer->sMemory->GetFence();
ret = WriteSurfaceData(bufferMemory, surfaceInfo, bufferFormat);
} else {
ret = WriteBufferData(bufferMemory, scaleData_, scaleLineSize_, bufferFormat);
}
SetBufferCropMetadata(surfaceBuffer);
FillHdrInfo(surfaceBuffer);
#ifdef BUILD_ENG_VERSION
DumpConvertOut(surfaceInfo);
#endif
frameBuffer->avBuffer->pts_ = cachedFrame_->pts;
AVCODEC_LOGD("Fill frame buffer successful");
return ret;
}
void VideoDecoder::FramePostProcess(const std::shared_ptr<CodecBuffer> &frameBuffer, uint32_t index, int32_t status)
{
if (status == AVCS_ERR_OK) {
codecAvailQue_->Pop();
frameBuffer->owner_ = Owner::OWNED_BY_USER;
if (sInfo_.surface) {
outAVBuffer4Surface_[index]->pts_ = frameBuffer->avBuffer->pts_;
outAVBuffer4Surface_[index]->flag_ = frameBuffer->avBuffer->flag_;
}
callback_->OnOutputBufferAvailable(index, sInfo_.surface ?
outAVBuffer4Surface_[index] : frameBuffer->avBuffer);
} else if (status == AVCS_ERR_UNSUPPORT) {
AVCODEC_LOGE("Recevie frame from codec failed: OnError");
callback_->OnError(AVCodecErrorType::AVCODEC_ERROR_INTERNAL, AVCodecServiceErrCode::AVCS_ERR_UNSUPPORT);
state_ = State::ERROR;
} else {
AVCODEC_LOGE("Recevie frame from codec failed");
callback_->OnError(AVCodecErrorType::AVCODEC_ERROR_INTERNAL, AVCodecServiceErrCode::AVCS_ERR_UNKNOWN);
state_ = State::ERROR;
}
}
#ifdef BUILD_ENG_VERSION
void VideoDecoder::DumpOutputBuffer(int32_t bitDepth)
{
std::lock_guard<std::mutex> convertLock(convertDataMutex_);
if (!dumpOutFile_ || !dumpOutFile_->is_open()) {
return;
}
int32_t pixelBytes = 1;
if (bitDepth == BITS_PER_PIXEL_COMPONENT_10) {
pixelBytes = 2;
}
for (int32_t i = 0; i < cachedFrame_->height; i++) {
dumpOutFile_->write(reinterpret_cast<char *>(cachedFrame_->data[0] + i * cachedFrame_->linesize[0]),
static_cast<int32_t>(cachedFrame_->width * pixelBytes));
}
for (int32_t i = 0; i < cachedFrame_->height / 2; i++) {
dumpOutFile_->write(reinterpret_cast<char *>(cachedFrame_->data[1] + i * cachedFrame_->linesize[1]),
static_cast<int32_t>(cachedFrame_->width * pixelBytes / 2));
}
for (int32_t i = 0; i < cachedFrame_->height / 2; i++) {
dumpOutFile_->write(reinterpret_cast<char *>(cachedFrame_->data[2] + i * cachedFrame_->linesize[2]),
static_cast<int32_t>(cachedFrame_->width * pixelBytes / 2));
}
}
void VideoDecoder::DumpConvertOut(struct SurfaceInfo &surfaceInfo)
{
if (!dumpConvertFile_ || !dumpConvertFile_->is_open()) {
return;
}
if (surfaceInfo.scaleData[0] != nullptr) {
int32_t srcPos = 0;
int32_t dataSize = surfaceInfo.scaleLineSize[0];
int32_t writeSize = dataSize > static_cast<int32_t>(surfaceInfo.surfaceStride) ?
static_cast<int32_t>(surfaceInfo.surfaceStride) : dataSize;
for (int32_t i = 0; i < height_; i++) {
dumpConvertFile_->write(reinterpret_cast<char *>(surfaceInfo.scaleData[0] + srcPos), writeSize);
srcPos += dataSize;
}
srcPos = 0;
dataSize = surfaceInfo.scaleLineSize[1];
writeSize = dataSize > static_cast<int32_t>(surfaceInfo.surfaceStride) ?
static_cast<int32_t>(surfaceInfo.surfaceStride) : dataSize;
for (int32_t i = 0; i < height_ / 2; i++) {
dumpConvertFile_->write(reinterpret_cast<char *>(surfaceInfo.scaleData[1] + srcPos), writeSize);
srcPos += dataSize;
}
}
}
#endif
int32_t VideoDecoder::ReleaseOutputBuffer(uint32_t index)
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGI_LIMIT(LOG_LOW_FREQUENCY, "Num %{public}u release output buffer with index=%{public}u",
decInstanceID_, index);
std::unique_lock<std::mutex> oLock(outputMutex_);
CHECK_AND_RETURN_RET_LOG(index < buffers_[INDEX_OUTPUT].size(), AVCS_ERR_INVALID_VAL,
"Failed to release output buffer: invalid index");
std::shared_ptr<CodecBuffer> frameBuffer = buffers_[INDEX_OUTPUT][index];
if (frameBuffer->owner_ == Owner::OWNED_BY_USER) {
frameBuffer->owner_ = Owner::OWNED_BY_CODEC;
oLock.unlock();
codecAvailQue_->Push(index);
return AVCS_ERR_OK;
} else {
AVCODEC_LOGE("Release output buffer failed: check your index=%{public}u, owner=%{public}d",
index, frameBuffer->owner_.load());
return AVCS_ERR_INVALID_VAL;
}
}
int32_t VideoDecoder::Detach(sptr<SurfaceBuffer> surfaceBuffer)
{
int32_t err = sInfo_.surface->DetachBufferFromQueue(surfaceBuffer);
CHECK_AND_RETURN_RET_LOG(
err == 0, err, "Surface(%{public}" PRIu64 "), detach buffer(%{public}u) to queue failed, GSError=%{public}d",
sInfo_.surface->GetUniqueId(), surfaceBuffer->GetSeqNum(), err);
return AVCS_ERR_OK;
}
int32_t VideoDecoder::SetCallback(const std::shared_ptr<MediaCodecCallback> &callback)
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG(callback != nullptr, AVCS_ERR_INVALID_VAL, "Set callback failed: callback is NULL");
callback_ = callback;
return AVCS_ERR_OK;
}
int32_t VideoDecoder::NotifyMemoryRecycle()
{
CHECK_AND_RETURN_RET_LOG(sInfo_.surface != nullptr, AVCS_ERR_UNKNOWN, "Only surface mode support!");
CHECK_AND_RETURN_RET_LOGD(state_ == State::RUNNING || state_ == State::FLUSHED || state_ == State::EOS,
AVCS_ERR_INVALID_STATE, "Current state can't recycle memory!");
AVCODEC_LOGI("Num %{public}u Begin to freeze this codec", decInstanceID_);
State currentState = state_;
state_ = State::FREEZING;
int32_t errCode = FreezeBuffers(currentState);
CHECK_AND_RETURN_RET_LOG(errCode == AVCS_ERR_OK, errCode, "Codec freeze buffers failed!");
state_ = State::FROZEN;
return AVCS_ERR_OK;
}
int32_t VideoDecoder::NotifyMemoryWriteBack()
{
CHECK_AND_RETURN_RET_LOG(sInfo_.surface != nullptr, AVCS_ERR_UNKNOWN, "Only surface mode support!");
AVCODEC_LOGI("Num %{public}u Begin to active this codec", decInstanceID_);
int32_t errCode = ActiveBuffers();
CHECK_AND_RETURN_RET_LOG(errCode == AVCS_ERR_OK, errCode, "Codec active buffers failed!");
state_ = State::RUNNING;
return AVCS_ERR_OK;
}
void VideoDecoder::SetCallerToBuffer(sptr<SurfaceBuffer> surfaceBuffer)
{
CHECK_AND_RETURN_LOG(surfaceBuffer != nullptr, "Surface buffer is nullptr!");
int32_t fd = surfaceBuffer->GetFileDescriptor();
CHECK_AND_RETURN_LOG(fd > 0, "Invalid fd %{public}d, surfacebuf(%{public}u)", fd, surfaceBuffer->GetSeqNum());
std::string type = "sw-video-decoder";
std::string mime(decInfo_.mimeType);
std::vector<std::string> splitMime;
std::string token;
std::istringstream iss(mime);
while (std::getline(iss, token, '/')) {
splitMime.push_back(token);
}
if (!splitMime.empty()) {
mime = splitMime.back();
}
std::string name = decInfo_.processName + "-" + std::to_string(width_) + "x" + std::to_string(height_)
+ "-" + mime + "-" + decInfo_.instanceId;
ioctl(fd, DMA_BUF_SET_LEAK_TYPE, type.c_str());
std::string pid = std::to_string(decInfo_.pid);
ioctl(fd, DMA_BUF_SET_NAME_A, pid.c_str());
ioctl(fd, DMA_BUF_SET_NAME_A, name.c_str());
}
void VideoDecoder::SetBufferCropMetadata(sptr<SurfaceBuffer> surfaceBuffer)
{
using namespace OHOS::HDI::Display::Graphic::Common::V1_0;
if (surfaceBuffer == nullptr) {
AVCODEC_LOGE("SetBufferCropMetadata failed: surfaceBuffer is null");
return;
}
std::vector<uint8_t> cropVec(sizeof(BufferHandleMetaRegion));
BufferHandleMetaRegion* crop = reinterpret_cast<BufferHandleMetaRegion*>(cropVec.data());
crop->left = 0;
crop->top = 0;
crop->width = static_cast<UINT32>(width_ - 1);
crop->height = static_cast<UINT32>(height_ - 1);
GSError err = surfaceBuffer->SetMetadata(ATTRKEY_CROP_REGION, cropVec);
if (err != GSERROR_OK) {
AVCODEC_LOGE("SetBufferCropMetadata failed: GSError = %{public}d", err);
return;
}
}
void VideoDecoder::CallCallBack(AVCodecErrorType errType, AVCodecServiceErrCode serEcode)
{
if (callback_ == nullptr) { return; }
callback_->OnError(errType, serEcode);
state_ = State::ERROR;
}
std::mutex VideoDecoder::decoderCountMutex_;
std::vector<uint32_t> VideoDecoder::decInstanceIDSet_;
std::vector<uint32_t> VideoDecoder::freeIDSet_;
}
}
}