Files
BPI-CM9-Linux/aml-comp/multimedia/libvideorender/weston/wayland_display.cpp
T

2458 lines
85 KiB
C++
Raw Normal View History

2026-08-13 11:28:40 +08:00
/*
* 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 <sys/utsname.h>
#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"
#include "Times.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)
/*do not use -1 to indicate invalid pts.
player maybe display -1 pts frame*/
#define INVALID_PTS (INT64_MIN)
#define DUMP_WAYLAND_BUFFER_LOG (0)
/**
* @brief this log function registered to wayland protocol
* to print wayland server or wayland log
* @param fmt log format
*/
static void wl_log_handle(const char *format, va_list args)
{
wl_log(format, args);
}
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);
ATools::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);
ATools::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;
}
}
self->mDefaultTransform = transform;
}
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 ) {
ATools::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);
ATools::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;
/*
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;
INFO(self->mLogCategory, "XDG toplevel got a configure event, width:height [ %d, %d ].", width, height);
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");
ATools::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");
if (self->mMutex.lock() == 0) {
self->mCondition.signal();
self->mMutex.unlock();
}
wl_callback_destroy (callback);
}
static const struct wl_callback_listener surface_destroy_listener = {
WaylandDisplay::handleSurfaceDestroy,
};
void WaylandDisplay::surfaceEnter(void *data, struct wl_surface *wl_surface, struct wl_output *output)
{
UNUSED_PARAM(data);
UNUSED_PARAM(wl_surface);
UNUSED_PARAM(output);
}
void WaylandDisplay::surfaceLeave(void *data, struct wl_surface *wl_surface, struct wl_output *output)
{
UNUSED_PARAM(data);
UNUSED_PARAM(wl_surface);
UNUSED_PARAM(output);
}
void WaylandDisplay::surfaceUnderFlow(void *data, struct wl_surface *wl_surface, uint32_t pts_hi, uint32_t pts_lo)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
int64_t displayTime = ((int64_t)pts_hi << 32) | pts_lo;
INFO(self->mLogCategory,"underflow displaytime:%lld", displayTime);
self->mWaylandPlugin->reportUnderFlow();
}
static const struct wl_surface_listener surface_listener = {
WaylandDisplay::surfaceEnter,
WaylandDisplay::surfaceLeave,
WaylandDisplay::surfaceUnderFlow,
};
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, "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;
}
if (strstr(list, "force_update_rect")) {
TRACE(self->mLogCategory,"weston enable force_update_rect");
self->mAmlConfigAPIList.enableForceUpdateRect = true;
}
if (strstr(list, "render_to_osd")) {
TRACE(self->mLogCategory,"weston enable render_to_osd");
self->mAmlConfigAPIList.enableVideoOnOSD = true;
}
if (strstr(list, "need_original_size")) {
TRACE(self->mLogCategory,"weston enable need_original_size");
self->mAmlConfigAPIList.enableNeedOriginalSize = true;
}
if (strstr(list, "dpss_enabled")) {
TRACE(self->mLogCategory,"weston enable dpss_enabled");
self->mAmlConfigAPIList.enableDpss = true;
}
if (strstr(list, "vsync_callback")) {
TRACE(self->mLogCategory,"weston enable vsync_callback");
self->mAmlConfigAPIList.enableVsyncCallback = true;
}
}
}
void WaylandDisplay::dpssLatency(void *data, struct aml_config *config, uint32_t latency)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
if (self) {
self->mHWCLatency = (uint32_t)latency;
TRACE(self->mLogCategory,"compositor latency:%u us",latency);
}
}
void WaylandDisplay::vsyncCallback(void *data, struct aml_config *config, uint32_t vsync_hi, uint32_t vsync_lo, uint32_t interval)
{
WaylandDisplay *self = static_cast<WaylandDisplay *>(data);
if (self) {
int64_t lastVsyncUs = self->mVsyncTimeUs;
self->mVsyncTimeUs = ((int64_t)vsync_hi << 32) | vsync_lo;
self->mVsyncPeriodUs = (int64_t)interval;
TRACE(self->mLogCategory,"vsync_hi:%d, vsync_lo:%d, (%lld, %lld->%lld)",
vsync_hi, vsync_lo, self->mVsyncTimeUs, self->mVsyncPeriodUs,self->mVsyncTimeUs - lastVsyncUs);
}
}
static const struct aml_config_listener aml_config_listener = {
WaylandDisplay::amlConfigure,
WaylandDisplay::vsyncCallback,
WaylandDisplay::dpssLatency,
};
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 = 0;
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;
if (self->mOutput[i].wlOutput) {
wl_output_destroy(self->mOutput[i].wlOutput);
}
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 = NULL;
//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 = INVALID_PTS;
mSignalFirstFramePts = false;
mFirstFramePts = INVALID_PTS;
mToSendKeepLastFrame = false;
mVideoPlaneZorder = -1;
mVideoPlaneZorderChanged = false;
mVideoRotateDegree = -1;
mEnableWaylandClientLog = false;
mMuteVideoOnFlush = false;
mPaused = false;
mKeepVideoAsptectRatio = true;
mHideVideo = 0;
mHideVideoChanged = false;
mDisplayConnected = false;
mVideoOnOSD = false;
mDefaultTransform = 0;
mHWCLatency = 0;
mVsyncTimeUs = 0;
mVsyncPeriodUs = 0;
memset(&mWindowRect, 0, sizeof(struct Rectangle));
memset(&mCropRect, 0, sizeof(struct Rectangle));
mCommittedBufferMap.clear();
mWaylandBuffersMap.clear();
mDmaBufferFormats.clear();
}
WaylandDisplay::~WaylandDisplay()
{
TRACE(mLogCategory,"desconstruct WaylandDisplay");
}
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()
{
mPoll = new ATools::Poll(true);
mPrepareFrameWidth = 0;
mPrepareFrameHeight = 0;
mWaylandBufferCookieId = 0;
mDisplayedFrameWidth = 0;
mDisplayedFrameHeight = 0;
mUpdateWaylandBuffersMap = false;
frameCallbackTimeUs = 0;
mLastDisplayFramePts = -1;
mLastDisplayFrameTimeUs = -1;
mFrameDurationUs = 0;
mPreFramePts = -1;
mFrameRateDetectCnt = 0;
mFrameRateDetectPeriod = 0;
mPixelAspectRatio = 1.0;
mUpdateVideoSurface = false;
mUpdateCropSize = false;
mNoBorderUpdate = false;
mReCommitAreaSurface = false;
mXdgSurfaceConfigured = false;
mUpdateRenderRectangle = false;
mFrameRateFractionNum = 0;
mFrameRateFractionDenom = 1;
mFrameRateChanged = false;
mVideoPlaneZorder = -1;
mVideoPlaneZorderChanged = false;
mVideoIsUnderflow = false;
mSignalFirstFramePts = false;
mFirstFramePts = INVALID_PTS;
mPaused = false;
mMuteVideoOnFlush = false;
mDisplayConnected = false;
memset(&mRenderRect, 0, sizeof(struct Rectangle));
memset(&mVideoRect, 0, sizeof(struct Rectangle));
//don't full screen when osd displayed
if (!mVideoOnOSD) {
mFullScreen = true; //default is full screen
}
mAmlConfig = NULL;
//weston config private api
mAmlConfigAPIList.enableKeepLastFrame = false;
mAmlConfigAPIList.enableSetPts = false;
mAmlConfigAPIList.enableSetVideoPlane = false;
mAmlConfigAPIList.enableSurfaceDestroyCallback = false;
mAmlConfigAPIList.enableSetDisplayRate = false;
mAmlConfigAPIList.enableSetSurfaceInvisible = false;
mAmlConfigAPIList.enableDisplayTime = false;
mAmlConfigAPIList.enableForceUpdateRect = false;
mAmlConfigAPIList.enableVideoOnOSD = false;
mAmlConfigAPIList.enableNeedOriginalSize = false;
mAmlConfigAPIList.enableDpss = false;
mAmlConfigAPIList.enableVsyncCallback = 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);
char* debug = getenv("WAYLAND_DEBUG");
if (debug && strstr(debug, "1")) {
mEnableWaylandClientLog = true;
wl_log_set_handler_client(wl_log_handle);
}
debug = getenv("VIDEO_RENDER_KEEP_VIDEO_ASPECT_RATIO");
if (debug && strstr(debug, "0")) {
mKeepVideoAsptectRatio = false;
}
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);
}
//enable weston vsync callback
if (mAmlConfigAPIList.enableVsyncCallback) {
INFO(mLogCategory,"set vsync output id:%d",mSelectOutputIndex);
aml_config_need_vsync_callback(mAmlConfig, mCompositor, mSelectOutputIndex);
}
if (mHideVideoChanged) {
setHideVideo(mHideVideo);
}
//run wl display queue dispatch
DEBUG(mLogCategory,"To run wl display dispatch queue");
setThreadPriority(69);
run("waylandProtocol");
mRedrawingPending = false;
mDisplayConnected = true;
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");
if (mMutex.lock() == 0) { //success
if (ERROR_TIMED_OUT == mCondition.waitRelative(mMutex, 50/*microsecond*/)) {
WARNING(mLogCategory,"waited surface_destroy_cb timeout");
}
mMutex.unlock();
}
}
//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 (mShm) {
wl_shm_destroy(mShm);
mShm = 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 (mDirect_display) {
weston_direct_display_v1_destroy(mDirect_display);
mDirect_display = NULL;
}
for (int i = 0; i < DEFAULT_DISPLAY_OUTPUT_NUM; i++) {
if (mOutput[i].wlOutput) {
wl_output_destroy(mOutput[i].wlOutput);
}
mOutput[i].wlOutput = 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;
}
mDisplayConnected = false;
//do not handle wayland client logs after disconnect
if (mEnableWaylandClientLog) {
wl_log_set_handler_client(NULL);
mEnableWaylandClientLog = false;
}
mHideVideoChanged = false;
mHideVideo = 0;
//free all obtain buff
TRACE(mLogCategory,"mCommittedBufferMap size:%d",mCommittedBufferMap.size());
for (auto item = mCommittedBufferMap.begin(); item != mCommittedBufferMap.end(); ) {
mCommittedBufferMap.erase(item++);
}
mCommittedBufferMap.clear();
mDmaBufferFormats.clear();
if (mPoll) {
delete mPoll;
mPoll = 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;
}
}
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);
}
if (mAmlConfigAPIList.enableSetPts) {
INFO(mLogCategory,"add surface underflow listener");
wl_surface_add_listener(mVideoSurface, &surface_listener, this);
}
/* 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");
}
} 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;
}
if (mAreaViewport) {
wp_viewport_destroy(mAreaViewport);
mAreaViewport = NULL;
}
if (mVideoViewport) {
wp_viewport_destroy(mVideoViewport);
mVideoViewport = NULL;
}
}
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 default transform is 1(90 degree) or 3(270 degree)
or 5(flip and 90 degree) or 7(flip and 270 degree),transform w/h*/
if (mVideoOnOSD &&
(mDefaultTransform == 1 || mDefaultTransform == 3 || mDefaultTransform == 5 || mDefaultTransform == 7)) {
mRenderRect.w = h;
mRenderRect.h = w;
DEBUG(mLogCategory,"transform %d,reset w:%d,h:%d",mDefaultTransform, mRenderRect.w, mRenderRect.h);
}
if (!mXdgSurfaceConfigured) {
WARNING(mLogCategory,"Not configured xdg");
return;
}
if (mAreaViewport) {
wp_viewport_set_destination (mAreaViewport, mRenderRect.w, mRenderRect.h);
}
updateBorders();
if (mVideoWidth != 0 && mVideoSurface) {
wl_subsurface_set_sync (mVideoSubSurface);
resizeVideoSurface(true);
}
wl_surface_damage (mAreaSurfaceWrapper, 0, 0, mRenderRect.w, mRenderRect.h);
wl_surface_commit (mAreaSurfaceWrapper);
if (mVideoWidth != 0) {
wl_subsurface_set_desync (mVideoSubSurface);
}
}
void WaylandDisplay::setFrameSize(int w, int h)
{
mVideoWidth = w;
mVideoHeight = h;
mUpdateCropSize = true;
INFO(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;
INFO(mLogCategory,"window size:x:%d,y:%d,w:%d,h:%d",mWindowRect.x,mWindowRect.y,mWindowRect.w,mWindowRect.h);
//if user set window size,force update window size
if (mAmlConfigAPIList.enableForceUpdateRect &&
mVideoSurfaceWrapper && mVideoWidth != 0) {
if (mFullScreen) {
mFullScreen = false;
ensureFullscreen(mFullScreen);
}
resizeVideoSurface(false);
//force weston update rectangle
wl_surface_force_update_rect(mVideoSurfaceWrapper);
wl_display_flush (mWlDisplay);
} else {
mUpdateVideoSurface = true;
}
}
void WaylandDisplay::setCropSize(int x, int y, int w, int h)
{
if (w <= 0 || h <= 0) {
WARNING(mLogCategory, "wrong crop width or height %dx%d",w,h);
return;
}
mCropRect.x = x;
mCropRect.y = y;
mCropRect.w = w;
mCropRect.h = h;
INFO(mLogCategory,"set crop window size:x:%d,y:%d,w:%d,h:%d",mCropRect.x,mCropRect.y,mCropRect.w,mCropRect.h);
mUpdateCropSize = true;
}
void WaylandDisplay::setPixelAspectRatio(double ratio)
{
//TRACE(mLogCategory,"pixel aspect ratio:%f",ratio);
if (fabs(mPixelAspectRatio - ratio) > 0.001) {
mUpdateVideoSurface = true;
}
mPixelAspectRatio = ratio;
}
void WaylandDisplay::configCropSize(int dma_width, int dma_height)
{
if (mVideoViewport && mVideoHeight > 0 && mVideoWidth > 0 && dma_width > 0 && dma_height > 0) {
int cx = mCropRect.x*dma_width/mVideoWidth;
int cy = mCropRect.y*dma_height/mVideoHeight;
int cw = mCropRect.w*dma_width/mVideoWidth;
int ch = mCropRect.h*dma_height/mVideoHeight;
if (cw > 0 && ch > 0) {
INFO(mLogCategory,"set crop size:x:%d,y:%d,w:%d,h:%d to weston",cx,cy,cw,ch);
wl_fixed_t fixed_t_x = wl_fixed_from_int(cx);
wl_fixed_t fixed_t_y = wl_fixed_from_int(cy);
wl_fixed_t fixed_t_w = wl_fixed_from_int(cw);
wl_fixed_t fixed_t_h = wl_fixed_from_int(ch);
wp_viewport_set_source(mVideoViewport, fixed_t_x,fixed_t_y,fixed_t_w,fixed_t_h);
wl_display_flush (mWlDisplay);
} else {
INFO(mLogCategory,"warning crop size ,crop:(%d,%d,%d,%d) w:%d/%d,h:%d/%d",
mCropRect.x, mCropRect.y, mCropRect.w, mCropRect.h,dma_width, mVideoWidth,dma_height, mVideoHeight);
}
} else {
mUpdateCropSize = true;
}
}
void WaylandDisplay::resizeVideoSurface(bool commit)
{
Rectangle src = {0,};
Rectangle dst = {0,};
Rectangle res = {0,};
/* 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,do not limit 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;
} else { //scal video to full screen
dst.w = mRenderRect.w;
dst.h = mRenderRect.h;
}
/* 90/270 rotation window position check, dst window position updated
* only hardware rotation is available */
if (mVideoRotateDegree == 90 || mVideoRotateDegree == 270) {
if (checkHWRotationAvailable()) {
src.w = mVideoHeight;
src.h = mVideoWidth;
INFO(mLogCategory,"rotation %d degree src (%d,%d) -> (%d,%d)",mVideoRotateDegree,mVideoWidth, mVideoHeight, src.w, src.h);
}
}
if (mKeepVideoAsptectRatio) {
if (mViewporter) {
videoCenterRect(src, dst, &res, true);
} else {
videoCenterRect(src, dst, &res, false);
}
} else {
res.x = dst.x;
res.y = dst.y;
res.w = dst.w;
res.h = dst.h;
}
if (mVideoSubSurface) {
wl_subsurface_set_position (mVideoSubSurface, res.x, res.y);
}
if (mVideoSurfaceWrapper && 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
if (mVideoViewport) {
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 */
if (mCompositor) {
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 = NO_ERROR;
bool isNew = false;
if (!mWlDisplay || !mDisplayConnected) {
ERROR(mLogCategory,"Error disconnected from weston %s", mWlDisplay == NULL?"mWlDisplay is NULL":" ");
return ERROR_UNKNOWN;
}
if (buf->flag & BUFFER_FLAG_LAST_EMPTY_BUFFER) {
mWaylandBufferCookieId += 1;
DEBUG(mLogCategory,"drs, get last buffer, update cookie:%d",mWaylandBufferCookieId);
if (DUMP_WAYLAND_BUFFER_LOG) {
dumpAllWaylandBuffer();
}
cleanWaylandBufferBeforeResChanged();
return NO_ERROR;
}
if (DUMP_WAYLAND_BUFFER_LOG) {
DEBUG(mLogCategory,"pts:%lld us w:%d,h:%d",buf->pts/1000, buf->dma.width,buf->dma.height);
}
//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;
}
//prepare frame with and height
if ((mPrepareFrameWidth == 0 || mPrepareFrameHeight == 0) &&
buf->dma.width > 0) {
mPrepareFrameWidth = buf->dma.width;
mPrepareFrameHeight = buf->dma.height;
}
//if resolution changed,update waylandbuffer cookie id
if (mPrepareFrameWidth != buf->dma.width ||
mPrepareFrameHeight != buf->dma.height) {
mWaylandBufferCookieId += 1;
DEBUG(mLogCategory,"drs,pts:%lld us,w:%d,h:%d,update cookie:%d",buf->pts/1000,buf->dma.width,buf->dma.height,mWaylandBufferCookieId);
}
//TRACE(mLogCategory,"renderbuf:%p,pts:%lld us,w:%d,h:%d,cookie:%d",buf,buf->pts/1000,buf->dma.width,buf->dma.height,mWaylandBufferCookieId);
waylandBuf = findWaylandBuffer(buf);
if (waylandBuf == NULL || waylandBuf->getCookieId() != mWaylandBufferCookieId) {
/*When the fps changes, sometimes the hash value of the dmabuffer is the same as that of the previous buffer.
we should free previous wayland buffer*/
if (waylandBuf && waylandBuf->isFree()) {
removeWaylandBuffer(buf);
delete waylandBuf;
waylandBuf = NULL;
}
waylandBuf = new WaylandBuffer(this, mLogCategory, mWaylandBufferCookieId);
isNew = true;
}
waylandBuf->setBufferFormat(mBufferFormat);
ret = waylandBuf->constructWlBuffer(buf);
if (ret != NO_ERROR) {
WARNING(mLogCategory,"dmabufConstructWlBuffer fail");
}
mPrepareFrameWidth = buf->dma.width;
mPrepareFrameHeight = buf->dma.height;
if (isNew) {
addWaylandBuffer(buf, waylandBuf);
}
return NO_ERROR;
}
void WaylandDisplay::displayFrameBuffer(RenderBuffer * buf, int64_t realDisplayTime)
{
WaylandBuffer *waylandBuf = NULL;
struct wl_buffer * wlbuffer = NULL;
int ret = NO_ERROR;
if (!buf) {
ERROR(mLogCategory,"Error input params, RenderBuffer is null");
return;
}
if (!mWlDisplay || !mDisplayConnected) {
ERROR(mLogCategory,"Error disconnect from weston %s", mWlDisplay == NULL?"mWlDisplay is NULL":" ");
goto waylandbuf_fail;
}
if (buf->flag & BUFFER_FLAG_LAST_EMPTY_BUFFER) {
int num = 0;
DEBUG(mLogCategory,"drs, last buffer, cookie:%d",mWaylandBufferCookieId);
mWaylandPlugin->handleBufferRelease(buf);
/*clean prepared waylandbuffers but dropped,
we should force buffer state to idle and clean it*/
{
ATools::Mutex::Autolock _l(mBufferMutex);
for (auto item = mWaylandBuffersMap.begin(); item != mWaylandBuffersMap.end(); ) {
WaylandBuffer *wlbuf = (WaylandBuffer*)item->second;
if (wlbuf->getCookieId() < mWaylandBufferCookieId && wlbuf->getBufferState() == BUFFER_PREPARED) {
wlbuf->forceBufferStateIdle();
}
item++;
}
}
num = cleanWaylandBufferBeforeResChanged();
if (num > 0) {
mUpdateWaylandBuffersMap = true;
} else {
mUpdateWaylandBuffersMap = false;
}
if (DUMP_WAYLAND_BUFFER_LOG) {
dumpAllWaylandBuffer();
}
return;
}
if ((mDisplayedFrameWidth == 0 || mDisplayedFrameHeight == 0) &&
buf->dma.width > 0) {
mDisplayedFrameWidth = buf->dma.width;
mDisplayedFrameHeight = buf->dma.height;
mUpdateVideoSurface = true;
}
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");
mRenderMutex.lock();
//insert this buffer to committed weston buffer manager
std::pair<int64_t, WaylandBuffer *> item(realDisplayTime, waylandBuf);
mCommittedBufferMap.insert(item);
mRenderMutex.unlock();
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);
//get dpss latency from weston
if (mAmlConfigAPIList.enableDpss) {
int fr = mFrameRateFractionNum * 1000/mFrameRateFractionDenom;
if (fr <= 0) {
fr = 60000;
}
TRACE(mLogCategory,"get dpss latency from weston");
aml_config_get_dpss_latency(mAmlConfig, mCompositor, fr, mSelectOutputIndex);
}
}
//resolution changed,update video surface size
if (buf->dma.width != mDisplayedFrameWidth || buf->dma.height != mDisplayedFrameHeight) {
mUpdateVideoSurface = true;
mUpdateCropSize = true;
mUpdateWaylandBuffersMap = true;
}
//update video crop size
if (mUpdateCropSize && mVideoViewport) {
mUpdateCropSize = false;
if (!mAmlConfigAPIList.enableNeedOriginalSize) {
configCropSize(buf->dma.width, buf->dma.height);
} else {
configCropSize(mVideoWidth, mVideoHeight);
}
}
//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);
}
/*don't wl_surface_commit when we ready to post buffer to weston.
wl_surface_commit before post buffer makes weston mistake it for
a new frame submission
*/
resizeVideoSurface(false);
}
if (mVideoPlaneZorderChanged && mVideoSurfaceWrapper) {
mVideoPlaneZorderChanged = false;
//only set zorder to area surface when doing osd displayed
if (mVideoOnOSD) {
wl_surface_set_zorder(mAreaSurfaceWrapper, mVideoPlaneZorder);
}
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) {
ATools::Mutex::Autolock _l(mRenderMutex);
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;
}
//if renderbuffer in WaylandBuffer is null, construct it
if (!waylandBuf->getRenderBuffer()) {
ret = waylandBuf->constructWlBuffer(buf);
if (ret != NO_ERROR) {
WARNING(mLogCategory,"waylandBuf construct failed");
goto waylandbuf_fail;
}
}
// some DTV case will wait for first frame signal to unmute video
// we need report first frame signal in advance before attach the frame
// otherwise, it may cause weston mute video until several frames attached
if (!mSignalFirstFramePts && mFirstFramePts == INVALID_PTS && mCommitCnt == 0) {
mSignalFirstFramePts = true;
INFO(mLogCategory,"signalFirstFrame:%lld ns",buf->pts);
mWaylandPlugin->handleMsgNotify(MSG_FIRST_FRAME, (void*)&buf->pts);
}
++mCommitCnt;
uint32_t hiPts = realDisplayTime >> 32;
uint32_t lowPts = realDisplayTime & 0xFFFFFFFF;
//attach this wl_buffer to weston
DEBUG(mLogCategory,"++attach,renderbuf:%p(%p),fd(%d,%d),wl_buffer:%p(%d,%d,%d,%d),pts:%lld us(%lld),commitCnt:%d",
buf,waylandBuf,buf->dma.fd[0],buf->dma.fd[1],wlbuffer,mVideoRect.x,mVideoRect.y,mVideoRect.w,mVideoRect.h,buf->pts/1000,realDisplayTime,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);
}
//record first pts after mute video when flush
if (!mKeepFrameOnFlush && mMuteVideoOnFlush && mFirstPtsAfterFlush == INVALID_PTS) {
mFirstPtsAfterFlush = buf->pts;
DEBUG(mLogCategory,"mFirstPtsAfterFlush,pts:%lld us",mFirstPtsAfterFlush);
}
if (mFirstFramePts == INVALID_PTS) {
mFirstFramePts = buf->pts;
DEBUG(mLogCategory,"mFirstFramePts,pts:%lld ns",mFirstFramePts);
}
//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;
}
DEBUG(mLogCategory,"set transform to weston:%d",transform);
wl_surface_set_buffer_transform(mVideoSurfaceWrapper, transform);
}
wl_surface_damage (mVideoSurfaceWrapper, 0, 0, mVideoRect.w, mVideoRect.h);
wl_surface_commit (mVideoSurfaceWrapper);
mDisplayedFrameWidth = buf->dma.width;
mDisplayedFrameHeight = buf->dma.height;
//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,pts:%lld us",buf->pts/1000);
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);
}
/**
* 1.clean released buffers by weston,these buffers had displayed and released
* 2.some buffer dropped before committed to weston, force these buffer to idle
* state and clean them
*/
if (mUpdateWaylandBuffersMap) {
int num = 0;
/*if waylandBuf's cookie reached mWaylandBufferCookieId,
those buffers allocated before waylandBuf had displayed, if
buffer state is prepared,maybe this buffer dropped.
we should force buffer state to idle and clean it*/
if (waylandBuf->getCookieId() == mWaylandBufferCookieId) {
ATools::Mutex::Autolock _l(mBufferMutex);
for (auto item = mWaylandBuffersMap.begin(); item != mWaylandBuffersMap.end(); ) {
WaylandBuffer *wlbuf = (WaylandBuffer*)item->second;
if (wlbuf->getCookieId() < mWaylandBufferCookieId && wlbuf->getBufferState() == BUFFER_PREPARED) {
wlbuf->forceBufferStateIdle();
}
item++;
}
}
num = cleanWaylandBufferBeforeResChanged();
if (num > 0) {
mUpdateWaylandBuffersMap = true;
} else {
mUpdateWaylandBuffersMap = false;
}
if (DUMP_WAYLAND_BUFFER_LOG) {
dumpAllWaylandBuffer();
}
}
return;
waylandbuf_fail:
//notify dropped
mWaylandPlugin->handleFrameDropped(buf);
//notify app release this buf
mWaylandPlugin->handleBufferRelease(buf);
if (waylandBuf) {
if (waylandBuf->getCookieId() < mWaylandBufferCookieId && waylandBuf->getBufferState() == BUFFER_PREPARED) {
waylandBuf->forceBufferStateIdle();
cleanWaylandBufferBeforeResChanged();
if (DUMP_WAYLAND_BUFFER_LOG) {
dumpAllWaylandBuffer();
}
}
}
return;
}
void WaylandDisplay::handleBufferReleaseCallback(WaylandBuffer *buf)
{
RenderBuffer *renderBuffer = buf->getRenderBuffer();
{
ATools::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(buf->getRenderRealTime());
} else {
if (renderBuffer) {
TRACE(mLogCategory,"Error,Can't find WaylandBuffer pts:%lld us (%lld) in buffer map %p",
renderBuffer->pts/1000,buf->getRenderRealTime(),renderBuffer);
}
return;
}
}
DEBUG(mLogCategory,"renderBuffer :%p,fd(%d,%d)priv:%p,PTS:%lld us,realtime:%lld us,commitCnt:%d",
renderBuffer,renderBuffer->dma.fd[0],renderBuffer->dma.fd[1],renderBuffer->priv,renderBuffer->pts/1000,buf->getRenderRealTime(),mCommitCnt);
mWaylandPlugin->handleBufferRelease(renderBuffer);
}
void WaylandDisplay::handleFrameDisplayedCallback(WaylandBuffer *buf)
{
RenderBuffer *renderBuffer = buf->getRenderBuffer();
DEBUG(mLogCategory,"renderBuffer:%p,PTS:%lld us,realtime:%lld us",renderBuffer,renderBuffer->pts/1000,buf->getRenderRealTime());
if (!mSignalFirstFramePts && mFirstFramePts != INVALID_PTS && renderBuffer->pts >= mFirstFramePts) {
mSignalFirstFramePts = true;
INFO(mLogCategory,"signalFirstFrame:%lld ns",renderBuffer->pts);
mWaylandPlugin->handleMsgNotify(MSG_FIRST_FRAME, (void*)&renderBuffer->pts);
}
if (!mAmlConfigAPIList.enableSetPts) {
mWaylandPlugin->signalWaitTimeout();
}
mWaylandPlugin->handleFrameDisplayed(renderBuffer);
if (!mKeepFrameOnFlush && mMuteVideoOnFlush &&
mFirstPtsAfterFlush != INVALID_PTS && mFirstPtsAfterFlush <= renderBuffer->pts) {
mFirstPtsAfterFlush = INVALID_PTS;
mMuteVideoOnFlush = false;
wl_surface_set_video_plane_mute(mVideoSurfaceWrapper, 0);
INFO(mLogCategory,"unmute video");
}
}
void WaylandDisplay::handleFrameDropedCallback(WaylandBuffer *buf)
{
RenderBuffer *renderBuffer = buf->getRenderBuffer();
DEBUG(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();
handleFrameDisplayedCallback(buf);
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,last dur:%lld us,time:%u us",
renderBuffer->pts/1000,duration,frameDisplayTimeUs);
if (mFrameDurationUs > 0 && duration > FRAME_FREEZE_THRESHOLD(mFrameDurationUs)) {
FrameDisplayInfo frameDisplayInfo = {-1, -1};
frameDisplayInfo.pts = mLastDisplayFramePts;
frameDisplayInfo.duration = duration;
INFO(mLogCategory,"reportFreezeFrame,pts:%lld us,dur:%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) {
ERROR(mLogCategory,"Error communicating with the wayland server");
mDisplayConnected = false;
return false;
}
wl_display_dispatch_queue_pending (mWlDisplay, mWlQueue);
return true;
}
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;
}
}
DEBUG(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 = 0;
int height = 0;
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->constructAreaShmWlBuffer(width, height, format);
if (wlbuf == NULL) {
delete mAreaShmBuffer;
mAreaShmBuffer = NULL;
}
if (mAreaSurfaceWrapper) {
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%d",i,dmabuf.width,dmabuf.height,dmabuf.planeCnt,
dmabuf.stride[i],dmabuf.offset[i],dmabuf.fd[i],dmabuf.handle[i]);
std::string tmp(hashtmp);
hashString += tmp;
}
std::size_t hashval = std::hash<std::string>()(hashString);
if (DUMP_WAYLAND_BUFFER_LOG) {
TRACE(mLogCategory,"hashstr:%s,val:%zu",hashString.c_str(),hashval);
}
return hashval;
}
void WaylandDisplay::addWaylandBuffer(RenderBuffer * buf, WaylandBuffer *waylandbuf)
{
ATools::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);
if (DUMP_WAYLAND_BUFFER_LOG) {
TRACE(mLogCategory,"fd[0]:%d,fd[1]:%d,w:%d,h:%d,%p,hash:%zu",buf->dma.fd[0],buf->dma.fd[1],buf->dma.width,buf->dma.height,waylandbuf,hashval);
}
mWaylandBuffersMap.insert(item);
}
TRACE(mLogCategory,"mWaylandBuffersMap size:%d",mWaylandBuffersMap.size());
if (DUMP_WAYLAND_BUFFER_LOG) {
dumpAllWaylandBuffer();
}
}
WaylandBuffer* WaylandDisplay::findWaylandBuffer(RenderBuffer * buf)
{
ATools::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)
{
ATools::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()
{
ATools::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;
}
mWaylandBuffersMap.clear();
}
void WaylandDisplay::dumpAllWaylandBuffer() {
INFO(mLogCategory,"mWaylandBuffersMap size:%d",mWaylandBuffersMap.size());
for (auto item = mWaylandBuffersMap.begin(); item != mWaylandBuffersMap.end(); ) {
WaylandBuffer *wlbuf = (WaylandBuffer*)item->second;
std::size_t hashVal = item->first;
item++;
INFO(mLogCategory,"WaylandBuffer:%p, hash:%zu, state:%d, cookie:%d, %dx%d",
wlbuf,hashVal,wlbuf->getBufferState(),wlbuf->getCookieId(),wlbuf->getFrameWidth(),wlbuf->getFrameHeight());
}
}
/**
* @brief clean wayland buffers those malloc before resolution changed
*
*/
int WaylandDisplay::cleanWaylandBufferBeforeResChanged()
{
int num = 0;
ATools::Mutex::Autolock _l(mBufferMutex);
for (auto item = mWaylandBuffersMap.begin(); item != mWaylandBuffersMap.end(); ) {
WaylandBuffer *wlbuf = (WaylandBuffer*)item->second;
std::size_t hashVal = item->first;
if (wlbuf->isFree()) {
mWaylandBuffersMap.erase(item++);
delete wlbuf;
} else {
item++;
if (wlbuf->getCookieId() < mWaylandBufferCookieId) {
num++;
TRACE(mLogCategory,"left WaylandBuffer:%p, hash:%zu, state:%d, cookie:%d, %dx%d",
wlbuf,hashVal,wlbuf->getBufferState(),wlbuf->getCookieId(),wlbuf->getFrameWidth(),wlbuf->getFrameHeight());
}
}
}
return num;
}
void WaylandDisplay::flushBuffers()
{
INFO(mLogCategory,"flushBuffers");
if (mVideoSurfaceWrapper && !mKeepFrameOnFlush) {
INFO(mLogCategory,"mute video");
mMuteVideoOnFlush = true;
mFirstPtsAfterFlush = INVALID_PTS;
wl_surface_set_video_plane_mute(mVideoSurfaceWrapper, 1);
}
/*buffers obtained by weston will displayed,so we don't force to do display callback*/
// ATools::Mutex::Autolock _l(mRenderMutex);
// for (auto item = mCommittedBufferMap.begin(); item != mCommittedBufferMap.end(); item++) {
// WaylandBuffer *waylandbuf = (WaylandBuffer*)item->second;
// waylandbuf->forceRedrawing();
// handleFrameDisplayedCallback(waylandbuf);
// }
mSignalFirstFramePts = false;
mFirstFramePts = INVALID_PTS;
}
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);
}
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 * (int64_t)mFrameRateFractionDenom/(int64_t)mFrameRateFractionNum;
mFrameRateChanged = true;
}
if (mVideoSurfaceWrapper && mFrameRateChanged && mAmlConfigAPIList.enableSetDisplayRate) {
mFrameRateChanged = false;
INFO(mLogCategory,"set frame rate %d/%d to weston", mFrameRateFractionNum, mFrameRateFractionDenom);
wl_surface_set_display_rate(mVideoSurfaceWrapper, mFrameRateFractionNum, mFrameRateFractionDenom);
//get dpss latency from weston
if (mAmlConfigAPIList.enableDpss) {
int fr = mFrameRateFractionNum * 1000/mFrameRateFractionDenom;
if (fr <= 0) {
fr = 60000;
}
TRACE(mLogCategory,"get dpss latency from weston");
aml_config_get_dpss_latency(mAmlConfig, mCompositor, fr, mSelectOutputIndex);
}
}
INFO(mLogCategory,"num:%d,denom:%d,fps:%d/100,frame dur:%lld us",
mFrameRateFractionNum, mFrameRateFractionDenom,mFrameRateFractionNum*100/mFrameRateFractionDenom,mFrameDurationUs);
}
void WaylandDisplay::setVideoPlaneZorder(int zorder)
{
mVideoPlaneZorder = zorder;
mVideoPlaneZorderChanged = true;
if (mVideoSurfaceWrapper) {
mVideoPlaneZorderChanged = false;
//only set zorder to area surface when doing osd displayed
if (mVideoOnOSD) {
wl_surface_set_zorder(mAreaSurfaceWrapper, mVideoPlaneZorder);
}
wl_surface_set_zorder(mVideoSurfaceWrapper, mVideoPlaneZorder);
}
}
void WaylandDisplay::pause()
{
mPaused = true;
if (!mVideoIsUnderflow) {
mLastDisplayFramePts = -1;
mLastDisplayFrameTimeUs = -1;
}
}
void WaylandDisplay::resume()
{
mPaused = false;
if (!mVideoIsUnderflow) {
mLastDisplayFramePts = -1;
mLastDisplayFrameTimeUs = -1;
}
mVideoIsUnderflow = false;
}
void WaylandDisplay::underflow()
{
mVideoIsUnderflow = true;
}
void WaylandDisplay::setHideVideo(int hide)
{
mHideVideo = hide;
if (mVideoSurfaceWrapper) {
int mute = hide > 0 ? 1: 0;
mHideVideoChanged = false;
INFO(mLogCategory,"mute:%d",hide);
wl_surface_set_video_plane_mute(mVideoSurfaceWrapper, mute);
} else {
INFO(mLogCategory,"pending mute:%d",hide);
mHideVideoChanged = true;
}
}
int WaylandDisplay::unexpectReleaseFrame(RenderBuffer *buffer)
{
WaylandBuffer *waylandBuf = NULL;
if (buffer->flag & BUFFER_FLAG_DMA_BUFFER) {
waylandBuf = findWaylandBuffer(buffer);
if (waylandBuf && waylandBuf->getBufferState() == BUFFER_PREPARED) {
DEBUG(mLogCategory," unexpected release pts:%lld us,cookie:%d",buffer->pts/1000,waylandBuf->getCookieId());
waylandBuf->forceBufferStateIdle();
}
}
return NO_ERROR;
}
void WaylandDisplay::setVideoOnOSD(int videoOnOSD)
{
mVideoOnOSD = videoOnOSD > 0? true: false;
INFO(mLogCategory,"video on osd:%d",mVideoOnOSD);
}
/*
* if v2d or dpss does 90/270 degree ratotion, we should change
* window position mapping 90/270 degree ratotion,this feature
* is available after 6.12 kernel version.
*/
bool WaylandDisplay::checkHWRotationAvailable()
{
bool ret = false;
struct utsname info = {0};
int major = 0, minor = 0;
if (uname(&info) || sscanf(info.release, "%d.%d", &major, &minor) <= 0)
{
WARNING(mLogCategory,"get linux version failed");
}
if (major >=6 && minor >= 12) {
ret = true;
}
INFO(mLogCategory,"linux major version %d.%d,HWRotation:%d", major,minor,ret);
return ret;
}
int WaylandDisplay::getHWCLatency()
{
return mHWCLatency;
}
int WaylandDisplay::getDisplayVsyncAndPeriod(uint64_t *vsyncTimestamp, uint64_t *vsyncPeriod)
{
if (vsyncTimestamp && vsyncPeriod) {
*vsyncTimestamp = mVsyncTimeUs;
*vsyncPeriod = mVsyncPeriodUs;
return NO_ERROR;
}
return ERROR_PARAM_NULL;
}