/* * 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 #include #include #include #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; mTunnelFd = -1; mNeedReleaseVtResource = false; mTunnelIdUpdate = false; mVtRefreshed = false; mNeedAskRefresh = false; mVtUpdate = false; mQueuedFrames = 0; mGameMode = false; mVideoDisplayStatus = VT_VIDEO_STATUS_SHOW; mPreUvmBufferFd = -1; mDifd = -1; } int32_t MesonLayer::setTunnelId(int tunnelId, std::shared_ptr observer) { mTunnelId = tunnelId; if (tunnelId >= 128) { setFbType(DRM_FB_VIDEO_UVM_DMA); mTunnelFd = meson_vt_open_by_mode(tunnelId >= 128 ? 0 : 1); 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() { DrmFramebufferBase::setZorder(mCurrZorder); } int32_t MesonLayer::setDisplayFrame(drm_rect_t dispFrame) { if (dispFrame.left <= 0 && dispFrame.top <= 0) setVideoPriority(0); else setVideoPriority(mIndex); 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(releaseFence), std::make_shared(processFence)); } return 0; } void MesonLayer::setPresentTime(nsecs_t expectedPresentTime) { std::lock_guard lock(mMutex); nsecs_t previousExpectedTime; previousExpectedTime = ((mExpectedPresentTime == 0) ? ns2us(expectedPresentTime) : mExpectedPresentTime); mExpectedPresentTime = ns2us(expectedPresentTime); 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 lock(mMutex); if (isVtBuffer()) return (shouldPresentNow(mCurBufferItem) && mVtUpdate) || mVtRefreshed; return mUpdated; } bool MesonLayer::shouldPresentNow(const std::shared_ptr 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 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))) { if (drop) expiredItemCount++; } } // drop expiredItemCount - 1 buffers dropCount = expiredItemCount - 1; while (true) { auto bufItem = mQueueItems.front(); if (dropCount <= 0) { break; } meson_vt_release_buffer(mTunnelFd, mTunnelId, bufItem->mBufferFd, -1); mQueueItems.pop_front(); dropCount--; } } void MesonLayer::updateBuffer() { if (!isVtBuffer()) { int buffer_fd = 0; int fence_fd = 0; int64_t present_time = 0; while (meson_vt_acquire_buffer(mTunnelFd, mTunnelId, &buffer_fd, &fence_fd, &present_time) == 0) { struct uvm_fd_info info = { 0 }; std::shared_ptr item = std::make_shared(buffer_fd, present_time, info); mQueueItems.push_back(item); } if (mQueueItems.empty()) return; } dropExpiredFrame(true); mUpdated = true; mCurBufferItem = mQueueItems[0]; int width = mSourceCrop.right - mSourceCrop.left; int height = mSourceCrop.bottom - mSourceCrop.top; if (shouldPresentNow(mCurBufferItem)) { setBufferInfo(width, height, VIDEO_ALIGN(width), DRM_FORMAT_NV21); } } void MesonLayer::setDiProcessorFd(int32_t fd) { std::lock_guard 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) { if (mCurBufferItem.get()) curFd = mCurBufferItem->mBufferFd; if (mPrevBufferItem.get()) prevFd = mPrevBufferItem->mBufferFd; if (shouldPresentNow(mCurBufferItem)) { fd = (mUpdated || mVtUpdate) ? curFd : prevFd; } else { fd = prevFd; } } 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 lock(mMutex); return getBufferFdLocked(sourceType); } int32_t MesonLayer::getBufferFd() { std::lock_guard lock(mMutex); return getBufferFdLocked(); } int32_t MesonLayer::releaseBuffer() { std::lock_guard 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; 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()); } 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::releaseResource() { std::lock_guard lock(mMutex); if (mPrevBufferItem.get()) { meson_vt_release_buffer( mTunnelFd, mTunnelId, mPrevBufferItem->mBufferFd, mPrevBufferItem->getReleaseFenceFd()); mPrevBufferItem.reset(); } if (mCurBufferItem.get()) { meson_vt_release_buffer( mTunnelFd, mTunnelId, mCurBufferItem->mBufferFd, mCurBufferItem->getReleaseFenceFd()); mQueueItems.pop_front(); mCurBufferItem.reset(); } if (!isVtBuffer()) { int buffer_fd = 0; int fence_fd = 0; int64_t present_time = 0; while (meson_vt_acquire_buffer(mTunnelFd, mTunnelId, &buffer_fd, &fence_fd, &present_time) == 0) { struct uvm_fd_info info = { 0 }; std::shared_ptr item = std::make_shared(buffer_fd, present_time, info); mQueueItems.push_back(item); } } // 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] [%" PRIu64 "] release buffer queuedFrames(%d)", __func__, mDisplayId, mId, mQueueItems.size()); } mQueueItems.clear(); mUpdated = false; return 0; } 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; } 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> & 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 (mTunnelId, mDisplayId, mId); if (!mContentListener) mContentListener = std::make_shared(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 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 & 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 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 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::releaseVtBuffer() { std::lock_guard 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 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 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 lock(mMutex); releaseUvmResourceLock(); if (mUvmDetach) return mUvmDetach->setEnable(enable); return 0; } int32_t MesonLayer::attachUvmBuffer(const int bufferFd) { if (!mUvmDetach) mUvmDetach = std::make_shared(mId); return mUvmDetach->attachUvmBuffer(bufferFd); } int32_t MesonLayer::attachUvmBuffer(const std::shared_ptr 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 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; }