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