358 lines
13 KiB
C++
358 lines
13 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 <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <sys/epoll.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "wayland-client-protocol.h"
|
|
#include "xdg-shell-client-protocol.h"
|
|
#include "viewporter-client-protocol.h"
|
|
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
|
#include "fullscreen-shell-unstable-v1-client-protocol.h"
|
|
#include "wayland_display.h"
|
|
#include "wayland_buffer.h"
|
|
#include "Logger.h"
|
|
#include "wayland_videoformat.h"
|
|
#include "wayland_dma.h"
|
|
#include "wayland_shm.h"
|
|
#include "Utils.h"
|
|
#include "ErrorCode.h"
|
|
#include "Times.h"
|
|
|
|
#define TAG "rlib:wayland_buffer"
|
|
#define ENABLE_MORE_LOG (0)
|
|
|
|
WaylandBuffer::WaylandBuffer(WaylandDisplay *display, int logCategory, int cookieId)
|
|
: mDisplay(display),
|
|
mLogCategory(logCategory)
|
|
{
|
|
mRenderBuffer = NULL;
|
|
mWaylandWlWrap = NULL;
|
|
mUsedByCompositor = false;
|
|
mRedrawingPending = false;
|
|
mRealTime = -1;
|
|
mBufferFormat = VIDEO_FORMAT_UNKNOWN;
|
|
mFrameWidth = 0;
|
|
mFrameHeight = 0;
|
|
mState = BUFFER_IDLE;
|
|
mCookieId = cookieId;
|
|
mCallback = NULL;
|
|
if (ENABLE_MORE_LOG) {
|
|
TRACE(mLogCategory,"new WaylandBuffer:%p,cookie:%d",this, mCookieId);
|
|
}
|
|
}
|
|
|
|
WaylandBuffer::~WaylandBuffer()
|
|
{
|
|
if (ENABLE_MORE_LOG) {
|
|
TRACE(mLogCategory,"delete WaylandBuffer:%p,DmaBuffer:%p,cookie:%d",this,mWaylandWlWrap,mCookieId);
|
|
}
|
|
/*if weston obtains the wl_buffer,we need
|
|
* notify user to release renderBuffer*/
|
|
if (mRenderBuffer && (mState == BUFFER_DISPLAYING || mState == BUFFER_DISPLAYED)) {
|
|
mDisplay->handleBufferReleaseCallback(this);
|
|
mRenderBuffer = NULL;
|
|
}
|
|
mLock.lock();
|
|
if (mWaylandWlWrap) {
|
|
delete mWaylandWlWrap;
|
|
mWaylandWlWrap = NULL;
|
|
}
|
|
mLock.unlock();
|
|
if (mCallback) {
|
|
wl_callback_destroy (mCallback);
|
|
mCallback = NULL;
|
|
}
|
|
mRenderBuffer = NULL;
|
|
mDisplay = NULL;
|
|
}
|
|
|
|
void WaylandBuffer::bufferRelease (void *data, struct wl_buffer *wl_buffer)
|
|
{
|
|
WaylandBuffer* waylandBuffer = static_cast<WaylandBuffer*>(data);
|
|
TRACE(waylandBuffer->mLogCategory,"--wl_buffer:%p(%p),renderBuffer:%p",wl_buffer,waylandBuffer,waylandBuffer->mRenderBuffer);
|
|
waylandBuffer->mUsedByCompositor = false;
|
|
//sometimes this callback be called twice
|
|
//this cause double free,so check mRenderBuffer
|
|
if (waylandBuffer->mRenderBuffer) {
|
|
if (waylandBuffer->mDisplay->isVideoOnOSD() && waylandBuffer->mState == BUFFER_DISPLAYING) { //osd displayed
|
|
waylandBuffer->mState = BUFFER_DISPLAYED;
|
|
waylandBuffer->mDisplay->handleFrameDisplayedCallback(waylandBuffer);
|
|
}
|
|
//check committed to weston but lost displayed or dropped callback
|
|
if (waylandBuffer->mState == BUFFER_DISPLAYING) {
|
|
DEBUG(waylandBuffer->mLogCategory,"lost callback,renderBuffer:%p,pts:%lld us",waylandBuffer->mRenderBuffer,waylandBuffer->mRenderBuffer->pts/1000);
|
|
waylandBuffer->mDisplay->handleFrameDisplayedCallback(waylandBuffer);
|
|
}
|
|
waylandBuffer->mDisplay->handleBufferReleaseCallback(waylandBuffer);
|
|
}
|
|
//double check waylandBuffer->mRenderBuffer and release it
|
|
waylandBuffer->mLock.lock();
|
|
if (waylandBuffer->mRenderBuffer) {
|
|
waylandBuffer->mRenderBuffer = NULL;
|
|
waylandBuffer->mRealTime = -1;
|
|
}
|
|
waylandBuffer->mLock.unlock();
|
|
|
|
//resolution changed,release buffers before resolution changed
|
|
if ((waylandBuffer->mCookieId < waylandBuffer->mDisplay->getWaylandBufferCookieId())) {
|
|
waylandBuffer->mLock.lock();
|
|
if (waylandBuffer->mWaylandWlWrap) {
|
|
DEBUG(waylandBuffer->mLogCategory,"drs,delete WaylandBuffer:%p,WaylandDmaBuffer:%p,cookie:%d",
|
|
waylandBuffer,waylandBuffer->mWaylandWlWrap,waylandBuffer->mCookieId);
|
|
delete waylandBuffer->mWaylandWlWrap;
|
|
waylandBuffer->mWaylandWlWrap = NULL;
|
|
}
|
|
waylandBuffer->mLock.unlock();
|
|
}
|
|
/*make sure release buffer first then do recycle buffer when
|
|
resolution changed*/
|
|
waylandBuffer->mState = BUFFER_IDLE;
|
|
}
|
|
|
|
void WaylandBuffer::bufferdroped (void *data, struct wl_buffer *wl_buffer)
|
|
{
|
|
WaylandBuffer* waylandBuffer = static_cast<WaylandBuffer*>(data);
|
|
WARNING(waylandBuffer->mLogCategory,"--dropped wl_buffer:%p(%p),renderBuffer:%p",wl_buffer,waylandBuffer,waylandBuffer->mRenderBuffer);
|
|
waylandBuffer->mUsedByCompositor = false;
|
|
|
|
waylandBuffer->mState = BUFFER_DISPLAYED;
|
|
if (waylandBuffer->mRenderBuffer) {
|
|
waylandBuffer->mDisplay->handleFrameDropedCallback(waylandBuffer);
|
|
waylandBuffer->mDisplay->handleBufferReleaseCallback(waylandBuffer);
|
|
}
|
|
waylandBuffer->mLock.lock();
|
|
if (waylandBuffer->mRenderBuffer) {
|
|
waylandBuffer->mRenderBuffer = NULL;
|
|
waylandBuffer->mRealTime = -1;
|
|
}
|
|
waylandBuffer->mLock.unlock();
|
|
|
|
//resolution changed,release buffers before resolution changed
|
|
if ((waylandBuffer->mCookieId < waylandBuffer->mDisplay->getWaylandBufferCookieId())) {
|
|
waylandBuffer->mLock.lock();
|
|
if (waylandBuffer->mWaylandWlWrap) {
|
|
DEBUG(waylandBuffer->mLogCategory,"drs,delete WaylandBuffer:%p,WaylandDmaBuffer:%p,cookie:%d",
|
|
waylandBuffer,waylandBuffer->mWaylandWlWrap,waylandBuffer->mCookieId);
|
|
delete waylandBuffer->mWaylandWlWrap;
|
|
waylandBuffer->mWaylandWlWrap = NULL;
|
|
}
|
|
waylandBuffer->mLock.unlock();
|
|
}
|
|
/*make sure release buffer first then do recycle buffer when
|
|
resolution changed*/
|
|
waylandBuffer->mState = BUFFER_IDLE;
|
|
}
|
|
|
|
void WaylandBuffer::bufferDisplayTime (void *data, struct wl_buffer *wl_buffer, uint32_t sec, uint32_t usec)
|
|
{
|
|
WaylandBuffer* waylandBuffer = static_cast<WaylandBuffer*>(data);
|
|
if (ENABLE_MORE_LOG) {
|
|
TRACE(waylandBuffer->mLogCategory,"wl_buffer:%p(%p)",wl_buffer,waylandBuffer);
|
|
}
|
|
waylandBuffer->mState = BUFFER_DISPLAYED;
|
|
if (waylandBuffer->mRenderBuffer) {
|
|
waylandBuffer->mDisplay->handleFrameTime(waylandBuffer, sec, usec);
|
|
}
|
|
}
|
|
|
|
static const struct wl_buffer_listener buffer_with_drop_listener = {
|
|
WaylandBuffer::bufferRelease,
|
|
WaylandBuffer::bufferdroped,
|
|
};
|
|
|
|
static const struct wl_buffer_listener buffer_with_drop_and_display_listener = {
|
|
WaylandBuffer::bufferRelease,
|
|
WaylandBuffer::bufferdroped,
|
|
WaylandBuffer::bufferDisplayTime,
|
|
};
|
|
|
|
static const struct wl_buffer_listener buffer_listener = {
|
|
WaylandBuffer::bufferRelease
|
|
};
|
|
|
|
/*if we commit buffers to weston too fast,it causes weston can't invoke this callback*/
|
|
void WaylandBuffer::frameCallback(void *data, struct wl_callback *callback, uint32_t time)
|
|
{
|
|
WaylandBuffer* waylandBuffer = static_cast<WaylandBuffer*>(data);
|
|
waylandBuffer->mDisplay->setRedrawingPending(false);
|
|
int64_t elapsed = 0;
|
|
int64_t nowTimeUs = ATools::Times::getSystemTimeUs();
|
|
//debug for detecting framecallback elapsed time
|
|
if (waylandBuffer->mDisplay->frameCallbackTimeUs == 0) {
|
|
waylandBuffer->mDisplay->frameCallbackTimeUs = nowTimeUs;
|
|
} else {
|
|
elapsed = (nowTimeUs - waylandBuffer->mDisplay->frameCallbackTimeUs)/1000;
|
|
waylandBuffer->mDisplay->frameCallbackTimeUs = nowTimeUs;
|
|
}
|
|
waylandBuffer->mLock.lock();
|
|
bool redrawing = waylandBuffer->mRedrawingPending;
|
|
waylandBuffer->mRedrawingPending = false;
|
|
waylandBuffer->mLock.unlock();
|
|
waylandBuffer->mState = BUFFER_DISPLAYED;
|
|
TRACE(waylandBuffer->mLogCategory,"--WaylandBuffer:%p,renderBuffer:%p,elapsed:%lld ms",waylandBuffer,waylandBuffer->mRenderBuffer,elapsed);
|
|
if (waylandBuffer->mRenderBuffer && redrawing) {
|
|
waylandBuffer->mDisplay->handleFrameDisplayedCallback(waylandBuffer);
|
|
}
|
|
if (callback == waylandBuffer->mCallback) {
|
|
waylandBuffer->mCallback = NULL;
|
|
}
|
|
wl_callback_destroy (callback);
|
|
}
|
|
|
|
static const struct wl_callback_listener frame_callback_listener = {
|
|
WaylandBuffer::frameCallback
|
|
};
|
|
|
|
int WaylandBuffer::constructWlBuffer(RenderBuffer *buf)
|
|
{
|
|
int ret = NO_ERROR;
|
|
struct wl_buffer * wlbuffer = NULL;
|
|
WaylandDisplay::AmlConfigAPIList *amlConfigAPI = mDisplay->getAmlConfigAPIList();
|
|
|
|
mLock.lock();
|
|
mRenderBuffer = buf;
|
|
mState = BUFFER_PREPARED;
|
|
|
|
if (!mWaylandWlWrap) {
|
|
if (ENABLE_MORE_LOG) {
|
|
TRACE(mLogCategory,"---planecnt:%d, format:%d,%d",buf->dma.planeCnt,mBufferFormat,is_direct_display_format(mBufferFormat));
|
|
}
|
|
//to osd display
|
|
if (!is_direct_display_format(mBufferFormat) && buf->dma.planeCnt == 1 && buf->dma.fd[0] >=0) {
|
|
mWaylandWlWrap = new WaylandShmBuffer(mDisplay, mLogCategory);
|
|
} else { //only NV21, NV12, YUYV format support dma buffer to video plane,otherwise to osd
|
|
mWaylandWlWrap = new WaylandDmaBuffer(mDisplay, mLogCategory);
|
|
}
|
|
}
|
|
|
|
if (mWaylandWlWrap) {
|
|
WaylandDmaBuffer *waylanddma = NULL;
|
|
WaylandShmBuffer *shmBuffer = NULL;
|
|
if (mWaylandWlWrap->getWlBuffer()) {
|
|
mLock.unlock();
|
|
return NO_ERROR;
|
|
}
|
|
//to osd display for I420 or RGB ...
|
|
if (!is_direct_display_format(mBufferFormat) && buf->dma.planeCnt == 1 && buf->dma.fd[0] >=0) {
|
|
shmBuffer = static_cast<WaylandShmBuffer*>(mWaylandWlWrap);
|
|
wlbuffer = shmBuffer->constructVideoWlBuffer(buf, mBufferFormat);
|
|
} else { //maybe to video plane or osd if buffer format support osd and plane cnt > 1
|
|
waylanddma = static_cast<WaylandDmaBuffer*>(mWaylandWlWrap);
|
|
wlbuffer = waylanddma->constructWlBuffer(&buf->dma, mBufferFormat);
|
|
}
|
|
if (wlbuffer) {
|
|
mFrameWidth = buf->dma.width;
|
|
mFrameHeight = buf->dma.height;
|
|
/*register buffer release listen*/
|
|
if (amlConfigAPI->enableSetPts && amlConfigAPI->enableDisplayTime) {
|
|
wl_buffer_add_listener (wlbuffer, &buffer_with_drop_and_display_listener, this);
|
|
} else if (amlConfigAPI->enableSetPts) {
|
|
wl_buffer_add_listener (wlbuffer, &buffer_with_drop_listener, this);
|
|
}else {
|
|
wl_buffer_add_listener (wlbuffer, &buffer_listener, this);
|
|
}
|
|
if (ENABLE_MORE_LOG) {
|
|
TRACE(mLogCategory,"WaylandBuffer:%p,WaylandDmaBuffer:%p,pts:%lld us %dx%d cookie:%d",
|
|
this,mWaylandWlWrap,buf->pts/1000,mFrameWidth,mFrameHeight,mCookieId);
|
|
}
|
|
} else {
|
|
mRenderBuffer = NULL;
|
|
mState = BUFFER_IDLE;
|
|
ret = ERROR_INVALID_OPERATION;
|
|
ERROR(mLogCategory,"no wlbuffer");
|
|
}
|
|
mLock.unlock();
|
|
//TRACE(mLogCategory,"WaylandBuffer:%p,WaylandDmaBuffer:%p,renderBuffer:%p,pts:%lld us,cookie:%d",this,mWaylandWlWrap,mRenderBuffer,buf->pts/1000,mCookieId);
|
|
return ret;
|
|
} else {
|
|
mRenderBuffer = NULL;
|
|
mState = BUFFER_IDLE;
|
|
ret = ERROR_INVALID_OPERATION;
|
|
ERROR(mLogCategory,"new WaylandBuffer failed");
|
|
}
|
|
mLock.unlock();
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct wl_buffer *WaylandBuffer::getWlBuffer()
|
|
{
|
|
if (mWaylandWlWrap) {
|
|
return mWaylandWlWrap->getWlBuffer();
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void WaylandBuffer::attach(struct wl_surface *surface)
|
|
{
|
|
struct wl_buffer *wlbuffer = NULL;
|
|
WaylandDisplay::AmlConfigAPIList *amlConfigAPI = mDisplay->getAmlConfigAPIList();
|
|
if (mUsedByCompositor) {
|
|
TRACE(mLogCategory,"buffer used by compositor");
|
|
return;
|
|
}
|
|
|
|
//callback when this frame displayed
|
|
if (amlConfigAPI->enableDisplayTime == false) {
|
|
mCallback = wl_surface_frame (surface);
|
|
wl_callback_add_listener (mCallback, &frame_callback_listener, this);
|
|
}
|
|
mDisplay->setRedrawingPending(true);
|
|
|
|
wlbuffer = getWlBuffer();
|
|
if (wlbuffer) {
|
|
wl_surface_attach (surface, wlbuffer, 0, 0);
|
|
}
|
|
mState = BUFFER_DISPLAYING;
|
|
mUsedByCompositor = true;
|
|
mRedrawingPending = true;
|
|
}
|
|
|
|
/**
|
|
* @brief check wayland buffer is free. if this is free
|
|
* ,it will be delete by waylandbuffer manager
|
|
*
|
|
* @return true if this buffer is free
|
|
* @return false if this buffer will be used
|
|
*/
|
|
bool WaylandBuffer::isFree()
|
|
{
|
|
ATools::Mutex::Autolock _l(mLock);
|
|
if (!mWaylandWlWrap && !mRenderBuffer) {
|
|
return true;
|
|
}
|
|
if (mState == BUFFER_IDLE && !mRenderBuffer) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
void WaylandBuffer::forceBufferStateIdle()
|
|
{
|
|
mLock.lock();
|
|
mState = BUFFER_IDLE;
|
|
mRenderBuffer = NULL;
|
|
mLock.unlock();
|
|
if (ENABLE_MORE_LOG) {
|
|
TRACE(mLogCategory,"WaylandBuffer:%p,DmaBuffer:%p,cookie:%d",this,mWaylandWlWrap,mCookieId);
|
|
}
|
|
} |