blob: fd5fbbf2c7e6cacef344189f1fbc04bb21d15129 [file] [log] [blame]
/*
* 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 <string>
#include "wayland_display.h"
#include "ErrorCode.h"
#include "Logger.h"
#include "wayland_plugin.h"
#include "wayland_videoformat.h"
#include "wayland_shm.h"
#include "wayland_dma.h"
#include "wayland_buffer.h"
#ifndef MAX
# define MAX(a,b) ((a) > (b)? (a) : (b))
# define MIN(a,b) ((a) < (b)? (a) : (b))
#endif
#define UNUSED_PARAM(x) ((void)(x))
#define INVALID_OUTPUT_INDEX (-1)
#define TAG "rlib:wayland_display"
#define FRAME_FREEZE_THRESHOLD(x) (x*1.67)
#define DETECT_FRAMERATE_CNT (5)
void WaylandDisplay::dmabuf_modifiers(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
Tls::Mutex::Autolock _l(self->mMutex);
if (wl_dmabuf_format_to_video_format (format) != VIDEO_FORMAT_UNKNOWN) {
TRACE(self->mLogCategory,"regist dmabuffer format:%d (%s) hi:%x,lo:%x",format,print_dmabuf_format_name(format),modifier_hi,modifier_lo);
uint64_t modifier = ((uint64_t)modifier_hi << 32) | modifier_lo;
auto item = self->mDmaBufferFormats.find(format);
if (item == self->mDmaBufferFormats.end()) {
std::pair<uint32_t ,uint64_t> item(format, modifier);
self->mDmaBufferFormats.insert(item);
} else { //found format
item->second = modifier;
}
}
}
void
WaylandDisplay::dmaBufferFormat (void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf,
uint32_t format)
{
#if 0
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
if (wl_dmabuf_format_to_video_format (format) != VIDEO_FORMAT_UNKNOWN) {
TRACE(mLogCategory,"regist dmabuffer format:%d : %s",format);
//self->mDmaBufferFormats.push_back(format);
}
#endif
/* XXX: deprecated */
}
static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
WaylandDisplay::dmaBufferFormat,
WaylandDisplay::dmabuf_modifiers
};
static void
handle_xdg_wm_base_ping (void *user_data, struct xdg_wm_base *xdg_wm_base,
uint32_t serial)
{
xdg_wm_base_pong (xdg_wm_base, serial);
}
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
handle_xdg_wm_base_ping
};
void WaylandDisplay::shmFormat(void *data, struct wl_shm *wl_shm, uint32_t format)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
self->mShmFormats.push_back(format);
}
static const struct wl_shm_listener shm_listener = {
WaylandDisplay::shmFormat
};
void WaylandDisplay::outputHandleGeometry( void *data,
struct wl_output *output,
int x,
int y,
int physicalWidth,
int physicalHeight,
int subPixel,
const char *make,
const char *model,
int transform )
{
UNUSED_PARAM(make);
UNUSED_PARAM(model);
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
DEBUG(self->mLogCategory,"wl_output %p x:%d,y:%d,physicalWidth:%d,physicalHeight:%d,subPixel:%d,trans:%d",
output,x, y,physicalWidth, physicalHeight,subPixel,transform);
Tls::Mutex::Autolock _l(self->mMutex);
for (int i = 0; i < DEFAULT_DISPLAY_OUTPUT_NUM; i++) {
if (output == self->mOutput[i].wlOutput) {
self->mOutput[i].offsetX = x;
self->mOutput[i].offsetY = y;
}
}
}
void WaylandDisplay::outputHandleMode( void *data,
struct wl_output *output,
uint32_t flags,
int width,
int height,
int refreshRate )
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
if ( flags & WL_OUTPUT_MODE_CURRENT ) {
Tls::Mutex::Autolock _l(self->mMutex);
for (int i = 0; i < DEFAULT_DISPLAY_OUTPUT_NUM; i++) {
if (output == self->mOutput[i].wlOutput) {
self->mOutput[i].width = width;
self->mOutput[i].height = height;
self->mOutput[i].refreshRate = refreshRate;
}
}
DEBUG(self->mLogCategory,"wl_output: %p (%dx%d) refreshrate:%d,select output index %d",output, width, height,refreshRate,self->mSelectOutputIndex);
if (self->mCurrentDisplayOutput->width > 0 &&
self->mCurrentDisplayOutput->height > 0) {
self->updateDisplayOutput();
}
}
}
void WaylandDisplay::outputHandleDone( void *data,
struct wl_output *output )
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
DEBUG(self->mLogCategory,"wl_output: %p",output);
UNUSED_PARAM(data);
UNUSED_PARAM(output);
}
void WaylandDisplay::outputHandleScale( void *data,
struct wl_output *output,
int32_t scale )
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
DEBUG(self->mLogCategory,"wl_output: %p scale %d",output, scale);
UNUSED_PARAM(data);
UNUSED_PARAM(output);
UNUSED_PARAM(scale);
}
void WaylandDisplay::outputHandleCrtcIndex( void *data,
struct wl_output *output,
int32_t index )
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
DEBUG(self->mLogCategory,"wl_output: %p crtc index %d",output, index);
Tls::Mutex::Autolock _l(self->mMutex);
for (int i = 0; i < DEFAULT_DISPLAY_OUTPUT_NUM; i++) {
if (output == self->mOutput[i].wlOutput) {
self->mOutput[i].crtcIndex = index;
}
}
}
//aml weston always add crtcIndex in callbacks.
static const struct wl_output_listener outputListener = {
WaylandDisplay::outputHandleGeometry,
WaylandDisplay::outputHandleMode,
WaylandDisplay::outputHandleDone,
WaylandDisplay::outputHandleScale,
WaylandDisplay::outputHandleCrtcIndex,
};
void WaylandDisplay::pointerHandleEnter(void *data, struct wl_pointer *pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t sx, wl_fixed_t sy)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
if (self->mFullScreen) {
wl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
}
}
void WaylandDisplay::pointerHandleLeave(void *data, struct wl_pointer *pointer,
uint32_t serial, struct wl_surface *surface)
{
UNUSED_PARAM(data);
UNUSED_PARAM(pointer);
UNUSED_PARAM(serial);
UNUSED_PARAM(surface);
}
void WaylandDisplay::pointerHandleMotion(void *data, struct wl_pointer *pointer,
uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
int x = wl_fixed_to_int(sx);
int y = wl_fixed_to_int(sy);
DEBUG(self->mLogCategory,"pointer motion fixed[%d, %d] to-int: x[%d] y[%d]\n", sx, sy, x, y);
}
void WaylandDisplay::pointerHandleButton(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
{
UNUSED_PARAM(data);
UNUSED_PARAM(wl_pointer);
UNUSED_PARAM(time);
UNUSED_PARAM(serial);
UNUSED_PARAM(button);
UNUSED_PARAM(state);
}
void WaylandDisplay::pointerHandleAxis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value)
{
UNUSED_PARAM(data);
UNUSED_PARAM(wl_pointer);
UNUSED_PARAM(time);
UNUSED_PARAM(axis);
UNUSED_PARAM(value);
}
static const struct wl_pointer_listener pointer_listener = {
WaylandDisplay::pointerHandleEnter,
WaylandDisplay::pointerHandleLeave,
WaylandDisplay::pointerHandleMotion,
WaylandDisplay::pointerHandleButton,
WaylandDisplay::pointerHandleAxis,
};
void WaylandDisplay::touchHandleDown(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, struct wl_surface *surface,
int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
}
void WaylandDisplay::touchHandleUp(void *data, struct wl_touch *wl_touch,
uint32_t serial, uint32_t time, int32_t id)
{
UNUSED_PARAM(data);
UNUSED_PARAM(wl_touch);
UNUSED_PARAM(serial);
UNUSED_PARAM(time);
UNUSED_PARAM(id);
}
void WaylandDisplay::touchHandleMotion(void *data, struct wl_touch *wl_touch,
uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
{
UNUSED_PARAM(data);
UNUSED_PARAM(wl_touch);
UNUSED_PARAM(time);
UNUSED_PARAM(id);
UNUSED_PARAM(x_w);
UNUSED_PARAM(y_w);
}
void WaylandDisplay::touchHandleFrame(void *data, struct wl_touch *wl_touch)
{
UNUSED_PARAM(data);
UNUSED_PARAM(wl_touch);
}
void WaylandDisplay::touchHandleCancel(void *data, struct wl_touch *wl_touch)
{
UNUSED_PARAM(data);
UNUSED_PARAM(wl_touch);
}
static const struct wl_touch_listener touch_listener = {
WaylandDisplay::touchHandleDown,
WaylandDisplay::touchHandleUp,
WaylandDisplay::touchHandleMotion,
WaylandDisplay::touchHandleFrame,
WaylandDisplay::touchHandleCancel,
};
void WaylandDisplay::keyboardHandleKeymap(void *data, struct wl_keyboard *keyboard,
uint32_t format, int fd, uint32_t size)
{
UNUSED_PARAM(data);
UNUSED_PARAM(keyboard);
UNUSED_PARAM(format);
UNUSED_PARAM(fd);
UNUSED_PARAM(size);
}
void WaylandDisplay::keyboardHandleEnter(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys)
{
UNUSED_PARAM(data);
UNUSED_PARAM(keyboard);
UNUSED_PARAM(serial);
UNUSED_PARAM(surface);
UNUSED_PARAM(keys);
}
void WaylandDisplay::keyboardHandleLeave(void *data, struct wl_keyboard *keyboard,
uint32_t serial, struct wl_surface *surface)
{
UNUSED_PARAM(data);
UNUSED_PARAM(keyboard);
UNUSED_PARAM(serial);
UNUSED_PARAM(surface);
}
void WaylandDisplay::keyboardHandleKey(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
UNUSED_PARAM(data);
UNUSED_PARAM(keyboard);
UNUSED_PARAM(serial);
UNUSED_PARAM(time);
UNUSED_PARAM(key);
UNUSED_PARAM(state);
}
void WaylandDisplay::keyboardHandleModifiers(void *data, struct wl_keyboard *keyboard,
uint32_t serial, uint32_t mods_depressed,
uint32_t mods_latched, uint32_t mods_locked,
uint32_t group)
{
UNUSED_PARAM(data);
UNUSED_PARAM(keyboard);
UNUSED_PARAM(serial);
UNUSED_PARAM(mods_depressed);
UNUSED_PARAM(mods_latched);
UNUSED_PARAM(mods_locked);
UNUSED_PARAM(group);
}
static const struct wl_keyboard_listener keyboard_listener = {
WaylandDisplay::keyboardHandleKeymap,
WaylandDisplay::keyboardHandleEnter,
WaylandDisplay::keyboardHandleLeave,
WaylandDisplay::keyboardHandleKey,
WaylandDisplay::keyboardHandleModifiers,
};
void WaylandDisplay::seatHandleCapabilities(void *data, struct wl_seat *seat,
uint32_t caps)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !self->mPointer) {
self->mPointer = wl_seat_get_pointer(seat);
wl_pointer_add_listener(self->mPointer, &pointer_listener, data);
} else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && self->mPointer) {
wl_pointer_destroy(self->mPointer);
self->mPointer = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !self->mKeyboard) {
self->mKeyboard = wl_seat_get_keyboard(seat);
wl_keyboard_add_listener(self->mKeyboard, &keyboard_listener, data);
} else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && self->mKeyboard) {
wl_keyboard_destroy(self->mKeyboard);
self->mKeyboard = NULL;
}
if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !self->mTouch) {
self->mTouch = wl_seat_get_touch(seat);
wl_touch_set_user_data(self->mTouch, data);
wl_touch_add_listener(self->mTouch, &touch_listener, data);
} else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && self->mTouch) {
wl_touch_destroy(self->mTouch);
self->mTouch = NULL;
}
}
static const struct wl_seat_listener seat_listener = {
WaylandDisplay::seatHandleCapabilities,
};
void WaylandDisplay::handleXdgToplevelClose (void *data, struct xdg_toplevel *xdg_toplevel)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
INFO(self->mLogCategory,"XDG toplevel got a close event.");
}
void WaylandDisplay::handleXdgToplevelConfigure (void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height, struct wl_array *states)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
uint32_t *state;
INFO(self->mLogCategory, "XDG toplevel got a configure event, width:height [ %d, %d ].", width, height);
/*
wl_array_for_each (state, states) {
switch (*state) {
case XDG_TOPLEVEL_STATE_FULLSCREEN:
case XDG_TOPLEVEL_STATE_MAXIMIZED:
case XDG_TOPLEVEL_STATE_RESIZING:
case XDG_TOPLEVEL_STATE_ACTIVATED:
break;
}
}
*/
if (width <= 0 || height <= 0)
return;
if (width == self->mCurrentDisplayOutput->width && height == self->mCurrentDisplayOutput->height && self->mUpdateRenderRectangle) {
self->mUpdateRenderRectangle = false;
self->setRenderRectangle(self->mCurrentDisplayOutput->offsetX,
self->mCurrentDisplayOutput->offsetY,
self->mCurrentDisplayOutput->width,
self->mCurrentDisplayOutput->height);
} else{
self->setRenderRectangle(self->mRenderRect.x, self->mRenderRect.y, width, height);
}
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
WaylandDisplay::handleXdgToplevelConfigure,
WaylandDisplay::handleXdgToplevelClose,
};
void WaylandDisplay::handleXdgSurfaceConfigure (void *data, struct xdg_surface *xdg_surface,
uint32_t serial)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
xdg_surface_ack_configure (xdg_surface, serial);
TRACE(self->mLogCategory,"handleXdgSurfaceConfigure");
Tls::Mutex::Autolock _l(self->mConfigureMutex);
self->mXdgSurfaceConfigured = true;
self->updateDisplayOutput();
}
static const struct xdg_surface_listener xdg_surface_listener = {
WaylandDisplay::handleXdgSurfaceConfigure,
};
void WaylandDisplay::handleSurfaceDestroy(void *data, struct wl_callback *callback, uint32_t time)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
INFO(self->mLogCategory,"handle video surface destroy");
Tls::Mutex::Autolock _l(self->mMutex);
self->mCondition.signal();
wl_callback_destroy (callback);
}
static const struct wl_callback_listener surface_destroy_listener = {
WaylandDisplay::handleSurfaceDestroy,
};
void WaylandDisplay::amlConfigure(void *data, struct aml_config *config, const char *list) {
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
TRACE(self->mLogCategory,"aml_config:%s",list);
if (list && strlen(list) > 0) {
if (strstr(list, "set_video_plane")) {
TRACE(self->mLogCategory,"weston enable set_video_plane");
self->mAmlConfigAPIList.enableSetVideoPlane = true;
}
if (strstr(list, "set_pts")) {
TRACE(self->mLogCategory,"weston enable set_pts");
self->mAmlConfigAPIList.enableSetPts = true;
}
if (strstr(list, "drop")) {
TRACE(self->mLogCategory,"weston enable drop");
self->mAmlConfigAPIList.enableDropFrame = true;
}
if (strstr(list, "keep_last_frame")) {
TRACE(self->mLogCategory,"weston enable keep_last_frame");
self->mAmlConfigAPIList.enableKeepLastFrame = true;
}
if (strstr(list, "surface_destroy_cb")) {
TRACE(self->mLogCategory,"weston enable surface_destroy_cb");
self->mAmlConfigAPIList.enableSurfaceDestroyCallback = true;
}
if (strstr(list, "set_display_rate")) {
TRACE(self->mLogCategory,"weston enable set_display_rate");
self->mAmlConfigAPIList.enableSetDisplayRate = true;
}
if (strstr(list, "set_surface_invisible")) {
TRACE(self->mLogCategory,"weston enable set_surface_invisible");
self->mAmlConfigAPIList.enableSetSurfaceInvisible = true;
}
if (strstr(list, "display_time")) {
TRACE(self->mLogCategory,"weston enable display_time");
self->mAmlConfigAPIList.enableDisplayTime = true;
}
}
}
static const struct aml_config_listener aml_config_listener = {
WaylandDisplay::amlConfigure,
};
void
WaylandDisplay::registryHandleGlobal (void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
TRACE(self->mLogCategory,"registryHandleGlobal,name:%u,interface:%s,version:%d",name,interface,version);
if (strcmp (interface, "wl_compositor") == 0) {
self->mCompositor = (struct wl_compositor *)wl_registry_bind (registry, name, &wl_compositor_interface, version);
} else if (strcmp (interface, "wl_subcompositor") == 0) {
self->mSubCompositor = (struct wl_subcompositor *)wl_registry_bind (registry, name, &wl_subcompositor_interface, 1);
} else if (strcmp (interface, "xdg_wm_base") == 0) {
self->mXdgWmBase = (struct xdg_wm_base *)wl_registry_bind (registry, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener (self->mXdgWmBase, &xdg_wm_base_listener, (void *)self);
} else if (strcmp (interface, "wl_shm") == 0) {
self->mShm = (struct wl_shm *)wl_registry_bind (registry, name, &wl_shm_interface, 1);
wl_shm_add_listener (self->mShm, &shm_listener, self);
} else if (strcmp (interface, "zwp_fullscreen_shell_v1") == 0) {
//self->mFullscreenShell = (struct zwp_fullscreen_shell_v1 *)wl_registry_bind (registry, name,
// &zwp_fullscreen_shell_v1_interface, 1);
} else if (strcmp (interface, "wp_viewporter") == 0) {
self->mViewporter = (struct wp_viewporter *)wl_registry_bind (registry, name, &wp_viewporter_interface, 1);
} else if (strcmp (interface, "zwp_linux_dmabuf_v1") == 0) {
if (version < 3)
return;
self->mDmabuf = (struct zwp_linux_dmabuf_v1 *)wl_registry_bind (registry, name, &zwp_linux_dmabuf_v1_interface, 3);
zwp_linux_dmabuf_v1_add_listener (self->mDmabuf, &dmabuf_listener, (void *)self);
} else if (strcmp (interface, "wl_output") == 0) {
int i = 0;
uint32_t oriName = self->mCurrentDisplayOutput->name;
for (int i = 0; i < DEFAULT_DISPLAY_OUTPUT_NUM; i++) {
if (self->mOutput[i].wlOutput == NULL) {
self->mOutput[i].name = name;
self->mOutput[i].wlOutput = (struct wl_output*)wl_registry_bind(registry, name, &wl_output_interface, version);
TRACE(self->mLogCategory,"name:%u, wl_output:%p, select:%d",self->mOutput[i].name,self->mOutput[i].wlOutput,self->mSelectOutputIndex);
wl_output_add_listener(self->mOutput[i].wlOutput, &outputListener, (void *)self);
if (i == 0) { //primary wl_output
self->mOutput[i].isPrimary = true;
}
break;
}
}
if (i == DEFAULT_DISPLAY_OUTPUT_NUM) {
WARNING(self->mLogCategory,"Not enough free output");
}
//select current wl_output
if (self->mSelectOutputIndex != INVALID_OUTPUT_INDEX && !self->isRunning()) {
TRACE(self->mLogCategory,"select %d output",self->mSelectOutputIndex);
self->mCurrentDisplayOutput = &self->mOutput[self->mSelectOutputIndex];
}
//if user select a wrong output index, we using a suiteble wl_output
if (self->mCurrentDisplayOutput->wlOutput == NULL) {
WARNING(self->mLogCategory,"wl_output is null,we should find a suiteble output");
for (i = 0; i < DEFAULT_DISPLAY_OUTPUT_NUM; i++) {
if (self->mOutput[i].wlOutput) {
self->mCurrentDisplayOutput = &self->mOutput[i];
break;
}
}
}
//if current wl_output update, we should update render rectangle
if (self->mCurrentDisplayOutput->name != oriName) {
self->mUpdateRenderRectangle = true;
}
//if wl_output plugin,active sending frame
self->setRedrawingPending(false);
} else if (strcmp(interface, "wl_seat") == 0) {
//self->mSeat = (struct wl_seat *)wl_registry_bind(registry, name, &wl_seat_interface, 1);
//wl_seat_add_listener(self->mSeat, &seat_listener, (void *)self);
} else if (strcmp(interface, "weston_direct_display_v1") == 0) {
self->mDirect_display = (struct weston_direct_display_v1 *)wl_registry_bind(registry,name, &weston_direct_display_v1_interface, 1);
} else if (strcmp(interface, "aml_config") == 0) {
self->mAmlConfig = (struct aml_config*)wl_registry_bind(registry, name, &aml_config_interface, 1);
aml_config_add_listener(self->mAmlConfig, &aml_config_listener, (void *)self);
}
}
void
WaylandDisplay::registryHandleGlobalRemove (void *data, struct wl_registry *registry, uint32_t name)
{
int i;
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
/* check wl_output changed */
DEBUG(self->mLogCategory,"wayland display remove registry handle global,name:%u",name);
//if user selected wl_output removed, reset selected output index
if (self->mSelectOutputIndex != INVALID_OUTPUT_INDEX &&
self->mOutput[self->mSelectOutputIndex].wlOutput &&
self->mOutput[self->mSelectOutputIndex].name == name) {
self->mSelectOutputIndex = INVALID_OUTPUT_INDEX;
}
for (i = 0; i < DEFAULT_DISPLAY_OUTPUT_NUM; i++) {
if (self->mOutput[i].name == name) {
DEBUG(self->mLogCategory,"remove wl_output name:%u,wl_output:%p",name,self->mOutput[i].wlOutput);
self->mOutput[i].name = 0;
self->mOutput[i].wlOutput = NULL;
}
}
//if current output removed, select a suiteble output
if (self->mCurrentDisplayOutput->wlOutput == NULL) {
for (i = 0; i < DEFAULT_DISPLAY_OUTPUT_NUM; i++) {
if (self->mOutput[i].wlOutput) {
self->mCurrentDisplayOutput = &self->mOutput[i];
self->mUpdateRenderRectangle = true;
}
}
//set new output rectangle
if (self->mUpdateRenderRectangle) {
self->mUpdateRenderRectangle = false;
self->setRenderRectangle(self->mCurrentDisplayOutput->offsetX,
self->mCurrentDisplayOutput->offsetY,
self->mCurrentDisplayOutput->width,
self->mCurrentDisplayOutput->height);
}
}
}
static const struct wl_registry_listener registry_listener = {
WaylandDisplay::registryHandleGlobal,
WaylandDisplay::registryHandleGlobalRemove
};
WaylandDisplay::WaylandDisplay(WaylandPlugin *plugin, int logCategory)
:mBufferMutex("bufferMutex"),
mWaylandPlugin(plugin),
mLogCategory(logCategory)
{
TRACE(mLogCategory,"construct WaylandDisplay");
mWlDisplay = NULL;
mWlDisplayWrapper = NULL;
mWlQueue = NULL;
mRegistry = NULL;
mCompositor = NULL;
mXdgWmBase = NULL;
mViewporter = NULL;
mDmabuf = NULL;
mShm = NULL;
mSeat = NULL;
mPointer = NULL;
mTouch = NULL;
mKeyboard = NULL;
mDirect_display = NULL;
mSelectOutputIndex = INVALID_OUTPUT_INDEX;
mPoll = new Tls::Poll(true);
//window
mVideoWidth = 0;
mVideoHeight = 0;
mVideoSurface = NULL;
mXdgSurface = NULL;
mXdgToplevel = NULL;
mAreaViewport = NULL;
mVideoViewport = NULL;
mAreaShmBuffer = NULL;
mCommitCnt = 0;
mAreaSurface = NULL;
mAreaSurfaceWrapper = NULL;
mVideoSurfaceWrapper = NULL;
mVideoSubSurface = NULL;
mPip = 0;
mKeepLastFrame = 0; //default is off keep last frame
mKeepFrameOnFlush = 1;
mFirstPtsAfterFlush = -1;
mSignalFirstFramePts = false;
mToSendKeepLastFrame = false;
mVideoPlaneZorder = -1;
mVideoPlaneZorderChanged = false;
mVideoRotateDegree = -1;
}
WaylandDisplay::~WaylandDisplay()
{
TRACE(mLogCategory,"desconstruct WaylandDisplay");
if (mPoll) {
delete mPoll;
mPoll = NULL;
}
}
char *WaylandDisplay::require_xdg_runtime_dir()
{
char *val = getenv("XDG_RUNTIME_DIR");
INFO(mLogCategory,"XDG_RUNTIME_DIR=%s",val);
//if not set XDG_RUNTIME_DIR,set default value
// if (!val) {
// val = const_cast<char *>("/run/user/0");
// setenv("XDG_RUNTIME_DIR",val,0);
// WARNING(mLogCategory,"XDG_RUNTIME_DIR is not set,set default %s",val);
// }
return val;
}
int WaylandDisplay::openDisplay()
{
frameCallbackTimeUs = 0;
mLastDisplayFramePts = -1;
mLastDisplayFrameTimeUs = -1;
mFrameDurationUs = 0;
mPreFramePts = -1;
mFrameRateDetectCnt = 0;
mFrameRateDetectPeriod = 0;
mPixelAspectRatio = 1.0;
mUpdateVideoSurface = false;
mNoBorderUpdate = false;
mReCommitAreaSurface = false;
mXdgSurfaceConfigured = false;
mUpdateRenderRectangle = false;
mFrameRateFractionNum = 0;
mFrameRateFractionDenom = 1;
mFrameRateChanged = false;
mVideoPlaneZorder = -1;
mVideoPlaneZorderChanged = false;
memset(&mRenderRect, 0, sizeof(struct Rectangle));
memset(&mVideoRect, 0, sizeof(struct Rectangle));
memset(&mWindowRect, 0, sizeof(struct Rectangle));
mFullScreen = true; //default is full screen
mAmlConfig = NULL;
//weston config private api
mAmlConfigAPIList.enableDropFrame = false;
mAmlConfigAPIList.enableKeepLastFrame = false;
mAmlConfigAPIList.enableSetPts = false;
mAmlConfigAPIList.enableSetVideoPlane = false;
mAmlConfigAPIList.enableSurfaceDestroyCallback = false;
mAmlConfigAPIList.enableSetDisplayRate = false;
mAmlConfigAPIList.enableSetSurfaceInvisible = false;
mAmlConfigAPIList.enableDisplayTime = false;
mCurrentDisplayOutput = &mOutput[0];
for (int i = 0; i < DEFAULT_DISPLAY_OUTPUT_NUM; i++) {
mOutput[i].wlOutput = NULL;
mOutput[i].offsetX = 0;
mOutput[i].offsetY = 0;
mOutput[i].width = 0;
mOutput[i].height = 0;
mOutput[i].refreshRate = 0;
mOutput[i].isPrimary = false;
mOutput[i].name = 0;
mOutput[i].crtcIndex = 0;
}
/*if mKeepLastFrame had set but mToSendKeepLastFrame is false,maybe
playback is doing FF/FW action,so we keep set it on new connection*/
if (!mToSendKeepLastFrame && mKeepLastFrame) {
mToSendKeepLastFrame = true;
}
char *name = require_xdg_runtime_dir();
//DEBUG(mLogCategory,"name:%s",name);
DEBUG(mLogCategory,"openDisplay in");
mWlDisplay = wl_display_connect(NULL);
if (!mWlDisplay) {
ERROR(mLogCategory,"Failed to connect to the wayland display, XDG_RUNTIME_DIR='%s'",
name ? name : "NULL");
return ERROR_OPEN_FAIL;
}
mWlDisplayWrapper = (struct wl_display *)wl_proxy_create_wrapper ((void *)mWlDisplay);
mWlQueue = wl_display_create_queue (mWlDisplay);
wl_proxy_set_queue ((struct wl_proxy *)mWlDisplayWrapper, mWlQueue);
mRegistry = wl_display_get_registry (mWlDisplayWrapper);
wl_registry_add_listener (mRegistry, &registry_listener, (void *)this);
/* we need exactly 2 roundtrips to discover global objects and their state */
for (int i = 0; i < 2; i++) {
if (wl_display_roundtrip_queue (mWlDisplay, mWlQueue) < 0) {
ERROR(mLogCategory,"Error communicating with the wayland display");
return ERROR_OPEN_FAIL;
}
}
if (!mCompositor) {
ERROR(mLogCategory,"Could not bind to wl_compositor. Either it is not implemented in " \
"the compositor, or the implemented version doesn't match");
return ERROR_OPEN_FAIL;
}
if (!mDmabuf) {
ERROR(mLogCategory,"Could not bind to zwp_linux_dmabuf_v1");
return ERROR_OPEN_FAIL;
}
if (!mXdgWmBase) {
/* If wl_surface and wl_display are passed via GstContext
* wl_shell, xdg_shell and zwp_fullscreen_shell are not used.
* In this case is correct to continue.
*/
ERROR(mLogCategory,"Could not bind to either wl_shell, xdg_wm_base or "
"zwp_fullscreen_shell, video display may not work properly.");
return ERROR_OPEN_FAIL;
}
//create window surface
createCommonWindowSurface();
createXdgShellWindowSurface();
//config weston video plane
if (mAmlConfigAPIList.enableSetVideoPlane) {
INFO(mLogCategory,"set weston video plane:%d",mPip);
wl_surface_set_video_plane(mVideoSurfaceWrapper, mPip);
}
//run wl display queue dispatch
DEBUG(mLogCategory,"To run wl display dispatch queue");
if (mPoll) {
mPoll->setFlushing(false);
}
setThreadPriority(60);
run("waylandProtocolThread");
mRedrawingPending = false;
wl_surface_set_video_plane_mute(mVideoSurfaceWrapper, 0);
DEBUG(mLogCategory,"openDisplay out");
return NO_ERROR;
}
void WaylandDisplay::closeDisplay()
{
DEBUG(mLogCategory,"closeDisplay in");
//first destroy window surface
destroyWindowSurfaces();
//flush pending event to weston
if (mWlDisplay) {
wl_display_flush (mWlDisplay);
}
//wait video surface destroyed or 50ms timeout
if (mAmlConfigAPIList.enableSurfaceDestroyCallback) {
INFO(mLogCategory,"waiting surface_destroy_cb from weston");
Tls::Mutex::Autolock _l(mMutex);
if (ERROR_TIMED_OUT == mCondition.waitRelative(mMutex, 50/*microsecond*/)) {
WARNING(mLogCategory,"waited surface_destroy_cb timeout");
}
}
//we should receive all event from weston before stopped dispatch queue
if (isRunning()) {
TRACE(mLogCategory,"try stop dispatch thread");
if (mPoll) {
mPoll->setFlushing(true);
}
requestExitAndWait();
}
//after destroyed surface,then destroy buffers,otherwise maybe crash
if (mAreaShmBuffer) {
delete mAreaShmBuffer;
mAreaShmBuffer = NULL;
}
//clean all wayland buffers
cleanAllWaylandBuffer();
if (mViewporter) {
wp_viewporter_destroy (mViewporter);
mViewporter = NULL;
}
if (mDmabuf) {
zwp_linux_dmabuf_v1_destroy (mDmabuf);
mDmabuf = NULL;
}
if (mXdgWmBase) {
xdg_wm_base_destroy (mXdgWmBase);
mXdgWmBase = NULL;
}
if (mCompositor) {
wl_compositor_destroy (mCompositor);
mCompositor = NULL;
}
if (mSubCompositor) {
wl_subcompositor_destroy (mSubCompositor);
mSubCompositor = NULL;
}
if (mRegistry) {
wl_registry_destroy (mRegistry);
mRegistry= NULL;
}
if (mWlDisplayWrapper) {
wl_proxy_wrapper_destroy (mWlDisplayWrapper);
mWlDisplayWrapper = NULL;
}
if (mAmlConfig) {
aml_config_destroy(mAmlConfig);
mAmlConfig = NULL;
}
if (mWlQueue) {
wl_event_queue_destroy (mWlQueue);
mWlQueue = NULL;
}
if (mWlDisplay) {
wl_display_flush (mWlDisplay);
wl_display_disconnect (mWlDisplay);
mWlDisplay = NULL;
}
DEBUG(mLogCategory,"closeDisplay out");
}
int WaylandDisplay::toDmaBufferFormat(RenderVideoFormat format, uint32_t *outDmaformat /*out param*/, uint64_t *outDmaformatModifiers /*out param*/)
{
if (!outDmaformat || !outDmaformatModifiers) {
WARNING(mLogCategory,"NULL params");
return ERROR_PARAM_NULL;
}
*outDmaformat = 0;
*outDmaformatModifiers = 0;
uint32_t dmaformat = video_format_to_wl_dmabuf_format (format);
if (dmaformat == -1) {
ERROR(mLogCategory,"Error not found render video format:%d to wl dmabuf format",format);
return ERROR_NOT_FOUND;
}
TRACE(mLogCategory,"render video format:%d -> dmabuf format:%d",format,dmaformat);
*outDmaformat = (uint32_t)dmaformat;
/*get dmaformat and modifiers*/
auto item = mDmaBufferFormats.find(dmaformat);
if (item == mDmaBufferFormats.end()) { //not found
WARNING(mLogCategory,"Not found dmabuf for render video format :%d",format);
*outDmaformatModifiers = 0;
return NO_ERROR;
}
*outDmaformatModifiers = (uint64_t)item->second;
return NO_ERROR;
}
int WaylandDisplay::toShmBufferFormat(RenderVideoFormat format, uint32_t *outformat)
{
if (!outformat) {
WARNING(mLogCategory,"NULL params");
return ERROR_PARAM_NULL;
}
*outformat = 0;
int shmformat = (int)video_format_to_wl_shm_format(format);
if (shmformat < 0) {
ERROR(mLogCategory,"Error not found render video format:%d to wl shmbuf format",format);
return ERROR_NOT_FOUND;
}
for (auto item = mShmFormats.begin(); item != mShmFormats.end(); ++item) {
uint32_t registFormat = (uint32_t)*item;
if (registFormat == (uint32_t)shmformat) {
*outformat = registFormat;
return NO_ERROR;
}
}
return ERROR_NOT_FOUND;
}
void WaylandDisplay::setVideoBufferFormat(RenderVideoFormat format)
{
TRACE(mLogCategory,"set video buffer format: %d",format);
mBufferFormat = format;
};
void WaylandDisplay::setDisplayOutput(int output)
{
TRACE(mLogCategory,"select display output: %d",output);
if (output < 0 || output >= DEFAULT_DISPLAY_OUTPUT_NUM) {
ERROR(mLogCategory, "display output index error,please set 0:primary or 1:extend,now:%d",output);
return;
}
//only do select output before video playing
if (mSelectOutputIndex != output) {
mSelectOutputIndex = output;
// if (mOutput[output].wlOutput) {
// mCurrentDisplayOutput = &mOutput[output];
// setRenderRectangle(mOutput[output].offsetX, mOutput[output].offsetY,
// mOutput[output].width, mOutput[output].height);
// }
}
}
int WaylandDisplay::getDisplayOutput()
{
return mSelectOutputIndex == INVALID_OUTPUT_INDEX? 0: mSelectOutputIndex;
}
void WaylandDisplay::setPip(int pip)
{
INFO(mLogCategory,"set pip:%d",pip);
mPip = pip;
}
void WaylandDisplay::updateDisplayOutput()
{
if (!mCurrentDisplayOutput->wlOutput || !mXdgToplevel || !mXdgSurface)
{
return;
}
if (mUpdateRenderRectangle) {
if (mFullScreen) {
DEBUG(mLogCategory,"unset full screen");
xdg_toplevel_unset_fullscreen (mXdgToplevel);
}
if (mXdgSurface) {
DEBUG(mLogCategory,"set geometry");
xdg_surface_set_window_geometry(mXdgSurface,
mCurrentDisplayOutput->offsetX,
mCurrentDisplayOutput->offsetY,
mCurrentDisplayOutput->width,
mCurrentDisplayOutput->height);
}
if (mFullScreen && mXdgToplevel) {
DEBUG(mLogCategory,"set full screen");
xdg_toplevel_set_fullscreen (mXdgToplevel, mCurrentDisplayOutput->wlOutput);
}
setRenderRectangle(mCurrentDisplayOutput->offsetX, mCurrentDisplayOutput->offsetY,
mCurrentDisplayOutput->width, mCurrentDisplayOutput->height);
mUpdateRenderRectangle = false;
}
}
void WaylandDisplay::createCommonWindowSurface()
{
struct wl_region *region;
mAreaSurface = wl_compositor_create_surface (mCompositor);
mVideoSurface = wl_compositor_create_surface (mCompositor);
mAreaSurfaceWrapper = (struct wl_surface *)wl_proxy_create_wrapper (mAreaSurface);
mVideoSurfaceWrapper = (struct wl_surface *)wl_proxy_create_wrapper (mVideoSurface);
wl_proxy_set_queue ((struct wl_proxy *) mAreaSurfaceWrapper, mWlQueue);
wl_proxy_set_queue ((struct wl_proxy *) mVideoSurfaceWrapper, mWlQueue);
/* embed video_surface in area_surface */
mVideoSubSurface = wl_subcompositor_get_subsurface (mSubCompositor, mVideoSurface, mAreaSurface);
wl_subsurface_set_desync (mVideoSubSurface);
//add video surface callback to weston if weston enable this feature
if (mVideoSurface && mAmlConfigAPIList.enableSurfaceDestroyCallback) {
struct wl_callback *surfaceDestroyCb = wl_surface_destroy_callback(mVideoSurface);
wl_callback_add_listener(surfaceDestroyCb, &surface_destroy_listener, this);
}
if (mViewporter) {
mAreaViewport = wp_viewporter_get_viewport (mViewporter, mAreaSurface);
mVideoViewport = wp_viewporter_get_viewport (mViewporter, mVideoSurface);
}
/*set area surface to invisible. prevent frame droped at start playback,
wl_surface_set_invisible must called before mAreaSurface commit called*/
if (mAmlConfigAPIList.enableSetSurfaceInvisible) {
int invisible = 1;
INFO(mLogCategory,"set surface invisible:%d",invisible);
wl_surface_set_invisible(mAreaSurfaceWrapper, invisible);
}
if (mAmlConfigAPIList.enableDisplayTime) {
INFO(mLogCategory,"set enable display time");
wl_surface_enable_display_time(mVideoSurface, 1);
}
/* do not accept input */
region = wl_compositor_create_region (mCompositor);
wl_surface_set_input_region (mAreaSurface, region);
wl_region_destroy (region);
region = wl_compositor_create_region (mCompositor);
wl_surface_set_input_region (mVideoSurface, region);
wl_region_destroy (region);
}
void WaylandDisplay::createXdgShellWindowSurface()
{
/* Check which protocol we will use (in order of preference) */
if (mXdgWmBase) {
/* First create the XDG surface */
mXdgSurface= xdg_wm_base_get_xdg_surface (mXdgWmBase, mAreaSurface);
if (!mXdgSurface) {
ERROR(mLogCategory,"Unable to get xdg_surface");
return;
}
xdg_surface_add_listener (mXdgSurface, &xdg_surface_listener,(void *)this);
/* Then the toplevel */
mXdgToplevel= xdg_surface_get_toplevel (mXdgSurface);
if (!mXdgSurface) {
ERROR(mLogCategory,"Unable to get xdg_toplevel");
return;
}
xdg_toplevel_add_listener (mXdgToplevel, &xdg_toplevel_listener, this);
/* Finally, commit the xdg_surface state as toplevel */
mXdgSurfaceConfigured = false;
wl_surface_commit (mAreaSurface);
wl_display_flush (mWlDisplay);
/* we need exactly 3 roundtrips to discover global objects and their state */
for (int i = 0; i < 3; i++) {
if (wl_display_roundtrip_queue(mWlDisplay, mWlQueue) < 0) {
ERROR(mLogCategory,"Error communicating with the wayland display");
}
}
if (mXdgSurfaceConfigured) {
INFO(mLogCategory,"xdg surface had configured");
} else {
WARNING(mLogCategory,"xdg surface not configured");
}
//full screen show
// if (mFullScreen && mCurrentDisplayOutput->wlOutput) {
// //ensureFullscreen(mFullScreen);
// }
} else {
ERROR(mLogCategory,"Unable to use xdg_wm_base ");
return;
}
}
void WaylandDisplay::destroyWindowSurfaces()
{
if (mXdgToplevel) {
xdg_toplevel_destroy (mXdgToplevel);
mXdgToplevel = NULL;
}
if (mXdgSurface) {
xdg_surface_destroy (mXdgSurface);
mXdgSurface = NULL;
}
if (mVideoSurfaceWrapper) {
wl_proxy_wrapper_destroy (mVideoSurfaceWrapper);
mVideoSurfaceWrapper = NULL;
}
if (mVideoSubSurface) {
wl_subsurface_destroy (mVideoSubSurface);
mVideoSubSurface = NULL;
}
if (mVideoSurface) {
wl_surface_destroy (mVideoSurface);
mVideoSurface = NULL;
}
if (mAreaSurfaceWrapper) {
wl_proxy_wrapper_destroy (mAreaSurfaceWrapper);
mAreaSurfaceWrapper = NULL;
}
if (mAreaSurface) {
wl_surface_destroy (mAreaSurface);
mAreaSurface = NULL;
mReCommitAreaSurface = false;
}
}
void WaylandDisplay::ensureFullscreen(bool fullscreen)
{
if (mXdgWmBase) {
DEBUG(mLogCategory,"full screen : %d",fullscreen);
if (fullscreen) {
xdg_toplevel_set_fullscreen (mXdgToplevel, mCurrentDisplayOutput->wlOutput);
} else {
xdg_toplevel_unset_fullscreen (mXdgToplevel);
}
}
}
void WaylandDisplay::setRenderRectangle(int x, int y, int w, int h)
{
DEBUG(mLogCategory,"set render rect:x:%d,y:%d,w:%d,h:%d",x,y,w,h);
if (w <= 0 || h <= 0) {
WARNING(mLogCategory, "wrong render width or height %dx%d",w,h);
return;
}
mRenderRect.x = x;
mRenderRect.y = y;
mRenderRect.w = w;
mRenderRect.h = h;
if (!mXdgSurfaceConfigured) {
WARNING(mLogCategory,"Not configured xdg");
return;
}
if (mAreaViewport) {
wp_viewport_set_destination (mAreaViewport, w, h);
}
updateBorders();
if (mVideoWidth != 0 && mVideoSurface) {
wl_subsurface_set_sync (mVideoSubSurface);
resizeVideoSurface(true);
}
wl_surface_damage (mAreaSurfaceWrapper, 0, 0, w, h);
wl_surface_commit (mAreaSurfaceWrapper);
if (mVideoWidth != 0) {
wl_subsurface_set_desync (mVideoSubSurface);
}
}
void WaylandDisplay::setFrameSize(int w, int h)
{
mVideoWidth = w;
mVideoHeight = h;
mUpdateVideoSurface = true;
TRACE(mLogCategory,"frame w:%d,h:%d",mVideoWidth,mVideoHeight);
}
void WaylandDisplay::setWindowSize(int x, int y, int w, int h)
{
mWindowRect.x = x;
mWindowRect.y = y;
mWindowRect.w = w;
mWindowRect.h = h;
mUpdateVideoSurface = true;
TRACE(mLogCategory,"window size:x:%d,y:%d,w:%d,h:%d",mWindowRect.x,mWindowRect.y,mWindowRect.w,mWindowRect.h);
}
void WaylandDisplay::setPixelAspectRatio(double ratio)
{
mPixelAspectRatio = ratio;
mUpdateVideoSurface = true;
}
void WaylandDisplay::resizeVideoSurface(bool commit)
{
Rectangle src = {0,};
Rectangle dst = {0,};
Rectangle res;
/* center the video_subsurface inside area_subsurface */
src.w = mVideoWidth;
src.h = mVideoHeight;
/*if had set the window size, we will scall
video surface to this window size*/
if (mWindowRect.w > 0 && mWindowRect.h > 0) {
dst.x = mWindowRect.x;
dst.y = mWindowRect.y;
dst.w = mWindowRect.w;
dst.h = mWindowRect.h;
if (mWindowRect.w > mRenderRect.w && mWindowRect.h > mRenderRect.h) {
WARNING(mLogCategory,"Error window size:%dx%d, but render size:%dx%d,reset to render size",
mWindowRect.w,mWindowRect.h,mRenderRect.w,mRenderRect.h);
dst.x = mRenderRect.x;
dst.y = mRenderRect.y;
dst.w = mRenderRect.w;
dst.h = mRenderRect.h;
}
//to do,we need set geometry?
//if (mXdgSurface) {
// xdg_surface_set_window_geometry(mXdgSurface, mWindowRect.x, mWindowRect.y, mWindowRect.w, mWindowRect.h);
//}
} else { //scal video to full screen
dst.w = mRenderRect.w;
dst.h = mRenderRect.h;
}
if (mViewporter) {
videoCenterRect(src, dst, &res, true);
} else {
videoCenterRect(src, dst, &res, false);
}
wl_subsurface_set_position (mVideoSubSurface, res.x, res.y);
if (commit) {
wl_surface_damage (mVideoSurfaceWrapper, 0, 0, res.w, res.h);
wl_surface_commit (mVideoSurfaceWrapper);
}
//top level setting
if (mXdgToplevel) {
struct wl_region *region;
region = wl_compositor_create_region (mCompositor);
wl_region_add (region, 0, 0, mRenderRect.w, mRenderRect.h);
wl_surface_set_input_region (mAreaSurface, region);
wl_region_destroy (region);
}
/* this is saved for use in wl_surface_damage */
mVideoRect.x = res.x;
mVideoRect.y = res.y;
mVideoRect.w = res.w;
mVideoRect.h = res.h;
//to scale video surface
wp_viewport_set_destination(mVideoViewport, res.w, res.h);
wl_display_flush (mWlDisplay);
TRACE(mLogCategory,"video rectangle,x:%d,y:%d,w:%d,h:%d",mVideoRect.x, mVideoRect.y, mVideoRect.w, mVideoRect.h);
}
void WaylandDisplay::setOpaque()
{
struct wl_region *region;
/* Set area opaque */
region = wl_compositor_create_region (mCompositor);
wl_region_add (region, 0, 0, mRenderRect.w, mRenderRect.h);
wl_surface_set_opaque_region (mAreaSurface, region);
wl_region_destroy (region);
}
int WaylandDisplay::prepareFrameBuffer(RenderBuffer * buf)
{
WaylandBuffer *waylandBuf = NULL;
int ret;
bool isNew = false;
//detect frame rate and frame duration
if (mFrameRateFractionNum == 0) {
if (mPreFramePts == -1) {
mPreFramePts = buf->pts; //ns
} else {
if (mFrameRateDetectCnt < DETECT_FRAMERATE_CNT) {
mFrameRateDetectPeriod += (buf->pts - mPreFramePts);
++mFrameRateDetectCnt;
}
if (mFrameRateDetectCnt == DETECT_FRAMERATE_CNT) {
int64_t mFrameDurationUs = (mFrameRateDetectPeriod/mFrameRateDetectCnt)/1000;
double rate = 1000000.0/mFrameDurationUs;
mFrameRateFractionNum = rate * 100;
mFrameRateFractionDenom = 100;
mFrameRateChanged = true;
INFO(mLogCategory,"detect frame num:%d,denom:%d,dur:%lld us",
mFrameRateFractionNum,mFrameRateFractionDenom,mFrameDurationUs);
}
}
}
mPreFramePts = buf->pts;
if (!mDmabuf)
{
ERROR(mLogCategory,"Error zwp_linux_dmabuf_v1");
return ERROR_UNKNOWN;
}
waylandBuf = findWaylandBuffer(buf);
if (waylandBuf == NULL) {
waylandBuf = new WaylandBuffer(this, mLogCategory);
isNew = true;
}
waylandBuf->setBufferFormat(mBufferFormat);
ret = waylandBuf->constructWlBuffer(buf);
if (ret != NO_ERROR) {
WARNING(mLogCategory,"dmabufConstructWlBuffer fail,release waylandbuf");
//delete waylanBuf,WaylandBuffer object destruct will call release callback
goto waylandbuf_fail;
}
if (isNew) {
addWaylandBuffer(buf, waylandBuf);
}
return NO_ERROR;
waylandbuf_fail:
if (!isNew) {
removeWaylandBuffer(buf);
}
//delete waylandbuf
delete waylandBuf;
waylandBuf = NULL;
return ERROR_UNKNOWN;
}
void WaylandDisplay::displayFrameBuffer(RenderBuffer * buf, int64_t realDisplayTime)
{
WaylandBuffer *waylandBuf = NULL;
struct wl_buffer * wlbuffer = NULL;
int ret;
if (!buf) {
ERROR(mLogCategory,"Error input params, RenderBuffer is null");
return;
}
if (buf->flag & BUFFER_FLAG_DMA_BUFFER) {
if (buf->dma.width <=0 || buf->dma.height <=0) {
buf->dma.width = mVideoWidth;
buf->dma.height = mVideoHeight;
}
waylandBuf = findWaylandBuffer(buf);
if (waylandBuf) {
waylandBuf->setRenderRealTime(realDisplayTime);
} else {
ERROR(mLogCategory,"NOT found wayland buffer,please prepare buffer first");
goto waylandbuf_fail;
}
}
//if no wl_output, drop this buffer
if (mCurrentDisplayOutput->wlOutput == NULL) {
TRACE(mLogCategory,"No wl_output");
//insert this buffer to committed weston buffer manager
std::pair<int64_t, WaylandBuffer *> item(realDisplayTime, waylandBuf);
mCommittedBufferMap.insert(item);
mWaylandPlugin->handleFrameDropped(buf);
WaylandBuffer::bufferRelease(waylandBuf,NULL);
return;
}
//must commit areasurface first, because weston xdg surface maybe timeout
//this cause video is not display,commit can resume xdg surface
if (!mReCommitAreaSurface) {
mReCommitAreaSurface = true;
wl_surface_commit (mAreaSurface);
}
//set frame rate to weston,it lets weston to select suitable mode
if (mFrameRateChanged && mAmlConfigAPIList.enableSetDisplayRate) {
mFrameRateChanged = false;
TRACE(mLogCategory,"set frame rate %d/%d to weston", mFrameRateFractionNum, mFrameRateFractionDenom);
wl_surface_set_display_rate(mVideoSurfaceWrapper, mFrameRateFractionNum, mFrameRateFractionDenom);
}
//update video surface size
if (mUpdateVideoSurface && mVideoSurface) {
mUpdateVideoSurface = false;
//if had full screen, unset it and set window size
if (mFullScreen) {
mFullScreen = false;
ensureFullscreen(mFullScreen);
}
resizeVideoSurface(true);
/*clean wayland buffers those allocated
before resolution changed and had release by weston */
cleanWaylandBufferBeforeResChanged();
}
if (mVideoPlaneZorderChanged && mVideoSurfaceWrapper) {
mVideoPlaneZorderChanged = false;
wl_surface_set_zorder(mVideoSurfaceWrapper, mVideoPlaneZorder);
}
//TRACE(mLogCategory,"display renderBuffer:%p,PTS:%lld us,realtime:%lld",buf, buf->pts/1000, realDisplayTime);
if (waylandBuf) {
wlbuffer = waylandBuf->getWlBuffer();
}
if (wlbuffer) {
auto cItem = mCommittedBufferMap.find(realDisplayTime);
if (cItem != mCommittedBufferMap.end()) {
TRACE(mLogCategory,"Error.release same display time buffer,pts:%lld us",buf->pts/1000);
goto waylandbuf_fail;
}
Tls::Mutex::Autolock _l(mRenderMutex);
++mCommitCnt;
uint32_t hiPts = realDisplayTime >> 32;
uint32_t lowPts = realDisplayTime & 0xFFFFFFFF;
//attach this wl_buffer to weston
TRACE(mLogCategory,"++attach,renderbuf:%p,wl_buffer:%p(%d,%d,%d,%d),pts:%lld us,commitCnt:%d",
buf,wlbuffer,mVideoRect.x,mVideoRect.y,mVideoRect.w,mVideoRect.h,buf->pts/1000,mCommitCnt);
waylandBuf->attach(mVideoSurfaceWrapper);
if (mAmlConfigAPIList.enableSetPts) {
TRACE(mLogCategory,"display time:%lld,hiPts:%u,lowPts:%u",realDisplayTime, hiPts, lowPts);
wl_surface_set_pts(mVideoSurfaceWrapper, hiPts, lowPts);
}
if (!mKeepFrameOnFlush && mFirstPtsAfterFlush == -1) {
mFirstPtsAfterFlush = buf->pts;
}
//doing video rotate if needed
if (mVideoRotateDegree >= 0) {
/*weston accept degree map: transfer value->degree, 0->0, 1->90,2->180,3->270
detail see: enum wl_output_transform or search WL_OUTPUT_TRANSFORM_xxx*/
int transform = 0;
switch (mVideoRotateDegree) {
case 90: {
transform = 1;
} break;
case 180: {
transform = 2;
} break;
case 270: {
transform = 3;
} break;
default:
transform = 0;
}
wl_surface_set_buffer_transform(mVideoSurfaceWrapper, transform);
}
wl_surface_damage (mVideoSurfaceWrapper, 0, 0, mVideoRect.w, mVideoRect.h);
wl_surface_commit (mVideoSurfaceWrapper);
//insert this buffer to committed weston buffer manager
std::pair<int64_t, WaylandBuffer *> item(realDisplayTime, waylandBuf);
mCommittedBufferMap.insert(item);
} else {
WARNING(mLogCategory,"wlbuffer is NULL");
/* clear both video and parent surfaces */
//cleanSurface();
goto waylandbuf_fail;
}
wl_display_flush (mWlDisplay);
//set keep last frame or not when after send first buffer to weston,1 keep last frame, 0 not
if (mToSendKeepLastFrame) {
setKeepLastFrame(mKeepLastFrame);
}
return;
waylandbuf_fail:
//notify dropped
mWaylandPlugin->handleFrameDropped(buf);
//notify app release this buf
mWaylandPlugin->handleBufferRelease(buf);
return;
}
void WaylandDisplay::handleBufferReleaseCallback(WaylandBuffer *buf)
{
RenderBuffer *renderBuffer = buf->getRenderBuffer();
{
Tls::Mutex::Autolock _l(mRenderMutex);
//remove buffer if this buffer is ready to release
auto item = mCommittedBufferMap.find(buf->getRenderRealTime());
if (item != mCommittedBufferMap.end()) {
--mCommitCnt;
mCommittedBufferMap.erase(item);
} else {
TRACE(mLogCategory,"Error,Can't find WaylandBuffer pts:%lld us (%lld) in buffer map",
renderBuffer->pts/1000,buf->getRenderRealTime());
return;
}
}
TRACE(mLogCategory,"renderBuffer :%p,priv:%p,PTS:%lld us,realtime:%lld us,commitCnt:%d",renderBuffer,renderBuffer->priv,renderBuffer->pts/1000,buf->getRenderRealTime(),mCommitCnt);
mWaylandPlugin->handleBufferRelease(renderBuffer);
}
void WaylandDisplay::handleFrameDisplayedCallback(WaylandBuffer *buf)
{
RenderBuffer *renderBuffer = buf->getRenderBuffer();
TRACE(mLogCategory,"renderBuffer :%p,PTS:%lld us,realtime:%lld us",renderBuffer,renderBuffer->pts/1000,buf->getRenderRealTime());
if (!mSignalFirstFramePts) {
mSignalFirstFramePts = true;
mWaylandPlugin->handleMsgNotify(MSG_FIRST_FRAME, (void*)&renderBuffer->pts);
}
mWaylandPlugin->signalWaitTimeout();
mWaylandPlugin->handleFrameDisplayed(renderBuffer);
if (!mKeepFrameOnFlush && mFirstPtsAfterFlush == renderBuffer->pts) {
mKeepFrameOnFlush = 1;
mFirstPtsAfterFlush = -1;
wl_surface_set_video_plane_mute(mVideoSurfaceWrapper, 0);
INFO(mLogCategory,"unmute video plane");
}
}
void WaylandDisplay::handleFrameDropedCallback(WaylandBuffer *buf)
{
RenderBuffer *renderBuffer = buf->getRenderBuffer();
TRACE(mLogCategory,"renderBuffer :%p,PTS:%lld us,realtime:%lld us",renderBuffer,renderBuffer->pts/1000,buf->getRenderRealTime());
mWaylandPlugin->handleFrameDropped(renderBuffer);
}
void WaylandDisplay::handleFrameTime(WaylandBuffer *buf,uint32_t sec, uint32_t usec)
{
RenderBuffer *renderBuffer = buf->getRenderBuffer();
int64_t frameDisplayTimeUs = int64_t(sec)*1000000LL + usec;
if (mLastDisplayFramePts == -1) {
mLastDisplayFramePts = renderBuffer->pts;
mLastDisplayFrameTimeUs = frameDisplayTimeUs;
} else { //calculate last frame displayed duration
int64_t duration = frameDisplayTimeUs - mLastDisplayFrameTimeUs;
TRACE(mLogCategory,"now pts:%lld us,time:%u us,pre pts:%lld us,dur:%lld us",
renderBuffer->pts/1000,frameDisplayTimeUs,mLastDisplayFramePts/1000,duration);
if (mFrameDurationUs > 0 && duration > FRAME_FREEZE_THRESHOLD(mFrameDurationUs)) {
FrameDisplayInfo frameDisplayInfo = {-1, -1};
frameDisplayInfo.pts = mLastDisplayFramePts;
frameDisplayInfo.duration = duration;
TRACE(mLogCategory,"report freeze frame,pts:%lld us,duration:%lld us",frameDisplayInfo.pts/1000,frameDisplayInfo.duration);
mWaylandPlugin->handleMsgNotify(MSG_FRAME_FREEZE, &frameDisplayInfo);
}
mLastDisplayFramePts = renderBuffer->pts;
mLastDisplayFrameTimeUs = frameDisplayTimeUs;
}
}
void WaylandDisplay::readyToRun()
{
mFd = wl_display_get_fd (mWlDisplay);
if (mPoll) {
mPoll->addFd(mFd);
mPoll->setFdReadable(mFd, true);
}
}
void WaylandDisplay::readyToExit()
{
if (mPoll && mFd >= 0) {
mPoll->removeFd(mFd);
}
}
bool WaylandDisplay::threadLoop()
{
int ret;
while (wl_display_prepare_read_queue (mWlDisplay, mWlQueue) != 0) {
wl_display_dispatch_queue_pending (mWlDisplay, mWlQueue);
}
wl_display_flush (mWlDisplay);
/*poll timeout value must > 300 ms,otherwise zwp_linux_dmabuf will create failed,
so do use -1 to wait for ever*/
ret = mPoll->wait(-1); //wait for ever
if (ret < 0) { //poll error
WARNING(mLogCategory,"poll error");
wl_display_cancel_read(mWlDisplay);
return false;
} else if (ret == 0) { //poll time out
return true; //run loop
}
if (wl_display_read_events (mWlDisplay) == -1) {
goto tag_error;
}
wl_display_dispatch_queue_pending (mWlDisplay, mWlQueue);
return true;
tag_error:
ERROR(mLogCategory,"Error communicating with the wayland server");
return false;
}
void WaylandDisplay::videoCenterRect(Rectangle src, Rectangle dst, Rectangle *result, bool scaling)
{
//if dst is a small window, we scale video to map window size,don't doing center
// if (mRenderRect.w != dst.w && mRenderRect.h != dst.h) {
// result->x = dst.x;
// result->y = dst.y;
// result->w = dst.w;
// result->h = dst.h;
// TRACE(mLogCategory,"small window source is %dx%d dest is %dx%d, result is %d,%d,%d,%d",
// src.w, src.h, dst.w, dst.h, result->x, result->y, result->w, result->h);
// return;
// }
if (!scaling) {
result->w = MIN (src.w, dst.w);
result->h = MIN (src.h, dst.h);
result->x = dst.x + (dst.w - result->w) / 2;
result->y = dst.y + (dst.h - result->h) / 2;
} else {
double src_ratio, dst_ratio;
src_ratio = (double) (src.w * mPixelAspectRatio) / src.h;
dst_ratio = (double) dst.w / dst.h;
if (src_ratio > dst_ratio) {
result->w = dst.w;
result->h = dst.w / src_ratio;
result->x = dst.x;
result->y = dst.y + (dst.h - result->h) / 2;
} else if (src_ratio < dst_ratio) {
result->w = dst.h * src_ratio;
result->h = dst.h;
result->x = dst.x + (dst.w - result->w) / 2;
result->y = dst.y;
} else {
result->x = dst.x;
result->y = dst.y;
result->w = dst.w;
result->h = dst.h;
}
}
TRACE(mLogCategory,"source is %dx%d dest is %dx%d, result is %d,%d,%d,%d",
src.w, src.h, dst.w, dst.h, result->x, result->y,result->w, result->h);
}
void WaylandDisplay::updateBorders()
{
int width,height;
if (mNoBorderUpdate)
return;
if (mViewporter) {
width = height = 1;
mNoBorderUpdate = true;
} else {
width = mRenderRect.w;
height = mRenderRect.h;
}
RenderVideoFormat format = VIDEO_FORMAT_BGRA;
mAreaShmBuffer = new WaylandShmBuffer(this, mLogCategory);
struct wl_buffer *wlbuf = mAreaShmBuffer->constructWlBuffer(width, height, format);
if (wlbuf == NULL) {
delete mAreaShmBuffer;
mAreaShmBuffer = NULL;
}
wl_surface_attach(mAreaSurfaceWrapper, wlbuf, 0, 0);
}
std::size_t WaylandDisplay::calculateDmaBufferHash(RenderDmaBuffer &dmabuf)
{
std::string hashString("");
for (int i = 0; i < dmabuf.planeCnt; i++) {
char hashtmp[1024];
snprintf (hashtmp, 1024, "%d%d%d%d%d%d%d",i,dmabuf.width,dmabuf.height,dmabuf.planeCnt,
dmabuf.stride[i],dmabuf.offset[i],dmabuf.fd[i]);
std::string tmp(hashtmp);
hashString += tmp;
}
std::size_t hashval = std::hash<std::string>()(hashString);
//TRACE(mLogCategory,"hashstr:%s,val:%zu",hashString.c_str(),hashval);
return hashval;
}
void WaylandDisplay::addWaylandBuffer(RenderBuffer * buf, WaylandBuffer *waylandbuf)
{
Tls::Mutex::Autolock _l(mBufferMutex);
if (buf->flag & BUFFER_FLAG_DMA_BUFFER) {
std::size_t hashval = calculateDmaBufferHash(buf->dma);
std::pair<std::size_t, WaylandBuffer *> item(hashval, waylandbuf);
//TRACE(mLogCategory,"fd:%d,w:%d,h:%d,%p,hash:%zu",buf->dma.fd[0],buf->dma.width,buf->dma.height,waylandbuf,hashval);
mWaylandBuffersMap.insert(item);
}
TRACE(mLogCategory,"mWaylandBuffersMap size:%d",mWaylandBuffersMap.size());
}
WaylandBuffer* WaylandDisplay::findWaylandBuffer(RenderBuffer * buf)
{
Tls::Mutex::Autolock _l(mBufferMutex);
std::size_t hashval = calculateDmaBufferHash(buf->dma);
auto item = mWaylandBuffersMap.find(hashval);
if (item == mWaylandBuffersMap.end()) {
return NULL;
}
return (WaylandBuffer*) item->second;
}
void WaylandDisplay::removeWaylandBuffer(RenderBuffer * buf)
{
Tls::Mutex::Autolock _l(mBufferMutex);
std::size_t hashval = calculateDmaBufferHash(buf->dma);
auto item = mWaylandBuffersMap.find(hashval);
if (item == mWaylandBuffersMap.end()) {
return ;
}
mWaylandBuffersMap.erase(item);
}
void WaylandDisplay::cleanAllWaylandBuffer()
{
Tls::Mutex::Autolock _l(mBufferMutex);
//free all obtain buff
for (auto item = mWaylandBuffersMap.begin(); item != mWaylandBuffersMap.end(); ) {
WaylandBuffer *waylandbuf = (WaylandBuffer*)item->second;
mWaylandBuffersMap.erase(item++);
delete waylandbuf;
}
}
/**
* @brief clean wayland buffers those malloc before resolution changed
*
*/
void WaylandDisplay::cleanWaylandBufferBeforeResChanged()
{
Tls::Mutex::Autolock _l(mBufferMutex);
for (auto item = mWaylandBuffersMap.begin(); item != mWaylandBuffersMap.end(); ) {
WaylandBuffer *wlbuf = (WaylandBuffer*)item->second;
if (wlbuf->isFree()) {
mWaylandBuffersMap.erase(item++);
delete wlbuf;
} else {
item++;
}
}
}
void WaylandDisplay::flushBuffers()
{
INFO(mLogCategory,"flushBuffers");
Tls::Mutex::Autolock _l(mRenderMutex);
for (auto item = mCommittedBufferMap.begin(); item != mCommittedBufferMap.end(); item++) {
WaylandBuffer *waylandbuf = (WaylandBuffer*)item->second;
waylandbuf->forceRedrawing();
handleFrameDisplayedCallback(waylandbuf);
}
}
void WaylandDisplay::cleanSurface()
{
/* clear both video and parent surfaces */
wl_surface_attach (mVideoSurfaceWrapper, NULL, 0, 0);
wl_surface_commit (mVideoSurfaceWrapper);
wl_surface_attach (mAreaSurfaceWrapper, NULL, 0, 0);
wl_surface_commit (mAreaSurfaceWrapper);
}
void WaylandDisplay::setKeepLastFrame(int keep)
{
mKeepLastFrame = keep;
if (mVideoSurfaceWrapper && mAmlConfigAPIList.enableKeepLastFrame) {
INFO(mLogCategory,"keep last frame:%d",keep);
wl_surface_keep_last_frame(mVideoSurfaceWrapper, keep);
mToSendKeepLastFrame = false;
return;
}
mToSendKeepLastFrame = true;
}
void WaylandDisplay::setKeepLastFrameOnFlush(int keepOnFlush)
{
mKeepFrameOnFlush = keepOnFlush;
INFO(mLogCategory,"keep last frame:%d",mKeepFrameOnFlush);
if (mVideoSurfaceWrapper && !mKeepFrameOnFlush) {
INFO(mLogCategory,"mute video plane");
wl_surface_set_video_plane_mute(mVideoSurfaceWrapper, 1);
}
}
void WaylandDisplay::setImmediatelyOutput(int enable)
{
if (mVideoSurfaceWrapper) {
wl_surface_enable_ll_mode(mVideoSurfaceWrapper, enable);
}
INFO(mLogCategory,"set immediately output:%d", enable);
}
void WaylandDisplay::setFrameRate(int frameRateNum, int frameRateDenom)
{
mFrameRateFractionNum = frameRateNum;
mFrameRateFractionDenom = frameRateDenom;
if (mFrameRateFractionDenom == 0) {
mFrameRateFractionDenom = 1;
}
if (mFrameRateFractionNum > 0) {
mFrameDurationUs = 1000000 * mFrameRateFractionDenom/mFrameRateFractionNum;
mFrameRateChanged = true;
}
INFO(mLogCategory,"num:%d,denom:%d,frame dur:%lld us",
mFrameRateFractionNum, mFrameRateFractionDenom,mFrameDurationUs);
}
void WaylandDisplay::setVideoPlaneZorder(int zorder)
{
mVideoPlaneZorder = zorder;
mVideoPlaneZorderChanged = true;
if (mVideoSurfaceWrapper) {
mVideoPlaneZorderChanged = false;
wl_surface_set_zorder(mVideoSurfaceWrapper, mVideoPlaneZorder);
}
}
void WaylandDisplay::pause()
{
mLastDisplayFramePts = -1;
mLastDisplayFrameTimeUs = -1;
}
void WaylandDisplay::resume()
{
}