458 lines
12 KiB
C++
458 lines
12 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 <fcntl.h>
|
|
#include <drm/drm_fourcc.h>
|
|
#include "meson_drm_util.h"
|
|
#include "drm_display.h"
|
|
#include "drm_framepost.h"
|
|
#include "drm_framerecycle.h"
|
|
#include "Thread.h"
|
|
#include "Queue.h"
|
|
#include "Logger.h"
|
|
#include "ErrorCode.h"
|
|
|
|
using namespace ATools;
|
|
|
|
#define TAG "rlib:drm_display"
|
|
|
|
#define BLACK_FRAME_WIDTH 64
|
|
#define BLACK_FRAME_HEIGHT 64
|
|
|
|
DrmDisplay::DrmDisplay(DrmPlugin *plugin, int logcategory)
|
|
: mPlugin(plugin),
|
|
mLogCategory(logcategory)
|
|
{
|
|
mDrmHandle = NULL;
|
|
mIsPip = false;
|
|
mDrmFramePost = NULL;
|
|
mDrmFrameRecycle = NULL;
|
|
mVideoFormat = VIDEO_FORMAT_UNKNOWN;
|
|
mWinRect.x = 0;
|
|
mWinRect.y = 0;
|
|
mWinRect.w = 0;
|
|
mWinRect.h = 0;
|
|
mFrameWidth = 0;
|
|
mFrameHeight = 0;
|
|
mHideVideo = false;
|
|
mKeepLastFrame = false;
|
|
mDrmMesonLib = drmMesonLoadLib(logcategory);
|
|
}
|
|
|
|
DrmDisplay::~DrmDisplay()
|
|
{
|
|
if (mDrmHandle) {
|
|
if (mDrmMesonLib) {
|
|
mDrmMesonLib->libDrmDisplayDestroy(mDrmHandle);
|
|
mDrmHandle = NULL;
|
|
}
|
|
}
|
|
//free dlopen resources
|
|
if (mDrmMesonLib) {
|
|
drmMesonUnloadLib(mLogCategory, mDrmMesonLib);
|
|
mDrmMesonLib = NULL;
|
|
}
|
|
}
|
|
|
|
bool DrmDisplay::start(bool pip)
|
|
{
|
|
mIsPip = pip;
|
|
|
|
DEBUG(mLogCategory, "start pip:%d",pip);
|
|
if (mDrmMesonLib) {
|
|
mDrmHandle = mDrmMesonLib->libDrmDisplayInit();
|
|
}
|
|
|
|
if (!mDrmHandle) {
|
|
ERROR(mLogCategory, "drm display init failed");
|
|
return false;
|
|
}
|
|
#ifdef SUPPORT_DRM_FREEZEN
|
|
//set keep last frame if needed;
|
|
if (mKeepLastFrame) {
|
|
mDrmHandle->freeze = 1;
|
|
} else {
|
|
mDrmHandle->freeze = 0;
|
|
}
|
|
INFO(mLogCategory, "set keep last frame %d", mDrmHandle->freeze);
|
|
#endif
|
|
|
|
if (!mDrmFramePost) {
|
|
mDrmFramePost = new DrmFramePost(this, mLogCategory);
|
|
mDrmFramePost->start();
|
|
/*if window size set, we set to frame post,this needed
|
|
when yts resize case,window size must effect when post
|
|
buffer to drm
|
|
*/
|
|
if (mWinRect.w > 0 && mWinRect.h > 0) {
|
|
mDrmFramePost->setWindowSize(mWinRect.x, mWinRect.y, mWinRect.w, mWinRect.h);
|
|
}
|
|
}
|
|
|
|
if (!mDrmFrameRecycle) {
|
|
mDrmFrameRecycle = new DrmFrameRecycle(this,mLogCategory);
|
|
mDrmFrameRecycle->start();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DrmDisplay::stop()
|
|
{
|
|
if (mDrmFramePost) {
|
|
DEBUG(mLogCategory, "stop frame post thread");
|
|
mDrmFramePost->stop();
|
|
delete mDrmFramePost;
|
|
mDrmFramePost = NULL;
|
|
}
|
|
if (mDrmFrameRecycle) {
|
|
DEBUG(mLogCategory, "stop frame recycle thread");
|
|
mDrmFrameRecycle->stop();
|
|
delete mDrmFrameRecycle;
|
|
mDrmFrameRecycle = NULL;
|
|
}
|
|
|
|
//if video plane hided,we should unhide here,otherwise next playback will no video showed
|
|
if (mHideVideo) {
|
|
setHideVideo(false);
|
|
}
|
|
if (mDrmHandle) {
|
|
if (mDrmMesonLib) {
|
|
mDrmMesonLib->libDrmDisplayDestroy(mDrmHandle);
|
|
mDrmHandle = NULL;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool DrmDisplay::displayFrame(RenderBuffer *buf, int64_t displayTime)
|
|
{
|
|
int ret;
|
|
|
|
FrameEntity *frameEntity = createFrameEntity(buf, displayTime);
|
|
if (frameEntity) {
|
|
if (mDrmFramePost) {
|
|
mDrmFramePost->readyPostFrame(frameEntity);
|
|
} else {
|
|
WARNING(mLogCategory,"no frame post service");
|
|
handleDroppedFrameEntity(frameEntity);
|
|
handleReleaseFrameEntity(frameEntity);
|
|
}
|
|
} else { //drop and release render buffer
|
|
if (mPlugin) {
|
|
mPlugin->handleFrameDropped(buf);
|
|
mPlugin->handleBufferRelease(buf);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void DrmDisplay::flush()
|
|
{
|
|
DEBUG(mLogCategory,"flush");
|
|
ATools::Mutex::Autolock _l(mMutex);
|
|
if (mDrmFramePost) {
|
|
mDrmFramePost->flush();
|
|
}
|
|
}
|
|
|
|
void DrmDisplay::pause()
|
|
{
|
|
mDrmFramePost->pause();
|
|
}
|
|
|
|
void DrmDisplay::resume()
|
|
{
|
|
mDrmFramePost->resume();
|
|
}
|
|
|
|
void DrmDisplay::setVideoFormat(RenderVideoFormat videoFormat)
|
|
{
|
|
mVideoFormat = videoFormat;
|
|
DEBUG(mLogCategory,"video format:%d",mVideoFormat);
|
|
}
|
|
|
|
void DrmDisplay::setWindowSize(int x, int y, int w, int h)
|
|
{
|
|
mWinRect.x = x;
|
|
mWinRect.y = y;
|
|
mWinRect.w = w;
|
|
mWinRect.h = h;
|
|
DEBUG(mLogCategory,"window size:(%dx%dx%dx%d)",x, y,w,h);
|
|
if (mDrmFramePost) {
|
|
mDrmFramePost->setWindowSize(x, y, w, h);
|
|
}
|
|
}
|
|
|
|
void DrmDisplay::setFrameSize(int width, int height)
|
|
{
|
|
mFrameWidth = width;
|
|
mFrameHeight = height;
|
|
DEBUG(mLogCategory,"frame size:%dx%d",width, height);
|
|
}
|
|
|
|
/**
|
|
* @brief only disable_plane takes effect for showing
|
|
* black frame. so don't alloc drm buffer from drm.
|
|
* disable_plane flag will cause drm to destroy the pipeline
|
|
* if we want to show frame again,we should send new frame to drm.
|
|
*/
|
|
void DrmDisplay::showBlackFrame()
|
|
{
|
|
int rc;
|
|
struct drm_buf drmBuf;
|
|
|
|
memset(&drmBuf, 0 , sizeof(struct drm_buf));
|
|
|
|
if (!mIsPip)
|
|
drmBuf.flags |= MESON_USE_VD1;
|
|
else
|
|
drmBuf.flags |= MESON_USE_VD2;
|
|
|
|
//disable plane,only this flag can take effect for black screen
|
|
drmBuf.disable_plane = 1;
|
|
INFO(mLogCategory,"flags:0x%x disable_plane:%d",drmBuf.flags, drmBuf.disable_plane);
|
|
|
|
//post black frame buf to drm
|
|
if (mDrmHandle && mDrmMesonLib) {
|
|
rc = mDrmMesonLib->libDrmPostBuf(mDrmHandle, &drmBuf);
|
|
}
|
|
if (rc) {
|
|
ERROR(mLogCategory, "post black frame to drm failed");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void DrmDisplay::setImmediatelyOutout(bool on)
|
|
{
|
|
if (mDrmFramePost) {
|
|
mDrmFramePost->setImmediatelyOutout(on);
|
|
}
|
|
}
|
|
|
|
void DrmDisplay::setHideVideo(bool hide) {
|
|
INFO(mLogCategory,"hide video:%d",hide);
|
|
mHideVideo = hide;
|
|
if (mDrmHandle && mDrmMesonLib && mDrmMesonLib->libDrmMutePlane) {
|
|
unsigned int planeType = 1; //0:osd plane,1:video plane
|
|
unsigned int planeMute; //0: unmute,1:mute
|
|
if (hide) {
|
|
planeMute = 1; //1:mute
|
|
INFO(mLogCategory,"mute video");
|
|
} else {
|
|
planeMute = 0; //0: unmute
|
|
INFO(mLogCategory,"unmute video");
|
|
}
|
|
mDrmMesonLib->libDrmMutePlane(mDrmHandle->drm_fd, planeType, planeMute);
|
|
} else {
|
|
ERROR(mLogCategory, "Error dlsym drm_mute_plane");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief set keep last frame or not
|
|
*
|
|
* @param keep true keep last frame,false is not keep last frame
|
|
*/
|
|
void DrmDisplay::setKeepLastFrame(bool keep)
|
|
{
|
|
int kp = keep? 1: 0;
|
|
mKeepLastFrame = keep;
|
|
|
|
#ifdef SUPPORT_DRM_FREEZEN
|
|
if (mDrmHandle) {
|
|
mDrmHandle->freeze = kp;
|
|
}
|
|
INFO(mLogCategory, "set keep last frame %d(%d)", keep,mDrmHandle->freeze);
|
|
if (!kp) {
|
|
INFO(mLogCategory, "set keep last frame %d, show black frame to drm", kp);
|
|
showBlackFrame();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
FrameEntity *DrmDisplay::createFrameEntity(RenderBuffer *buf, int64_t displayTime)
|
|
{
|
|
struct drm_buf_import info;
|
|
FrameEntity* frame = NULL;
|
|
struct drm_buf * drmBuf = NULL;
|
|
int displayWidth = 0, displayHeight = 0;
|
|
int r_flags = -1;
|
|
|
|
memset(&info, 0 , sizeof(struct drm_buf_import));
|
|
|
|
info.fd[0] = -1;
|
|
info.fd[1] = -1;
|
|
info.fd[2] = -1;
|
|
info.fd[3] = -1;
|
|
|
|
frame = (FrameEntity*)calloc(1, sizeof(FrameEntity));
|
|
if (!frame) {
|
|
ERROR(mLogCategory,"oom calloc FrameEntity mem failed");
|
|
goto tag_error;
|
|
}
|
|
|
|
frame->displayTime = displayTime;
|
|
frame->renderBuf = buf;
|
|
|
|
info.width = buf->dma.width;
|
|
info.height = buf->dma.height;
|
|
info.flags = 0;
|
|
switch (mVideoFormat)
|
|
{
|
|
case VIDEO_FORMAT_NV21: {
|
|
info.fourcc = DRM_FORMAT_NV21;
|
|
} break;
|
|
case VIDEO_FORMAT_NV12: {
|
|
info.fourcc = DRM_FORMAT_NV12;
|
|
} break;
|
|
|
|
default: {
|
|
info.fourcc = DRM_FORMAT_YUYV;
|
|
WARNING(mLogCategory,"unknown video format, set to default YUYV format");
|
|
}
|
|
}
|
|
|
|
if (!mIsPip) {
|
|
info.flags |= MESON_USE_VD1;
|
|
} else {
|
|
info.flags |= MESON_USE_VD2;
|
|
}
|
|
|
|
/*warning: must dup fd, because drm_free_buf will close buf fd
|
|
if not dup buf fd, fd will be double free*/
|
|
for (int i = 0; i < buf->dma.planeCnt; i++) {
|
|
info.fd[i] = dup(buf->dma.fd[i]);
|
|
// FD_CLOEXEC
|
|
r_flags = fcntl(info.fd[i], F_GETFD);
|
|
r_flags |= FD_CLOEXEC;
|
|
fcntl(info.fd[i], F_SETFD, r_flags);
|
|
TRACE(mLogCategory,"dup fd[%d]:%d->%d",i,buf->dma.fd[i],info.fd[i]);
|
|
}
|
|
|
|
if (mDrmHandle && mDrmMesonLib) {
|
|
drmBuf = mDrmMesonLib->libDrmImportBuf(mDrmHandle, &info);
|
|
}
|
|
|
|
if (!drmBuf) {
|
|
ERROR(mLogCategory, "unable drm_import_buf");
|
|
goto tag_error;
|
|
}
|
|
|
|
//set source content's size
|
|
drmBuf->src_x = 0;
|
|
drmBuf->src_y = 0;
|
|
drmBuf->src_w = buf->dma.width;
|
|
drmBuf->src_h = buf->dma.height;
|
|
|
|
/*default set window size to full screen
|
|
*if window size set,the crtc size will be reset
|
|
*before post drm buffer
|
|
*/
|
|
//get current display mode
|
|
if (mDrmHandle && mDrmMesonLib) {
|
|
DisplayMode displayMode;
|
|
mDrmMesonLib->libDrmGetModeInfo(mDrmHandle->drm_fd, MESON_CONNECTOR_RESERVED, &displayMode);
|
|
displayWidth = displayMode.w;
|
|
displayHeight = displayMode.h;
|
|
TRACE(mLogCategory, "displayWidth:%d,displayHeight:%d",displayWidth,displayHeight);
|
|
}
|
|
drmBuf->crtc_x = 0;
|
|
drmBuf->crtc_y = 0;
|
|
if (displayWidth > 0 && displayHeight > 0) {
|
|
drmBuf->crtc_w = displayWidth;
|
|
drmBuf->crtc_h = displayHeight;
|
|
} else {
|
|
drmBuf->crtc_w = mDrmHandle->width;
|
|
drmBuf->crtc_h = mDrmHandle->height;
|
|
}
|
|
|
|
frame->drmBuf = drmBuf;
|
|
TRACE(mLogCategory,"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);
|
|
|
|
return frame;
|
|
tag_error:
|
|
for (int i = 0; i < buf->dma.planeCnt; i++) {
|
|
if (info.fd[i] > 0) {
|
|
close(info.fd[i]);
|
|
info.fd[i] = -1;
|
|
}
|
|
}
|
|
if (frame) {
|
|
free(frame);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void DrmDisplay::destroyFrameEntity(FrameEntity * frameEntity)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (!frameEntity) {
|
|
return;
|
|
}
|
|
if (frameEntity->drmBuf) {
|
|
if (mDrmMesonLib) {
|
|
rc = mDrmMesonLib->libDrmFreeBuf(frameEntity->drmBuf);
|
|
}
|
|
|
|
if (rc) {
|
|
WARNING(mLogCategory, "drm_free_buf free %p failed",frameEntity->drmBuf);
|
|
}
|
|
TRACE(mLogCategory,"drm_free_buf displaytime:%lld(pts:%lld ms)",frameEntity->displayTime,frameEntity->renderBuf->pts/1000000);
|
|
}
|
|
free(frameEntity);
|
|
frameEntity = NULL;
|
|
}
|
|
|
|
void DrmDisplay::handlePostedFrameEntity(FrameEntity * frameEntity)
|
|
{
|
|
if (mDrmFrameRecycle) {
|
|
mDrmFrameRecycle->recycleFrame(frameEntity);
|
|
}
|
|
}
|
|
|
|
void DrmDisplay::handleDroppedFrameEntity(FrameEntity * frameEntity)
|
|
{
|
|
if (mPlugin) {
|
|
mPlugin->handleFrameDropped(frameEntity->renderBuf);
|
|
}
|
|
}
|
|
|
|
void DrmDisplay::handleDisplayedFrameEntity(FrameEntity * frameEntity)
|
|
{
|
|
if (mPlugin) {
|
|
mPlugin->handleFrameDisplayed(frameEntity->renderBuf);
|
|
}
|
|
}
|
|
|
|
void DrmDisplay::handleReleaseFrameEntity(FrameEntity * frameEntity)
|
|
{
|
|
if (mPlugin) {
|
|
mPlugin->handleBufferRelease(frameEntity->renderBuf);
|
|
}
|
|
destroyFrameEntity(frameEntity);
|
|
}
|
|
|
|
void DrmDisplay::handleMsg(int type, void *detail)
|
|
{
|
|
if (mPlugin) {
|
|
mPlugin->handleMsgNotify(type, detail);
|
|
}
|
|
}
|