2458 lines
85 KiB
C++
2458 lines
85 KiB
C++
/*
|
||
* Copyright (C) 2021 Amlogic Corporation.
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*/
|
||
#include <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, ®istry_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;
|
||
}
|