* 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 "hcodec.h"
#include <cassert>
#include <vector>
#include <algorithm>
#include <thread>
#include "syspara/parameters.h"
#include "qos.h"
#include "res_sched_client.h"
#include "hdf_base.h"
#include "codec_omx_ext.h"
#include "hcodec_list.h"
#include "hencoder.h"
#include "hdecoder.h"
#include "hitrace_meter.h"
#include "hcodec_log.h"
#include "hcodec_dfx.h"
#include "hcodec_utils.h"
#include <sys/ioctl.h>
#include <linux/dma-buf.h>
#define DMA_BUF_SET_LEAK_TYPE _IOW(DMA_BUF_BASE, 5, const char *)
namespace OHOS::MediaAVCodec {
using namespace std;
using namespace CodecHDI;
using namespace Media;
void HCodec::SetCallerToBuffer(int fd, uint32_t w, uint32_t h)
{
const char* type = isEncoder_ ? "hw-video-encoder" : "hw-video-decoder";
ioctl(fd, DMA_BUF_SET_LEAK_TYPE, type);
string pid = to_string(caller_.playerCaller.pid);
ioctl(fd, DMA_BUF_SET_NAME_A, pid.c_str());
string bufName = caller_.app.processName + '-' + to_string(w) + 'x' + to_string(h)
+ '-' + mime_ + '-' + to_string(componentId_);
ioctl(fd, DMA_BUF_SET_NAME_A, bufName.c_str());
}
std::shared_ptr<HCodec> HCodec::Create(const std::string &name)
{
vector<CodecCompCapability> capList = GetCapList();
shared_ptr<HCodec> codec;
for (const auto& cap : capList) {
if (cap.compName != name) {
continue;
}
optional<OMX_VIDEO_CODINGTYPE> type = TypeConverter::HdiRoleToOmxCodingType(cap.role);
if (!type) {
LOGE("unsupported role %d", cap.role);
return nullptr;
}
if (cap.type == VIDEO_DECODER) {
codec = make_shared<HDecoder>(cap, type.value());
} else if (cap.type == VIDEO_ENCODER) {
codec = make_shared<HEncoder>(cap, type.value());
}
break;
}
if (codec == nullptr) {
LOGE("cannot find %s", name.c_str());
return nullptr;
}
return codec;
}
int32_t HCodec::Init(Media::Meta &callerInfo)
{
if (callerInfo.GetData(Tag::AV_CODEC_FORWARD_CALLER_PID, caller_.playerCaller.pid) &&
callerInfo.GetData(Tag::AV_CODEC_FORWARD_CALLER_PROCESS_NAME, caller_.playerCaller.processName)) {
caller_.calledByAvcodec = false;
caller_.app = caller_.playerCaller;
} else if (callerInfo.GetData(Tag::AV_CODEC_CALLER_PID, caller_.avcodecCaller.pid) &&
callerInfo.GetData(Tag::AV_CODEC_CALLER_PROCESS_NAME, caller_.avcodecCaller.processName)) {
caller_.calledByAvcodec = true;
caller_.app = caller_.avcodecCaller;
}
callerInfo.GetData("av_codec_event_info_instance_id", instanceId_);
return DoSyncCall(MsgWhat::INIT, nullptr);
}
std::shared_mutex HCodec::g_mtx;
std::unordered_map<HCodec::CallerInfo, std::vector<HCodec::InstInfo>,
HCodec::CallerInfo, HCodec::CallerInfo> HCodec::g_callers[2];
void HCodec::PrintCaller()
{
if (caller_.calledByAvcodec) {
HLOGI("[pid %d][%s] -> avcodec", caller_.avcodecCaller.pid, caller_.avcodecCaller.processName.c_str());
} else {
HLOGI("[pid %d][%s] -> player -> avcodec", caller_.playerCaller.pid, caller_.playerCaller.processName.c_str());
}
std::unique_lock<std::shared_mutex> lk(g_mtx);
g_callers[isEncoder_][caller_.app].emplace_back(InstInfo{GetNowUs(), compUniqueStr_});
size_t totalInstCntNow = CalculateTotalInstCnt();
size_t singleAppInstCntNow = g_callers[isEncoder_][caller_.app].size();
if ((totalInstCntNow >= totalWarnInstCnt_) || (singleAppInstCntNow >= singleAppWarnInstCnt_)) {
ReportToRss();
}
}
size_t HCodec::CalculateTotalInstCnt()
{
size_t ret = 0;
const auto& callers = g_callers[isEncoder_];
for (const auto& [_, vec] : callers) {
ret += vec.size();
}
return ret;
}
void HCodec::PrintAllCaller()
{
std::shared_lock<std::shared_mutex> lk(g_mtx);
for (size_t i = 0; i < 2; i++) {
for (const auto& [app, vec] : g_callers[i]) {
LOGI("[pid %d][%s] hold %zu %s", app.pid, app.processName.c_str(), vec.size(), coderNames[i]);
for (const InstInfo& inst : vec) {
LOGI("createTime: %" PRId64 ", %s", inst.createTimeUs, inst.compUniqueStr.c_str());
}
}
}
}
void HCodec::RemoveCaller()
{
std::unique_lock<std::shared_mutex> lk(g_mtx);
auto& callers = g_callers[isEncoder_];
for (auto mapIter = callers.begin(); mapIter != callers.end(); mapIter++) {
std::vector<InstInfo>& vec = mapIter->second;
auto iter = find_if(vec.begin(), vec.end(), [this](const InstInfo& inst) {
return inst.compUniqueStr == compUniqueStr_;
});
if (iter == vec.end()) {
continue;
}
size_t totalInstCntBefore = CalculateTotalInstCnt();
size_t singleAppInstCntBefore = vec.size();
vec.erase(iter);
if (vec.empty()) {
callers.erase(mapIter);
}
if ((totalInstCntBefore >= totalWarnInstCnt_) || (singleAppInstCntBefore >= singleAppWarnInstCnt_)) {
ReportToRss();
}
return;
}
}
void HCodec::ReportToRss()
{
std::unordered_map<std::string, std::string> mapPayload;
for (const auto& [app, vec] : g_callers[isEncoder_]) {
nlohmann::json js = {
{"AppProcessName", app.processName},
{"AppHold" + std::string(coderNames[isEncoder_]) + "Cnt", vec.size()},
{"MaxAllow" + std::string(coderNames[isEncoder_]) + "Cnt", maxCodecInst_},
};
string pidStr = to_string(app.pid);
string jsStr = js.dump();
mapPayload[pidStr] = jsStr;
}
ResourceSchedule::ResSchedClient::GetInstance().ReportData(
ResourceSchedule::ResType::RES_TYPE_AVCODE_HARDWARE_RESOURCES, isEncoder_ ? 1 : 0, mapPayload);
}
int32_t HCodec::SetCallback(const std::shared_ptr<MediaCodecCallback> &callback)
{
HLOGD(">>");
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("callback", callback);
};
return DoSyncCall(MsgWhat::SET_CALLBACK, proc);
}
int32_t HCodec::Configure(const Format &format)
{
SCOPED_TRACE();
HLOGI("%s", format.Stringify().c_str());
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("format", format);
};
return DoSyncCall(MsgWhat::CONFIGURE, proc);
}
int32_t HCodec::SetCustomBuffer(std::shared_ptr<AVBuffer> buffer)
{
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("buffer", buffer);
};
return DoSyncCall(MsgWhat::CONFIGURE_BUFFER, proc);
}
int32_t HCodec::SetOutputSurface(sptr<Surface> surface)
{
HLOGI(">>");
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("surface", surface);
};
return DoSyncCall(MsgWhat::SET_OUTPUT_SURFACE, proc);
}
int32_t HCodec::Start()
{
SCOPED_TRACE();
FUNC_TRACKER();
return DoSyncCall(MsgWhat::START, nullptr,
(isSecure_ ? FIVE_SECONDS_IN_MS * 2 : FIVE_SECONDS_IN_MS));
}
int32_t HCodec::Stop()
{
SCOPED_TRACE();
FUNC_TRACKER();
return DoSyncCall(MsgWhat::STOP, nullptr);
}
int32_t HCodec::Flush()
{
SCOPED_TRACE();
FUNC_TRACKER();
return DoSyncCall(MsgWhat::FLUSH, nullptr);
}
int32_t HCodec::Reset()
{
SCOPED_TRACE();
FUNC_TRACKER();
int32_t ret = Release();
if (ret == AVCS_ERR_OK) {
ret = DoSyncCall(MsgWhat::INIT, nullptr);
}
return ret;
}
int32_t HCodec::Release()
{
SCOPED_TRACE();
FUNC_TRACKER();
return DoSyncCall(MsgWhat::RELEASE, nullptr);
}
int32_t HCodec::NotifyEos()
{
HLOGI(">>");
return DoSyncCall(MsgWhat::NOTIFY_EOS, nullptr);
}
int32_t HCodec::SetParameter(const Format &format)
{
HLOGI("%s", format.Stringify().c_str());
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("params", format);
};
return DoSyncCall(MsgWhat::SET_PARAMETERS, proc);
}
int32_t HCodec::GetInputFormat(Format& format)
{
HLOGI(">>");
ParamSP reply;
int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_INPUT_FORMAT, nullptr, reply);
if (ret != AVCS_ERR_OK) {
HLOGE("failed to get input format");
return ret;
}
IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("format", format),
AVCS_ERR_UNKNOWN, "input format not replied");
OnGetInputFormat(format);
return AVCS_ERR_OK;
}
int32_t HCodec::GetOutputFormat(Format &format)
{
ParamSP reply;
int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_OUTPUT_FORMAT, nullptr, reply);
if (ret != AVCS_ERR_OK) {
HLOGE("failed to get output format");
return ret;
}
IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("format", format),
AVCS_ERR_UNKNOWN, "output format not replied");
format.PutStringValue(MediaDescriptionKey::MD_KEY_CODEC_NAME, caps_.compName);
format.PutIntValue("IS_VENDOR", 1);
return AVCS_ERR_OK;
}
int32_t HCodec::GetChannelId(int32_t &channelId)
{
GetChannelIdParam param {};
InitOMXParamExt(param);
if (!GetParameter(OMX_IndexParamChannelId, param)) {
HLOGI("LowPowerPlayer GetChannelId failed");
return AVCS_ERR_UNKNOWN;
}
HLOGI("LowPowerPlayer GetChannelId Success");
channelId = param.channelId;
return AVCS_ERR_OK;
}
int32_t HCodec::SetLowPowerPlayerMode(bool isLpp)
{
bool originalIsLpp = isLpp_;
isLpp_ = isLpp;
GetLppModeParam param {};
InitOMXParamExt(param);
param.enable = isLpp;
if (!SetParameter(OMX_IndexParamLppMode, param)) {
HLOGE("set LowPowerPlayer Mode failed");
isLpp_ = originalIsLpp;
return AVCS_ERR_UNKNOWN;
}
return AVCS_ERR_OK;
}
std::string HCodec::GetHidumperInfo()
{
HLOGI(">>");
ParamSP reply;
int32_t ret = DoSyncCallAndGetReply(MsgWhat::GET_HIDUMPER_INFO, nullptr, reply);
if (ret != AVCS_ERR_OK) {
HLOGW("failed to get hidumper info");
return "";
}
string info;
IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("hidumper-info", info),
"", "hidumper info not replied");
return info;
}
sptr<Surface> HCodec::CreateInputSurface()
{
HLOGI(">>");
ParamSP reply;
int32_t ret = DoSyncCallAndGetReply(MsgWhat::CREATE_INPUT_SURFACE, nullptr, reply);
if (ret != AVCS_ERR_OK) {
HLOGE("failed to create input surface");
return nullptr;
}
sptr<Surface> inputSurface;
IF_TRUE_RETURN_VAL_WITH_MSG(!reply->GetValue("surface", inputSurface), nullptr, "input surface not replied");
return inputSurface;
}
int32_t HCodec::SetInputSurface(sptr<Surface> surface)
{
HLOGI(">>");
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue("surface", surface);
};
return DoSyncCall(MsgWhat::SET_INPUT_SURFACE, proc);
}
int32_t HCodec::SignalRequestIDRFrame()
{
HLOGI(">>");
return DoSyncCall(MsgWhat::REQUEST_IDR_FRAME, nullptr);
}
int32_t HCodec::QueueInputBuffer(uint32_t index)
{
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue(BUFFER_ID, index);
};
return DoSyncCall(MsgWhat::QUEUE_INPUT_BUFFER, proc);
}
int32_t HCodec::RenderOutputBuffer(uint32_t index)
{
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue(BUFFER_ID, index);
};
return DoSyncCall(MsgWhat::RENDER_OUTPUT_BUFFER, proc);
}
int32_t HCodec::ReleaseOutputBuffer(uint32_t index)
{
std::function<void(ParamSP)> proc = [&](ParamSP msg) {
msg->SetValue(BUFFER_ID, index);
};
return DoSyncCall(MsgWhat::RELEASE_OUTPUT_BUFFER, proc);
}
HCodec::HCodec(CodecCompCapability caps, OMX_VIDEO_CODINGTYPE codingType, bool isEncoder)
: caps_(caps), codingType_(codingType), isEncoder_(isEncoder)
{
debugMode_ = HiLogIsLoggable(HCODEC_DOMAIN, HCODEC_TAG, LOG_DEBUG);
string dumpModeStr = OHOS::system::GetParameter("hcodec.dump", "0");
dumpMode_ = static_cast<DumpMode>(strtoul(dumpModeStr.c_str(), nullptr, 2));
disableDmaSwap_ = OHOS::system::GetBoolParameter("hcodec.dmaswap.disable", false);
pid_ = getpid();
switch (static_cast<int>(codingType_)) {
case OMX_VIDEO_CodingAVC:
mime_ = "avc";
break;
case CODEC_OMX_VIDEO_CodingHEVC:
mime_ = "hevc";
break;
case CODEC_OMX_VIDEO_CodingVVC:
mime_ = "vvc";
break;
default:
break;
};
isSecure_ = IsSecureMode(caps_.compName);
uninitializedState_ = make_shared<UninitializedState>(this);
initializedState_ = make_shared<InitializedState>(this);
startingState_ = make_shared<StartingState>(this);
runningState_ = make_shared<RunningState>(this);
outputPortChangedState_ = make_shared<OutputPortChangedState>(this);
stoppingState_ = make_shared<StoppingState>(this);
flushingState_ = make_shared<FlushingState>(this);
frozenState_ = make_shared<FrozenState>(this);
StateMachine::ChangeStateTo(uninitializedState_);
}
HCodec::~HCodec()
{
HLOGI(">>");
MsgHandleLoop::Stop();
ReleaseComponent();
}
int32_t HCodec::HdiCallback::EventHandler(CodecEventType event, const EventInfo &info)
{
LOGD("event = %d, data1 = %u, data2 = %u", event, info.data1, info.data2);
ParamSP msg = make_shared<ParamBundle>();
msg->SetValue("event", event);
msg->SetValue("data1", info.data1);
msg->SetValue("data2", info.data2);
std::shared_ptr<MsgToken> codec = codec_.lock();
if (codec == nullptr) {
LOGI("HCodec is gone");
return HDF_SUCCESS;
}
codec->SendAsyncMsg(MsgWhat::CODEC_EVENT, msg);
return HDF_SUCCESS;
}
int32_t HCodec::HdiCallback::EmptyBufferDone(int64_t appData, const OmxCodecBuffer& buffer)
{
ParamSP msg = make_shared<ParamBundle>();
msg->SetValue(BUFFER_ID, buffer.bufferId);
std::shared_ptr<MsgToken> codec = codec_.lock();
if (codec == nullptr) {
LOGI("HCodec is gone");
return HDF_SUCCESS;
}
codec->SendAsyncMsg(MsgWhat::OMX_EMPTY_BUFFER_DONE, msg);
return HDF_SUCCESS;
}
int32_t HCodec::HdiCallback::FillBufferDone(int64_t appData, const OmxCodecBuffer& buffer)
{
ParamSP msg = make_shared<ParamBundle>();
msg->SetValue("omxBuffer", buffer);
std::shared_ptr<MsgToken> codec = codec_.lock();
if (codec == nullptr) {
LOGI("HCodec is gone");
return HDF_SUCCESS;
}
codec->SendAsyncMsg(MsgWhat::OMX_FILL_BUFFER_DONE, msg);
return HDF_SUCCESS;
}
int32_t HCodec::SetFrameRateAdaptiveMode(const Format &format)
{
if (!format.ContainKey(OHOS::Media::Tag::VIDEO_FRAME_RATE_ADAPTIVE_MODE)) {
return AVCS_ERR_UNKNOWN;
}
WorkingFrequencyParam param {};
InitOMXParamExt(param);
if (!GetParameter(OMX_IndexParamWorkingFrequency, param)) {
HLOGW("get working freq param failed");
return AVCS_ERR_UNKNOWN;
}
if (param.level == 0) {
return AVCS_ERR_UNKNOWN;
}
HLOGI("level cnt is %d, set level to %d", param.level, param.level - 1);
param.level = param.level - 1;
if (!SetParameter(OMX_IndexParamWorkingFrequency, param)) {
HLOGW("set working freq param failed");
return AVCS_ERR_UNKNOWN;
}
return AVCS_ERR_OK;
}
int32_t HCodec::SetProcessName()
{
const std::string& processName = caller_.app.processName;
HLOGI("processName is %s", processName.c_str());
ProcessNameParam param {};
InitOMXParamExt(param);
if (strcpy_s(param.processName, sizeof(param.processName), processName.c_str()) != EOK) {
HLOGW("strcpy failed");
return AVCS_ERR_UNKNOWN;
}
if (!SetParameter(OMX_IndexParamProcessName, param)) {
HLOGW("set process name failed");
return AVCS_ERR_UNKNOWN;
}
return AVCS_ERR_OK;
}
int32_t HCodec::SetLowLatency(const Format &format)
{
int32_t enableLowLatency;
if (!format.GetIntValue(OHOS::Media::Tag::VIDEO_ENABLE_LOW_LATENCY, enableLowLatency)) {
return AVCS_ERR_OK;
}
if (!HCodecList::FindFeature(caps_.port.video.features, VIDEO_FEATURE_LOW_LATENCY).support) {
HLOGW("platform not support LowLatency");
return AVCS_ERR_OK;
}
OMX_CONFIG_BOOLEANTYPE param {};
InitOMXParam(param);
param.bEnabled = enableLowLatency ? OMX_TRUE : OMX_FALSE;
if (!SetParameter(OMX_IndexParamLowLatency, param)) {
HLOGW("set low latency failed");
return AVCS_ERR_UNKNOWN;
}
HLOGI("set low latency %s", enableLowLatency ? "enable" : "disable");
return AVCS_ERR_OK;
}
bool HCodec::GetPixelFmtFromUser(const Format &format)
{
optional<PixelFmt> fmt;
VideoPixelFormat innerFmt;
if (format.GetIntValue(MediaDescriptionKey::MD_KEY_PIXEL_FORMAT, *(int *)&innerFmt) &&
innerFmt != VideoPixelFormat::SURFACE_FORMAT) {
fmt = TypeConverter::InnerFmtToFmt(innerFmt);
} else {
HLOGI("user don't set VideoPixelFormat, use default");
for (int32_t f : caps_.port.video.supportPixFmts) {
fmt = TypeConverter::GraphicFmtToFmt(static_cast<GraphicPixelFormat>(f));
if (fmt.has_value()) {
break;
}
}
}
if (!fmt) {
HLOGE("pixel format unspecified");
return false;
}
configuredFmt_ = fmt.value();
HLOGI("configured pixel format is %s", configuredFmt_.strFmt.c_str());
return true;
}
std::optional<double> HCodec::GetFrameRateFromUser(const Format &format)
{
double frameRateDouble;
if (format.GetDoubleValue(MediaDescriptionKey::MD_KEY_FRAME_RATE, frameRateDouble) && frameRateDouble > 0) {
LOGD("user set frame rate %.2f", frameRateDouble);
return frameRateDouble;
}
int frameRateInt;
if (format.GetIntValue(MediaDescriptionKey::MD_KEY_FRAME_RATE, frameRateInt) && frameRateInt > 0) {
LOGD("user set frame rate %d", frameRateInt);
return static_cast<double>(frameRateInt);
}
return nullopt;
}
std::optional<double> HCodec::GetOperatingRateFromUser(const Format &format)
{
double operatingRate = 0.0;
if (format.GetDoubleValue(OHOS::Media::Tag::VIDEO_OPERATING_RATE, operatingRate) && operatingRate > 0.0) {
return operatingRate;
}
return nullopt;
}
bool HCodec::CheckBufPixFmt(int32_t dispFmt)
{
const std::vector<int32_t>& supportFmts = caps_.port.video.supportPixFmts;
if (std::find(supportFmts.begin(), supportFmts.end(), dispFmt) == supportFmts.end()) {
LOGE("unsupported buffer pixel format %d", dispFmt);
callback_->OnError(AVCODEC_ERROR_INTERNAL, AVCS_ERR_INPUT_DATA_ERROR);
return false;
}
return true;
}
int32_t HCodec::SetVideoPortInfo(OMX_DIRTYPE portIndex, const PortInfo& info)
{
if (info.pixelFmt.has_value()) {
CodecVideoPortFormatParam param;
InitOMXParamExt(param);
param.portIndex = portIndex;
param.codecCompressFormat = info.codingType;
param.codecColorFormat = info.pixelFmt->graphicFmt;
param.framerate = info.frameRate * FRAME_RATE_COEFFICIENT;
if (!SetParameter(OMX_IndexCodecVideoPortFormat, param)) {
HLOGE("set port format failed");
return AVCS_ERR_UNKNOWN;
}
}
{
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParam(def);
def.nPortIndex = portIndex;
if (!GetParameter(OMX_IndexParamPortDefinition, def)) {
HLOGE("get port definition failed");
return AVCS_ERR_UNKNOWN;
}
def.format.video.nFrameWidth = info.width;
def.format.video.nFrameHeight = info.height;
def.format.video.eCompressionFormat = info.codingType;
def.format.video.xFramerate = info.frameRate * FRAME_RATE_COEFFICIENT;
if (portIndex == OMX_DirInput && info.inputBufSize.has_value()) {
def.nBufferSize = info.inputBufSize.value();
}
if (portIndex == OMX_DirInput && info.inputBufferCount.has_value()) {
def.nBufferCountActual = info.inputBufferCount.value();
HLOGI("set input buffer count: %d", def.nBufferCountActual);
}
if (!SetParameter(OMX_IndexParamPortDefinition, def)) {
HLOGE("set port definition failed");
return AVCS_ERR_UNKNOWN;
}
if (portIndex == OMX_DirOutput) {
if (outputFormat_ == nullptr) {
outputFormat_ = make_shared<Format>();
}
outputFormat_->PutDoubleValue(MediaDescriptionKey::MD_KEY_FRAME_RATE, info.frameRate);
}
}
return (portIndex == OMX_DirInput) ? UpdateInPortFormat() : UpdateOutPortFormat();
}
void HCodec::PrintPortDefinition(const OMX_PARAM_PORTDEFINITIONTYPE& def)
{
const OMX_VIDEO_PORTDEFINITIONTYPE& video = def.format.video;
HLOGI("%s: bufCntAct %u, bufCntMin %u bufSize %u, %u x %u @ %u(%.2f)",
(def.nPortIndex == OMX_DirInput) ? "INPUT" : "OUTPUT",
def.nBufferCountActual, def.nBufferCountMin, def.nBufferSize, video.nFrameWidth, video.nFrameHeight,
video.xFramerate, video.xFramerate / FRAME_RATE_COEFFICIENT);
}
int32_t HCodec::GetPortDefinition(OMX_DIRTYPE portIndex, OMX_PARAM_PORTDEFINITIONTYPE& def)
{
InitOMXParam(def);
def.nPortIndex = portIndex;
if (!GetParameter(OMX_IndexParamPortDefinition, def)) {
HLOGE("get %s port definition failed", (portIndex == OMX_DirInput ? "input" : "output"));
return AVCS_ERR_INVALID_VAL;
}
if (def.nBufferSize == 0 || def.nBufferSize > MAX_HCODEC_BUFFER_SIZE) {
HLOGE("invalid nBufferSize %u", def.nBufferSize);
return AVCS_ERR_INVALID_VAL;
}
return AVCS_ERR_OK;
}
int32_t HCodec::AllocateAvLinearBuffers(OMX_DIRTYPE portIndex)
{
SCOPED_TRACE();
OMX_PARAM_PORTDEFINITIONTYPE def;
int32_t ret = GetPortDefinition(portIndex, def);
if (ret != AVCS_ERR_OK) {
return ret;
}
SupportBufferType type;
InitOMXParamExt(type);
type.portIndex = portIndex;
if (GetParameter(OMX_IndexParamSupportBufferType, type) && (type.bufferTypes & CODEC_BUFFER_TYPE_DMA_MEM_FD)) {
HLOGI("allocate hardware buffer");
return AllocateAvHardwareBuffers(portIndex, def);
} else {
HLOGI("allocate shared buffer");
return AllocateAvSharedBuffers(portIndex, def);
}
}
int32_t HCodec::AllocateAvHardwareBuffers(OMX_DIRTYPE portIndex, const OMX_PARAM_PORTDEFINITIONTYPE& def)
{
SCOPED_TRACE();
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
pool.clear();
for (uint32_t i = 0; i < def.nBufferCountActual; ++i) {
std::shared_ptr<OmxCodecBuffer> omxBuffer = std::make_shared<OmxCodecBuffer>();
omxBuffer->size = sizeof(OmxCodecBuffer);
omxBuffer->version.version.majorVersion = 1;
omxBuffer->bufferType = CODEC_BUFFER_TYPE_DMA_MEM_FD;
omxBuffer->fd = -1;
omxBuffer->allocLen = def.nBufferSize;
omxBuffer->fenceFd = -1;
shared_ptr<OmxCodecBuffer> outBuffer = make_shared<OmxCodecBuffer>();
int32_t ret = compNode_->AllocateBuffer(portIndex, *omxBuffer, *outBuffer);
if (ret != HDF_SUCCESS) {
HLOGE("Failed to AllocateBuffer on %s port", (portIndex == OMX_DirInput ? "input" : "output"));
return AVCS_ERR_INVALID_VAL;
}
MemoryFlag memFlag = MEMORY_READ_WRITE;
std::shared_ptr<AVAllocator> avAllocator = AVAllocatorFactory::CreateHardwareAllocator(
outBuffer->fd, static_cast<int32_t>(def.nBufferSize), memFlag, isSecure_);
IF_TRUE_RETURN_VAL_WITH_MSG(avAllocator == nullptr, AVCS_ERR_INVALID_VAL, "CreateHardwareAllocator failed");
std::shared_ptr<AVBuffer> avBuffer = AVBuffer::CreateAVBuffer(
avAllocator, static_cast<int32_t>(def.nBufferSize));
if (avBuffer == nullptr || avBuffer->memory_ == nullptr ||
avBuffer->memory_->GetCapacity() != static_cast<int32_t>(def.nBufferSize)) {
HLOGE("CreateAVBuffer failed");
return AVCS_ERR_NO_MEMORY;
}
SetCallerToBuffer(outBuffer->fd, def.format.video.nFrameWidth, def.format.video.nFrameHeight);
BufferInfo bufInfo((portIndex == OMX_DirInput), BufferOwner::OWNED_BY_US, record_);
bufInfo.surfaceBuffer = nullptr;
bufInfo.avBuffer = avBuffer;
bufInfo.omxBuffer = outBuffer;
bufInfo.bufferId = outBuffer->bufferId;
bufInfo.CleanUpUnusedInfo();
pool.push_back(bufInfo);
}
return AVCS_ERR_OK;
}
int32_t HCodec::AllocateAvSharedBuffers(OMX_DIRTYPE portIndex, const OMX_PARAM_PORTDEFINITIONTYPE& def)
{
SCOPED_TRACE();
MemoryFlag memFlag = MEMORY_READ_WRITE;
std::shared_ptr<AVAllocator> avAllocator = AVAllocatorFactory::CreateSharedAllocator(memFlag);
IF_TRUE_RETURN_VAL_WITH_MSG(avAllocator == nullptr, AVCS_ERR_INVALID_VAL, "CreateSharedAllocator failed");
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
pool.clear();
for (uint32_t i = 0; i < def.nBufferCountActual; ++i) {
std::shared_ptr<AVBuffer> avBuffer = AVBuffer::CreateAVBuffer(avAllocator,
static_cast<int32_t>(def.nBufferSize));
if (avBuffer == nullptr || avBuffer->memory_ == nullptr ||
avBuffer->memory_->GetCapacity() != static_cast<int32_t>(def.nBufferSize)) {
HLOGE("CreateAVBuffer failed");
return AVCS_ERR_NO_MEMORY;
}
std::shared_ptr<OmxCodecBuffer> omxBuffer = std::make_shared<OmxCodecBuffer>();
omxBuffer->size = sizeof(OmxCodecBuffer);
omxBuffer->version.version.majorVersion = 1;
omxBuffer->bufferType = CODEC_BUFFER_TYPE_AVSHARE_MEM_FD;
omxBuffer->fd = avBuffer->memory_->GetFileDescriptor();
omxBuffer->allocLen = def.nBufferSize;
omxBuffer->fenceFd = -1;
omxBuffer->type = (portIndex == OMX_DirInput) ? READ_ONLY_TYPE : READ_WRITE_TYPE;
shared_ptr<OmxCodecBuffer> outBuffer = make_shared<OmxCodecBuffer>();
int32_t ret = compNode_->UseBuffer(portIndex, *omxBuffer, *outBuffer);
if (ret != HDF_SUCCESS) {
HLOGE("Failed to UseBuffer on %s port", (portIndex == OMX_DirInput ? "input" : "output"));
return AVCS_ERR_INVALID_VAL;
}
BufferInfo bufInfo((portIndex == OMX_DirInput), BufferOwner::OWNED_BY_US, record_);
bufInfo.surfaceBuffer = nullptr;
bufInfo.avBuffer = avBuffer;
bufInfo.omxBuffer = outBuffer;
bufInfo.bufferId = outBuffer->bufferId;
pool.push_back(bufInfo);
}
return AVCS_ERR_OK;
}
int32_t HCodec::AllocateAvSurfaceBuffers(OMX_DIRTYPE portIndex)
{
SCOPED_TRACE();
OMX_PARAM_PORTDEFINITIONTYPE def;
int32_t ret = GetPortDefinition(portIndex, def);
if (ret != AVCS_ERR_OK) {
return ret;
}
std::shared_ptr<AVAllocator> avAllocator = AVAllocatorFactory::CreateSurfaceAllocator(requestCfg_);
IF_TRUE_RETURN_VAL_WITH_MSG(avAllocator == nullptr, AVCS_ERR_INVALID_VAL, "CreateSurfaceAllocator failed");
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
pool.clear();
for (uint32_t i = 0; i < def.nBufferCountActual; ++i) {
std::shared_ptr<AVBuffer> avBuffer = AVBuffer::CreateAVBuffer(avAllocator,
static_cast<int32_t>(def.nBufferSize));
if (avBuffer == nullptr || avBuffer->memory_ == nullptr) {
HLOGE("CreateAVBuffer failed");
return AVCS_ERR_NO_MEMORY;
}
sptr<SurfaceBuffer> surfaceBuffer = avBuffer->memory_->GetSurfaceBuffer();
IF_TRUE_RETURN_VAL_WITH_MSG(surfaceBuffer == nullptr, AVCS_ERR_INVALID_VAL, "avbuffer has null surfacebuffer");
shared_ptr<OmxCodecBuffer> omxBuffer = isEncoder_ ?
DynamicSurfaceBufferToOmxBuffer() : SurfaceBufferToOmxBuffer(surfaceBuffer);
IF_TRUE_RETURN_VAL(omxBuffer == nullptr, AVCS_ERR_INVALID_VAL);
shared_ptr<OmxCodecBuffer> outBuffer = make_shared<OmxCodecBuffer>();
int32_t hdiRet = compNode_->UseBuffer(portIndex, *omxBuffer, *outBuffer);
if (hdiRet != HDF_SUCCESS) {
HLOGE("Failed to UseBuffer on %s port", (portIndex == OMX_DirInput ? "input" : "output"));
return AVCS_ERR_INVALID_VAL;
}
SetCallerToBuffer(surfaceBuffer->GetFileDescriptor(),
static_cast<uint32_t>(surfaceBuffer->GetWidth()),
static_cast<uint32_t>(surfaceBuffer->GetHeight()));
BufferInfo bufInfo((portIndex == OMX_DirInput), BufferOwner::OWNED_BY_US, record_);
bufInfo.surfaceBuffer = surfaceBuffer;
bufInfo.avBuffer = avBuffer;
bufInfo.omxBuffer = outBuffer;
bufInfo.bufferId = outBuffer->bufferId;
bufInfo.needDealWithCache = true;
pool.push_back(bufInfo);
}
return AVCS_ERR_OK;
}
shared_ptr<OmxCodecBuffer> HCodec::SurfaceBufferToOmxBuffer(const sptr<SurfaceBuffer>& surfaceBuffer)
{
BufferHandle* bufferHandle = surfaceBuffer->GetBufferHandle();
IF_TRUE_RETURN_VAL_WITH_MSG(bufferHandle == nullptr, nullptr, "surfacebuffer has null bufferhandle");
auto omxBuffer = std::make_shared<OmxCodecBuffer>();
omxBuffer->size = sizeof(OmxCodecBuffer);
omxBuffer->version.version.majorVersion = 1;
omxBuffer->bufferType = CODEC_BUFFER_TYPE_HANDLE;
omxBuffer->bufferhandle = new NativeBuffer(bufferHandle);
omxBuffer->fd = -1;
omxBuffer->allocLen = surfaceBuffer->GetSize();
omxBuffer->fenceFd = -1;
return omxBuffer;
}
shared_ptr<OmxCodecBuffer> HCodec::DynamicSurfaceBufferToOmxBuffer()
{
auto omxBuffer = make_shared<OmxCodecBuffer>();
omxBuffer->size = sizeof(OmxCodecBuffer);
omxBuffer->version.version.majorVersion = 1;
omxBuffer->bufferType = CODEC_BUFFER_TYPE_DYNAMIC_HANDLE;
omxBuffer->fd = -1;
omxBuffer->allocLen = 0;
omxBuffer->fenceFd = -1;
return omxBuffer;
}
const char* HCodec::ToString(BufferOwner owner)
{
switch (owner) {
case BufferOwner::OWNED_BY_US:
return "us";
case BufferOwner::OWNED_BY_USER:
return "user";
case BufferOwner::OWNED_BY_OMX:
return "omx";
case BufferOwner::OWNED_BY_SURFACE:
return "surface";
default:
return "";
}
}
void HCodec::BufferInfo::CleanUpUnusedInfo()
{
if (omxBuffer == nullptr || omxBuffer->fd < 0) {
return;
}
if (omxBuffer->fd == 0) {
LOGW("fd of omxbuffer should never be 0");
}
close(omxBuffer->fd);
omxBuffer->fd = -1;
}
void HCodec::BufferInfo::BeginCpuAccess()
{
if (surfaceBuffer && needDealWithCache) {
GSError err = surfaceBuffer->InvalidateCache();
if (err != GSERROR_OK) {
LOGW("InvalidateCache failed, GSError=%d", err);
}
}
}
void HCodec::BufferInfo::EndCpuAccess()
{
if (surfaceBuffer && needDealWithCache) {
GSError err = surfaceBuffer->Map();
if (err != GSERROR_OK) {
LOGW("Map failed, GSError=%d", err);
return;
}
err = surfaceBuffer->FlushCache();
if (err != GSERROR_OK) {
LOGW("FlushCache failed, GSError=%d", err);
}
}
}
HCodec::BufferInfo* HCodec::FindBufferInfoByID(OMX_DIRTYPE portIndex, uint32_t bufferId)
{
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (BufferInfo &info : pool) {
if (info.bufferId == bufferId) {
return &info;
}
}
HLOGE("unknown buffer id %u", bufferId);
return nullptr;
}
optional<size_t> HCodec::FindBufferIndexByID(OMX_DIRTYPE portIndex, uint32_t bufferId)
{
const vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (size_t i = 0; i < pool.size(); i++) {
if (pool[i].bufferId == bufferId) {
return i;
}
}
HLOGE("unknown buffer id %u", bufferId);
return nullopt;
}
uint32_t HCodec::UserFlagToOmxFlag(AVCodecBufferFlag userFlag)
{
uint32_t flags = 0;
if (userFlag & AVCODEC_BUFFER_FLAG_EOS) {
flags |= OMX_BUFFERFLAG_EOS;
HLOGI("got input eos");
}
if (userFlag & AVCODEC_BUFFER_FLAG_SYNC_FRAME) {
flags |= OMX_BUFFERFLAG_SYNCFRAME;
}
if (userFlag & AVCODEC_BUFFER_FLAG_CODEC_DATA) {
flags |= OMX_BUFFERFLAG_CODECCONFIG;
}
if (isLpp_ && (userFlag & AVCODEC_BUFFER_FLAG_MUL_FRAME)) {
flags |= OMX_BUFFERFLAG_MULTI_FRAME;
}
return flags;
}
AVCodecBufferFlag HCodec::OmxFlagToUserFlag(uint32_t omxFlag)
{
uint32_t flags = 0;
if (omxFlag & OMX_BUFFERFLAG_EOS) {
flags |= AVCODEC_BUFFER_FLAG_EOS;
HLOGI("got output eos");
}
if (omxFlag & OMX_BUFFERFLAG_SYNCFRAME) {
flags |= AVCODEC_BUFFER_FLAG_SYNC_FRAME;
}
if (omxFlag & OMX_BUFFERFLAG_CODECCONFIG) {
flags |= AVCODEC_BUFFER_FLAG_CODEC_DATA;
}
return static_cast<AVCodecBufferFlag>(flags);
}
bool HCodec::WaitFence(const sptr<SyncFence>& fence, uint32_t seq, const std::string& prefix, uint64_t& costUs)
{
if (fence == nullptr || !fence->IsValid()) {
return true;
}
HITRACE_METER_FMT(HITRACE_TAG_ZMEDIA, "[hcodec][%s]WaitFence for seq=%u", prefix.c_str(), seq);
auto before = chrono::steady_clock::now();
int waitRes = fence->Wait(WAIT_FENCE_MS);
if (waitRes == 0) {
costUs = static_cast<uint64_t>(
chrono::duration_cast<chrono::microseconds>(chrono::steady_clock::now() - before).count());
return true;
} else {
LOGW("wait fence time out, cost more than %u ms", WAIT_FENCE_MS);
costUs = WAIT_FENCE_MS * TIME_RATIO_S_TO_MS;
return false;
}
}
bool HCodec::WaitFence(const sptr<SyncFence>& fence, uint32_t seq)
{
uint64_t costUs = 0;
bool ret = WaitFence(fence, seq, compUniqueStr_, costUs);
uint64_t& waitFenceCostUs = isEncoder_ ?
record_[OMX_DirInput].waitFenceCostUsInterval_ : record_[OMX_DirOutput].waitFenceCostUsInterval_;
waitFenceCostUs += costUs;
return ret;
}
void HCodec::NotifyUserToFillThisInBuffer(BufferInfo &info)
{
SCOPED_TRACE_FMT("id: %u", info.bufferId);
callback_->OnInputBufferAvailable(info.bufferId, info.avBuffer);
ChangeOwner(info, BufferOwner::OWNED_BY_USER);
}
void HCodec::OnQueueInputBuffer(const MsgInfo &msg, BufferOperationMode mode)
{
uint32_t bufferId = 0;
(void)msg.param->GetValue(BUFFER_ID, bufferId);
SCOPED_TRACE_FMT("id: %u", bufferId);
BufferInfo* bufferInfo = FindBufferInfoByID(OMX_DirInput, bufferId);
if (bufferInfo == nullptr) {
ReplyErrorCode(msg.id, AVCS_ERR_INVALID_VAL);
return;
}
if (bufferInfo->owner != BufferOwner::OWNED_BY_USER) {
HLOGE("wrong ownership: buffer id=%d, owner=%s", bufferId, ToString(bufferInfo->owner));
ReplyErrorCode(msg.id, AVCS_ERR_INVALID_VAL);
return;
}
bufferInfo->omxBuffer->filledLen = static_cast<uint32_t>
(bufferInfo->avBuffer->memory_->GetSize());
bufferInfo->omxBuffer->offset = static_cast<uint32_t>(bufferInfo->avBuffer->memory_->GetOffset());
bufferInfo->omxBuffer->pts = bufferInfo->avBuffer->pts_;
bufferInfo->omxBuffer->flag = UserFlagToOmxFlag(static_cast<AVCodecBufferFlag>(bufferInfo->avBuffer->flag_));
RecordProcessTimeOfUpstream(bufferInfo->avBuffer);
ChangeOwner(*bufferInfo, BufferOwner::OWNED_BY_US);
ReplyErrorCode(msg.id, AVCS_ERR_OK);
int32_t ret = OnQueueInputBuffer(mode, bufferInfo);
if (ret != AVCS_ERR_OK) {
SignalError(AVCODEC_ERROR_INTERNAL, AVCS_ERR_UNKNOWN);
}
}
int32_t HCodec::OnQueueInputBuffer(BufferOperationMode mode, BufferInfo* info)
{
switch (mode) {
case KEEP_BUFFER: {
return AVCS_ERR_OK;
}
case RESUBMIT_BUFFER: {
if (inputPortEos_) {
HLOGI("input already eos, keep this buffer");
return AVCS_ERR_OK;
}
bool eos = (info->omxBuffer->flag & OMX_BUFFERFLAG_EOS);
if (!eos && info->omxBuffer->filledLen == 0) {
HLOGI("this is not a eos buffer but not filled, ask user to re-fill it");
NotifyUserToFillThisInBuffer(*info);
return AVCS_ERR_OK;
}
if (eos) {
inputPortEos_ = true;
}
return InBufUsToOmx(*info);
}
default: {
HLOGE("SHOULD NEVER BE HERE");
return AVCS_ERR_UNKNOWN;
}
}
}
void HCodec::WrapSurfaceBufferToSlot(BufferInfo &info,
const sptr<SurfaceBuffer>& surfaceBuffer, int64_t pts, uint32_t flag)
{
info.surfaceBuffer = surfaceBuffer;
info.omxBuffer->bufferhandle = new NativeBuffer(surfaceBuffer->GetBufferHandle());
info.omxBuffer->filledLen = surfaceBuffer->GetSize();
info.omxBuffer->fd = -1;
info.omxBuffer->fenceFd = -1;
info.omxBuffer->pts = pts;
info.omxBuffer->flag = flag;
}
void HCodec::OnSignalEndOfInputStream(const MsgInfo &msg)
{
ReplyErrorCode(msg.id, AVCS_ERR_UNSUPPORT);
}
int32_t HCodec::InBufUsToOmx(BufferInfo& info)
{
SCOPED_TRACE_FMT("id: %u, pts: %" PRId64, info.bufferId, info.omxBuffer->pts);
OnFrameSubmitted(info.omxBuffer->pts);
if (!gotFirstInput_) {
HLOGI("got first input, pts = %" PRId64 ", len = %u, flags = 0x%x",
info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag);
gotFirstInput_ = true;
}
#ifdef BUILD_ENG_VERSION
info.Dump(compUniqueStrWithMime_, record_[OMX_DirInput].frameCntTotal_, dumpMode_, isEncoder_);
#endif
info.EndCpuAccess();
int32_t ret = compNode_->EmptyThisBuffer(*(info.omxBuffer));
if (ret != HDF_SUCCESS) {
HLOGE("EmptyThisBuffer failed");
return AVCS_ERR_UNKNOWN;
}
ChangeOwner(info, BufferOwner::OWNED_BY_OMX);
return AVCS_ERR_OK;
}
int32_t HCodec::NotifyOmxToFillThisOutBuffer(BufferInfo& info)
{
SCOPED_TRACE_FMT("id: %u", info.bufferId);
info.omxBuffer->flag = 0;
int32_t ret = compNode_->FillThisBuffer(*(info.omxBuffer));
if (ret != HDF_SUCCESS) {
HLOGE("outBufId = %u failed", info.bufferId);
return AVCS_ERR_UNKNOWN;
}
ChangeOwner(info, BufferOwner::OWNED_BY_OMX);
return AVCS_ERR_OK;
}
void HCodec::OnOMXFillBufferDone(const OmxCodecBuffer& omxBuffer, BufferOperationMode mode)
{
SCOPED_TRACE_FMT("id: %u", omxBuffer.bufferId);
optional<size_t> idx = FindBufferIndexByID(OMX_DirOutput, omxBuffer.bufferId);
if (!idx.has_value()) {
return;
}
BufferInfo& info = outputBufferPool_[idx.value()];
if (info.owner != BufferOwner::OWNED_BY_OMX) {
HLOGE("wrong ownership: buffer id=%d, owner=%s", info.bufferId, ToString(info.owner));
return;
}
info.omxBuffer->offset = omxBuffer.offset;
info.omxBuffer->filledLen = omxBuffer.filledLen;
info.omxBuffer->pts = omxBuffer.pts;
info.omxBuffer->flag = omxBuffer.flag;
info.omxBuffer->alongParam = std::move(omxBuffer.alongParam);
ChangeOwner(info, BufferOwner::OWNED_BY_US);
OnOMXFillBufferDone(mode, info, idx.value());
}
void HCodec::OnOMXFillBufferDone(BufferOperationMode mode, BufferInfo& info, size_t bufferIdx)
{
switch (mode) {
case KEEP_BUFFER:
return;
case RESUBMIT_BUFFER: {
if (outputPortEos_) {
HLOGD("output eos, keep this buffer");
return;
}
bool eos = (info.omxBuffer->flag & OMX_BUFFERFLAG_EOS);
if (!eos && info.omxBuffer->filledLen == 0) {
HLOGW("it's not a eos buffer but not filled, ask omx to re-fill it");
NotifyOmxToFillThisOutBuffer(info);
return;
}
#ifdef USE_VIDEO_PROCESSING_ENGINE
if (!isEncoder_ && isVrrInitialized_) {
(void)VrrPrediction(info);
}
#endif
OutBufUsToUser(info);
if (eos) {
outputPortEos_ = true;
}
return;
}
case FREE_BUFFER:
EraseBufferFromPool(OMX_DirOutput, bufferIdx);
return;
default:
HLOGE("SHOULD NEVER BE HERE");
return;
}
}
void HCodec::OutBufUsToUser(BufferInfo &info)
{
SCOPED_TRACE_FMT("id: %u, pts: %" PRId64, info.bufferId, info.omxBuffer->pts);
if (!gotFirstOutput_) {
HLOGI("got first output, pts = %" PRId64 ", len = %u, flags = 0x%x",
info.omxBuffer->pts, info.omxBuffer->filledLen, info.omxBuffer->flag);
gotFirstOutput_ = true;
}
#ifndef AV_CODEC_HCODEC_ENABLE_QOS_THE_WHOLE_TIME
if (!isEncoder_ || codecRate_ < HIGH_FPS) {
SetThreadInteractiveQos(false);
}
#endif
info.BeginCpuAccess();
#ifdef BUILD_ENG_VERSION
info.Dump(compUniqueStrWithMime_, record_[OMX_DirOutput].frameCntTotal_, dumpMode_, isEncoder_);
#endif
shared_ptr<OmxCodecBuffer> omxBuffer = info.omxBuffer;
info.avBuffer->pts_ = omxBuffer->pts;
info.avBuffer->flag_ = OmxFlagToUserFlag(omxBuffer->flag);
if (info.avBuffer->memory_) {
info.avBuffer->memory_->SetSize(static_cast<int32_t>(omxBuffer->filledLen));
info.avBuffer->memory_->SetOffset(static_cast<int32_t>(omxBuffer->offset));
}
BeforeCbOutToUser(info);
TimePoint now = ChangeOwner(info, BufferOwner::OWNED_BY_USER);
AppendProcessTimeOfUs(info.avBuffer, omxBuffer->pts, now);
callback_->OnOutputBufferAvailable(info.bufferId, info.avBuffer);
}
void HCodec::OnReleaseOutputBuffer(const MsgInfo &msg, BufferOperationMode mode)
{
uint32_t bufferId = 0;
(void)msg.param->GetValue(BUFFER_ID, bufferId);
SCOPED_TRACE_FMT("id: %u", bufferId);
optional<size_t> idx = FindBufferIndexByID(OMX_DirOutput, bufferId);
if (!idx.has_value()) {
ReplyErrorCode(msg.id, AVCS_ERR_INVALID_VAL);
return;
}
BufferInfo& info = outputBufferPool_[idx.value()];
if (info.owner != BufferOwner::OWNED_BY_USER) {
HLOGE("wrong ownership: buffer id=%d, owner=%s", bufferId, ToString(info.owner));
ReplyErrorCode(msg.id, AVCS_ERR_INVALID_VAL);
return;
}
OnReleaseOutputBuffer(info);
ChangeOwner(info, BufferOwner::OWNED_BY_US);
ReplyErrorCode(msg.id, AVCS_ERR_OK);
OnReleaseOutputBuffer(bufferId, mode);
}
void HCodec::OnReleaseOutputBuffer(uint32_t bufferId, BufferOperationMode mode)
{
optional<size_t> idx = FindBufferIndexByID(OMX_DirOutput, bufferId);
if (!idx.has_value()) {
return;
}
BufferInfo& info = outputBufferPool_[idx.value()];
switch (mode) {
case KEEP_BUFFER: {
return;
}
case RESUBMIT_BUFFER: {
if (outputPortEos_) {
HLOGD("output eos, keep this buffer");
return;
}
int32_t ret = NotifyOmxToFillThisOutBuffer(info);
if (ret != AVCS_ERR_OK) {
SignalError(AVCODEC_ERROR_INTERNAL, AVCS_ERR_UNKNOWN);
}
return;
}
case FREE_BUFFER: {
EraseBufferFromPool(OMX_DirOutput, idx.value());
return;
}
default: {
HLOGE("SHOULD NEVER BE HERE");
return;
}
}
}
void HCodec::OnRenderOutputBuffer(const MsgInfo &msg, BufferOperationMode mode)
{
ReplyErrorCode(msg.id, AVCS_ERR_UNSUPPORT);
}
void HCodec::ReclaimBuffer(OMX_DIRTYPE portIndex, BufferOwner owner, bool erase)
{
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (size_t i = pool.size(); i > 0;) {
i--;
BufferInfo& info = pool[i];
if (info.owner == owner) {
ChangeOwner(info, BufferOwner::OWNED_BY_US);
if (erase) {
EraseBufferFromPool(portIndex, i);
}
}
}
}
bool HCodec::ThereAreBufsStillOwnedByOmx(OMX_DIRTYPE portIndex)
{
vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
return std::any_of(pool.begin(), pool.end(), [](const BufferInfo& info) {
return info.owner == BufferOwner::OWNED_BY_OMX;
});
}
bool HCodec::IsAllBufferOwnedByUsOrSurface(OMX_DIRTYPE portIndex)
{
const vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (const BufferInfo& info : pool) {
if (info.owner != BufferOwner::OWNED_BY_US &&
info.owner != BufferOwner::OWNED_BY_SURFACE) {
return false;
}
}
return true;
}
bool HCodec::IsAllBufferOwnedByUsOrSurface()
{
return IsAllBufferOwnedByUsOrSurface(OMX_DirInput) &&
IsAllBufferOwnedByUsOrSurface(OMX_DirOutput);
}
void HCodec::ClearBufferPool(OMX_DIRTYPE portIndex)
{
const vector<BufferInfo>& pool = (portIndex == OMX_DirInput) ? inputBufferPool_ : outputBufferPool_;
for (size_t i = pool.size(); i > 0;) {
i--;
EraseBufferFromPool(portIndex, i);
}
OnClearBufferPool(portIndex);
}
void HCodec::FreeOmxBuffer(OMX_DIRTYPE portIndex, const BufferInfo& info)
{
if (compNode_ && info.omxBuffer) {
int32_t omxRet = compNode_->FreeBuffer(portIndex, *(info.omxBuffer));
if (omxRet != HDF_SUCCESS) {
HLOGW("notify omx to free buffer failed");
}
}
}
void HCodec::EraseOutBuffersOwnedByUsOrSurface()
{
for (size_t i = outputBufferPool_.size(); i > 0;) {
i--;
const BufferInfo& info = outputBufferPool_[i];
if (info.owner == BufferOwner::OWNED_BY_US || info.owner == BufferOwner::OWNED_BY_SURFACE) {
EraseBufferFromPool(OMX_DirOutput, i);
}
}
}
void HCodec::OnSetOutputSurface(const MsgInfo &msg, BufferOperationMode mode)
{
(void)msg;
(void)mode;
ReplyErrorCode(msg.id, AVCS_ERR_UNSUPPORT);
}
int32_t HCodec::ForceShutdown(int32_t generation, bool isNeedNotifyCaller)
{
if (generation != stateGeneration_) {
HLOGE("ignoring stale force shutdown message: #%d (now #%d)",
generation, stateGeneration_);
return AVCS_ERR_OK;
}
HLOGI("force to shutdown");
isShutDownFromRunning_ = true;
notifyCallerAfterShutdownComplete_ = isNeedNotifyCaller;
keepComponentAllocated_ = false;
auto err = compNode_->SendCommand(CODEC_COMMAND_STATE_SET, CODEC_STATE_IDLE, {});
if (err == HDF_SUCCESS) {
ChangeStateTo(stoppingState_);
}
return AVCS_ERR_OK;
}
void HCodec::SignalError(AVCodecErrorType errorType, int32_t errorCode)
{
HLOGE("fatal error happened: errType=%d, errCode=%d", errorType, errorCode);
hasFatalError_ = true;
callback_->OnError(errorType, errorCode);
}
int32_t HCodec::DoSyncCall(MsgWhat msgType, std::function<void(ParamSP)> oper, uint32_t waitMs)
{
ParamSP reply;
return DoSyncCallAndGetReply(msgType, oper, reply, waitMs);
}
int32_t HCodec::DoSyncCallAndGetReply(MsgWhat msgType, std::function<void(ParamSP)> oper,
ParamSP &reply, uint32_t waitMs)
{
ParamSP msg = make_shared<ParamBundle>();
IF_TRUE_RETURN_VAL_WITH_MSG(msg == nullptr, AVCS_ERR_NO_MEMORY, "out of memory");
if (oper) {
oper(msg);
}
bool ret = MsgHandleLoop::SendSyncMsg(msgType, msg, reply, waitMs);
if (!ret) {
HLOGE("wait msg %d(%s) time out", msgType, ToString(msgType));
return AVCS_ERR_UNKNOWN;
}
int32_t err;
IF_TRUE_RETURN_VAL_WITH_MSG(reply == nullptr || !reply->GetValue("err", err),
AVCS_ERR_UNKNOWN, "error code of msg %d not replied", msgType);
return err;
}
void HCodec::DeferMessage(const MsgInfo &info)
{
deferredQueue_.push_back(info);
}
void HCodec::ProcessDeferredMessages()
{
std::list<MsgInfo> queue = std::exchange(deferredQueue_, {});
for (const MsgInfo &info : queue) {
StateMachine::OnMsgReceived(info);
}
queue.clear();
}
void HCodec::ReplyToSyncMsgLater(const MsgInfo& msg)
{
syncMsgToReply_[msg.type].push(std::make_pair(msg.id, msg.param));
}
bool HCodec::GetFirstSyncMsgToReply(MsgInfo& msg)
{
auto iter = syncMsgToReply_.find(msg.type);
if (iter == syncMsgToReply_.end()) {
return false;
}
if (iter->second.empty()) {
return false;
}
std::tie(msg.id, msg.param) = iter->second.front();
iter->second.pop();
if (iter->second.empty()) {
syncMsgToReply_.erase(iter);
}
return true;
}
void HCodec::ReplyErrorCode(MsgId id, int32_t err)
{
if (id == ASYNC_MSG_ID) {
return;
}
ParamSP reply = make_shared<ParamBundle>();
reply->SetValue("err", err);
PostReply(id, reply);
}
void HCodec::ChangeOmxToTargetState(CodecStateType &state, CodecStateType targetState)
{
int32_t ret = compNode_->SendCommand(CODEC_COMMAND_STATE_SET, targetState, {});
if (ret != HDF_SUCCESS) {
HLOGE("failed to change omx state, ret=%d", ret);
return;
}
if (targetState == CODEC_STATE_LOADED) {
for (const BufferInfo& info : inputBufferPool_) {
FreeOmxBuffer(OMX_DirInput, info);
}
for (const BufferInfo& info : outputBufferPool_) {
FreeOmxBuffer(OMX_DirOutput, info);
}
}
int tryCnt = 0;
do {
if (tryCnt++ > 10) {
HLOGE("failed to change to state(%d), abort", targetState);
state = CODEC_STATE_INVALID;
break;
}
this_thread::sleep_for(10ms);
ret = compNode_->GetState(state);
if (ret != HDF_SUCCESS) {
HLOGE("failed to get omx state, ret=%d", ret);
}
} while (ret == HDF_SUCCESS && state != targetState && state != CODEC_STATE_INVALID);
}
bool HCodec::RollOmxBackToLoaded()
{
CodecStateType state;
int32_t ret = compNode_->GetState(state);
if (ret != HDF_SUCCESS) {
HLOGE("failed to get omx node status(ret=%d), can not perform state rollback", ret);
return false;
}
HLOGI("current omx state (%d)", state);
switch (state) {
case CODEC_STATE_EXECUTING: {
ChangeOmxToTargetState(state, CODEC_STATE_IDLE);
[[fallthrough]];
}
case CODEC_STATE_IDLE: {
ChangeOmxToTargetState(state, CODEC_STATE_LOADED);
[[fallthrough]];
}
case CODEC_STATE_LOADED:
case CODEC_STATE_INVALID: {
return true;
}
default: {
HLOGE("invalid omx state: %d", state);
return false;
}
}
}
void HCodec::CleanUpOmxNode()
{
if (compNode_ == nullptr) {
return;
}
RollOmxBackToLoaded();
}
int32_t HCodec::OnAllocateComponent()
{
HitraceMeterFmtScoped trace(HITRACE_TAG_ZMEDIA, "hcodec %s %s", __func__, caps_.compName.c_str());
compMgr_ = GetManager(false,
HCodecList::FindFeature(caps_.port.video.features, VIDEO_FEATURE_PASS_THROUGH).support,
isSecure_);
if (compMgr_ == nullptr) {
HLOGE("GetCodecComponentManager failed");
return AVCS_ERR_UNKNOWN;
}
maxCodecInst_ = isEncoder_ ? GetMaxEncInstCnt() : GetMaxDecInstCnt();
compCb_ = new HdiCallback(m_token);
int32_t ret = compMgr_->CreateComponent(compNode_, componentId_, caps_.compName, 0, compCb_);
if (ret != HDF_SUCCESS || compNode_ == nullptr) {
compCb_ = nullptr;
compMgr_ = nullptr;
HLOGE("CreateComponent failed, ret=%d", ret);
PrintAllCaller();
std::unique_lock<std::shared_mutex> lk(g_mtx);
auto& callers = g_callers[isEncoder_];
size_t totalInstCntNow = CalculateTotalInstCnt();
size_t singleAppInstCntNow = callers.count(caller_.app) > 0 ? callers[caller_.app].size() : 0;
if ((totalInstCntNow >= totalWarnInstCnt_) || (singleAppInstCntNow >= singleAppWarnInstCnt_)) {
ReportToRss();
}
return ret == OMX_ErrorInsufficientResources ? AVCS_ERR_INSUFFICIENT_HARDWARE_RESOURCES : AVCS_ERR_UNKNOWN;
}
totalWarnInstCnt_ = maxCodecInst_ * 0.6;
singleAppWarnInstCnt_ = maxCodecInst_ * 0.5;
compUniqueStr_ = to_string(componentId_) + (isEncoder_ ? "_e" : "_d");
compUniqueStrWithMime_ = compUniqueStr_ + "_" + mime_;
record_[OMX_DirInput].ownerTraceTag_ = { "[" + compUniqueStr_ + "]in_us", "[" + compUniqueStr_ + "]in_user",
"[" + compUniqueStr_ + "]in_omx", "[" + compUniqueStr_ + "]in_surface"};
record_[OMX_DirOutput].ownerTraceTag_ = { "[" + compUniqueStr_ + "]out_us", "[" + compUniqueStr_ + "]out_user",
"[" + compUniqueStr_ + "]out_omx", "[" + compUniqueStr_ + "]out_surface"};
HLOGI("create omx node %s succ", caps_.compName.c_str());
PrintCaller();
return AVCS_ERR_OK;
}
void HCodec::ReleaseComponent()
{
CleanUpOmxNode();
if (compMgr_ != nullptr) {
RemoveCaller();
compMgr_->DestroyComponent(componentId_);
}
compNode_ = nullptr;
compCb_ = nullptr;
compMgr_ = nullptr;
componentId_ = 0;
}
int32_t HCodec::SetColorAspects(const Format &format)
{
int range = 0;
int primary = static_cast<int>(COLOR_PRIMARY_UNSPECIFIED);
int transfer = static_cast<int>(TRANSFER_CHARACTERISTIC_UNSPECIFIED);
int matrix = static_cast<int>(MATRIX_COEFFICIENT_UNSPECIFIED);
if (format.GetIntValue(MediaDescriptionKey::MD_KEY_RANGE_FLAG, range)) {
HLOGI("user set range flag %d", range);
}
if (format.GetIntValue(MediaDescriptionKey::MD_KEY_COLOR_PRIMARIES, primary)) {
HLOGI("user set primary %d", primary);
}
if (format.GetIntValue(MediaDescriptionKey::MD_KEY_TRANSFER_CHARACTERISTICS, transfer)) {
HLOGI("user set transfer %d", transfer);
}
if (format.GetIntValue(MediaDescriptionKey::MD_KEY_MATRIX_COEFFICIENTS, matrix)) {
HLOGI("user set matrix %d", matrix);
}
if (primary < 0 || primary > UINT8_MAX ||
transfer < 0 || transfer > UINT8_MAX ||
matrix < 0 || matrix > UINT8_MAX) {
HLOGW("invalid color");
return AVCS_ERR_INVALID_VAL;
}
CodecVideoColorspace param;
InitOMXParamExt(param);
param.portIndex = isEncoder_ ? OMX_DirInput : OMX_DirOutput;
param.aspects.range = static_cast<bool>(range);
param.aspects.primaries = static_cast<uint8_t>(primary);
param.aspects.transfer = static_cast<uint8_t>(transfer);
param.aspects.matrixCoeffs = static_cast<uint8_t>(matrix);
if (!SetParameter(OMX_IndexColorAspects, param)) {
HLOGE("failed to set CodecVideoColorSpace");
return AVCS_ERR_UNKNOWN;
}
HLOGI("set color aspects (isFullRange %d, primary %u, transfer %u, matrix %u) succ",
param.aspects.range, param.aspects.primaries,
param.aspects.transfer, param.aspects.matrixCoeffs);
return AVCS_ERR_OK;
}
}