Files
BPI-CM9-Linux/aml-comp/vendor/amlogic/meson_display/common/videotunnel/VtInstance.cpp
T
2026-08-24 10:03:24 +08:00

511 lines
18 KiB
C++

/*
* Copyright (c) 2021 Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
* Description:
*/
#define LOG_NDEBUG 1
#include <cinttypes>
#include "VtInstance.h"
#include "VtAllocSolidColorBuffer.h"
#ifndef LINUX_COMPILE
#include <HwcConfig.h>
#endif
#define VTINST_LOGV(fmt,...) MESON_LOGV("[%s] [%s] " fmt, mName, __func__, ##__VA_ARGS__)
#define VTINST_LOGD(fmt,...) MESON_LOGD("[%s] [%s] " fmt, mName, __func__, ##__VA_ARGS__)
#define VTINST_LOGW(fmt,...) MESON_LOGW("[%s] [%s] " fmt, mName, __func__, ##__VA_ARGS__)
#define VTINST_LOGE(fmt,...) MESON_LOGE("[%s] [%s] " fmt, mName, __func__, ##__VA_ARGS__)
VtInstance::VtInstance(int tunnelId)
: mTunnelId(tunnelId),
mVideoTunnelDev(VideoTunnelDev::getInstance()),
mSolidBufferAllocator(VtAllocSolidColorBuffer::getInstance()),
mOwnsDefaultDependencies(false) {
mConsumers.clear();
mRefreshConsumers.clear();
mBufferQueue.clear();
memset(&mCrop, 0, sizeof(vt_rect_t));
snprintf(mName, 64, "VtInstance-%d", tunnelId);
memset(&mLastData, 0, sizeof(last_data_t));
mLastData.videoStatus = VT_VIDEO_STATUS_SHOW;
mLastData.solidBufferType = SOLID_COLOR_FOR_INVALID;
}
// New constructor with dependency injection (for testing)
VtInstance::VtInstance(int tunnelId,
VideoTunnelDev& videoTunnelDev,
ISolidColorBufferAllocator& solidBufferAllocator)
: mTunnelId(tunnelId),
mVideoTunnelDev(videoTunnelDev),
mSolidBufferAllocator(solidBufferAllocator),
mOwnsDefaultDependencies(false) {
mConsumers.clear();
mBufferQueue.clear();
memset(&mCrop, 0, sizeof(vt_rect_t));
snprintf(mName, 64, "VtInstance-%d", tunnelId);
memset(&mLastData, 0, sizeof(last_data_t));
mLastData.videoStatus = VT_VIDEO_STATUS_SHOW;
mLastData.solidBufferType = SOLID_COLOR_FOR_INVALID;
}
VtInstance::~VtInstance() {
auto it = mConsumers.begin();
for (; it != mConsumers.end(); it++) {
if ((*it).get())
(*it)->setReleaseListener(nullptr);
}
mConsumers.clear();
mRefreshConsumers.clear();
releaseInstanceBuffers();
disconnect();
}
int32_t VtInstance::onFrameDisplayed(std::shared_ptr<VtBufferItem> & item) {
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<VtBufferItem> ptrBufferItem;
if (mBufferQueue.empty()) {
VTINST_LOGV("buffer queue is empty (%s)", item->toString().c_str());
item->closeFd();
return -1;
}
for (auto it = mBufferQueue.begin(); it != mBufferQueue.end();) {
ptrBufferItem = *it;
if (ptrBufferItem.get() != item.get()) {
++it;
continue;
}
if (ptrBufferItem->unrefHandle()) {
it = mBufferQueue.erase(it);
releaseBufferLocked(ptrBufferItem);
}
return 0;
}
if (!item->mIsSolidBuffer)
VTINST_LOGE("cannot find (%s) in buffer queue", item->toString().c_str());
item->closeFd();
return -1;
}
int32_t VtInstance::handleRefresh() {
int32_t ret = 0;
enum vt_cmd cmd;
struct vt_cmd_data cmdData;
if (mRefreshConsumers.empty())
return ret;
for (auto it = mRefreshConsumers.begin(); it != mRefreshConsumers.end(); it++) {
if (mLastData.gameMode == 1) {
memset(&cmdData, 0, sizeof(cmdData));
cmd = VT_CMD_SET_GAME_MODE;
cmdData.data = mLastData.gameMode;
ret = (*it)->onVtCmds(cmd, cmdData);
}
if (mLastData.transform != 0) {
memset(&cmdData, 0, sizeof(cmdData));
cmd = VT_CMD_SET_VIDEO_TRANSFORM;
cmdData.transform = mLastData.transform;
ret = (*it)->onVtCmds(cmd, cmdData);
}
if (vt_rect_is_valid(&mLastData.displayFrame)) {
memset(&cmdData, 0, sizeof(cmdData));
cmd = VT_CMD_SET_DISPLAY_FRAME;
cmdData.rect = mLastData.displayFrame;
ret = (*it)->onVtCmds(cmd, cmdData);
}
if (mLastData.solidBufferType != SOLID_COLOR_FOR_INVALID &&
(mLastData.videoStatus == VT_VIDEO_STATUS_COLOR_ONCE ||
mLastData.videoStatus == VT_VIDEO_STATUS_COLOR_ALWAYS ||
mLastData.videoStatus == VT_VIDEO_STATUS_COLOR_ALWAYS_FOR_VRR)) {
VTINST_LOGD("status:%s, solidBufferType:%s",
vtVideoStatusToString(mLastData.videoStatus),
vtVideoColorTypeToString(mLastData.solidBufferType));
struct uvm_fd_info info;
memset(&info, 0, sizeof(info));
mSolidBufferAllocator.getBufferInfo(mLastData.solidBufferType, info);
if (info.fd >= 0) {
std::vector<std::shared_ptr<VtBufferItem>> items;
info.fd = dup(info.fd);
std::shared_ptr<VtBufferItem> item =
std::make_shared<VtBufferItem>(info.fd, info.timestamp, info, true);
items.push_back(item);
ret = sendBufferToConsumer(mRefreshConsumers, items);
}
}
}
/* refresh buffers*/
if (!mBufferQueue.empty() &&
mLastData.videoStatus == VT_VIDEO_STATUS_SHOW &&
mLastData.solidBufferType == SOLID_COLOR_FOR_INVALID) {
std::vector<std::shared_ptr<VtBufferItem>> items;
std::shared_ptr<VtBufferItem> lastItem = mBufferQueue.back();
if (lastItem.get()) {
items.push_back(lastItem);
ret = sendBufferToConsumer(mRefreshConsumers, items);
}
}
mRefreshConsumers.clear();
return ret;
}
int32_t VtInstance::registerVtConsumer(
std::shared_ptr<VtConsumer>& consumer, bool refresh) {
/* only Hwc2Layer will call this api when have
* an videotunnel layer create
*/
if (!consumer || !consumer.get()) {
VTINST_LOGE("consumer is null");
return -1;
}
consumer->setReleaseListener(this);
mConsumers.push_back(consumer);
if (refresh)
mRefreshConsumers.push_back(consumer);
return 0;
}
int32_t VtInstance::unregisterVtConsumer(
std::shared_ptr<VtConsumer>& consumer) {
/* only Hwc2Layer will call this api when videotunnel layer destroy.
* and Hwc2Layer will release all buffer Fd before call this api
*/
int32_t ret = -1;
if (!consumer.get()) {
VTINST_LOGE("consumer is null");
return ret;
}
for (auto it = mRefreshConsumers.begin(); it != mRefreshConsumers.end(); ) {
std::shared_ptr<VtConsumer> item = (*it);
if (item.get() == consumer.get()) {
it = mRefreshConsumers.erase(it);
ret = 0;
break;
} else {
it++;
}
}
for (auto it = mConsumers.begin(); it != mConsumers.end(); ) {
std::shared_ptr<VtConsumer> item = (*it);
if (item.get() == consumer.get()) {
VTINST_LOGD("remove consumer");
it = mConsumers.erase(it);
ret = 0;
break;
} else {
it++;
}
}
if (ret)
VTINST_LOGW("cannot find VT consumer %p",consumer.get());
return ret;
}
int32_t VtInstance::connect() {
int32_t ret = -1;
if (mTunnelId >= 0) {
ret = mVideoTunnelDev.connect(mTunnelId);
if (ret >= 0) {
VTINST_LOGD("connect to videotunnel succeeded");
} else {
VTINST_LOGE("connect to videotunnel filed");
}
} else {
VTINST_LOGE("tunnel id is invalid");
}
return ret;
}
void VtInstance::disconnect() {
VTINST_LOGD("disconnect videotunnel");
if (mTunnelId >= 0)
mVideoTunnelDev.disconnect(mTunnelId);
mTunnelId = -1;
memset(&mLastData, 0, sizeof(last_data_t));
mLastData.videoStatus = VT_VIDEO_STATUS_SHOW;
mLastData.solidBufferType = SOLID_COLOR_FOR_INVALID;
}
int32_t VtInstance::sendBufferToConsumer(
std::vector<std::shared_ptr<VtConsumer>> & consumers,
std::vector<std::shared_ptr<VtBufferItem>> & items) {
int32_t ret = 0;
if (!items.empty() && !consumers.empty()) {
for (auto it = consumers.begin(); it != consumers.end(); ) {
ret = (*it)->onFrameAvailable(items);
if (ret < 0 && ret != -EAGAIN) {
VTINST_LOGE("call consumer(%p) onFrameAvailable failed", (*it).get());
it = consumers.erase(it);
ret = -EAGAIN;
} else {
it++;
if (ret != -EAGAIN)
for (auto item = items.begin(); item != items.end(); item++)
(*item)->refHandle();
}
}
}
return ret;
}
int32_t VtInstance::acquireBuffer() {
bool isNeedDisplay = true;
int32_t ret = 0;
int bufFd = -1;
int64_t timeStamp = -1;
std::vector<std::shared_ptr<VtBufferItem>> items;
if (mLastData.videoStatus == VT_VIDEO_STATUS_COLOR_ALWAYS_FOR_VRR ||
mLastData.videoStatus == VT_VIDEO_STATUS_COLOR_ALWAYS ||
mLastData.videoStatus == VT_VIDEO_STATUS_HIDE ||
mLastData.videoStatus == VT_VIDEO_STATUS_HOLD_FRAME) {
isNeedDisplay = false;
}
{
std::lock_guard<std::mutex> lock(mMutex);
while (true) {
ret = mVideoTunnelDev.acquireBuffer(mTunnelId, bufFd, timeStamp);
if (ret == 0) {
if (isNeedDisplay) {
struct uvm_fd_info info;
memset(&info, 0, sizeof(info));
info.fd = bufFd;
UvmDev::getInstance().getVideoInfo(info);
#ifndef LINUX_COMPILE
if (HwcConfig::enableDrmVideo()) {
info.crop_left += mCrop.left;
info.crop_top += mCrop.top;
info.crop_right -= mCrop.right;
info.crop_bottom -= mCrop.bottom;
} else {
info.crop_left = mCrop.left;
info.crop_top = mCrop.top;
info.crop_right = mCrop.right;
info.crop_bottom = mCrop.bottom;
}
#else
info.crop_left += mCrop.left;
info.crop_top += mCrop.top;
info.crop_right -= mCrop.right;
info.crop_bottom -= mCrop.bottom;
#endif
info.buf_stride = info.buf_width;
info.buf_format = DRM_FORMAT_NV21;
VTINST_LOGV("acquire buffer %d timeStamp %" PRId64
", stride %d, w/h(%d %d), crop(%d %d %d %d)",
bufFd, timeStamp, info.buf_stride, info.buf_width,
info.buf_height, info.crop_left, info.crop_top,
info.crop_right, info.crop_bottom);
std::shared_ptr<VtBufferItem> item =
std::make_shared<VtBufferItem> (bufFd, timeStamp, info);
items.push_back(item);
mBufferQueue.push_back(item);
} else {
VTINST_LOGV("acquire buffer %d timeStamp %" PRId64 " will drop for status %s",
bufFd, timeStamp, vtVideoStatusToString(mLastData.videoStatus));
mVideoTunnelDev.releaseBuffer(mTunnelId, bufFd, -1);
}
} else if (ret == -EAGAIN) {
/* no buffer available */
break;
} else {
VTINST_LOGE("acquire buffer error %d", ret);
return ret;
}
}
}
if (isNeedDisplay) {
mLastData.videoStatus = VT_VIDEO_STATUS_SHOW;
mLastData.solidBufferType = SOLID_COLOR_FOR_INVALID;
ret = sendBufferToConsumer(mConsumers, items);
}
return ret;
}
void VtInstance::releaseBufferLocked(std::shared_ptr<VtBufferItem> & item) {
if (item.get()) {
if (!item->mIsSolidBuffer) {
VTINST_LOGV("release buffer (%s)", item->toString().c_str());
mVideoTunnelDev.releaseBuffer(mTunnelId, item->mBufferFd, item->getReleaseFenceFd());
} else {
VTINST_LOGV("release solid buffer %d", item->mBufferFd);
item->closeFd();
}
}
}
void VtInstance::releaseInstanceBuffers() {
std::lock_guard<std::mutex> lock(mMutex);
if (mBufferQueue.empty()) {
VTINST_LOGV("buffer queue is empty");
return;
}
for (auto it = mBufferQueue.begin(); it != mBufferQueue.end(); ++it) {
std::shared_ptr<VtBufferItem> item = *it;
if (!item->mIsSolidBuffer) {
VTINST_LOGV("release buffer(%s)", item->toString().c_str());
mVideoTunnelDev.releaseBuffer(mTunnelId, item->mBufferFd, -1);
} else {
VTINST_LOGV("release solid buffer %d", item->mBufferFd);
item->closeFd();
}
}
mBufferQueue.clear();
}
void VtInstance::onVtCmds(vt_cmd_t cmd,
vt_cmd_data_t cmdData,
bool & needSendToConsumer,
struct uvm_fd_info & info) {
bool debuglog = true;
vt_video_color_t prevType = mLastData.solidBufferType;
needSendToConsumer = false;
switch (cmd) {
case VT_CMD_SET_SHOW_SOLID_COLOR:
mLastData.videoStatus = VT_VIDEO_STATUS_COLOR_ONCE;
mLastData.solidBufferType = (vt_video_color_t)cmdData.data;
break;
case VT_CMD_SET_COLOR_BLACK:
mLastData.videoStatus = (vt_video_status_t)cmdData.data;
mLastData.solidBufferType = SOLID_COLOR_FOR_BLACK;
break;
case VT_CMD_SET_COLOR_BLUE:
mLastData.videoStatus = (vt_video_status_t)cmdData.data;
mLastData.solidBufferType = SOLID_COLOR_FOR_BLUE;
break;
case VT_CMD_SET_COLOR_GREEN:
/* cannot support green buffer */
//mLastData.videoStatus = (vt_video_status_t)cmdData.data;
//mLastData.solidBufferType = SOLID_COLOR_FOR_GREEN;
mLastData.videoStatus = VT_VIDEO_STATUS_SHOW;
mLastData.solidBufferType = SOLID_COLOR_FOR_INVALID;
break;
case VT_CMD_SET_VIDEO_STATUS:
mLastData.videoStatus = (vt_video_status_t)cmdData.data;
if (mLastData.videoStatus == VT_VIDEO_STATUS_BLANK ||
mLastData.videoStatus == VT_VIDEO_STATUS_HIDE) {
mLastData.solidBufferType = SOLID_COLOR_FOR_INVALID;
needSendToConsumer = true;
}
break;
case VT_CMD_SET_SOURCE_CROP:
mCrop = cmdData.rect;
debuglog = false;
VTINST_LOGD("cmd VT_CMD_SET_SOURCE_CROP(%d %d %d %d)", mCrop.left,
mCrop.top, mCrop.right, mCrop.bottom);
break;
case VT_CMD_SET_VIDEO_TRANSFORM:
mLastData.transform = cmdData.transform;
needSendToConsumer = true;
break;
case VT_CMD_SET_GAME_MODE:
mLastData.gameMode = cmdData.data;
needSendToConsumer = true;
break;
case VT_CMD_SET_DISPLAY_FRAME:
mLastData.displayFrame = cmdData.rect;
needSendToConsumer = true;
break;
default:
debuglog = false;
needSendToConsumer = true;
break;
}
if (mLastData.videoStatus == VT_VIDEO_STATUS_COLOR_DISABLE ||
mLastData.videoStatus == VT_VIDEO_STATUS_SHOW)
mLastData.solidBufferType = SOLID_COLOR_FOR_INVALID;
if (debuglog)
VTINST_LOGD("cmd:%s, status:%s, solidBufferType:%s",
vtCmdToString(cmd), vtVideoStatusToString(mLastData.videoStatus),
vtVideoColorTypeToString(mLastData.solidBufferType));
if (prevType != mLastData.solidBufferType && mLastData.solidBufferType != SOLID_COLOR_FOR_INVALID)
mSolidBufferAllocator.getBufferInfo(mLastData.solidBufferType, info);
}
int32_t VtInstance::receiveCmds() {
bool needSendToConsumer;
int32_t ret = 0;
enum vt_cmd cmd;
struct vt_cmd_data cmdData;
struct uvm_fd_info info;
std::vector<std::shared_ptr<VtBufferItem>> items;
ret = mVideoTunnelDev.receiveCmd(mTunnelId, cmd, cmdData);
if (ret < 0)
return ret;
memset(&info, 0, sizeof(uvm_fd_info));
info.fd = -1;
info.fps = -1;
onVtCmds(cmd, cmdData, needSendToConsumer, info);
if (info.fd >= 0) {
VTINST_LOGD("alloc solid buffer %d, stride %d, w/h(%d %d), crop(%d %d %d %d)",
info.fd, info.buf_stride, info.buf_width,
info.buf_height, info.crop_left, info.crop_top,
info.crop_right, info.crop_bottom);
std::lock_guard<std::mutex> lock(mMutex);
std::shared_ptr<VtBufferItem> item =
std::make_shared<VtBufferItem> (info.fd, info.timestamp, info, true);
items.push_back(item);
mBufferQueue.push_back(item);
}
if (!items.empty())
return sendBufferToConsumer(mConsumers,items);
if (needSendToConsumer) {
for (auto it = mConsumers.begin(); it != mConsumers.end(); it++) {
ret = (*it)->onVtCmds(cmd, cmdData);
}
}
return ret;
}
bool VtInstance::needDestroyThisInstance() {
return mConsumers.empty() ? true : false;
}
void VtInstance::onClearAlwaysVRRMode() {
std::lock_guard<std::mutex> lock(mMutex);
if (mLastData.videoStatus == VT_VIDEO_STATUS_COLOR_ALWAYS_FOR_VRR) {
mLastData.videoStatus = VT_VIDEO_STATUS_COLOR_ONCE;
VTINST_LOGV("change video statu %s -> %s",
vtVideoStatusToString(VT_VIDEO_STATUS_COLOR_ALWAYS_FOR_VRR),
vtVideoStatusToString(mLastData.videoStatus));
}
}