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

2485 lines
86 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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();
mDeferredReleaseBuffers.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));
mDeferredReleaseBuffers.clear();
//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;
}
/* Release deferred buffers after disconnecting from weston.
* These buffers were in DISPLAYING/DISPLAYED state during cleanup;
* weston no longer references them now that we've disconnected. */
releaseDeferredBuffers();
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;
/* If buffer is still held by weston (DISPLAYING or DISPLAYED state),
* defer the buffer release callback until after wl_display_disconnect.
* weston may still be using these buffers; releasing them prematurely
* could cause the upstream plugin to reuse a buffer weston still reads. */
if ((waylandbuf->getBufferState() == BUFFER_DISPLAYING ||
waylandbuf->getBufferState() == BUFFER_DISPLAYED) &&
waylandbuf->getRenderBuffer()) {
mDeferredReleaseBuffers.push_back(waylandbuf->getRenderBuffer());
waylandbuf->forceBufferStateIdle(); // sets state to IDLE, clears mRenderBuffer
}
mWaylandBuffersMap.erase(item++);
delete waylandbuf;
}
mWaylandBuffersMap.clear();
}
void WaylandDisplay::releaseDeferredBuffers()
{
for (auto buf : mDeferredReleaseBuffers) {
INFO(mLogCategory,"release deferred buffer:%p,pts:%lld us", buf, buf->pts/1000);
mWaylandPlugin->handleBufferRelease(buf);
}
mDeferredReleaseBuffers.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;
}