/* * Copyright (C) 2021 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 "Logger.h" #include "drm_display.h" #include "drm_framepost.h" #include "ErrorCode.h" #include "Times.h" using namespace ATools; #define TAG "rlib:drm_framepost" #define UNDER_FLOW_EXPIRED_TIME_MS 83 DrmFramePost::DrmFramePost(DrmDisplay *drmDisplay,int logCategory) { mDrmDisplay = drmDisplay; mLogCategory = logCategory; mPaused = false; mStop = false; mImmediatelyOutput = false; mQueue = new ATools::Queue(); mWinRect.x = 0; mWinRect.y = 0; mWinRect.w = 0; mWinRect.h = 0; mUnderFlowDetect = false; mSignalFirstFramePts = false; } DrmFramePost::~DrmFramePost() { if (isRunning()) { mStop = true; DEBUG(mLogCategory,"stop frame post thread"); requestExitAndWait(); } if (mQueue) { mQueue->flush(); delete mQueue; mQueue = NULL; } } bool DrmFramePost::start() { mSignalFirstFramePts = false; DEBUG(mLogCategory,"start frame post thread"); run("frame post thread"); return true; } bool DrmFramePost::stop() { DEBUG(mLogCategory,"stop frame post thread"); if (isRunning()) { mStop = true; DEBUG(mLogCategory,"stop frame post thread"); requestExitAndWait(); } flush(); return true; } bool DrmFramePost::readyPostFrame(FrameEntity * frameEntity) { ATools::Mutex::Autolock _l(mMutex); mQueue->push(frameEntity); if (mUnderFlowDetect) { mUnderFlowDetect = false; } TRACE(mLogCategory,"queue cnt:%d",mQueue->getCnt()); return true; } void DrmFramePost::flush() { FrameEntity *entity; ATools::Mutex::Autolock _l(mMutex); while (mQueue->pop((void **)&entity) == Q_OK) { mDrmDisplay->handleDroppedFrameEntity(entity); mDrmDisplay->handleReleaseFrameEntity(entity); } } void DrmFramePost::pause() { mPaused = true; } void DrmFramePost::resume() { mPaused = false; } void DrmFramePost::setImmediatelyOutout(bool on) { mImmediatelyOutput = on; DEBUG(mLogCategory,"set immediately output:%d",on); } void DrmFramePost::setWindowSize(int x, int y, int w, int h) { mWinRect.x = x; mWinRect.y = y; mWinRect.w = w; mWinRect.h = h; } void DrmFramePost::readyToRun() { } bool DrmFramePost::threadLoop() { int rc; drmVBlank vbl; int64_t vBlankTime; FrameEntity *curFrameEntity = NULL; FrameEntity *expiredFrameEntity = NULL; long long refreshInterval= 0LL; struct drm_display *drmHandle = mDrmDisplay->getDrmHandle(); DrmMesonLib *drmMesonLib = mDrmDisplay->getDrmMesonLib(); //stop thread if (mStop) { return false; } if (!drmHandle) { ERROR(mLogCategory,"drm handle is NULL"); return false; } memset(&vbl, 0, sizeof(drmVBlank)); vbl.request.type = DRM_VBLANK_RELATIVE; vbl.request.sequence = 1; vbl.request.signal = 0; //drmWaitBland need link libdrm.so rc = drmWaitVBlank(drmHandle->drm_fd, &vbl); if (rc) { ERROR(mLogCategory,"drmWaitVBlank error %d", rc); usleep(4000); return true; } if (drmHandle->vrefresh) { refreshInterval= (1000000LL+(drmHandle->vrefresh/2))/drmHandle->vrefresh; } vBlankTime = vbl.reply.tval_sec*1000000LL + vbl.reply.tval_usec; vBlankTime += refreshInterval; //check next blank time ATools::Mutex::Autolock _l(mMutex); //if queue is empty or paused, loop next if (mQueue->getCnt() < 1) { //TRACE(mLogCategory,"empty or paused"); if (mQueue->isEmpty() && ATools::Times::getSystemTimeMs() - mLastDisplayTime > UNDER_FLOW_EXPIRED_TIME_MS && !mUnderFlowDetect) { mUnderFlowDetect = true; mDrmDisplay->handleMsg(MSG_UNDER_FLOW, NULL); } goto tag_next; } //we output video frame asap if (mImmediatelyOutput) { //pop the peeked frame mQueue->pop((void **)&expiredFrameEntity); goto tag_post; } while (mQueue->peek((void **)&curFrameEntity, 0) == Q_OK) { TRACE(mLogCategory,"vBlankTime:%lld,frame time:%lld(pts:%lld ms),refreshInterval:%lld",vBlankTime,curFrameEntity->displayTime,curFrameEntity->renderBuf->pts/1000000,refreshInterval); //no frame expired,loop next if (vBlankTime < curFrameEntity->displayTime) { break; } //pop the peeked frame mQueue->pop((void **)&curFrameEntity); //drop last expired frame,got a new expired frame if (expiredFrameEntity) { DEBUG(mLogCategory,"drop frame,vBlankTime:%lld,frame time:%lld(pts:%lld ms)",vBlankTime,expiredFrameEntity->displayTime,expiredFrameEntity->renderBuf->pts/1000000); mDrmDisplay->handleDroppedFrameEntity(expiredFrameEntity); mDrmDisplay->handleReleaseFrameEntity(expiredFrameEntity); expiredFrameEntity = NULL; } expiredFrameEntity = curFrameEntity; TRACE(mLogCategory,"expire,frame time:%lld(pts:%lld ms)",expiredFrameEntity->displayTime,expiredFrameEntity->renderBuf->pts/1000000); } tag_post: //no frame will be posted to drm display if (!expiredFrameEntity) { TRACE(mLogCategory,"no frame expire"); goto tag_next; } //double check window size before post drm buffer if (mWinRect.w > 0 && mWinRect.h > 0) { struct drm_buf * drmBuf = expiredFrameEntity->drmBuf; drmBuf->crtc_x = mWinRect.x; drmBuf->crtc_y = mWinRect.y; drmBuf->crtc_w = mWinRect.w; drmBuf->crtc_h = mWinRect.h; TRACE(mLogCategory,"postBuf crtc(%d,%d,%d,%d),src(%d,%d,%d,%d)",drmBuf->crtc_x,drmBuf->crtc_y,drmBuf->crtc_w,drmBuf->crtc_h, drmBuf->src_x,drmBuf->src_y,drmBuf->src_w,drmBuf->src_h); } if (drmHandle && drmMesonLib) { rc = drmMesonLib->libDrmPostBuf(drmHandle, expiredFrameEntity->drmBuf); } if (rc) { ERROR(mLogCategory, "drm_post_buf error %d", rc); mDrmDisplay->handleDroppedFrameEntity(expiredFrameEntity); mDrmDisplay->handleReleaseFrameEntity(expiredFrameEntity); goto tag_next; } mLastDisplayTime = ATools::Times::getSystemTimeMs(); TRACE(mLogCategory,"drm_post_buf,frame time:%lld(pts:%lld ms)",expiredFrameEntity->displayTime,expiredFrameEntity->renderBuf->pts/1000000); if (!mSignalFirstFramePts) { mSignalFirstFramePts = true; int64_t firstPts = expiredFrameEntity->renderBuf->pts; mDrmDisplay->handleMsg(MSG_FIRST_FRAME, (void*)&firstPts); } //notify player buffer had displayed than recycle buffer mDrmDisplay->handleDisplayedFrameEntity(expiredFrameEntity); mDrmDisplay->handlePostedFrameEntity(expiredFrameEntity); tag_next: return true; }