Files
BPI-CM9-Linux/aml-comp/vendor/amlogic/meson_videoserver/display/MesonLayer.cpp
T

1296 lines
38 KiB
C++

/*
* Copyright (C) 2024 Amlogic Corporation.
*
* 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 <cinttypes>
#include <drm_fourcc.h>
#include <video_tunnel.h>
#include <VtAllocSolidColorBuffer.h>
#include <VtInstanceMgr.h>
#include "MesonDisplay.h"
#include "MesonLayer.h"
#include "DispAdptLog.h"
MesonLayer::MesonLayer() : DrmFramebufferBase()
{
init();
mDisplayId = 0;
}
MesonLayer::MesonLayer(uint32_t dispId) : DrmFramebufferBase()
{
init();
mDisplayId = dispId;
}
MesonLayer::~MesonLayer()
{
if (mFd >= 0) close(mFd);
mFd = -1;
if (mDifd >= 0) close(mDifd);
mDifd = -1;
if (isVtBuffer()) {
releaseVtResource();
releaseUvmResourceLock();
} else
releaseResource();
}
void MesonLayer::init()
{
mFd = -1;
mDifd = -1;
mTunnelFd = -1;
mNeedReleaseVtResource = false;
mTunnelIdUpdate = false;
mVtRefreshed = false;
mNeedAskRefresh = false;
mDataUpdate = false;
mVtUpdate = false;
mQueuedFrames = 0;
mGameMode = false;
mVideoDisplayStatus = VT_VIDEO_STATUS_SHOW;
mVideoStatus = VT_VIDEO_STATUS_SHOW;
mPreUvmBufferFd = -1;
mTunnelId = -1;
mExpectedPresentTime = 0;
memset(&mVtSourceCrop, 0, sizeof(mVtSourceCrop));
memset(&mVtDisplayFrame, 0, sizeof(mVtDisplayFrame));
}
int32_t MesonLayer::setTunnelId(int tunnelId, std::shared_ptr<VtDisplayObserver> observer)
{
mTunnelId = tunnelId;
if (tunnelId >= 128) {
setFbType(DRM_FB_VIDEO_UVM_DMA);
mTunnelFd = meson_vt_open_by_mode(0);
meson_vt_connect(mTunnelFd, mTunnelId, 1);
if (observer.get())
observer.reset();
} else if (tunnelId < 128 && tunnelId >= 0) {
setFbType(DRM_FB_VIDEO_TUNNEL_SIDEBAND);
mNeedReleaseVtResource = true;
mVtTransform = 0;
VtInstanceMgr::getInstance().lockInstancesMutex();
if (!mDisplayObserver.get())
mDisplayObserver = std::move(observer);
int ret = registerConsumer();
if (ret >= 0) {
DISPLAY_LOGD("%s [%" PRId64 "] register consumer for videotunnel %d succeeded",
__func__, mId, tunnelId);
mQueueItems.clear();
mTunnelIdUpdate = true;
} else {
DISPLAY_LOGE("%s [%" PRId64 "] register consumer for videotunnel %d failed, error %d",
__func__, mId, tunnelId, ret);
mTunnelId = -1;
VtInstanceMgr::getInstance().unlockInstancesMutex();
return -1;
}
VtInstanceMgr::getInstance().unlockInstancesMutex();
}
/* fbtype is not tunnel sideband */
if (mFbType != DRM_FB_VIDEO_TUNNEL_SIDEBAND)
releaseUvmResourceLock();
return 0;
}
int32_t MesonLayer::setIndex(int index)
{
mIndex = index;
setVideoPriority(mIndex);
return 0;
}
int32_t MesonLayer::getIndex()
{
return mIndex;
}
int32_t MesonLayer::setZorder(uint32_t z)
{
mCurrZorder = z;
return 0;
}
int32_t MesonLayer::getCurZorder()
{
return mCurrZorder;
}
void MesonLayer::updateZorder()
{
if (mCurrZorder != -1 && (uint32_t)mCurrZorder == mZorder)
return;
DrmFramebufferBase::setZorder(mCurrZorder);
mDataUpdate = true;
}
int32_t MesonLayer::setTransform(int32_t transform)
{
if (mTransform == transform)
return 0;
mTransform = transform;
mDataUpdate = true;
return 0;
}
int32_t MesonLayer::setDisplayFrame(drm_rect_t dispFrame)
{
if (mDisplayFrame.left == dispFrame.left &&
mDisplayFrame.top == dispFrame.top &&
mDisplayFrame.right == dispFrame.right &&
mDisplayFrame.bottom == dispFrame.bottom)
return 0;
if (dispFrame.left <= 0 && dispFrame.top <= 0)
setVideoPriority(0);
else
setVideoPriority(mIndex);
mDataUpdate = true;
return DrmFramebufferBase::setDisplayFrame(dispFrame);
}
int32_t MesonLayer::setCurReleaseFence(int32_t fenceFd)
{
mCurReleaseFence.reset(new DrmFence(fenceFd));
return 0;
}
int32_t MesonLayer::getCurReleaseFence()
{
return (mCurReleaseFence.get() ? mCurReleaseFence->dup() : -1);
}
int32_t MesonLayer::setProcessFence(int32_t fenceFd) {
mProcessFence.reset(new DrmFence(fenceFd));
return 0;
}
int32_t MesonLayer::getProcessFence() {
return (mProcessFence.get() ? mProcessFence->dup() : -1);
}
int32_t MesonLayer::onLayerDisplayed(int32_t releaseFence,
int32_t processFence) {
if (releaseFence < 0 || processFence < 0) {
int32_t fenceFd = (releaseFence < 0) ? processFence : releaseFence;
mCurReleaseFence.reset(new DrmFence(fenceFd));
} else {
mCurReleaseFence = DrmFence::merge(
"FBProcessor",
std::make_shared<DrmFence>(releaseFence),
std::make_shared<DrmFence>(processFence));
}
return 0;
}
void MesonLayer::registerCallback(void *context, meson_callback_t *cb)
{
mContext = context;
mCb = cb;
}
void MesonLayer::setPageFlipData(uint64_t id, void *context, adpt_page_flip_callback *cb, int fd)
{
std::lock_guard<std::mutex> lock(mFlipDataMutex);
struct meson_page_flip_data dat = { id, context, cb, fd };
mFlipData.emplace(id, dat);
}
void MesonLayer::registerPageFlipCallBack(void *context, adpt_page_flip_callback *cb)
{
setPageFlipData(mId, context, cb, 0);
}
std::map<uint64_t, meson_page_flip_data> MesonLayer::getPageFlipData()
{
std::lock_guard<std::mutex> lock(mFlipDataMutex);
return mFlipData;
}
void MesonLayer::setFlipDataToUserData(void *userData)
{
std::lock_guard<std::mutex> lock(mFlipDataMutex);
if (userData == nullptr)
return;
int fd = -1;
auto fData = mFlipData.find(mId);
if (fData != mFlipData.end()) {
if (mPrevBufferItem.get())
fd = mPrevBufferItem->mBufferFd;
if (mCurBufferItem.get())
fd = mCurBufferItem->mBufferFd;
fData->second.fd = fd;
}
MesonPageFlip *flip = (MesonPageFlip *)userData;
for (const auto& pair : mFlipData)
flip->pushData(pair.second);
flip->incIndex();
}
void MesonLayer::setPresentTime(nsecs_t expectedPresentTime)
{
std::lock_guard<std::mutex> lock(mMutex);
nsecs_t previousExpectedTime;
previousExpectedTime = ((mExpectedPresentTime == 0) ?
ns2us(expectedPresentTime) : mExpectedPresentTime);
mExpectedPresentTime = ns2us(expectedPresentTime);
mVsyncInterval = mExpectedPresentTime - previousExpectedTime;
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] vsyncTimeStamp:%" PRId64 ", diffExpected:%" PRId64,
__func__, mDisplayId, mId, mExpectedPresentTime, mExpectedPresentTime - previousExpectedTime);
}
bool MesonLayer::isVtBuffer()
{
return mFbType == DRM_FB_VIDEO_TUNNEL_SIDEBAND;
}
bool MesonLayer::isFbUpdated()
{
std::lock_guard<std::mutex> lock(mMutex);
if (isVtBuffer())
return (shouldPresentNow(mCurBufferItem) && mVtUpdate) || mVtRefreshed;
return mDataUpdate;
}
bool MesonLayer::shouldPresentNow(const std::shared_ptr<BufferItem> item)
{
if (!item.get())
return false;
if (item->mTimeStamp == -1)
return true;
const bool isDue = item->mTimeStamp < mExpectedPresentTime;
// Ignore timestamps more than a second in the future, timestamp is us
const bool isPlausible = item->mTimeStamp < (mExpectedPresentTime + s2ns(1)/1000);
return isDue || !isPlausible;
}
bool MesonLayer::shouldPresentNow(const std::shared_ptr<VtBufferItem> item)
{
if (!item.get())
return false;
if (item->mTimeStamp == -1)
return true;
const bool isDue = item->mTimeStamp < mExpectedPresentTime;
// Ignore timestamps more than a second in the future, timestamp is us
const bool isPlausible = item->mTimeStamp < (mExpectedPresentTime + s2ns(1)/1000);
return isDue || !isPlausible;
}
void MesonLayer::dropExpiredFrame(bool drop)
{
int expiredItemCount = 0;
int dropCount = 0;
for (auto it = mQueueItems.begin(); it != mQueueItems.end(); it++) {
if (((*it)->mTimeStamp > 0 && shouldPresentNow((*it))) ||
mVideoDisplayStatus == VT_VIDEO_STATUS_IMMEDIATELY_OUTPUT) {
if (drop)
expiredItemCount++;
}
}
// drop expiredItemCount - 1 buffers
dropCount = expiredItemCount - 1;
while (true) {
auto bufItem = mQueueItems.front();
if (dropCount <= 0) {
break;
}
DISPLAY_LOGD("%s[%d]: mId: %lld, buffer (%s) dropped!", __func__, __LINE__, mId, bufItem->toString().c_str());
meson_vt_release_buffer(mTunnelFd, mTunnelId, bufItem->mBufferFd, -1);
if (mCb != NULL && mCb->frame_dropped)
mCb->frame_dropped(mContext, bufItem->mBufferFd);
mQueueItems.pop_front();
dropCount--;
}
}
bool MesonLayer::interruptProcess(bool change) {
std::lock_guard<std::mutex> lock(mMutex);
if (isVtBuffer())
return false;
if (mVideoDisplayStatus == VT_VIDEO_STATUS_HIDE ||
mVideoDisplayStatus == VT_VIDEO_STATUS_BLANK) {
releaseResourceLocked();
if (change)
mVideoDisplayStatus = VT_VIDEO_STATUS_SHOW;
return true;
}
return false;
}
bool MesonLayer::keepFrame()
{
return mVideoStatus == VT_VIDEO_STATUS_HOLD_FRAME;
}
void MesonLayer::onVideoCmd()
{
vt_cmd_t cmd;
vt_cmd_data_t cmd_data;
while (meson_vt_recv_cmd(mTunnelFd, mTunnelId, &cmd, &cmd_data) == 0) {
switch (cmd) {
case VT_CMD_SET_VIDEO_STATUS:
mVideoStatus = cmd_data.data;
if (mVideoStatus == VT_VIDEO_STATUS_BLANK ||
mVideoStatus == VT_VIDEO_STATUS_HIDE)
mVideoDisplayStatus = cmd_data.data;
DISPLAY_LOGV("%s[%d]: status: %d", __func__, __LINE__, mVideoStatus);
break;
case VT_CMD_SET_COLOR_BLACK: {
struct uvm_fd_info info = { 0 };
struct vt_meta_data meta_data = { 0 };
VtAllocSolidColorBuffer::getInstance().getBufferInfo(SOLID_COLOR_FOR_BLACK, info);
if (info.fd >= 0) {
std::shared_ptr<VtBufferItem> item =
std::make_shared<VtBufferItem>(info.fd, info.timestamp, info, meta_data);
mQueueItems.push_back(std::move(item));
}
break;
}
default:
break;
}
}
}
void MesonLayer::updateBuffer()
{
std::lock_guard<std::mutex> lock(mMutex);
if (!isVtBuffer()) {
int buffer_fd = 0;
int fence_fd = 0;
int64_t present_time = 0;
struct vt_meta_data meta_data = { 0 };
while (meson_vt_acquire_buffer(mTunnelFd, mTunnelId, &buffer_fd,
&fence_fd, &present_time, &meta_data) == 0) {
if (mVideoStatus == VT_VIDEO_STATUS_HIDE ||
mVideoStatus == VT_VIDEO_STATUS_HOLD_FRAME) {
meson_vt_release_buffer(mTunnelFd, mTunnelId, buffer_fd, -1);
} else {
struct uvm_fd_info info = { 0 };
if (!mCropItems.empty()) {
info.crop_left = mCropItems[0].left;
info.crop_right = mCropItems[0].right;
info.crop_top = mCropItems[0].top;
info.crop_bottom = mCropItems[0].bottom;
info.buf_width = info.crop_right - info.crop_left;
info.buf_height = info.crop_bottom - info.crop_top;
mCropItems.pop_front();
}
std::shared_ptr<VtBufferItem> item =
std::make_shared<VtBufferItem>(buffer_fd, present_time, info, meta_data);
DISPLAY_LOGD("%s[%d]: mId: %lld, buffer (%s) add!", __func__, __LINE__, mId, item->toString().c_str());
mQueueItems.push_back(std::move(item));
mDropEnable = ((mVsyncInterval / 1000) * 1000 < present_time - mTimeStamp);
mTimeStamp = present_time;
}
}
onVideoCmd();
if (mQueueItems.empty())
return;
}
dropExpiredFrame(mDropEnable);
mUpdated = true;
mDataUpdate = true;
mCurBufferItem = mQueueItems[0];
if (mCb != NULL && mCb->frame_displayed)
mCb->frame_displayed(mContext, mCurBufferItem->mBufferFd, DispAdptGetCurrentTimeUs());
DISPLAY_LOGD("%s[%d]: mId: %lld, buffer (%s) displayed!", __func__, __LINE__, mId, mCurBufferItem->toString().c_str());
if (shouldPresentNow(mCurBufferItem)) {
struct uvm_fd_info info = mCurBufferItem->mInfo;
if (info.buf_width == 0 || info.buf_height == 0) {
int width = mSourceCrop.right - mSourceCrop.left;
int height = mSourceCrop.bottom - mSourceCrop.top;
setBufferInfo(width, height, VIDEO_ALIGN(width), height, DRM_FORMAT_NV21);
} else {
setBufferInfo(info.buf_width, info.buf_height, VIDEO_ALIGN(info.buf_width), info.buf_height, DRM_FORMAT_NV21);
}
}
}
void MesonLayer::setDiProcessorFd(int32_t fd) {
std::lock_guard<std::mutex> lock(mMutex);
// do not dup fd
if (mDifd >= 0)
close(mDifd);
if (fd >= 0)
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] set %d to DIFd (%d)",
__func__, mDisplayId, mId, getBufferFdLocked(), fd);
mDifd = fd;
}
int32_t MesonLayer::getBufferFdLocked(source_type_t & sourceType)
{
int curFd = -1;
int prevFd = -1;
int32_t fd = -1;
if (mFbType == DRM_FB_VIDEO_TUNNEL_SIDEBAND)
sourceType = DTV_FIX_TUNNEL;
else
sourceType = NORMAL_TYPE;
if (mDifd >= 0) {
fd = mDifd;
} else if (mFbType == DRM_FB_VIDEO_DMABUF ||
mFbType == DRM_FB_VIDEO_UVM_DMA ||
mFbType == DRM_FB_VIDEO_TUNNEL_SIDEBAND) {
source_type_t curType = DTV_FIX_TUNNEL;
source_type_t prevType = DTV_FIX_TUNNEL;
if (mCurBufferItem.get()) {
curFd = mCurBufferItem->mBufferFd;
if (mCurBufferItem->mIsSolidBuffer)
curType = HWC_CREAT_ION;
}
if (mPrevBufferItem.get()) {
prevFd = mPrevBufferItem->mBufferFd;
if (mPrevBufferItem->mIsSolidBuffer)
prevType = HWC_CREAT_ION;
}
if (shouldPresentNow(mCurBufferItem)) {
fd = (mUpdated || mVtUpdate) ? curFd : prevFd;
sourceType = (mUpdated || mVtUpdate) ? curType : prevType;
} else {
fd = prevFd;
sourceType = prevType;
}
} else if (mFbType == DRM_FB_VIDEO_COMPOSE_OUTPUT) {
fd = mFd;
}
return fd;
}
int32_t MesonLayer::getBufferFdLocked()
{
source_type_t type;
return getBufferFdLocked(type);
}
int32_t MesonLayer::getBufferFd(source_type_t & sourceType)
{
std::lock_guard<std::mutex> lock(mMutex);
return getBufferFdLocked(sourceType);
}
int32_t MesonLayer::getBufferFd()
{
std::lock_guard<std::mutex> lock(mMutex);
return getBufferFdLocked();
}
int32_t MesonLayer::releaseBuffer()
{
std::lock_guard<std::mutex> lock(mMutex);
int32_t ret = 0;
if (!mCurBufferItem.get() && mPrevBufferItem.get())
mPrevBufferItem->setReleaseFence(mCurReleaseFence);
if (!mUpdated) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] vt buffer not update", __func__, mDisplayId, mId);
return -EAGAIN;
}
if (!mCurBufferItem.get()) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] current buffer item is nullprt !!",
__func__, mDisplayId, mId);
return -EAGAIN;
}
if (shouldPresentNow(mCurBufferItem) == false) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] current buffer not present, timestamp not meet",
__func__, mDisplayId, mId);
return -EAGAIN;
}
if (mQueueItems.empty()) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] Queued vtbuffer is empty!!",
__func__, mDisplayId, mId);
return -EAGAIN;
}
if (!mCurReleaseFence.get() && !mPrevBufferItem.get()) {
/* TODO: this first buffer not send to display, need refresh */
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] first buffer not display", __func__, mDisplayId, mId);
return -EAGAIN;
}
// remove it from the queueItems
mQueueItems.pop_front();
mCurBufferItem->setReleaseFence(mCurReleaseFence);
mUpdated = false;
mDataUpdate = false;
setCurReleaseFence(-1);
// First release vt buffer, save to the previous
if (!mPrevBufferItem.get()) {
mPrevBufferItem = mCurBufferItem;
mCurBufferItem.reset();
return ret;
}
if (mPrevBufferItem.get()) {
meson_vt_release_buffer(
mTunnelFd, mTunnelId, mPrevBufferItem->mBufferFd, mPrevBufferItem->getReleaseFenceFd());
DISPLAY_LOGV("%s[%d]: mId: %lld, buffer (%s) release!", __func__, __LINE__, mId, mPrevBufferItem->toString().c_str());
} else {
DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] previous buffer is null, it is impossible",
__func__, mDisplayId, mId);
}
mPrevBufferItem = mCurBufferItem;
mCurBufferItem.reset();
return ret;
}
int32_t MesonLayer::releaseResourceLocked()
{
if (mPrevBufferItem.get()) {
meson_vt_release_buffer(
mTunnelFd, mTunnelId, mPrevBufferItem->mBufferFd, mPrevBufferItem->getReleaseFenceFd());
DISPLAY_LOGV("%s[%d]: mId: %lld, buffer (%s) release!", __func__, __LINE__, mId, mPrevBufferItem->toString().c_str());
mPrevBufferItem.reset();
}
if (mCurBufferItem.get()) {
meson_vt_release_buffer(
mTunnelFd, mTunnelId, mCurBufferItem->mBufferFd, mCurBufferItem->getReleaseFenceFd());
mQueueItems.pop_front();
DISPLAY_LOGV("%s[%d]: mId: %lld, buffer (%s) release!", __func__, __LINE__, mId, mCurBufferItem->toString().c_str());
mCurBufferItem.reset();
}
if (!isVtBuffer()) {
int buffer_fd = 0;
int fence_fd = 0;
int64_t present_time = 0;
struct vt_meta_data meta_data = { 0 };
while (meson_vt_acquire_buffer(mTunnelFd, mTunnelId, &buffer_fd,
&fence_fd, &present_time, &meta_data) == 0) {
struct uvm_fd_info info = { 0 };
std::shared_ptr<VtBufferItem> item =
std::make_shared<VtBufferItem>(buffer_fd, present_time, info, meta_data);
mQueueItems.push_back(std::move(item));
}
onVideoCmd();
}
// release the buffers in mQueueItems
for (auto it = mQueueItems.begin(); it != mQueueItems.end(); it++) {
meson_vt_release_buffer(
mTunnelFd, mTunnelId, (*it)->mBufferFd, (*it)->getReleaseFenceFd());
DISPLAY_LOGV("%s[%d]: mId: %lld, buffer (%s) release!", __func__, __LINE__, mId, (*it)->toString().c_str());
}
mQueueItems.clear();
mCropItems.clear();
mUpdated = false;
return 0;
}
int32_t MesonLayer::releaseResource()
{
std::lock_guard<std::mutex> lock(mMutex);
return releaseResourceLocked();
}
int32_t MesonLayer::getAcquireFenceFd()
{
return (mAcquireFence.get() ? mAcquireFence->dup() : -1);
}
int32_t MesonLayer::setAcquireFence(int32_t fenceFd)
{
mAcquireFence.reset(new DrmFence(fenceFd));
return 0;
}
void MesonLayer::setBufferInfo(int fd, int32_t acquireFence)
{
if (mFd >= 0)
close(mFd);
mFd = fd;
setAcquireFence(acquireFence);
mUpdated = true;
mDataUpdate = true;
}
int32_t MesonLayer::getBufferInfo(
uint32_t & width, uint32_t & height, uint32_t & stride, uint32_t & format)
{
width = mBufferWidth;
height = mBufferHeight;
stride = mBufferStride;
format = mBufferFormat;
DISPLAY_LOGV("[%s] [%" PRIu64 "] width:%d height:%d stride:%d format:%d",
__func__, mId, width, height, stride, format);
return 0;
}
int32_t MesonLayer::getBufferInfo(
uint32_t & width, uint32_t & height, uint32_t & stride, uint32_t & alignHeight, uint32_t & format) {
width = mBufferWidth;
height = mBufferHeight;
stride = mBufferStride;
format = mBufferFormat;
alignHeight = mBufferAlignHeight;
DISPLAY_LOGV("[%s] [%" PRIu64 "] width:%d height:%d stride:%d format:%d",
__func__, mId, width, height, stride, format);
return 0;
}
void MesonLayer::setBufferInfo(
uint32_t width, uint32_t height, uint32_t stride, uint32_t format)
{
mBufferWidth = width;
mBufferHeight = height;
mBufferStride = stride;
mBufferFormat = format;
}
/* videotunnel api */
int32_t MesonLayer::onVtFrameAvailable(
std::vector<std::shared_ptr<VtBufferItem>> & items) {
mMutex.lock();
bool haveSolidBuffer = false;
/* if mNeedReleaseVtResource is true that means
* this sideband layer receive blank frame. and layer's
* type change from sideband to color.
* don't return error.
*/
if (!isVtBuffer() && !mNeedReleaseVtResource) {
DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] not videotunnel type", __func__, mDisplayId, mId);
mMutex.unlock();
return -EINVAL;
}
if (mTunnelId < 0) {
DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] mTunnelId %d error", __func__, mDisplayId, mId, mTunnelId);
mMutex.unlock();
return -EINVAL;
}
for (auto it = items.begin(); it != items.end(); it ++) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] mTunnelId %d, buffer(%s)",
__func__, mDisplayId, mId, mTunnelId, (*it)->toString().c_str());
mQueueItems.push_back(*it);
mQueuedFrames++;
if (!(*it)->mIsSolidBuffer)
attachUvmBuffer((*it)->mBufferFd);
else
haveSolidBuffer = true;
}
if (mQueueItems.empty()) {
mMutex.unlock();
return -EAGAIN;
}
mMutex.unlock();
if (mGameMode || haveSolidBuffer)
mDisplayObserver->onFrameAvailable();
return 0;
}
int32_t MesonLayer::registerConsumer() {
int32_t ret = -1;
if (mTunnelId < 0) {
DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] tunnelId is less than 0, cannot register consumer",
__func__, mDisplayId, mId);
return ret;
}
if (mVtConsumer)
return 0;
mVtConsumer = std::make_shared<VtConsumer> (mTunnelId,
mDisplayId,
mId);
if (!mContentListener)
mContentListener = std::make_shared<VtContentChangeListener>(this);
ret = mVtConsumer->setVtContentListener(mContentListener);
if (ret < 0) {
DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] set %d content listener failed ",
__func__, mDisplayId, mId, mTunnelId);
return ret;
}
ret = VtInstanceMgr::getInstance().connectInstanceLocked(mTunnelId, mVtConsumer);
if (ret >= 0)
DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] connect to instance %d seccussed",
__func__, mDisplayId, mId, mTunnelId);
else
DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] connect to instance %d failed",
__func__, mDisplayId, mId, mTunnelId);
askVtRefresh(false);
return ret;
}
/* need lock VtInstanceMgr.mInstanceMutex before */
int32_t MesonLayer::unregisterConsumer() {
int32_t ret = -1;
if (mTunnelId < 0) {
DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] tunnelId is less than 0, cannot unregister consumer",
__func__, mDisplayId, mId);
return ret;
}
if (!mVtConsumer) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] consumer %d not create",
__func__, mDisplayId, mId, mTunnelId);
return ret;
}
memset(&mVtSourceCrop, 0, sizeof(mVtSourceCrop));
memset(&mVtDisplayFrame, 0, sizeof(mVtDisplayFrame));
ret = VtInstanceMgr::getInstance().disconnectInstanceLocked(mTunnelId, mVtConsumer);
mVtConsumer.reset();
mVtConsumer = nullptr;
mTunnelId = -1;
return ret;
}
void MesonLayer::askVtRefresh(bool flag) {
VideoTunnelDev::getInstance().askRefresh(mTunnelId, flag);
}
/* layer will destroy or layer FbType changed to non-videotunnel */
int32_t MesonLayer::releaseVtResource() {
int32_t ret = 0;
VtInstanceMgr::getInstance().lockInstancesMutex();
std::lock_guard<std::mutex> lock(mMutex);
ret = releaseVtResourceLocked();
VtInstanceMgr::getInstance().unlockInstancesMutex();
return ret;
}
/* need hold VtInstanceMgr::mInstanceMutex and MesonLayer::mMutex before call */
int32_t MesonLayer::releaseVtResourceLocked(bool needDisconnect) {
if (isVtBuffer() || mNeedReleaseVtResource) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "]", __func__, mDisplayId, mId);
if (!mVtConsumer)
return 0;
if (needDisconnect && mNeedAskRefresh ) {
askVtRefresh(true);
mNeedAskRefresh = false;
}
if (mPrevBufferItem.get()) {
onVtFrameDisplayed(mPrevBufferItem);
mQueuedFrames--;
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] release buffer(%s) queuedFrames(%d)",
__func__, mDisplayId, mId, mPrevBufferItem->toString().c_str(),
mQueuedFrames);
mPrevBufferItem.reset();
}
if (mCurBufferItem.get()) {
onVtFrameDisplayed(mCurBufferItem);
mQueuedFrames--;
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] release buffer(%s) queuedFrames(%d)",
__func__, mDisplayId, mId, mCurBufferItem->toString().c_str(),
mQueuedFrames);
mQueueItems.pop_front();
mCurBufferItem.reset();
}
// release the buffers in mQueueItems
for (auto it = mQueueItems.begin(); it != mQueueItems.end(); it++) {
onVtFrameDisplayed((*it));
mQueuedFrames--;
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] release buffer(%s) queuedFrames(%d)",
__func__, mDisplayId, mId, (*it)->toString().c_str(), mQueuedFrames);
}
mQueueItems.clear();
mVtUpdate = false;
if (mTunnelId >= 0 && needDisconnect) {
DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] Hwc2Layer release disconnect(%d) queuedFrames(%d)",
__func__, mDisplayId, mId, mTunnelId, mQueuedFrames);
unregisterConsumer();
}
mNeedReleaseVtResource = false;
}
return 0;
}
int32_t MesonLayer::onVtFrameDisplayed(std::shared_ptr<VtBufferItem> & item) {
int32_t ret = -1;
if (!mVtConsumer)
return ret;
ret = mVtConsumer->onVtFrameDisplayed(item);
if (ret) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] release buffer(%s) error" ,
__func__, mDisplayId, mId, item->toString().c_str());
}
return ret;
}
bool MesonLayer::newGameBuffer() {
std::lock_guard<std::mutex> lock(mMutex);
bool ret = false;
if (isVtBuffer() && mGameMode) {
if (!mCurReleaseFence.get() && !mPrevBufferItem.get()) {
ret = false;
} else {
ret = (mQueueItems.size() > 0) ? true : false;
}
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] return %d",
__func__, mDisplayId, mId, ret);
}
/* not game mode */
return ret;
}
/* refresh vt layer */
void MesonLayer::vtRefresh() {
mVtRefreshed = true;
}
void MesonLayer::setVtDisplayFrame(drm_rect_t & rect) {
std::lock_guard<std::mutex> lock(mMutex);
mVtDisplayFrame.left = rect.left;
mVtDisplayFrame.top = rect.top;
mVtDisplayFrame.right = rect.right;
mVtDisplayFrame.bottom = rect.bottom;
mUpdated = true;
vtRefresh();
}
void MesonLayer::setVtSourceCrop(int32_t l, int32_t t, int32_t r, int32_t b) {
if (mVtSourceCrop.left == l && mVtSourceCrop.top == t &&
mVtSourceCrop.right == r && mVtSourceCrop.bottom == b) {
return;
}
mVtSourceCrop.left = l;
mVtSourceCrop.top = t;
mVtSourceCrop.right = r;
mVtSourceCrop.bottom = b;
mUpdated = true;
vtRefresh();
}
void MesonLayer::onVtVideoGameMode(int data) {
if (data != 1 && data != 0) {
DISPLAY_LOGW("[%s] [%d] [%" PRIu64 "] get an invalid param for gameMode",
__func__, mDisplayId, mId);
} else {
mGameMode = (data == 1 ? true : false);
DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] %s video game mode",
__func__, mDisplayId, mId, mGameMode ? "enable" : "disable");
}
}
void MesonLayer::updateVtBuffer() {
mMutex.lock();
bool haveSolidBuffer = false;
bool isVideoTypeChange = false;
int expiredItemCount = 0;
int dropCount = 0;
int64_t diffAdded = 0;
int64_t prevTimestamp = 0;
if (!isVtBuffer()) {
mMutex.unlock();
return;
}
/*
* getBufferFd might drop the some buffers at the head of the queue
* if there is a buffer behind them which is timely to be presented.
*/
if (mQueueItems.empty()) {
mMutex.unlock();
return;
}
if (mPrevBufferItem.get())
prevTimestamp = mPrevBufferItem->mTimeStamp;
for (auto it = mQueueItems.begin(); it != mQueueItems.end(); it++) {
if ((*it)->mIsSolidBuffer)
haveSolidBuffer = true;
if ((*it)->mTimeStamp > 0 && shouldPresentNow((*it))) {
// do not drop frame in game mode
if (!mGameMode)
expiredItemCount++;
}
}
// drop expiredItemCount-1 buffers
dropCount = expiredItemCount - 1;
while (true) {
auto bufItem = mQueueItems.front();
if (haveSolidBuffer) {
if (bufItem->mIsSolidBuffer)
break;
} else if (dropCount <= 0) {
break;
}
diffAdded = bufItem->mTimeStamp - prevTimestamp;
prevTimestamp = bufItem->mTimeStamp;
DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] vt buffer (%s) expired dropped "
"expectedPresent time(%" PRId64 ") diffAdded(%" PRId64 ") queuedFrames(%d)",
__func__, mDisplayId,mId, bufItem->toString().c_str(),
mExpectedPresentTime, diffAdded, mQueuedFrames);
collectUvmBuffer(bufItem);
onVtFrameDisplayed(bufItem);
mQueuedFrames--;
mQueueItems.pop_front();
dropCount--;
}
// update current mVtBuffer
mVtUpdate = true;
mCurBufferItem = mQueueItems[0];
if (shouldPresentNow(mCurBufferItem)) {
struct uvm_fd_info info = mCurBufferItem->mInfo;
mTunnelIdUpdate = false;
if (info.buf_width == 0 || info.buf_height == 0) {
int width = mSourceCrop.right - mSourceCrop.left;
int height = mSourceCrop.bottom - mSourceCrop.top;
setBufferInfo(width, height, VIDEO_ALIGN(width), height, info.buf_format);
setVtSourceCrop(mSourceCrop.left, mSourceCrop.top, mSourceCrop.right, mSourceCrop.bottom);
} else {
setBufferInfo(info.buf_width, info.buf_height, info.buf_stride, info.buf_height, info.buf_format);
setVtSourceCrop(info.crop_left, info.crop_top, info.crop_right, info.crop_bottom);
}
}
diffAdded = mCurBufferItem->mTimeStamp - prevTimestamp;
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] currentBuffer(%s) expectedPresentTime(%"
PRId64 " us) diffAdded(%" PRId64 " us) shouldPresent:%d, queueFrameSize:%zu, isVideoTypeChange:%d",
__func__, mDisplayId, mId, mCurBufferItem->toString().c_str(),mExpectedPresentTime,
diffAdded, shouldPresentNow(mCurBufferItem), mQueueItems.size(), isVideoTypeChange);
mMutex.unlock();
}
void MesonLayer::setBufferInfo(
uint32_t width, uint32_t height, uint32_t stride, uint32_t alignHeight, uint32_t format) {
mBufferWidth = width;
mBufferHeight = height;
mBufferStride = stride;
mBufferFormat = format;
mBufferAlignHeight = alignHeight;
DISPLAY_LOGD("%s[%d]: w: %d, h: %d, s: %d, f: %d", __func__, __LINE__, width, height, stride, format);
}
int32_t MesonLayer::setSourceCrop(drm_rect_t crop)
{
std::lock_guard<std::mutex> lock(mMutex);
mCropItems.push_back(crop);
mSourceCrop = crop;
mUpdated = true;
return 0;
}
drm_rect_t MesonLayer::getSourceCrop()
{
std::lock_guard<std::mutex> lock(mMutex);
if (isVtBuffer())
return mSourceCrop;
struct uvm_fd_info curInfo = { 0 };
struct uvm_fd_info prevInfo = { 0 };
struct uvm_fd_info info = { 0 };
if (mCurBufferItem.get())
curInfo = mCurBufferItem->mInfo;
if (mPrevBufferItem.get())
prevInfo = mPrevBufferItem->mInfo;
if (shouldPresentNow(mCurBufferItem)) {
info = (mUpdated || mVtUpdate) ? curInfo : prevInfo;
} else {
info = prevInfo;
}
if (info.crop_bottom != 0 && info.crop_right != 0) {
mSourceCrop.left = info.crop_left;
mSourceCrop.top = info.crop_top;
mSourceCrop.right = info.crop_right;
mSourceCrop.bottom = info.crop_bottom;
}
return mSourceCrop;
}
int32_t MesonLayer::releaseVtBuffer() {
std::lock_guard<std::mutex> lock(mMutex);
int32_t ret = 0;
if (!isVtBuffer()) {
DISPLAY_LOGD("layer:%" PRId64 " is not videotunnel layer", getUniqueId());
return -EINVAL;
}
detachUvmBuffer();
if (mVtRefreshed) {
mVtRefreshed = false;
if (!shouldPresentNow(mCurBufferItem) ||
(shouldPresentNow(mCurBufferItem) && !mVtUpdate)) {
DISPLAY_LOGV("[%s] [%" PRIu64 "] mVtRefreshed", __func__, mId);
/* set release fence */
if (mPrevBufferItem.get())
mPrevBufferItem->setReleaseFence(mCurReleaseFence);
return ret;
}
}
if (!mVtUpdate) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] vt buffer not update", __func__, mDisplayId, mId);
return -EAGAIN;
}
if (!mCurBufferItem.get()) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] current buffer item is nullprt !!",
__func__, mDisplayId, mId);
return -EAGAIN;
}
if (shouldPresentNow(mCurBufferItem) == false) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] current buffer(%s) not present, timestamp not meet",
__func__, mDisplayId, mId, mCurBufferItem->toString().c_str());
return -EAGAIN;
}
if (mQueueItems.empty()) {
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] Queued vtbuffer is empty!!",
__func__, mDisplayId, mId);
return -EAGAIN;
}
if (!mCurReleaseFence.get() && !mPrevBufferItem.get()) {
/* TODO: this first buffer not send to display, need refresh */
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] first buffer not display", __func__, mDisplayId, mId);
return -EAGAIN;
}
// remove it from the queueItems
mQueueItems.pop_front();
mCurBufferItem->setReleaseFence(mCurReleaseFence);
mVtUpdate = false;
setCurReleaseFence(-1);
// First release vt buffer, save to the previous
if (!mPrevBufferItem.get()) {
mPrevBufferItem = mCurBufferItem;
mCurBufferItem.reset();
return ret;
}
if (mPrevBufferItem.get()) {
collectUvmBuffer(mPrevBufferItem);
DISPLAY_LOGV("[%s] [%d] [%" PRIu64 "] releaseBuffer(%s), curBuffer(%s), queuedFrames(%d)",
__func__, mDisplayId, mId,mPrevBufferItem->toString().c_str(),
mCurBufferItem->toString().c_str(), mQueuedFrames);
ret = onVtFrameDisplayed(mPrevBufferItem);
} else {
DISPLAY_LOGE("[%s] [%d] [%" PRIu64 "] previous buffer is null, it is impossible",
__func__, mDisplayId, mId);
}
mQueuedFrames--;
mPrevBufferItem = mCurBufferItem;
mCurBufferItem.reset();
return ret;
}
bool MesonLayer::interruptVtProcess(bool change) {
std::lock_guard<std::mutex> lock(mMutex);
if (!isVtBuffer())
return false;
if (mVideoDisplayStatus != VT_VIDEO_STATUS_SHOW) {
releaseVtResourceLocked(false);
releaseUvmResourceLock();
if (change)
mVideoDisplayStatus = VT_VIDEO_STATUS_SHOW;
return true;
}
if (getBufferFdLocked() < 0 && !mTunnelIdUpdate)
return true;
return false;
}
void MesonLayer::onVtVideoStatus(vt_video_status_t status) {
std::lock_guard<std::mutex> lock(mMutex);
DISPLAY_LOGD("[%s] [%d] [%" PRIu64 "] status: %s", __func__, mDisplayId, mId, vtVideoStatusToString(status));
mVideoDisplayStatus = status;
}
/* ========================== Uvm Attach =================================== */
int32_t MesonLayer::uvmDetachEnable(bool enable) {
std::lock_guard<std::mutex> lock(mMutex);
releaseUvmResourceLock();
if (mUvmDetach)
return mUvmDetach->setEnable(enable);
return 0;
}
int32_t MesonLayer::attachUvmBuffer(const int bufferFd) {
if (!mUvmDetach)
mUvmDetach = std::make_shared<UvmDetach>(mId);
return mUvmDetach->attachUvmBuffer(bufferFd);
}
int32_t MesonLayer::attachUvmBuffer(const std::shared_ptr<VtBufferItem> item){
if (!item->mIsSolidBuffer)
return attachUvmBuffer(item->mBufferFd);
return 0;
}
int32_t MesonLayer::detachUvmBuffer() {
if (mUvmDetach)
return mUvmDetach->detachUvmBuffer();
return 0;
}
int32_t MesonLayer::collectUvmBuffer(const int fd, const int fence) {
if (mUvmDetach) {
return mUvmDetach->collectUvmBuffer(fd, fence);
} else {
if (fd >= 0) close(fd);
if (fence >= 0 ) close(fence);
}
return 0;
}
int32_t MesonLayer::collectUvmBuffer(const std::shared_ptr<VtBufferItem> item) {
if (!item)
return 0;
if (!item->mIsSolidBuffer) {
int bufferFd = dup(item->mBufferFd);
int fenceFd = item->getReleaseFenceFd();
collectUvmBuffer(bufferFd, fenceFd);
}
return 0;
}
int32_t MesonLayer::releaseUvmResourceLock() {
if (mPreUvmBufferFd >= 0)
close(mPreUvmBufferFd);
mPreUvmBufferFd = -1;
if (mUvmDetach)
return mUvmDetach->releaseUvmResource();
return 0;
}