260 lines
7.2 KiB
C++
260 lines
7.2 KiB
C++
/*
|
|||
|
|
* 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 <errno.h>
|
||
|
|
#include <sys/mman.h>
|
||
|
|
#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;
|
||
|
|
}
|