511 lines
18 KiB
C++
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));
|
|
}
|
|
}
|