* 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 "fcodec.h"
#include <iostream>
#include <set>
#include <thread>
#include <malloc.h>
#include <sstream>
#include <sys/ioctl.h>
#include <linux/dma-buf.h>
#include "syspara/parameters.h"
#include "securec.h"
#include "avcodec_trace.h"
#include "avcodec_log.h"
#include "utils.h"
#include "avcodec_codec_name.h"
#include "dma_swap.h"
#include "fcodec_surport_codec.h"
#ifndef EMULATOR_ENABLED
#include "fcodec_utils.h"
#endif
namespace OHOS {
namespace MediaAVCodec {
namespace Codec {
namespace {
constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, LOG_DOMAIN_FRAMEWORK, "FCodec"};
constexpr uint32_t INDEX_INPUT = 0;
constexpr uint32_t INDEX_OUTPUT = 1;
constexpr int32_t DEFAULT_IN_BUFFER_CNT = 4;
constexpr int32_t DEFAULT_OUT_SURFACE_CNT = 4;
constexpr int32_t DEFAULT_OUT_BUFFER_CNT = 3;
constexpr int32_t DEFAULT_MIN_BUFFER_CNT = 2;
constexpr uint32_t VIDEO_PIX_DEPTH_YUV = 3;
constexpr int32_t VIDEO_MIN_BUFFER_SIZE = 1474560;
constexpr int32_t VIDEO_MIN_SIZE = 2;
constexpr int32_t VIDEO_MAX_WIDTH_SIZE = 5120;
constexpr int32_t VIDEO_MAX_HEIGHT_SIZE = 5120;
constexpr int32_t DEFAULT_VIDEO_WIDTH = 1920;
constexpr int32_t DEFAULT_VIDEO_HEIGHT = 1080;
constexpr uint32_t DEFAULT_TRY_DECODE_TIME = 1;
constexpr uint32_t DEFAULT_DECODE_WAIT_TIME = 200;
constexpr uint32_t LOG_FREQUENCE = 2000;
constexpr int32_t DEFAULT_THREAD_COUNT = 2;
#ifdef BUILD_ENG_VERSION
constexpr uint32_t PATH_MAX_LEN = 128;
constexpr char DUMP_PATH[] = "/data/misc/fcodecdump";
#endif
static std::map<RawVideoPixelFormat, AVPixelFormat> g_convertFfmpegPixFmt = {
{RawVideoPixelFormat::YUV420P, AVPixelFormat::AV_PIX_FMT_YUV420P},
{RawVideoPixelFormat::NV12, AVPixelFormat::AV_PIX_FMT_NV12},
{RawVideoPixelFormat::NV21, AVPixelFormat::AV_PIX_FMT_NV21},
{RawVideoPixelFormat::RGBA, AVPixelFormat::AV_PIX_FMT_RGBA},
};
#define DMA_BUF_SET_LEAK_TYPE _IOW(DMA_BUF_BASE, 5, const char *)
}
using namespace OHOS::Media;
using FMTKey = Media::Tag;
FCodec::FCodec(const std::string &name) : codecName_(name), state_(State::UNINITIALIZED) {}
FCodec::~FCodec()
{
ReleaseResource();
callback_ = nullptr;
#ifdef BUILD_ENG_VERSION
if (dumpInFile_ != nullptr) {
dumpInFile_->close();
}
if (dumpOutFile_ != nullptr) {
dumpOutFile_->close();
}
#endif
mallopt(M_FLUSH_THREAD_CACHE, 0);
}
int32_t FCodec::Init(Meta &callerInfo)
{
if (callerInfo.GetData(Tag::AV_CODEC_FORWARD_CALLER_PID, fDecInfo_.pid) &&
callerInfo.GetData(Tag::AV_CODEC_FORWARD_CALLER_PROCESS_NAME, fDecInfo_.processName)) {
fDecInfo_.calledByAvcodec = false;
} else if (callerInfo.GetData(Tag::AV_CODEC_CALLER_PID, fDecInfo_.pid) &&
callerInfo.GetData(Tag::AV_CODEC_CALLER_PROCESS_NAME, fDecInfo_.processName)) {
fDecInfo_.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");
return AVCS_ERR_OK;
}
#ifdef BUILD_ENG_VERSION
void FCodec::OpenDumpFile()
{
std::string dumpModeStr = OHOS::system::GetParameter("fcodec.dump", "0");
AVCODEC_LOGI("dumpModeStr %{public}s", dumpModeStr.c_str());
CHECK_AND_RETURN_LOG(dumpModeStr.length() == 2, "dumpModeStr length should equal 2");
char fileName[PATH_MAX_LEN] = {0};
int ret;
if (dumpModeStr[0] == '1') {
ret = sprintf_s(fileName, sizeof(fileName), "%s/input_%p.h264", DUMP_PATH, this);
CHECK_AND_RETURN_LOG(ret > 0, "Fail to sprintf input fileName");
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') {
ret = sprintf_s(fileName, sizeof(fileName), "%s/output_%p.yuv", DUMP_PATH, this);
CHECK_AND_RETURN_LOG(ret > 0, "Fail to sprintf output fileName");
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;
}
}
}
#endif
int32_t FCodec::Initialize()
{
AVCODEC_SYNC_TRACE;
fDecInfo_.instanceId = std::to_string(instanceId_);
decName_ = "fdecoder_[" + std::to_string(instanceId_) + "]";
AVCODEC_LOGI("current decoder name: %{public}s, codec name: %{public}s", decName_.c_str(), codecName_.c_str());
CHECK_AND_RETURN_RET_LOG(!codecName_.empty(), AVCS_ERR_INVALID_VAL, "Init codec failed: empty name");
pid_ = getpid();
std::string fcodecName;
std::string_view mime;
for (uint32_t i = 0; i < SUPPORT_VCODEC_NUM; ++i) {
if (SUPPORT_VCODEC[i].codecName == codecName_) {
fcodecName = SUPPORT_VCODEC[i].ffmpegCodec;
mime = SUPPORT_VCODEC[i].mimeType;
break;
}
}
CHECK_AND_RETURN_RET_LOG(!fcodecName.empty(), AVCS_ERR_INVALID_VAL,
"Init codec failed: not support name: %{public}s", codecName_.c_str());
format_.PutStringValue(FMTKey::MIME_TYPE, mime);
format_.PutStringValue(FMTKey::MEDIA_CODEC_NAME, codecName_);
fDecInfo_.mimeType = mime;
avCodec_ = std::shared_ptr<AVCodec>(const_cast<AVCodec *>(avcodec_find_decoder_by_name(fcodecName.c_str())),
[](void *ptr) {});
CHECK_AND_RETURN_RET_LOG(avCodec_ != nullptr, AVCS_ERR_INVALID_VAL,
"Init codec failed: cannot find codec with name %{public}s", codecName_.c_str());
sendTask_ = std::make_shared<TaskThread>("SendFrame");
sendTask_->RegisterHandler([this] { SendFrame(); });
receiveTask_ = std::make_shared<TaskThread>("ReceiveFrame");
receiveTask_->RegisterHandler([this] { ReceiveFrame(); });
#ifdef BUILD_ENG_VERSION
OpenDumpFile();
#endif
state_ = State::INITIALIZED;
AVCODEC_LOGI("Init codec successful, state: Uninitialized -> Initialized");
return AVCS_ERR_OK;
}
void FCodec::GetSurfaceCfgFromFmt(const Format &format)
{
int32_t val = -1;
std::lock_guard<std::mutex> fLock(formatMutex_);
if (format.GetIntValue(FMTKey::VIDEO_PIXEL_FORMAT, val)) {
if (IsValidPixelFormat(val)) {
outputPixelFmt_ = static_cast<VideoPixelFormat>(val);
GraphicPixelFormat surfacePixelFmt = TranslateSurfaceFormat(outputPixelFmt_);
format_.PutIntValue(FMTKey::VIDEO_PIXEL_FORMAT, val);
format_.PutIntValue(FMTKey::VIDEO_GRAPHIC_PIXEL_FORMAT, static_cast<int32_t>(surfacePixelFmt));
AVCODEC_LOGI("Set parameter pixel_format: %{public}d success.", val);
} else {
AVCODEC_LOGE("Invalid pixel_format: %{public}d.", val);
}
}
if (format.GetIntValue(FMTKey::VIDEO_SCALE_TYPE, val)) {
if (IsValidScaleType(val)) {
format_.PutIntValue(FMTKey::VIDEO_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(FMTKey::VIDEO_ORIENTATION_TYPE, val)) {
orientation = val;
AVCODEC_LOGI("Get parameter video_orientation_type: %{public}d success.", orientation.value());
}
if (!orientation.has_value() && format.GetIntValue(FMTKey::VIDEO_ROTATION, val)) {
if (IsValidRotation(val)) {
orientation = static_cast<int32_t>(TranslateSurfaceRotation(static_cast<VideoRotation>(val)));
AVCODEC_LOGI("Get parameter rotation_angle: %{public}d success.", orientation.value());
} else {
AVCODEC_LOGE("Invalid rotation_angle: %{public}d.", val);
}
}
if (orientation) {
format_.PutIntValue(FMTKey::VIDEO_ORIENTATION_TYPE, orientation.value());
}
}
void FCodec::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",
std::string(formatKey).c_str(), minVal, maxVal);
}
}
int32_t FCodec::AllocateCodecContext()
{
avCodecContext_ = std::shared_ptr<AVCodecContext>(avcodec_alloc_context3(avCodec_.get()), [](AVCodecContext *p) {
if (p != nullptr) {
if (p->extradata) {
av_free(p->extradata);
p->extradata = nullptr;
}
avcodec_free_context(&p);
}
});
CHECK_AND_RETURN_RET_LOG(avCodecContext_ != nullptr, AVCS_ERR_INVALID_OPERATION,
"Configure codec failed: Allocate context error");
avCodecContext_->codec_type = AVMEDIA_TYPE_VIDEO;
return AVCS_ERR_OK;
}
int32_t FCodec::ValidateAndSetVideoResolutionFormat(const Format &format)
{
CHECK_AND_RETURN_RET_LOG(format.GetIntValue(FMTKey::VIDEO_WIDTH, width_), AVCS_ERR_INVALID_VAL,
"Get video width failed");
CHECK_AND_RETURN_RET_LOG(format.GetIntValue(FMTKey::VIDEO_HEIGHT, height_), AVCS_ERR_INVALID_VAL,
"Get video height failed");
bool isValid = (width_ >= VIDEO_MIN_SIZE && width_ <= VIDEO_MAX_WIDTH_SIZE && height_ >= VIDEO_MIN_SIZE &&
height_ <= VIDEO_MAX_HEIGHT_SIZE);
CHECK_AND_RETURN_RET_LOG(isValid, AVCS_ERR_INVALID_VAL,
"Invalid video resolution: width=%{public}d, height=%{public}d", width_, height_);
format_.PutIntValue(FMTKey::VIDEO_STRIDE,
outputPixelFmt_ == VideoPixelFormat::RGBA ? width_ * VIDEO_PIX_DEPTH_RGBA : width_);
format_.PutIntValue(FMTKey::VIDEO_SLICE_HEIGHT, height_);
format_.PutIntValue(FMTKey::VIDEO_PIC_WIDTH, width_);
format_.PutIntValue(FMTKey::VIDEO_PIC_HEIGHT, height_);
return AVCS_ERR_OK;
}
int32_t FCodec::ConfigureRawVideoPixelFormat(const Format &format)
{
int32_t pixelFormat = 0;
if (!format.GetIntValue(FMTKey::VIDEO_RAWVIDEO_INPUT_PIXEL_FORMAT, pixelFormat)) {
return AVCS_ERR_OK;
}
rawvideoPixFmt_ = static_cast<RawVideoPixelFormat>(pixelFormat);
CHECK_AND_RETURN_RET_LOG(
rawvideoPixFmt_ >= RawVideoPixelFormat::YUV420P && rawvideoPixFmt_ <= RawVideoPixelFormat::RGBA,
AVCS_ERR_INVALID_VAL, "Unsupported rawvideo pixel format %{public}d!", static_cast<int32_t>(rawvideoPixFmt_));
avCodecContext_->pix_fmt = g_convertFfmpegPixFmt[rawvideoPixFmt_];
return AVCS_ERR_OK;
}
void FCodec::ConfigureThread(const Format &format)
{
avCodecContext_->width = width_;
avCodecContext_->height = height_;
avCodecContext_->thread_count = DEFAULT_THREAD_COUNT;
int32_t decodeOrder = 0;
format.GetIntValue(FMTKey::VIDEO_DECODER_OUTPUT_IN_DECODING_ORDER, decodeOrder);
if (codecName_ == AVCodecCodecName::VIDEO_DECODER_AVC_NAME && decodeOrder != 0) {
CHECK_AND_RETURN_LOG(av_opt_set(avCodecContext_.get(), "output_decode_order", "1", AV_OPT_SEARCH_CHILDREN) == 0,
"H264 decode order enable failed");
AVCODEC_LOGI("H264 decode order enabled by %{public}d", decodeOrder);
}
}
int32_t FCodec::ConfigureContext(const Format &format)
{
int32_t ret = AllocateCodecContext();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Configure codec failed: Allocate context error");
ret = ValidateAndSetVideoResolutionFormat(format);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Invalid video resolution");
ret = ConfigureRawVideoPixelFormat(format);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Configure pixel format failed");
ConfigureThread(format);
#if (defined SUPPORT_CODEC_RV) || (defined SUPPORT_CODEC_MP4V_ES) || (defined SUPPORT_CODEC_VC1)
return SetCodecExtradata(format);
#else
return AVCS_ERR_OK;
#endif
}
#if (defined SUPPORT_CODEC_RV) || (defined SUPPORT_CODEC_MP4V_ES) || (defined SUPPORT_CODEC_VC1)
int32_t FCodec::SetCodecExtradata(const Format &format)
{
size_t extraSize = 0;
uint8_t *extraData = nullptr;
if (format.GetBuffer(FMTKey::MEDIA_CODEC_CONFIG, &extraData, extraSize)) {
if (extraData == nullptr || extraSize == 0) {
AVCODEC_LOGE("extradata getBufer failed!");
return AVCS_ERR_INVALID_VAL;
}
avCodecContext_->extradata = static_cast<uint8_t *>(av_mallocz(extraSize + AV_INPUT_BUFFER_PADDING_SIZE));
if (avCodecContext_->extradata == nullptr) {
AVCODEC_LOGE("extradata malloc failed!");
return AVCS_ERR_INVALID_VAL;
}
avCodecContext_->extradata_size = static_cast<int>(extraSize);
errno_t rc = memcpy_s(avCodecContext_->extradata, extraSize, extraData, extraSize);
if (rc != EOK) {
AVCODEC_LOGE("extradata memcpy_s failed.");
return AVCodecServiceErrCode::AVCS_ERR_INVALID_VAL;
}
rc = memset_s(avCodecContext_->extradata + extraSize, AV_INPUT_BUFFER_PADDING_SIZE, 0,
AV_INPUT_BUFFER_PADDING_SIZE);
if (rc != EOK) {
AVCODEC_LOGE("extradata memset_s failed.");
return AVCodecServiceErrCode::AVCS_ERR_INVALID_VAL;
}
}
return AVCS_ERR_OK;
}
#endif
int32_t FCodec::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(FMTKey::VIDEO_WIDTH, DEFAULT_VIDEO_WIDTH);
format_.PutIntValue(FMTKey::VIDEO_HEIGHT, DEFAULT_VIDEO_HEIGHT);
format_.PutIntValue(FMTKey::REQUIRED_OUT_BUFFER_CNT, DEFAULT_OUT_BUFFER_CNT);
format_.PutIntValue(FMTKey::REQUIRED_IN_BUFFER_CNT, DEFAULT_IN_BUFFER_CNT);
for (auto &it : format.GetFormatMap()) {
if (it.first == FMTKey::REQUIRED_OUT_BUFFER_CNT) {
isOutBufSetted_ = true;
ConfigureDefaultVal(format, it.first, DEFAULT_MIN_BUFFER_CNT);
} else if (it.first == FMTKey::REQUIRED_IN_BUFFER_CNT) {
ConfigureDefaultVal(format, it.first, DEFAULT_MIN_BUFFER_CNT);
} else if (it.first == FMTKey::VIDEO_WIDTH) {
ConfigureDefaultVal(format, it.first, VIDEO_MIN_SIZE, VIDEO_MAX_WIDTH_SIZE);
} else if (it.first == FMTKey::VIDEO_HEIGHT) {
ConfigureDefaultVal(format, it.first, VIDEO_MIN_SIZE, VIDEO_MAX_HEIGHT_SIZE);
} else if (it.first == FMTKey::MEDIA_BITRATE) {
int64_t val64 = 0L;
CHECK_AND_RETURN_RET_LOG(format.GetLongValue(FMTKey::MEDIA_BITRATE, val64) && val64 > 0L,
AVCS_ERR_INVALID_VAL, "Cannot get bit rate!");
format_.PutLongValue(FMTKey::MEDIA_BITRATE, val64);
} else if (it.first == FMTKey::VIDEO_FRAME_RATE) {
double val = 0;
CHECK_AND_RETURN_RET_LOG(format.GetDoubleValue(FMTKey::VIDEO_FRAME_RATE, val) && val > 0.0f,
AVCS_ERR_INVALID_VAL, "Cannot get frame rate!");
format_.PutDoubleValue(FMTKey::VIDEO_FRAME_RATE, val);
} else if (it.first == FMTKey::VIDEO_PIXEL_FORMAT || it.first == FMTKey::VIDEO_ROTATION ||
it.first == FMTKey::VIDEO_ORIENTATION_TYPE || it.first == FMTKey::VIDEO_SCALE_TYPE ||
it.first == FMTKey::VIDEO_DECODER_OUTPUT_IN_DECODING_ORDER) {
continue;
} else {
AVCODEC_LOGW("Set parameter failed: %{public}s, unsupport key", it.first.data());
}
}
GetSurfaceCfgFromFmt(format);
if (outputPixelFmt_ == VideoPixelFormat::UNKNOWN) {
outputPixelFmt_ = VideoPixelFormat::NV12;
format_.PutIntValue(FMTKey::VIDEO_PIXEL_FORMAT, static_cast<int32_t>(VideoPixelFormat::NV12));
}
AVCODEC_LOGI("current output pixel format %{public}d", static_cast<int32_t>(outputPixelFmt_));
int32_t ret = ConfigureContext(format);
state_ = State::CONFIGURED;
AVCODEC_LOGI("Configured codec successful: state: Initialized -> Configured");
return ret;
}
bool FCodec::IsActive() const
{
return state_ == State::RUNNING || state_ == State::FLUSHED || state_ == State::EOS;
}
void FCodec::FreeExtraData()
{
if (avCodecContext_->extradata) {
av_free(avCodecContext_->extradata);
avCodecContext_->extradata = nullptr;
avCodecContext_->extradata_size = 0;
}
avCodecContext_->coded_width = 0;
avCodecContext_->coded_height = 0;
}
void FCodec::ResetContext(bool isNeedFree)
{
CHECK_AND_RETURN_LOG(avCodecContext_ != nullptr, "Avcodec context is nullptr");
if (isNeedFree) {
FreeExtraData();
avCodecContext_->width = 0;
avCodecContext_->height = 0;
avCodecContext_->get_buffer2 = nullptr;
return;
}
if (codecName_ == AVCodecCodecName::VIDEO_DECODER_AVC_NAME) {
FreeExtraData();
}
}
int32_t FCodec::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");
if (state_ != State::FLUSHED) {
CHECK_AND_RETURN_RET_LOG(avcodec_open2(avCodecContext_.get(), avCodec_.get(), nullptr) == 0, AVCS_ERR_UNKNOWN,
"Start codec failed: cannot open avcodec");
}
if (!isBufferAllocated_) {
cachedFrame_ = std::shared_ptr<AVFrame>(av_frame_alloc(), [](AVFrame *p) { av_frame_free(&p); });
avPacket_ = std::shared_ptr<AVPacket>(av_packet_alloc(), [](AVPacket *p) { av_packet_free(&p); });
CHECK_AND_RETURN_RET_LOG((cachedFrame_ != nullptr && avPacket_ != nullptr), AVCS_ERR_UNKNOWN,
"Start codec failed: cannot allocate frame or packet");
for (int32_t i = 0; i < AV_NUM_DATA_POINTERS; i++) {
scaleData_[i] = nullptr;
scaleLineSize_[i] = 0;
}
isConverted_ = false;
int32_t ret = AllocateBuffers();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Start codec failed: cannot allocate buffers");
isBufferAllocated_ = true;
}
InitBuffers();
isSendEos_ = false;
sendTask_->Start();
receiveTask_->Start();
state_ = State::RUNNING;
AVCODEC_LOGI("%{public}s Start codec successful, state: Running", decName_.c_str());
return AVCS_ERR_OK;
}
void FCodec::InitBuffers()
{
inputAvailQue_->SetActive(true);
codecAvailQue_->SetActive(true);
CHECK_AND_RETURN_LOG(buffers_[INDEX_INPUT].size() > 0, "Input buffers is null!");
for (uint32_t i = 0u; 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());
}
std::lock_guard<std::mutex> oLock(outputMutex_);
CHECK_AND_RETURN_LOG(buffers_[INDEX_OUTPUT].size() > 0, "Output buffers is null!");
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;
}
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 FCodec::ResetData()
{
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 FCodec::FlushBuffers()
{
inputAvailQue_->Clear();
std::unique_lock<std::mutex> iLock(inputMutex_);
synIndex_ = std::nullopt;
iLock.unlock();
codecAvailQue_->Clear();
ResetData();
av_frame_unref(cachedFrame_.get());
av_packet_unref(avPacket_.get());
}
void FCodec::StopThread()
{
if (sendTask_ != nullptr && inputAvailQue_ != nullptr) {
std::unique_lock<std::mutex> sLock(sendMutex_);
sendCv_.notify_one();
sLock.unlock();
inputAvailQue_->SetActive(false, false);
sendTask_->Stop();
}
if (receiveTask_ != nullptr && codecAvailQue_ != nullptr) {
std::unique_lock<std::mutex> rLock(recvMutex_);
recvCv_.notify_one();
rLock.unlock();
codecAvailQue_->SetActive(false, false);
receiveTask_->Stop();
}
}
int32_t FCodec::Stop()
{
AVCODEC_SYNC_TRACE;
CHECK_AND_RETURN_RET_LOG((IsActive()), AVCS_ERR_INVALID_STATE, "Stop codec failed: not in executing state");
state_ = State::STOPPING;
AVCODEC_LOGI("step into STOPPING status");
StopThread();
avcodec_close(avCodecContext_.get());
ResetContext(false);
ReleaseBuffers();
state_ = State::CONFIGURED;
AVCODEC_LOGI("Stop codec successful, state: Configured");
return AVCS_ERR_OK;
}
int32_t FCodec::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());
std::unique_lock<std::mutex> sLock(sendMutex_);
sendCv_.notify_one();
sLock.unlock();
inputAvailQue_->SetActive(false, false);
sendTask_->Pause();
std::unique_lock<std::mutex> rLock(recvMutex_);
recvCv_.notify_one();
rLock.unlock();
codecAvailQue_->SetActive(false, false);
receiveTask_->Pause();
avcodec_flush_buffers(avCodecContext_.get());
ResetContext(false);
FlushBuffers();
state_ = State::FLUSHED;
AVCODEC_LOGI("%{public}s Flush codec successful, state: Flushed", decName_.c_str());
return AVCS_ERR_OK;
}
int32_t FCodec::Reset()
{
AVCODEC_SYNC_TRACE;
AVCODEC_LOGI("Reset codec called");
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 FCodec::ReleaseResource()
{
StopThread();
if (avCodecContext_ != nullptr) {
avcodec_close(avCodecContext_.get());
ResetContext();
}
ReleaseBuffers();
format_ = Format();
{
std::lock_guard<std::mutex> sLock(surfaceMutex_);
CHECK_AND_RETURN_LOG(sInfo_.surface != nullptr, "Surface is nullptr!");
transform_ = GRAPHIC_ROTATE_NONE;
AVCODEC_LOGI("Reset surface video_orientation_type: %{public}d", static_cast<int32_t>(transform_));
sInfo_.surface->SetTransform(transform_);
UnRegisterListenerToSurface(sInfo_.surface);
sInfo_.surface = nullptr;
}
StopRequestSurfaceBufferThread();
}
int32_t FCodec::Release()
{
AVCODEC_SYNC_TRACE;
state_ = State::STOPPING;
AVCODEC_LOGI("step into STOPPING status");
ReleaseResource();
state_ = State::UNINITIALIZED;
AVCODEC_LOGI("Release codec successful, state: Uninitialized");
return AVCS_ERR_OK;
}
void FCodec::SetSurfaceParameter()
{
int32_t val = -1;
std::optional<GraphicPixelFormat> surfacePixelFmt = std::nullopt;
std::optional<ScalingMode> scaling = std::nullopt;
std::optional<GraphicTransformType> orientation = std::nullopt;
std::unique_lock<std::mutex> fLock(formatMutex_);
if (format_.GetIntValue(FMTKey::VIDEO_PIXEL_FORMAT, val)) {
surfacePixelFmt = TranslateSurfaceFormat(static_cast<VideoPixelFormat>(val));
}
if (format_.GetIntValue(FMTKey::VIDEO_SCALE_TYPE, val)) {
scaling = static_cast<ScalingMode>(val);
}
if (format_.GetIntValue(FMTKey::VIDEO_ORIENTATION_TYPE, val)) {
orientation = static_cast<GraphicTransformType>(val);
}
fLock.unlock();
std::lock_guard<std::mutex> sLock(surfaceMutex_);
if (surfacePixelFmt) {
sInfo_.requestConfig.format = surfacePixelFmt.value();
}
if (scaling) {
sInfo_.scalingMode = scaling.value();
sInfo_.surface->SetScalingMode(sInfo_.scalingMode.value());
}
if (orientation) {
transform_ = orientation.value();
AVCODEC_LOGI("Set surface video_orientation_type: %{public}d success.", static_cast<int32_t>(transform_));
sInfo_.surface->SetTransform(transform_);
}
}
int32_t FCodec::SetParameter(const Format &format)
{
AVCODEC_SYNC_TRACE;
if (sInfo_.surface != nullptr) {
GetSurfaceCfgFromFmt(format);
SetSurfaceParameter();
}
AVCODEC_LOGI("Set parameter successful");
return AVCS_ERR_OK;
}
int32_t FCodec::GetOutputFormat(Format &format)
{
AVCODEC_SYNC_TRACE;
std::lock_guard<std::mutex> lock(formatMutex_);
if (!format_.ContainKey(FMTKey::MEDIA_BITRATE)) {
if (avCodecContext_ != nullptr) {
format_.PutLongValue(FMTKey::MEDIA_BITRATE, avCodecContext_->bit_rate);
}
}
if (!format_.ContainKey(FMTKey::VIDEO_FRAME_RATE)) {
if (avCodecContext_ != nullptr && avCodecContext_->framerate.den > 0) {
double value = static_cast<double>(avCodecContext_->framerate.num) /
static_cast<double>(avCodecContext_->framerate.den);
format_.PutDoubleValue(FMTKey::VIDEO_FRAME_RATE, value);
}
}
format = format_;
if (!format.ContainKey(FMTKey::REQUIRED_IN_BUFFER_SIZE)) {
int32_t stride = AlignUp(width_, VIDEO_ALIGN_SIZE);
int32_t maxInputSize = static_cast<int32_t>((stride * height_ * VIDEO_PIX_DEPTH_YUV) / UV_SCALE_FACTOR);
format.PutIntValue(FMTKey::REQUIRED_IN_BUFFER_SIZE, maxInputSize);
}
AVCODEC_LOGI("Get outputFormat successful");
return AVCS_ERR_OK;
}
void FCodec::CalculateBufferSize()
{
const int64_t stride = AlignUp(width_, VIDEO_ALIGN_SIZE);
int64_t size = stride * height_ * VIDEO_PIX_DEPTH_RGBA;
if (outputPixelFmt_ != VideoPixelFormat::RGBA) {
size = stride * height_ * VIDEO_PIX_DEPTH_YUV / UV_SCALE_FACTOR;
}
if (size >= INT32_MAX || size <= 0) {
outputBufferSize_ = 0;
AVCODEC_LOGE("width = %{public}d, height = %{public}d invalid", width_, height_);
return;
}
outputBufferSize_ = static_cast<int32_t>(size);
if (rawvideoPixFmt_ == RawVideoPixelFormat::RGBA) {
size = stride * height_ * VIDEO_PIX_DEPTH_RGBA;
} else {
size = std::max(VIDEO_MIN_BUFFER_SIZE, outputBufferSize_);
}
if (size >= INT32_MAX || size <= 0) {
inputBufferSize_ = 0;
AVCODEC_LOGE("width = %{public}d, height = %{public}d invalid", width_, height_);
return;
}
inputBufferSize_ = static_cast<int32_t>(size);
AVCODEC_LOGI("width = %{public}d, height = %{public}d, stride = %{public}d, Input buffer size = %{public}d, output "
"buffer size = %{public}d",
width_, height_, static_cast<int32_t>(stride), inputBufferSize_, outputBufferSize_);
}
int32_t FCodec::AllocateInputBuffer(int32_t inBufferSize)
{
int32_t valBufferCnt = 0;
for (uint32_t i = 0u; i < inputBufferCnt_; i++) {
std::shared_ptr<FBuffer> buf = std::make_shared<FBuffer>();
std::shared_ptr<AVAllocator> allocator =
AVAllocatorFactory::CreateSharedAllocator(MemoryFlag::MEMORY_READ_WRITE);
CHECK_AND_CONTINUE_LOG(allocator != nullptr, "input buffer %{public}u allocator is nullptr", i);
buf->avBuffer_ = AVBuffer::CreateAVBuffer(allocator, inBufferSize);
CHECK_AND_CONTINUE_LOG(buf->avBuffer_ != nullptr && buf->avBuffer_->memory_ != nullptr,
"Allocate input buffer failed, index=%{public}u", i);
AVCODEC_LOGI("Allocate input buffer success: index=%{public}u, size=%{public}d",
i, buf->avBuffer_->memory_->GetCapacity());
buffers_[INDEX_INPUT].emplace_back(buf);
valBufferCnt++;
}
CHECK_AND_RETURN_RET_LOGD(valBufferCnt < DEFAULT_MIN_BUFFER_CNT, AVCS_ERR_OK, "Allocate input buffers successful");
AVCODEC_LOGE("Allocate input buffer failed: only %{public}d buffer is allocated, no memory", valBufferCnt);
buffers_[INDEX_INPUT].clear();
return AVCS_ERR_NO_MEMORY;
}
int32_t FCodec::SetSurfaceCfg()
{
int32_t val = -1;
std::optional<ScalingMode> scaling = std::nullopt;
std::optional<GraphicTransformType> orientation = std::nullopt;
std::unique_lock<std::mutex> fLock(formatMutex_);
int32_t pixelFormat = 0;
format_.GetIntValue(FMTKey::VIDEO_PIXEL_FORMAT, pixelFormat);
GraphicPixelFormat surfacePixelFmt = TranslateSurfaceFormat(static_cast<VideoPixelFormat>(pixelFormat));
CHECK_AND_RETURN_RET_LOG(surfacePixelFmt != GraphicPixelFormat::GRAPHIC_PIXEL_FMT_BUTT, AVCS_ERR_UNSUPPORT,
"Failed to allocate output buffer: unsupported surface format");
format_.PutIntValue(FMTKey::VIDEO_GRAPHIC_PIXEL_FORMAT, static_cast<int32_t>(surfacePixelFmt));
if (format_.GetIntValue(FMTKey::VIDEO_SCALE_TYPE, val)) {
CHECK_AND_RETURN_RET_LOG(IsValidScaleType(val), AVCS_ERR_INVALID_VAL, "Invalid scale_type: %{public}d", val);
scaling = static_cast<ScalingMode>(val);
}
if (format_.GetIntValue(FMTKey::VIDEO_ORIENTATION_TYPE, val)) {
orientation = static_cast<GraphicTransformType>(val);
}
fLock.unlock();
{
std::lock_guard<std::mutex> sLock(surfaceMutex_);
sInfo_.requestConfig.width = width_;
sInfo_.requestConfig.height = height_;
sInfo_.requestConfig.format = surfacePixelFmt;
if (scaling) {
sInfo_.scalingMode = scaling.value();
sInfo_.surface->SetScalingMode(scaling.value());
}
if (orientation) {
transform_ = orientation.value();
}
AVCODEC_LOGI("Set surface video_orientation_type: %{public}d success.", static_cast<int32_t>(transform_));
sInfo_.surface->SetTransform(transform_);
}
return AVCS_ERR_OK;
}
void FCodec::RequestSurfaceBuffer(SurfaceBufferInfo &bufInfo)
{
std::unique_lock<std::mutex> sLock(surfaceMutex_);
AVCODEC_LOGD(
"surface request config, width: %{public}d, height: %{public}d, strideAlignment: %{public}d, "
"format: %{public}d, usage: %{public}" PRIu64 ", timeout: %{public}d",
sInfo_.requestConfig.width,
sInfo_.requestConfig.height,
sInfo_.requestConfig.strideAlignment,
sInfo_.requestConfig.format,
sInfo_.requestConfig.usage,
sInfo_.requestConfig.timeout);
GSError err = sInfo_.surface->RequestBuffer(bufInfo.buf, bufInfo.fence, sInfo_.requestConfig);
if (err != GSERROR_OK || bufInfo.buf == nullptr || bufInfo.buf->GetBufferHandle() == nullptr) {
AVCODEC_LOGE("Request surface buffer fail, ret:%{public}d", static_cast<int32_t>(err));
return;
}
sLock.unlock();
bufInfo.seqNum = bufInfo.buf->GetSeqNum();
AVCODEC_LOGD("Request surface buffer seqNum: %{public}u", bufInfo.seqNum);
}
bool FCodec::FBufferAvailable(const std::shared_ptr<FBuffer> &buffer, SurfaceBufferInfo &bufInfo)
{
int32_t sw = bufInfo.buf->GetWidth();
int32_t sh = bufInfo.buf->GetHeight();
int32_t format = bufInfo.buf->GetFormat();
uint64_t usage = bufInfo.buf->GetUsage();
std::shared_ptr<FSurfaceMemory> surfaceMemory = buffer->sMemory_;
if (surfaceMemory == nullptr) {
return false;
}
return buffer->owner_ == Owner::OWNED_BY_SURFACE &&
(buffer->width_ != sw || buffer->height_ != sh || buffer->format_ != format || buffer->usage_ != usage) &&
surfaceMemory->isAttached;
}
void FCodec::OnSurfaceBufferAvailable(SurfaceBufferInfo &bufInfo)
{
CHECK_AND_RETURN_LOG(bufInfo.buf != nullptr, "Surface buffer is nullptr!");
uint32_t bufIdx = static_cast<uint32_t>(buffers_[INDEX_OUTPUT].size());
if (seqNumToFbufMap_.count(bufInfo.seqNum) == 0) {
AVCODEC_LOGI("surface buffer %{public}u not in map", bufInfo.seqNum);
for (const std::shared_ptr<FBuffer> &buffer : buffers_[INDEX_OUTPUT]) {
if (FBufferAvailable(buffer, bufInfo)) {
uint32_t oldSeqNum = buffer->sMemory_->GetSurfaceBufferSeqNum();
bufIdx = seqNumToFbufMap_[oldSeqNum];
seqNumToFbufMap_.erase(oldSeqNum);
seqNumToFbufMap_[bufInfo.seqNum] = bufIdx;
AVCODEC_LOGI("buf(%{public}u), %{public}u -> %{public}u", bufIdx, oldSeqNum, bufInfo.seqNum);
buffers_[INDEX_OUTPUT][bufIdx]->avBuffer_ = AVBuffer::CreateAVBuffer(bufInfo.buf);
break;
}
}
} else {
bufIdx = seqNumToFbufMap_[bufInfo.seqNum];
}
if (bufIdx >= buffers_[INDEX_OUTPUT].size() || buffers_[INDEX_OUTPUT][bufIdx]->avBuffer_ == nullptr) {
AVCODEC_LOGE("surfae buffer(%{public}u) cannot match available output buffer", bufInfo.seqNum);
callback_->OnError(AVCodecErrorType::AVCODEC_ERROR_INTERNAL, AVCodecServiceErrCode::AVCS_ERR_UNKNOWN);
state_ = State::ERROR;
return;
}
std::shared_ptr<FSurfaceMemory> surfaceMemory = buffers_[INDEX_OUTPUT][bufIdx]->sMemory_;
surfaceMemory->SetSurfaceBuffer(bufInfo.buf, Owner::OWNED_BY_CODEC, bufInfo.fence);
buffers_[INDEX_OUTPUT][bufIdx]->format_ = sInfo_.requestConfig.format;
buffers_[INDEX_OUTPUT][bufIdx]->usage_ = sInfo_.requestConfig.usage,
buffers_[INDEX_OUTPUT][bufIdx]->owner_ = Owner::OWNED_BY_CODEC;
codecAvailQue_->Push(bufIdx);
AVCODEC_LOGD("Request output buffer %{public}u, available index %{public}d", bufInfo.seqNum, bufIdx);
}
void FCodec::RequestSurfaceBufferThread()
{
while (!requestBufferThreadExit_.load()) {
std::unique_lock<std::mutex> lck(requestBufferMutex_);
if (count_ == 0) {
requestBufferCV_.wait(lck, [this]() { return requestBufferThreadExit_.load() || count_ != 0; });
}
if (requestBufferThreadExit_.load()) {
count_ = 0u;
break;
}
std::lock_guard<std::mutex> oLock(outputMutex_);
SurfaceBufferInfo bufInfo;
RequestSurfaceBuffer(bufInfo);
OnSurfaceBufferAvailable(bufInfo);
count_--;
}
AVCODEC_LOGI("RequestSurfaceBufferThread exit.");
}
void FCodec::StartRequestSurfaceBufferThread()
{
if (!mRequestSurfaceBufferThread_.joinable()) {
requestBufferThreadExit_ = false;
count_ = 0u;
mRequestSurfaceBufferThread_ = std::thread(&FCodec::RequestSurfaceBufferThread, this);
}
}
void FCodec::StopRequestSurfaceBufferThread()
{
if (mRequestSurfaceBufferThread_.joinable()) {
requestBufferThreadExit_ = true;
requestBufferCV_.notify_all();
mRequestSurfaceBufferThread_.join();
}
}
void FCodec::RequestSurfaceBufferOnce()
{
CHECK_AND_RETURN_LOG(!requestBufferThreadExit_.load(), "request surfacebuffer thread exited!");
std::unique_lock<std::mutex> lck(requestBufferMutex_);
count_++;
requestBufferCV_.notify_one();
}
int32_t FCodec::AllocateSurfaceBuffer(sptr<SurfaceBuffer> &surfacebuffer)
{
GraphicPixelFormat surfacePixelFmt = TranslateSurfaceFormat(outputPixelFmt_);
BufferRequestConfig reqConfig = {.width = width_,
.height = height_,
.strideAlignment = SURFACE_STRIDE_ALIGN,
.format = static_cast<int32_t>(surfacePixelFmt),
.usage = SURFACE_DEFAULT_USAGE,
.timeout = TIMEOUT};
surfacebuffer = SurfaceBuffer::Create();
CHECK_AND_RETURN_RET_LOG(surfacebuffer, AVCS_ERR_UNKNOWN, "Create surface buffer failed!");
GSError err = surfacebuffer->Alloc(reqConfig);
CHECK_AND_RETURN_RET_LOG(err == GSERROR_OK && surfacebuffer != nullptr, err,
"Alloc surface buffer failed, GSERROR=%{public}d", err);
int32_t fd = surfacebuffer->GetFileDescriptor();
CHECK_AND_RETURN_RET_LOG(fd > 0, AVCS_ERR_UNKNOWN, "Invalid fd %{public}d, surface buffer(%{public}u)", fd,
surfacebuffer->GetSeqNum());
std::string type = "sw-video-decoder";
std::string mime(fDecInfo_.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 = fDecInfo_.processName + "-" + std::to_string(width_) + "x" + std::to_string(height_)
+ "-" + mime + "-" + fDecInfo_.instanceId;
ioctl(fd, DMA_BUF_SET_LEAK_TYPE, type.c_str());
std::string pid = std::to_string(fDecInfo_.pid);
ioctl(fd, DMA_BUF_SET_NAME_A, pid.c_str());
ioctl(fd, DMA_BUF_SET_NAME_A, name.c_str());
return AVCS_ERR_OK;
}
int32_t FCodec::AllocateOutputBuffer()
{
CHECK_AND_RETURN_RET_LOG(sInfo_.surface == nullptr, AVCS_ERR_UNKNOWN, "Not in buffer mode!");
int32_t valBufferCnt = 0;
for (uint32_t i = 0u; i < outputBufferCnt_; i++) {
std::shared_ptr<FBuffer> buf = std::make_shared<FBuffer>();
CHECK_AND_RETURN_RET_LOG(buf != nullptr, AVCS_ERR_UNKNOWN, "Creata output buffer failed!");
buf->width_ = width_;
buf->height_ = height_;
#ifdef EMULATOR_ENABLED
std::shared_ptr<AVAllocator> allocator =
AVAllocatorFactory::CreateSharedAllocator(MemoryFlag::MEMORY_READ_WRITE);
CHECK_AND_CONTINUE_LOG(allocator != nullptr, "output buffer %{public}u allocator is nullptr", i);
buf->avBuffer_ = AVBuffer::CreateAVBuffer(allocator, outputBufferSize_);
#else
sptr<SurfaceBuffer> sb = nullptr;
int32_t ret = AllocateSurfaceBuffer(sb);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK && sb != nullptr, ret,
"Alloc surface buffer failed, ret=%{public}d", ret);
buf->avBuffer_ = AVBuffer::CreateAVBuffer(sb);
#endif
CHECK_AND_CONTINUE_LOG(buf->avBuffer_ != nullptr && buf->avBuffer_->memory_ !=nullptr,
"create output avbuffer failed, index=%{public}d", i);
int32_t curBufSize = buf->avBuffer_->memory_->GetCapacity();
AVCODEC_LOGI("Allocate output buffer success: index=%{public}u, size=%{public}d", i, curBufSize);
buffers_[INDEX_OUTPUT].emplace_back(buf);
valBufferCnt++;
}
CHECK_AND_RETURN_RET_LOGD(valBufferCnt < DEFAULT_MIN_BUFFER_CNT, AVCS_ERR_OK, "Allocate output buffers successful");
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;
}
int32_t FCodec::ClearSurfaceAndSetQueueSize()
{
std::unique_lock<std::mutex> sLock(surfaceMutex_);
sInfo_.surface->Connect();
sInfo_.surface->CleanCache();
sInfo_.surface->Disconnect();
int32_t ret = SetQueueSize(sInfo_.surface, outputBufferCnt_);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Set surface queue size failed!");
sLock.unlock();
ret = SetSurfaceCfg();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Set surface cfg failed!");
std::lock_guard<std::mutex> oLock(outputMutex_);
CHECK_AND_RETURN_RET_LOGD(buffers_[INDEX_OUTPUT].size() > 0u, AVCS_ERR_OK, "Set surface cfg & queue size success.");
uint32_t valBufferCnt = 0;
for (auto &it : buffers_[INDEX_OUTPUT]) {
std::shared_ptr<FSurfaceMemory> surfaceMemory = it->sMemory_;
surfaceMemory->isAttached = false;
valBufferCnt++;
}
CHECK_AND_RETURN_RET_LOG(valBufferCnt == outputBufferCnt_, AVCS_ERR_UNKNOWN, "Outbuf cnt(%{public}u) != %{public}u",
valBufferCnt, outputBufferCnt_);
return AVCS_ERR_OK;
}
int32_t FCodec::AllocateOutputBuffersFromSurface()
{
CHECK_AND_RETURN_RET_LOG(sInfo_.surface != nullptr, AVCS_ERR_UNKNOWN, "Not in surface mode!");
int32_t ret = ClearSurfaceAndSetQueueSize();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Clean surface and set queue size failed!");
StartRequestSurfaceBufferThread();
for (uint32_t i = 0u; i < outputBufferCnt_; i++) {
std::shared_ptr<FSurfaceMemory> surfaceMemory = std::make_shared<FSurfaceMemory>(&sInfo_, fDecInfo_);
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;
uint32_t seqNum = surfaceBuffer->GetSeqNum();
seqNumToFbufMap_[seqNum] = i;
AVCODEC_LOGI("Surface buffer seqNum: %{public}u -> index: [%{public}u]", seqNum, i);
std::shared_ptr<FBuffer> buf = std::make_shared<FBuffer>();
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(surfaceBuffer);
CHECK_AND_RETURN_RET_LOG(buf->avBuffer_ != nullptr, AVCS_ERR_UNKNOWN, "avbuffer is nullptr!");
AVCODEC_LOGI("Allocate output surface buffer success, index=%{public}u, size=%{public}d, stride=%{public}d", i,
buf->sMemory_->GetSize(), buf->sMemory_->GetSurfaceBufferStride());
buffers_[INDEX_OUTPUT].emplace_back(buf);
}
uint32_t outputBufferNum = static_cast<uint32_t>(buffers_[INDEX_OUTPUT].size());
CHECK_AND_RETURN_RET_LOG(outputBufferNum == outputBufferCnt_, AVCS_ERR_UNKNOWN,
"Only alloc %{public}u buffers, less %{public}u", outputBufferNum, outputBufferCnt_);
return AVCS_ERR_OK;
}
int32_t FCodec::AllocateBuffers()
{
AVCODEC_SYNC_TRACE;
CalculateBufferSize();
CHECK_AND_RETURN_RET_LOG(inputBufferSize_ > 0 && outputBufferSize_ > 0, AVCS_ERR_INVALID_VAL,
"Allocate buffer with input size=%{public}d, output size=%{public}d failed",
inputBufferSize_, outputBufferSize_);
if (sInfo_.surface != nullptr && isOutBufSetted_ == false) {
format_.PutIntValue(FMTKey::REQUIRED_OUT_BUFFER_CNT, DEFAULT_OUT_SURFACE_CNT);
}
int32_t val = -1;
CHECK_AND_RETURN_RET_LOG(format_.GetIntValue(FMTKey::REQUIRED_IN_BUFFER_CNT, val) && val >= DEFAULT_MIN_BUFFER_CNT,
AVCS_ERR_INVALID_VAL, "Invalid input buffer cnt: %{public}d", val);
inputBufferCnt_ = static_cast<uint32_t>(val);
CHECK_AND_RETURN_RET_LOG(
format_.GetIntValue(FMTKey::REQUIRED_OUT_BUFFER_CNT, val) &&
val >= DEFAULT_MIN_BUFFER_CNT, AVCS_ERR_INVALID_VAL,
"Invalid output buffer cnt: %{public}d", val);
outputBufferCnt_ = static_cast<uint32_t>(val);
inputAvailQue_ = std::make_shared<BlockQueue<uint32_t>>("inputAvailQue", inputBufferCnt_);
codecAvailQue_ = std::make_shared<BlockQueue<uint32_t>>("codecAvailQue", outputBufferCnt_);
int32_t ret = AllocateInputBuffer(inputBufferSize_);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Allocate input buffers failed!");
ret = sInfo_.surface ? AllocateOutputBuffersFromSurface()
: AllocateOutputBuffer();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Allocate output buffers failed!");
return AVCS_ERR_OK;
}
int32_t FCodec::UpdateBuffers(uint32_t index)
{
if (buffers_[INDEX_OUTPUT][index]->width_ != width_ || buffers_[INDEX_OUTPUT][index]->height_ != height_) {
std::shared_ptr<FBuffer> buf = std::make_shared<FBuffer>();
CHECK_AND_RETURN_RET_LOG(buf != nullptr, AVCS_ERR_UNKNOWN, "Creata output buffer failed!");
buf->width_ = width_;
buf->height_ = height_;
#ifdef EMULATOR_ENABLED
std::shared_ptr<AVAllocator> allocator =
AVAllocatorFactory::CreateSharedAllocator(MemoryFlag::MEMORY_READ_WRITE);
CHECK_AND_RETURN_RET_LOG(allocator != nullptr, AVCS_ERR_NO_MEMORY,
"buffer %{public}u allocator is nullptr", index);
buf->avBuffer_ = AVBuffer::CreateAVBuffer(allocator, outputBufferSize_);
#else
sptr<SurfaceBuffer> sb = nullptr;
int32_t ret = AllocateSurfaceBuffer(sb);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK && sb != nullptr, ret,
"Alloc surface buffer failed, ret=%{public}d", ret);
buf->avBuffer_ = AVBuffer::CreateAVBuffer(sb);
CHECK_AND_RETURN_RET_LOG(buf->avBuffer_ != nullptr, AVCS_ERR_UNKNOWN,
"create output avbuffer failed, index=%{public}d", index);
#endif
int32_t curBufSize = buf->avBuffer_->memory_->GetCapacity();
AVCODEC_LOGI("update output buffer success: index=%{public}u, size=%{public}d", index, curBufSize);
buf->owner_ = Owner::OWNED_BY_CODEC;
buffers_[INDEX_OUTPUT][index] = buf;
}
return AVCS_ERR_OK;
}
int32_t FCodec::UpdateSurfaceMemory(uint32_t index)
{
AVCODEC_SYNC_TRACE;
std::shared_ptr<FBuffer> outputBuffer = buffers_[INDEX_OUTPUT][index];
if (width_ != outputBuffer->width_ || height_ != outputBuffer->height_) {
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_);
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;
seqNumToFbufMap_.erase(surfaceBuffer->GetSeqNum());
AVCODEC_LOGI("detach surfacebuffer(%{public}u) -> index: [%{public}u]", surfaceBuffer->GetSeqNum(), index);
}
surfaceMemory->ReleaseSurfaceBuffer();
std::unique_lock<std::mutex> sLock(surfaceMutex_);
int32_t ret = surfaceMemory->AllocSurfaceBuffer(width_, height_);
sLock.unlock();
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;
uint32_t seqNum = newSurfaceBuffer->GetSeqNum();
seqNumToFbufMap_[seqNum] = index;
AVCODEC_LOGI("Surface buffer seqNum: %{public}u -> index: [%{public}u]", seqNum, index);
outputBuffer->avBuffer_ = AVBuffer::CreateAVBuffer(newSurfaceBuffer);
CHECK_AND_RETURN_RET_LOG(outputBuffer->avBuffer_ != nullptr, AVCS_ERR_UNKNOWN, "avbuffer is nullptr!");
outputBuffer->width_ = width_;
outputBuffer->height_ = height_;
}
return AVCS_ERR_OK;
}
bool FCodec::CheckStrideChange(uint32_t index, bool& isChanged)
{
if (sInfo_.surface != nullptr) {
std::shared_ptr<FBuffer> frameBuffer = buffers_[INDEX_OUTPUT][index];
if (!frameBuffer || frameBuffer->sMemory_ == nullptr) {
AVCODEC_LOGE("frameBuffer or surfaceMemory is nullptr");
return false;
}
int32_t stride = frameBuffer->sMemory_->GetSurfaceBufferStride();
int32_t formatStride = 0;
format_.GetIntValue(FMTKey::VIDEO_STRIDE, formatStride);
if (stride != formatStride) {
AVCODEC_LOGI("format change, stride: %{public}d->%{public}d", formatStride, stride);
std::lock_guard<std::mutex> lock(formatMutex_);
format_.PutIntValue(FMTKey::VIDEO_STRIDE, stride);
isChanged = true;
}
}
return true;
}
void FCodec::PutFormatValue()
{
std::lock_guard<std::mutex> lock(formatMutex_);
format_.PutIntValue(FMTKey::VIDEO_WIDTH, width_);
format_.PutIntValue(FMTKey::VIDEO_HEIGHT, height_);
format_.PutIntValue(FMTKey::VIDEO_STRIDE,
outputPixelFmt_ == VideoPixelFormat::RGBA ? width_ * VIDEO_PIX_DEPTH_RGBA : width_);
format_.PutIntValue(FMTKey::VIDEO_SLICE_HEIGHT, height_);
format_.PutIntValue(FMTKey::VIDEO_PIC_WIDTH, width_);
format_.PutIntValue(FMTKey::VIDEO_PIC_HEIGHT, height_);
}
int32_t FCodec::CheckFormatChange(uint32_t index, bool &isChanged)
{
if (width_ != cachedFrame_->width || height_ != cachedFrame_->height) {
AVCODEC_LOGI("format change, width: %{public}d->%{public}d, height: %{public}d->%{public}d", width_,
cachedFrame_->width, height_, cachedFrame_->height);
width_ = cachedFrame_->width;
height_ = cachedFrame_->height;
CHECK_AND_RETURN_RET_LOG(ValidateVideoResolution(codecName_, width_, height_), AVCS_ERR_INVALID_VAL,
"Current decoder:%{public}s dosn't support width of %{public}d and height of %{public}d!",
codecName_.c_str(), width_, height_);
ResetData();
scale_ = nullptr;
CalculateBufferSize();
CHECK_AND_RETURN_RET_LOG(inputBufferSize_ > 0 && outputBufferSize_ > 0, AVCS_ERR_INVALID_VAL,
"new buffer with input size=%{public}d, output size=%{public}d failed",
inputBufferSize_, outputBufferSize_);
PutFormatValue();
{
std::lock_guard<std::mutex> sLock(surfaceMutex_);
if (sInfo_.surface) {
sInfo_.requestConfig.width = width_;
sInfo_.requestConfig.height = height_;
}
}
isChanged = true;
}
CHECK_AND_RETURN_RET_LOG(CheckStrideChange(index, isChanged), AVCS_ERR_UNKNOWN,
"frameBuffer or surfaceMemory is nullptr!");
return AVCS_ERR_OK;
}
void FCodec::ReleaseBuffers()
{
ResetData();
if (!isBufferAllocated_) {
return;
}
inputAvailQue_->Clear();
std::unique_lock<std::mutex> iLock(inputMutex_);
buffers_[INDEX_INPUT].clear();
inputBufferCnt_ = 0u;
synIndex_ = std::nullopt;
iLock.unlock();
codecAvailQue_->Clear();
if (sInfo_.surface != nullptr) {
StopRequestSurfaceBufferThread();
{
std::lock_guard<std::mutex> mLock(renderBufferMapMutex_);
renderSurfaceBufferMap_.clear();
}
{
std::lock_guard<std::mutex> sLock(surfaceMutex_);
sInfo_.surface->CleanCache();
}
AVCODEC_LOGI("surface cleancache success");
}
std::unique_lock<std::mutex> oLock(outputMutex_);
buffers_[INDEX_OUTPUT].clear();
outAVBuffer4Surface_.clear();
seqNumToFbufMap_.clear();
outputBufferCnt_ = 0u;
oLock.unlock();
isBufferAllocated_ = false;
}
int32_t FCodec::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<FBuffer> 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;
std::shared_ptr<AVBuffer> &inputAVBuffer = inputBuffer->avBuffer_;
if (synIndex_) {
const std::shared_ptr<AVBuffer> &curAVBuffer = buffers_[INDEX_INPUT][synIndex_.value()]->avBuffer_;
int32_t curAVBufferSize = curAVBuffer->memory_->GetSize();
int32_t inputAVBufferSize = inputAVBuffer->memory_->GetSize();
if ((curAVBufferSize + inputAVBufferSize <= curAVBuffer->memory_->GetCapacity()) &&
memcpy_s(curAVBuffer->memory_->GetAddr() + curAVBufferSize, inputAVBufferSize,
inputAVBuffer->memory_->GetAddr(), inputAVBufferSize) == EOK) {
curAVBuffer->memory_->SetSize(curAVBufferSize + inputAVBufferSize);
curAVBuffer->flag_ = inputAVBuffer->flag_;
curAVBuffer->pts_ = inputAVBuffer->pts_;
if (inputAVBuffer->flag_ != AVCODEC_BUFFER_FLAG_CODEC_DATA &&
inputAVBuffer->flag_ != AVCODEC_BUFFER_FLAG_PARTIAL_FRAME) {
inputAvailQue_->Push(synIndex_.value());
synIndex_ = std::nullopt;
}
inputBuffer->owner_ = Owner::OWNED_BY_USER;
callback_->OnInputBufferAvailable(index, inputAVBuffer);
return AVCS_ERR_OK;
} else {
AVCODEC_LOGE("packet size %{public}d over buffer size %{public}d", curAVBufferSize + inputAVBufferSize,
curAVBuffer->memory_->GetCapacity());
callback_->OnError(AVCodecErrorType::AVCODEC_ERROR_INTERNAL, AVCodecServiceErrCode::AVCS_ERR_NO_MEMORY);
state_ = State::ERROR;
return AVCS_ERR_NO_MEMORY;
}
} else {
if ((inputAVBuffer->flag_ == AVCODEC_BUFFER_FLAG_CODEC_DATA) ||
(inputAVBuffer->flag_ == AVCODEC_BUFFER_FLAG_PARTIAL_FRAME)) {
synIndex_ = index;
} else {
inputAvailQue_->Push(index);
}
}
return AVCS_ERR_OK;
}
void FCodec::SendFrame()
{
CHECK_AND_RETURN_LOG_LIMIT(state_ != State::STOPPING && state_ != State::FLUSHING, LOG_FREQUENCE, "Invalid state");
if (state_ != State::RUNNING || isSendEos_ || inputAvailQue_->Size() == 0u) {
std::this_thread::sleep_for(std::chrono::milliseconds(DEFAULT_TRY_DECODE_TIME));
return;
}
uint32_t index = inputAvailQue_->Front();
CHECK_AND_RETURN_LOG(state_ == State::RUNNING, "Not in running state");
std::shared_ptr<FBuffer> &inputBuffer = buffers_[INDEX_INPUT][index];
std::shared_ptr<AVBuffer> &inputAVBuffer = inputBuffer->avBuffer_;
if (inputAVBuffer->flag_ & AVCODEC_BUFFER_FLAG_EOS) {
avPacket_->data = nullptr;
avPacket_->size = 0;
avPacket_->pts = 0;
std::unique_lock<std::mutex> sendLock(sendMutex_);
isSendEos_ = true;
sendCv_.wait_for(sendLock, std::chrono::milliseconds(DEFAULT_DECODE_WAIT_TIME));
AVCODEC_LOGI("Send eos end");
} else {
avPacket_->data = inputAVBuffer->memory_->GetAddr();
avPacket_->size = static_cast<int32_t>(inputAVBuffer->memory_->GetSize());
avPacket_->pts = inputAVBuffer->pts_;
}
std::unique_lock<std::mutex> sLock(syncMutex_);
int ret = avcodec_send_packet(avCodecContext_.get(), avPacket_.get());
sLock.unlock();
if (ret == 0 || ret == AVERROR_INVALIDDATA) {
EXPECT_AND_LOGD(ret == AVERROR_INVALIDDATA, "ffmpeg ret = %{public}s", AVStrError(ret).c_str());
std::unique_lock<std::mutex> recvLock(recvMutex_);
recvCv_.notify_one();
recvLock.unlock();
inputAvailQue_->Pop();
inputBuffer->owner_ = Owner::OWNED_BY_USER;
#ifdef BUILD_ENG_VERSION
if (dumpInFile_ && dumpInFile_->is_open()) {
dumpInFile_->write(reinterpret_cast<char *>(inputAVBuffer->memory_->GetAddr()),
static_cast<int32_t>(inputAVBuffer->memory_->GetSize()));
}
#endif
callback_->OnInputBufferAvailable(index, inputAVBuffer);
} else if (ret == AVERROR(EAGAIN)) {
std::unique_lock<std::mutex> sendLock(sendMutex_);
isSendWait_ = true;
sendCv_.wait_for(sendLock, std::chrono::milliseconds(DEFAULT_DECODE_WAIT_TIME));
} else {
AVCODEC_LOGE("Cannot send frame to codec: ffmpeg ret = %{public}s", AVStrError(ret).c_str());
callback_->OnError(AVCodecErrorType::AVCODEC_ERROR_INTERNAL, AVCodecServiceErrCode::AVCS_ERR_UNKNOWN);
state_ = State::ERROR;
}
}
int32_t FCodec::FillFrameBuffer(const std::shared_ptr<FBuffer> &frameBuffer)
{
VideoPixelFormat targetPixelFmt = outputPixelFmt_;
if (outputPixelFmt_ == VideoPixelFormat::UNKNOWN) {
targetPixelFmt = VideoPixelFormat::NV12;
}
AVPixelFormat ffmpegFormat = ConvertPixelFormatToFFmpeg(targetPixelFmt);
int32_t ffpixfmt = cachedFrame_->format;
AVCODEC_LOGD("decode output ffmpeg pixfmt: %{public}d, target output ffmpeg pixfmt: %{public}d", ffpixfmt,
static_cast<int32_t>(ffmpegFormat));
int32_t ret;
if (ffmpegFormat == static_cast<AVPixelFormat>(ffpixfmt)) {
for (int32_t i = 0; i < AV_NUM_DATA_POINTERS && cachedFrame_->linesize[i] > 0; i++) {
scaleData_[i] = cachedFrame_->data[i];
scaleLineSize_[i] = cachedFrame_->linesize[i];
}
} else {
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::lock_guard<std::mutex> lock(formatMutex_);
format_.PutIntValue(FMTKey::VIDEO_PIXEL_FORMAT, static_cast<int32_t>(targetPixelFmt));
GraphicPixelFormat surfacePixelFmt = TranslateSurfaceFormat(targetPixelFmt);
format_.PutIntValue(FMTKey::VIDEO_GRAPHIC_PIXEL_FORMAT, static_cast<int32_t>(surfacePixelFmt));
}
std::shared_ptr<AVMemory> &bufferMemory = frameBuffer->avBuffer_->memory_;
CHECK_AND_RETURN_RET_LOG(bufferMemory != nullptr, AVCS_ERR_INVALID_VAL, "bufferMemory is nullptr");
bufferMemory->SetSize(0);
if (sInfo_.surface) {
struct SurfaceInfo surfaceInfo;
surfaceInfo.surfaceStride = static_cast<uint32_t>(frameBuffer->sMemory_->GetSurfaceBufferStride());
surfaceInfo.surfaceFence = frameBuffer->sMemory_->GetFence();
surfaceInfo.scaleData = scaleData_;
surfaceInfo.scaleLineSize = scaleLineSize_;
ret = WriteSurfaceData(bufferMemory, surfaceInfo, format_);
} else {
ret = WriteBufferData(bufferMemory, scaleData_, scaleLineSize_, format_);
}
frameBuffer->avBuffer_->pts_ = cachedFrame_->pts;
AVCODEC_LOGD("Fill frame buffer successful");
return ret;
}
void FCodec::FramePostProcess(std::shared_ptr<FBuffer> &frameBuffer, uint32_t index, int32_t status, int ret)
{
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_;
}
if (ret == AVERROR_EOF) {
std::unique_lock<std::mutex> sLock(syncMutex_);
avcodec_flush_buffers(avCodecContext_.get());
sLock.unlock();
} else {
if (isSendWait_) {
std::lock_guard<std::mutex> sLock(sendMutex_);
isSendWait_ = false;
sendCv_.notify_one();
}
}
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 FCodec::DumpOutputBuffer()
{
if (!dumpOutFile_ || !dumpOutFile_->is_open()) {
return;
}
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));
}
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 / 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 / 2));
}
}
#endif
#ifndef EMULATOR_ENABLED
void FCodec::GetCropInfo(std::vector<uint8_t> &vec, bool isFmtChanged)
{
int32_t cropTop = static_cast<int32_t>(cachedFrame_->crop_top);
int32_t cropBottom = static_cast<int32_t>(cachedFrame_->crop_bottom);
int32_t cropLeft = static_cast<int32_t>(cachedFrame_->crop_left);
int32_t cropRight = static_cast<int32_t>(cachedFrame_->crop_right);
AVCODEC_LOGD("ffmpeg decode crop info top: %{public}d, bottom: %{public}d, left: %{public}d, right: %{public}d",
cropTop, cropBottom, cropLeft, cropRight);
BufferHandleMetaRegion *crop = reinterpret_cast<BufferHandleMetaRegion *>(vec.data());
crop->left = static_cast<uint32_t>(cropLeft);
crop->top = static_cast<uint32_t>(cropTop);
crop->width = static_cast<uint32_t>(width_ - 1);
crop->height = static_cast<uint32_t>(height_ - 1);
if (isFmtChanged || decNum_ == 0) {
std::lock_guard<std::mutex> fLock(formatMutex_);
format_.PutIntValue(FMTKey::VIDEO_CROP_TOP, cropTop);
format_.PutIntValue(FMTKey::VIDEO_CROP_BOTTOM, cropBottom);
format_.PutIntValue(FMTKey::VIDEO_CROP_LEFT, cropLeft);
format_.PutIntValue(FMTKey::VIDEO_CROP_RIGHT, cropRight);
}
}
void FCodec::GetColorAspectsInfo(std::vector<uint8_t> &vec, bool isFmtChanged)
{
AVColorPrimaries colorPrimaries = cachedFrame_->color_primaries;
AVColorTransferCharacteristic colorTransFunc = cachedFrame_->color_trc;
AVColorSpace colorMatrix = cachedFrame_->colorspace;
AVColorRange colorRange = cachedFrame_->color_range;
AVCODEC_LOGD("ffmpeg color aspects info range: %{public}d, primaries: %{public}d, transferCharacteristic: "
"%{public}d, space: %{public}d",
static_cast<int32_t>(colorRange), static_cast<int32_t>(colorPrimaries),
static_cast<int32_t>(colorTransFunc), static_cast<int32_t>(colorMatrix));
CM_ColorPrimaries cmColorPrimaries = AVColorPrimariesToCM(colorPrimaries);
CM_TransFunc cmTrcFunc = AVColorTransferCharacteristicToCM(colorTransFunc);
CM_Matrix cmMatrix = AVColorSpaceToMatrix(colorMatrix);
CM_Range cmRange = AVColorRangeToRange(colorRange);
AVCODEC_LOGD("CM color range: %{public}d, colorprimaries: %{public}d, trans func: %{public}d, matrix: %{public}d",
static_cast<int32_t>(cmRange), static_cast<int32_t>(cmColorPrimaries), static_cast<int32_t>(cmTrcFunc),
static_cast<int32_t>(cmMatrix));
CM_ColorSpaceInfo *colorSpaceInfo = reinterpret_cast<CM_ColorSpaceInfo *>(vec.data());
colorSpaceInfo->primaries = cmColorPrimaries;
colorSpaceInfo->transfunc = cmTrcFunc;
colorSpaceInfo->matrix = cmMatrix;
colorSpaceInfo->range = cmRange;
int32_t colPrimary = static_cast<int32_t>(CMColorPrimariesToColorPrimary(cmColorPrimaries));
int32_t colTrcFunc = static_cast<int32_t>(CMTransFuncToTransferCharacteristic(cmTrcFunc));
int32_t colMatrix = static_cast<int32_t>(CMMatrixToMatrixCoefficient(cmMatrix));
int32_t colRange = CMRangeToBoolean(cmRange);
if (isFmtChanged || decNum_ == 0) {
std::lock_guard<std::mutex> fLock(formatMutex_);
format_.PutIntValue(FMTKey::VIDEO_COLOR_PRIMARIES, colPrimary);
format_.PutIntValue(FMTKey::VIDEO_COLOR_TRC, colTrcFunc);
format_.PutIntValue(FMTKey::VIDEO_COLOR_MATRIX_COEFF, colMatrix);
format_.PutIntValue(FMTKey::VIDEO_COLOR_RANGE, colRange);
}
}
#endif
int32_t FCodec::DecodedFrameProcess(uint32_t index)
{
AVCODEC_LOGD("cur decNum: %{public}u", decNum_);
#ifdef BUILD_ENG_VERSION
DumpOutputBuffer();
#endif
bool isChanged = false;
int32_t ret = CheckFormatChange(index, isChanged);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Check format change failed");
#ifndef EMULATOR_ENABLED
std::vector<uint8_t> cropVec(sizeof(BufferHandleMetaRegion));
std::vector<uint8_t> colorSpaceInfoData(sizeof(CM_ColorSpaceInfo));
GetCropInfo(cropVec, isChanged);
GetColorAspectsInfo(colorSpaceInfoData, isChanged);
#endif
if (decNum_ == 0) {
callback_->OnOutputFormatChanged(format_);
}
if (isChanged) {
callback_->OnOutputFormatChanged(format_);
}
std::unique_lock<std::mutex> oLock(outputMutex_);
if (sInfo_.surface == nullptr) {
CHECK_AND_RETURN_RET_LOG((UpdateBuffers(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");
}
oLock.unlock();
#ifndef EMULATOR_ENABLED
std::shared_ptr<FBuffer> frameBuffer = buffers_[INDEX_OUTPUT][index];
std::shared_ptr<AVMemory> &bufferMemory = frameBuffer->avBuffer_->memory_;
sptr<SurfaceBuffer> surfacebuffer = bufferMemory->GetSurfaceBuffer();
CHECK_AND_RETURN_RET_LOG(surfacebuffer != nullptr, AVCS_ERR_UNKNOWN, "surfacebuffer is nullptr");
GSError err = surfacebuffer->SetMetadata(ATTRKEY_CROP_REGION, cropVec);
CHECK_AND_RETURN_RET_LOG(err == GSERROR_OK, AVCS_ERR_UNKNOWN, "SetBufferMeta ATTRKEY_CROP_REGION failed");
err = surfacebuffer->SetMetadata(ATTRKEY_COLORSPACE_INFO, colorSpaceInfoData);
CHECK_AND_RETURN_RET_LOG(err == GSERROR_OK, AVCS_ERR_UNKNOWN, "SetBufferMeta ATTRKEY_COLORSPACE_INFO failed");
#endif
decNum_++;
return AVCS_ERR_OK;
}
void FCodec::ReceiveFrame()
{
CHECK_AND_RETURN_LOG_LIMIT(state_ != State::STOPPING && state_ != State::FLUSHING, LOG_FREQUENCE, "Invalid state");
if (state_ != State::RUNNING || codecAvailQue_->Size() == 0u) {
std::this_thread::sleep_for(std::chrono::milliseconds(DEFAULT_TRY_DECODE_TIME));
return;
}
CHECK_AND_RETURN_LOG(state_ == State::RUNNING, "Not in running state");
std::unique_lock<std::mutex> sLock(syncMutex_);
int ret = avcodec_receive_frame(avCodecContext_.get(), cachedFrame_.get());
sLock.unlock();
int32_t status = AVCS_ERR_OK;
auto index = codecAvailQue_->Front();
std::shared_ptr<FBuffer> frameBuffer = buffers_[INDEX_OUTPUT][index];
CHECK_AND_RETURN_LOG(ret != AVERROR_INVALIDDATA, "ffmpeg ret = %{public}s", AVStrError(ret).c_str());
if (ret >= 0) {
if (DecodedFrameProcess(index) == AVCS_ERR_OK) {
CHECK_AND_RETURN_LOG(state_ == State::RUNNING, "Not in running state");
frameBuffer = buffers_[INDEX_OUTPUT][index];
status = FillFrameBuffer(frameBuffer);
} else {
CHECK_AND_RETURN_LOG(state_ == State::RUNNING, "Not in running state");
callback_->OnError(AVCODEC_ERROR_EXTEND_START, AVCS_ERR_NO_MEMORY);
state_ = State::ERROR;
return;
}
frameBuffer->avBuffer_->flag_ = AVCODEC_BUFFER_FLAG_NONE;
} else if (ret == AVERROR_EOF) {
AVCODEC_LOGI("Receive eos");
frameBuffer->avBuffer_->flag_ = AVCODEC_BUFFER_FLAG_EOS;
frameBuffer->avBuffer_->memory_->SetSize(0);
state_ = State::EOS;
} else if (ret == AVERROR(EAGAIN)) {
std::unique_lock<std::mutex> sendLock(sendMutex_);
if (isSendWait_ || isSendEos_) {
isSendWait_ = false;
sendCv_.notify_one();
}
sendLock.unlock();
std::unique_lock<std::mutex> recvLock(recvMutex_);
recvCv_.wait_for(recvLock, std::chrono::milliseconds(DEFAULT_DECODE_WAIT_TIME));
return;
} else {
AVCODEC_LOGE("Cannot recv frame from codec: ffmpeg ret = %{public}s", AVStrError(ret).c_str());
callback_->OnError(AVCodecErrorType::AVCODEC_ERROR_INTERNAL, AVCodecServiceErrCode::AVCS_ERR_UNKNOWN);
state_ = State::ERROR;
return;
}
FramePostProcess(frameBuffer, index, status, ret);
}
int32_t FCodec::ReleaseOutputBuffer(uint32_t index)
{
AVCODEC_SYNC_TRACE;
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<FBuffer> frameBuffer = buffers_[INDEX_OUTPUT][index];
oLock.unlock();
if (frameBuffer->owner_ == Owner::OWNED_BY_USER) {
frameBuffer->owner_ = Owner::OWNED_BY_CODEC;
codecAvailQue_->Push(index);
return AVCS_ERR_OK;
} else {
AVCODEC_LOGE("Release output buffer failed: check your index=%{public}u", index);
return AVCS_ERR_INVALID_VAL;
}
}
int32_t FCodec::Attach(sptr<SurfaceBuffer> surfaceBuffer)
{
std::lock_guard<std::mutex> sLock(surfaceMutex_);
int32_t err = sInfo_.surface->AttachBufferToQueue(surfaceBuffer);
CHECK_AND_RETURN_RET_LOG(
err == 0, err, "Surface(%{public}" PRIu64 "), attach buffer(%{public}u) to queue failed, GSError=%{public}d",
sInfo_.surface->GetUniqueId(), surfaceBuffer->GetSeqNum(), err);
return AVCS_ERR_OK;
}
int32_t FCodec::Detach(sptr<SurfaceBuffer> surfaceBuffer)
{
std::lock_guard<std::mutex> sLock(surfaceMutex_);
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 FCodec::FlushSurfaceMemory(std::shared_ptr<FSurfaceMemory> &surfaceMemory, uint32_t index)
{
sptr<SurfaceBuffer> surfaceBuffer = surfaceMemory->GetSurfaceBuffer();
CHECK_AND_RETURN_RET_LOG(surfaceBuffer != nullptr, AVCS_ERR_UNKNOWN, "Get surface buffer failed!");
if (!surfaceMemory->isAttached) {
int32_t ret = Attach(surfaceBuffer);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Surface buffer attach failed!");
surfaceMemory->isAttached = true;
}
OHOS::BufferFlushConfig flushConfig = {{0, 0, surfaceBuffer->GetWidth(), surfaceBuffer->GetHeight()},
outAVBuffer4Surface_[index]->pts_, -1};
if (outAVBuffer4Surface_[index]->meta_->Find(FMTKey::VIDEO_DECODER_DESIRED_PRESENT_TIMESTAMP) !=
outAVBuffer4Surface_[index]->meta_->end()) {
outAVBuffer4Surface_[index]->meta_->Get<FMTKey::VIDEO_DECODER_DESIRED_PRESENT_TIMESTAMP>(
flushConfig.desiredPresentTimestamp);
outAVBuffer4Surface_[index]->meta_->Remove(FMTKey::VIDEO_DECODER_DESIRED_PRESENT_TIMESTAMP);
}
GSError res = GSERROR_OK;
{
std::lock_guard<std::mutex> sLock(surfaceMutex_);
res = sInfo_.surface->FlushBuffer(surfaceBuffer, -1, flushConfig);
}
if (res == GSERROR_BUFFER_NOT_INCACHE) {
AVCODEC_LOGW("Surface(%{public}" PRIu64 "), flush buffer(seq=%{public}u) failed, try to recover",
sInfo_.surface->GetUniqueId(), surfaceBuffer->GetSeqNum());
int32_t ret = ClearSurfaceAndSetQueueSize();
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Clean surface and set queue size failed!");
ret = Attach(surfaceBuffer);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Surface buffer attach failed!");
surfaceMemory->isAttached = true;
std::lock_guard<std::mutex> sLock(surfaceMutex_);
res = sInfo_.surface->FlushBuffer(surfaceBuffer, -1, flushConfig);
}
if (res != OHOS::SurfaceError::SURFACE_ERROR_OK) {
AVCODEC_LOGW("Failed to update surface memory: %{public}d", res);
return AVCS_ERR_UNKNOWN;
}
surfaceMemory->owner = Owner::OWNED_BY_SURFACE;
{
std::lock_guard<std::mutex> mLock(renderBufferMapMutex_);
renderSurfaceBufferMap_[index] = std::make_pair(surfaceBuffer, flushConfig);
}
return AVCS_ERR_OK;
}
int32_t FCodec::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!");
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<FBuffer> frameBuffer = buffers_[INDEX_OUTPUT][index];
oLock.unlock();
std::shared_ptr<AVMemory> &bufferMemory = frameBuffer->avBuffer_->memory_;
CHECK_AND_RETURN_RET_LOG(bufferMemory != nullptr, AVCS_ERR_INVALID_VAL, "bufferMemory is nullptr");
int32_t size = bufferMemory->GetSize();
CHECK_AND_RETURN_RET_LOGW(size > 0, AVCS_ERR_OK, "buf(%{public}u) size=%{public}d", index, size);
if (frameBuffer->owner_ == Owner::OWNED_BY_USER) {
std::shared_ptr<FSurfaceMemory> surfaceMemory = frameBuffer->sMemory_;
int32_t ret = FlushSurfaceMemory(surfaceMemory, index);
EXPECT_AND_LOGW(ret != AVCS_ERR_OK, "Flush surface memory(index=%{public}u) failed: %{public}d", index, ret);
frameBuffer->owner_ = Owner::OWNED_BY_SURFACE;
AVCODEC_LOGD("Render output buffer with index, index=%{public}u", index);
return AVCS_ERR_OK;
} else {
AVCODEC_LOGE("Failed to render outbuf(%{public}u), owner=%{public}d", index, frameBuffer->owner_.load());
return AVCS_ERR_INVALID_VAL;
}
}
int32_t FCodec::ReplaceOutputSurfaceWhenRunning(sptr<Surface> newSurface)
{
CHECK_AND_RETURN_RET_LOG(sInfo_.surface != nullptr, AV_ERR_OPERATE_NOT_PERMIT,
"Not support convert from AVBuffer Mode to Surface Mode");
sptr<Surface> oldSurface = sInfo_.surface;
uint64_t oldId = oldSurface->GetUniqueId();
uint64_t newId = newSurface->GetUniqueId();
AVCODEC_LOGI("Begin to switch surface %{public}" PRIu64 " -> %{public}" PRIu64 ".", oldId, newId);
if (oldId == newId) {
return AVCS_ERR_OK;
}
int32_t ret = RegisterListenerToSurface(newSurface);
CHECK_AND_RETURN_RET_LOG(ret == GSERROR_OK, ret,
"surface %{public}" PRIu64 ", RegisterListenerToSurface failed, GSError=%{public}d", newId, ret);
int32_t outputBufferCnt = 0;
CHECK_AND_RETURN_RET_LOG(
format_.GetIntValue(FMTKey::REQUIRED_OUT_BUFFER_CNT, outputBufferCnt) &&
outputBufferCnt >= DEFAULT_MIN_BUFFER_CNT, AVCS_ERR_INVALID_VAL,
"Invalid output buffer cnt: %{public}d", outputBufferCnt);
ret = SetQueueSize(newSurface, static_cast<uint32_t>(outputBufferCnt));
if (ret != AVCS_ERR_OK) {
UnRegisterListenerToSurface(newSurface);
return ret;
}
std::unique_lock<std::mutex> sLock(surfaceMutex_);
ret = SwitchBetweenSurface(newSurface);
if (ret != AVCS_ERR_OK) {
UnRegisterListenerToSurface(newSurface);
sInfo_.surface = oldSurface;
CombineConsumerUsage();
return ret;
}
sLock.unlock();
AVCODEC_LOGI("Switch surface %{public}" PRIu64 " -> %{public}" PRIu64 ".", oldId, newId);
return AVCS_ERR_OK;
}
int32_t FCodec::SetQueueSize(const sptr<Surface> &surface, uint32_t targetSize)
{
uint64_t surfaceId = surface->GetUniqueId();
int32_t err = surface->SetQueueSize(targetSize);
CHECK_AND_RETURN_RET_LOG(err == 0, err,
"Surface(%{public}" PRIu64 ") set queue size %{public}u failed, GSError=%{public}d",
surfaceId, targetSize, err);
AVCODEC_LOGI("Surface(%{public}" PRIu64 ") set queue size %{public}u succss.", surfaceId, targetSize);
return AVCS_ERR_OK;
}
int32_t FCodec::SwitchBetweenSurface(const sptr<Surface> &newSurface)
{
newSurface->Connect();
newSurface->CleanCache();
newSurface->Disconnect();
std::vector<uint32_t> ownedBySurfaceBufferIndex;
uint64_t newId = newSurface->GetUniqueId();
for (uint32_t index = 0; index < buffers_[INDEX_OUTPUT].size(); index++) {
if (buffers_[INDEX_OUTPUT][index]->sMemory_ == nullptr) {
continue;
}
sptr<SurfaceBuffer> surfaceBuffer = nullptr;
if (buffers_[INDEX_OUTPUT][index]->owner_ == Owner::OWNED_BY_SURFACE) {
std::lock_guard<std::mutex> mLock(renderBufferMapMutex_);
if (renderSurfaceBufferMap_.count(index)) {
surfaceBuffer = renderSurfaceBufferMap_[index].first;
ownedBySurfaceBufferIndex.push_back(index);
}
} else {
surfaceBuffer = buffers_[INDEX_OUTPUT][index]->sMemory_->GetSurfaceBuffer();
}
CHECK_AND_RETURN_RET_LOG(surfaceBuffer != nullptr, AVCS_ERR_UNKNOWN, "Get old surface buffer error!");
int32_t err = newSurface->AttachBufferToQueue(surfaceBuffer);
CHECK_AND_RETURN_RET_LOG(
err == 0, err, "surface(%{public}" PRIu64 ")attach buffer(seq=%{public}u) to queue failed,"
"GSError=%{public}d", newId, surfaceBuffer->GetSeqNum(), err);
buffers_[INDEX_OUTPUT][index]->sMemory_->isAttached = true;
}
newSurface->SetTransform(transform_);
sptr<Surface> curSurface = sInfo_.surface;
sInfo_.surface = newSurface;
CombineConsumerUsage();
for (uint32_t index: ownedBySurfaceBufferIndex) {
int32_t ret = RenderNewSurfaceWithOldBuffer(newSurface, index);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Old surface buffer render failed!");
}
UnRegisterListenerToSurface(curSurface);
curSurface->SetTransform(GRAPHIC_ROTATE_NONE);
curSurface->CleanCache(true);
return AVCS_ERR_OK;
}
int32_t FCodec::RenderNewSurfaceWithOldBuffer(const sptr<Surface> &newSurface, uint32_t index)
{
std::unique_lock<std::mutex> mLock(renderBufferMapMutex_);
sptr<SurfaceBuffer> surfaceBuffer = renderSurfaceBufferMap_[index].first;
OHOS::BufferFlushConfig flushConfig = renderSurfaceBufferMap_[index].second;
mLock.unlock();
if (sInfo_.scalingMode) {
newSurface->SetScalingMode(surfaceBuffer->GetSeqNum(), static_cast<ScalingMode>(sInfo_.scalingMode.value()));
}
auto res = newSurface->FlushBuffer(surfaceBuffer, -1, flushConfig);
if (res != OHOS::SurfaceError::SURFACE_ERROR_OK) {
AVCODEC_LOGE("Failed to update surface memory: %{public}d", res);
return AVCS_ERR_UNKNOWN;
}
return AVCS_ERR_OK;
}
GSError FCodec::BufferReleasedByConsumer(uint64_t surfaceId)
{
CHECK_AND_RETURN_RET_LOG(state_ == State::RUNNING || state_ == State::EOS || state_ == State::FLUSHING ||
state_ == State::FLUSHED, GSERROR_NO_PERMISSION, "Invalid state");
std::unique_lock<std::mutex> sLock(surfaceMutex_);
CHECK_AND_RETURN_RET_LOG(surfaceId == sInfo_.surface->GetUniqueId(), GSERROR_INVALID_ARGUMENTS,
"Ignore callback from old surface");
sLock.unlock();
RequestSurfaceBufferOnce();
return GSERROR_OK;
}
void FCodec::UnRegisterListenerToSurface(const sptr<Surface> &surface)
{
CHECK_AND_RETURN_LOGW(surface != nullptr, "Surface is null, not need to unregister listener.");
SurfaceTools::GetInstance().ReleaseSurface(instanceId_, surface, false);
}
int32_t FCodec::RegisterListenerToSurface(const sptr<Surface> &surface)
{
uint64_t surfaceId = surface->GetUniqueId();
wptr<FCodec> wp = this;
bool ret =
SurfaceTools::GetInstance().RegisterReleaseListener(instanceId_, surface,
[wp, surfaceId](sptr<SurfaceBuffer> &) {
sptr<FCodec> codec = wp.promote();
if (!codec) {
AVCODEC_LOGD("decoder is nullptr");
return GSERROR_OK;
}
return codec->BufferReleasedByConsumer(surfaceId);
});
CHECK_AND_RETURN_RET_LOG(ret, AVCS_ERR_UNKNOWN, "surface(%" PRIu64 ") register listener failed", surfaceId);
return AVCS_ERR_OK;
}
void FCodec::CombineConsumerUsage()
{
uint64_t defaultUsage = SURFACE_DEFAULT_USAGE;
uint64_t consumerUsage = sInfo_.surface->GetDefaultUsage();
uint64_t cfgedUsage = sInfo_.requestConfig.usage;
uint64_t finalUsage = defaultUsage | consumerUsage | cfgedUsage;
sInfo_.requestConfig.usage = finalUsage;
AVCODEC_LOGI("Usage: default(0x%{public}" PRIu64 ") | consumer(0x%{public}" PRIu64 ") | cfged(0x%{public}" PRIu64
") -> final(0x%{public}" PRIu64 ").",
defaultUsage, consumerUsage, cfgedUsage, finalUsage);
}
int32_t FCodec::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 == AVCS_ERR_OK, ret,
"surface(%{public}" PRIu64 ") register listener to surface failed, GSError=%{public}d",
sInfo_.surface->GetUniqueId(), ret);
AVCODEC_LOGI("%{public}s Set surface(%{public}" PRIu64 ") success.", decName_.c_str(), surfaceId);
return AVCS_ERR_OK;
}
int32_t FCodec::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;
}
bool FCodec::CanSwapOut(bool isOutputBuffer, std::shared_ptr<FBuffer> &fBuffer)
{
if (!isOutputBuffer) {
AVCODEC_LOGD("Current buffers unsupport.");
return false;
}
std::shared_ptr<FSurfaceMemory> surfaceMemory = fBuffer->sMemory_;
CHECK_AND_RETURN_RET_LOGD(surfaceMemory != nullptr, false, "Current buffer->sMemory error!");
Owner ownerValue = surfaceMemory->owner;
AVCODEC_LOGD("Buffer type: [%{public}u], fBuffer->owner_: [%{public}d], fBuffer->hasSwapedOut_: [%{public}d].",
isOutputBuffer, ownerValue, fBuffer->hasSwapedOut_.load());
return !(ownerValue == Owner::OWNED_BY_SURFACE || fBuffer->hasSwapedOut_);
}
int32_t FCodec::SwapOutBuffers(bool isOutputBuffer, State curState)
{
uint32_t bufferType = isOutputBuffer ? INDEX_OUTPUT : INDEX_INPUT;
CHECK_AND_RETURN_RET_LOGD(bufferType == INDEX_OUTPUT, AVCS_ERR_OK, "Input buffers can't be swapped out!");
for (uint32_t i = 0u; i < buffers_[bufferType].size(); i++) {
std::shared_ptr<FBuffer> fBuffer = buffers_[bufferType][i];
if (!CanSwapOut(isOutputBuffer, fBuffer)) {
AVCODEC_LOGW("Buf: [%{public}u] can't freeze, owner: [%{public}d] swaped out: [%{public}d]!", i,
fBuffer->owner_.load(), fBuffer->hasSwapedOut_.load());
continue;
}
std::shared_ptr<FSurfaceMemory> surfaceMemory = fBuffer->sMemory_;
CHECK_AND_RETURN_RET_LOG(surfaceMemory != nullptr, AVCS_ERR_UNKNOWN, "Buf[%{public}u]->sMemory error!", i);
sptr<SurfaceBuffer> surfaceBuffer = surfaceMemory->GetSurfaceBuffer();
CHECK_AND_RETURN_RET_LOG(surfaceBuffer != nullptr, AVCS_ERR_UNKNOWN, "Buf[%{public}u]->surfaceBuf error!", i);
int32_t fd = surfaceBuffer->GetFileDescriptor();
int32_t ret = DmaSwaper::GetInstance().SwapOutDma(pid_, fd);
if (ret != AVCS_ERR_OK) {
AVCODEC_LOGE("Buffer type[%{public}u] bufferId[%{public}u], fd[%{public}d], pid[%{public}d] freeze failed!",
bufferType, i, fd, pid_);
int32_t errCode = ActiveBuffers();
state_ = curState;
CHECK_AND_RETURN_RET_LOG(errCode == AVCS_ERR_OK, errCode, "Active buffers failed!");
return ret;
}
AVCODEC_LOGI("Buf[%{public}u] fd[%{public}u] swap out success!", i, fd);
fBuffer->hasSwapedOut_ = true;
}
return AVCS_ERR_OK;
}
int32_t FCodec::SwapInBuffers(bool isOutputBuffer)
{
uint32_t bufferType = isOutputBuffer ? INDEX_OUTPUT : INDEX_INPUT;
CHECK_AND_RETURN_RET_LOGD(bufferType == INDEX_OUTPUT, AVCS_ERR_OK, "Input buffers can't be swapped in!");
for (uint32_t i = 0u; i < buffers_[bufferType].size(); i++) {
std::shared_ptr<FBuffer> fBuffer = buffers_[bufferType][i];
if (!fBuffer->hasSwapedOut_) {
continue;
}
std::shared_ptr<FSurfaceMemory> surfaceMemory = fBuffer->sMemory_;
CHECK_AND_CONTINUE_LOG(surfaceMemory != nullptr, "Buf[%{public}u]->sMemory error!", i);
sptr<SurfaceBuffer> surfaceBuffer = surfaceMemory->GetSurfaceBuffer();
CHECK_AND_CONTINUE_LOG(surfaceBuffer != nullptr, "Buf[%{public}u]->surfaceBuf error!", i);
int32_t fd = surfaceBuffer->GetFileDescriptor();
int32_t ret = DmaSwaper::GetInstance().SwapInDma(pid_, fd);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Buf[%{public}u] fd[%{public}u] swap in error!", i, fd);
AVCODEC_LOGI("Buf[%{public}u] fd[%{public}u] swap in success!", i, fd);
fBuffer->hasSwapedOut_ = false;
}
return AVCS_ERR_OK;
}
int32_t FCodec::FreezeBuffers(State curState)
{
CHECK_AND_RETURN_RET_LOGD(state_ != State::FROZEN, AVCS_ERR_OK, "FCodec had been frozen!");
std::lock_guard<std::mutex> sLock(surfaceMutex_);
int32_t ret = SwapOutBuffers(INDEX_INPUT, curState);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Input buffers swap out failed!");
ret = SwapOutBuffers(INDEX_OUTPUT, curState);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Output buffers swap out failed!");
return AVCS_ERR_OK;
}
int32_t FCodec::ActiveBuffers()
{
CHECK_AND_RETURN_RET_LOGD(state_ == State::FREEZING || state_ == State::FROZEN, AVCS_ERR_INVALID_STATE,
"Only freezing or frozen state can swap in dma buffer!");
int32_t ret = SwapInBuffers(INDEX_INPUT);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Input buffers swap in failed!");
ret = SwapInBuffers(INDEX_OUTPUT);
CHECK_AND_RETURN_RET_LOG(ret == AVCS_ERR_OK, ret, "Output buffers swap in failed!");
return AVCS_ERR_OK;
}
int32_t FCodec::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("Begin to freeze this codec!");
State currentState = state_;
state_ = State::FREEZING;
int32_t errCode = FreezeBuffers(currentState);
CHECK_AND_RETURN_RET_LOG(errCode == AVCS_ERR_OK, errCode, "Fcodec freeze buffers failed!");
state_ = State::FROZEN;
return AVCS_ERR_OK;
}
int32_t FCodec::NotifyMemoryWriteBack()
{
CHECK_AND_RETURN_RET_LOG(sInfo_.surface != nullptr, AVCS_ERR_UNKNOWN, "Only surface mode support!");
AVCODEC_LOGI("Begin to active this codec!");
int32_t errCode = ActiveBuffers();
CHECK_AND_RETURN_RET_LOG(errCode == AVCS_ERR_OK, errCode, "Fcodec active buffers failed!");
state_ = State::RUNNING;
return AVCS_ERR_OK;
}
}
}
}