diff options
author | Lionel CHAZALLON <LongChair@hotmail.com> | 2017-10-23 08:51:49 -0700 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2017-10-23 21:07:24 +0200 |
commit | cfcee4cfe70536faeb9f2aaa87257d067e902b70 (patch) | |
tree | b6dfc61144a7394974657b5dfdfc34d9fcf1576a /video/out/opengl | |
parent | 762b8cc30007480f06d338ac77d6e91cc04cd320 (diff) | |
download | mpv-cfcee4cfe70536faeb9f2aaa87257d067e902b70.tar.bz2 mpv-cfcee4cfe70536faeb9f2aaa87257d067e902b70.tar.xz |
Add DRM_PRIME Format Handling and Display for RockChip MPP decoders
This commit allows to use the AV_PIX_FMT_DRM_PRIME newly introduced
format in ffmpeg that allows decoders to provide an AVDRMFrameDescriptor
struct.
That struct holds dmabuf fds and information allowing zerocopy rendering
using KMS / DRM Atomic.
This has been tested on RockChip ROCK64 device.
Diffstat (limited to 'video/out/opengl')
-rw-r--r-- | video/out/opengl/context_drm_egl.c | 69 | ||||
-rw-r--r-- | video/out/opengl/hwdec_drmprime_drm.c | 258 |
2 files changed, 311 insertions, 16 deletions
diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c index 071702bb08..c7de762e28 100644 --- a/video/out/opengl/context_drm_egl.c +++ b/video/out/opengl/context_drm_egl.c @@ -28,6 +28,7 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include "libmpv/opengl_cb.h" #include "video/out/drm_common.h" #include "common/common.h" @@ -75,6 +76,8 @@ struct priv { bool vt_switcher_active; struct vt_switcher vt_switcher; + + struct mpv_opengl_cb_drm_params drm_params; }; static bool init_egl(struct ra_ctx *ctx) @@ -161,13 +164,6 @@ static void update_framebuffer_from_bo(struct ra_ctx *ctx, struct gbm_bo *bo) p->fb = fb; } -static void page_flipped(int fd, unsigned int frame, unsigned int sec, - unsigned int usec, void *data) -{ - struct priv *p = data; - p->waiting_for_flip = false; -} - static bool crtc_setup(struct ra_ctx *ctx) { struct priv *p = ctx->priv; @@ -241,17 +237,47 @@ static void acquire_vt(void *data) crtc_setup(ctx); } +static bool drm_atomic_egl_start_frame(struct ra_swapchain *sw, struct ra_fbo *out_fbo) +{ + struct priv *p = sw->ctx->priv; + if (p->kms->atomic_context) { + p->kms->atomic_context->request = drmModeAtomicAlloc(); + p->drm_params.atomic_request = p->kms->atomic_context->request; + return ra_gl_ctx_start_frame(sw, out_fbo); + } + return false; +} + +static const struct ra_swapchain_fns drm_atomic_swapchain = { + .start_frame = drm_atomic_egl_start_frame, +}; + static void drm_egl_swap_buffers(struct ra_ctx *ctx) { struct priv *p = ctx->priv; + struct drm_atomic_context *atomic_ctx = p->kms->atomic_context; + int ret; + eglSwapBuffers(p->egl.display, p->egl.surface); p->gbm.next_bo = gbm_surface_lock_front_buffer(p->gbm.surface); p->waiting_for_flip = true; update_framebuffer_from_bo(ctx, p->gbm.next_bo); - int ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id, p->fb->id, - DRM_MODE_PAGE_FLIP_EVENT, p); - if (ret) { - MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno)); + + if (atomic_ctx) { + drm_object_set_property(atomic_ctx->request, atomic_ctx->primary_plane, "FB_ID", p->fb->id); + drm_object_set_property(atomic_ctx->request, atomic_ctx->primary_plane, "CRTC_ID", atomic_ctx->crtc->id); + drm_object_set_property(atomic_ctx->request, atomic_ctx->primary_plane, "ZPOS", 1); + + ret = drmModeAtomicCommit(p->kms->fd, atomic_ctx->request, + DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, NULL); + if (ret) + MP_WARN(ctx->vo, "Failed to commit atomic request (%d)\n", ret); + } else { + ret = drmModePageFlip(p->kms->fd, p->kms->crtc_id, p->fb->id, + DRM_MODE_PAGE_FLIP_EVENT, p); + if (ret) { + MP_WARN(ctx->vo, "Failed to queue page flip: %s\n", mp_strerror(errno)); + } } // poll page flip finish event @@ -262,9 +288,16 @@ static void drm_egl_swap_buffers(struct ra_ctx *ctx) ret = drmHandleEvent(p->kms->fd, &p->ev); if (ret != 0) { MP_ERR(ctx->vo, "drmHandleEvent failed: %i\n", ret); + p->waiting_for_flip = false; return; } } + p->waiting_for_flip = false; + + if (atomic_ctx) { + drmModeAtomicFree(atomic_ctx->request); + p->drm_params.atomic_request = atomic_ctx->request = NULL; + } gbm_surface_release_buffer(p->gbm.surface, p->gbm.bo); p->gbm.bo = p->gbm.next_bo; @@ -304,7 +337,6 @@ static bool drm_egl_init(struct ra_ctx *ctx) struct priv *p = ctx->priv = talloc_zero(ctx, struct priv); p->ev.version = DRM_EVENT_CONTEXT_VERSION; - p->ev.page_flip_handler = page_flipped; p->vt_switcher_active = vt_switcher_init(&p->vt_switcher, ctx->vo->log); if (p->vt_switcher_active) { @@ -316,9 +348,9 @@ static bool drm_egl_init(struct ra_ctx *ctx) MP_VERBOSE(ctx, "Initializing KMS\n"); p->kms = kms_create(ctx->log, ctx->vo->opts->drm_connector_spec, - ctx->vo->opts->drm_mode_id); + ctx->vo->opts->drm_mode_id, ctx->vo->opts->drm_overlay_id); if (!p->kms) { - MP_ERR(ctx->vo, "Failed to create KMS.\n"); + MP_ERR(ctx, "Failed to create KMS.\n"); return false; } @@ -360,10 +392,15 @@ static bool drm_egl_init(struct ra_ctx *ctx) return false; } + p->drm_params.fd = p->kms->fd; + p->drm_params.crtc_id = p->kms->crtc_id; + p->drm_params.atomic_request = p->kms->atomic_context->request; struct ra_gl_ctx_params params = { .swap_buffers = drm_egl_swap_buffers, - .native_display_type = "drm", - .native_display = (void *)(intptr_t)p->kms->fd, + .native_display_type = "opengl-cb-drm-params", + .native_display = &p->drm_params, + .external_swapchain = p->kms->atomic_context ? &drm_atomic_swapchain : + NULL, }; if (!ra_gl_ctx_init(ctx, &p->gl, params)) return false; diff --git a/video/out/opengl/hwdec_drmprime_drm.c b/video/out/opengl/hwdec_drmprime_drm.c new file mode 100644 index 0000000000..95e42254e0 --- /dev/null +++ b/video/out/opengl/hwdec_drmprime_drm.c @@ -0,0 +1,258 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <stdbool.h> + +#include <libavutil/hwcontext_drm.h> + +#include "common.h" +#include "video/hwdec.h" +#include "common/msg.h" +#include "options/m_config.h" +#include "libmpv/opengl_cb.h" +#include "video/out/drm_common.h" +#include "video/out/drm_prime.h" +#include "video/out/gpu/hwdec.h" +#include "video/mp_image.h" + +#include "ra_gl.h" + +struct drm_frame { + struct drm_prime_framebuffer fb; + struct mp_image *image; // associated mpv image +}; + +struct priv { + struct mp_log *log; + + struct mp_image_params params; + + struct drm_atomic_context *ctx; + struct drm_frame current_frame, old_frame; + + struct mp_rect src, dst; + + int display_w, display_h; +}; + +static void set_current_frame(struct ra_hwdec *hw, struct drm_frame *frame) +{ + struct priv *p = hw->priv; + + // frame will be on screen after next vsync + // current_frame is currently the displayed frame and will be replaced + // by frame after next vsync. + // We used old frame as triple buffering to make sure that the drm framebuffer + // is not being displayed when we release it. + + drm_prime_destroy_framebuffer(p->log, p->ctx->fd, &p->old_frame.fb); + mp_image_setrefp(&p->old_frame.image, p->current_frame.image); + p->old_frame.fb = p->current_frame.fb; + + if (frame) { + p->current_frame.fb = frame->fb; + mp_image_setrefp(&p->current_frame.image, frame->image); + } else { + memset(&p->current_frame.fb, 0, sizeof(p->current_frame.fb)); + mp_image_setrefp(&p->current_frame.image, NULL); + } +} + +static void scale_dst_rect(struct ra_hwdec *hw, int source_w, int source_h ,struct mp_rect *src, struct mp_rect *dst) +{ + struct priv *p = hw->priv; + double hratio, vratio, ratio; + + // drm can allow to have a layer that has a different size from framebuffer + // we scale here the destination size to video mode + hratio = vratio = ratio = 1.0; + + hratio = (double)p->display_w / (double)source_w; + vratio = (double)p->display_h / (double)source_h; + ratio = hratio <= vratio ? hratio : vratio; + + dst->x0 = src->x0 * ratio; + dst->x1 = src->x1 * ratio; + dst->y0 = src->y0 * ratio; + dst->y1 = src->y1 * ratio; + + int offset_x = (p->display_w - ratio * source_w) / 2; + int offset_y = (p->display_h - ratio * source_h) / 2; + + dst->x0 += offset_x; + dst->x1 += offset_x; + dst->y0 += offset_y; + dst->y1 += offset_y; +} + +static int overlay_frame(struct ra_hwdec *hw, struct mp_image *hw_image, + struct mp_rect *src, struct mp_rect *dst, bool newframe) +{ + struct priv *p = hw->priv; + GL *gl = ra_gl_get(hw->ra); + AVDRMFrameDescriptor *desc = NULL; + drmModeAtomicReq *request = NULL; + struct drm_frame next_frame = {0}; + int ret; + + if (hw_image) { + + // grab opengl-cb windowing info to eventually upscale the overlay + // as egl windows could be upscaled to primary plane. + struct mpv_opengl_cb_window_pos *glparams = + gl ? (struct mpv_opengl_cb_window_pos *) + mpgl_get_native_display(gl, "opengl-cb-window-pos") : NULL; + if (glparams) { + scale_dst_rect(hw, glparams->width, glparams->height, dst, &p->dst); + } else { + p->dst = *dst; + } + p->src = *src; + + // grab drm interop info + struct mpv_opengl_cb_drm_params *drmparams = + gl ? (struct mpv_opengl_cb_drm_params *) + mpgl_get_native_display(gl, "opengl-cb-drm-params") : NULL; + if (drmparams) + request = (drmModeAtomicReq *)drmparams->atomic_request; + + next_frame.image = hw_image; + desc = (AVDRMFrameDescriptor *)hw_image->planes[0]; + + if (desc) { + int srcw = p->src.x1 - p->src.x0; + int srch = p->src.y1 - p->src.y0; + int dstw = MP_ALIGN_UP(p->dst.x1 - p->dst.x0, 2); + int dsth = MP_ALIGN_UP(p->dst.y1 - p->dst.y0, 2); + + if (drm_prime_create_framebuffer(p->log, p->ctx->fd, desc, srcw, srch, &next_frame.fb)) { + ret = -1; + goto fail; + } + + if (request) { + drm_object_set_property(request, p->ctx->overlay_plane, "FB_ID", next_frame.fb.fb_id); + drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_ID", p->ctx->crtc->id); + drm_object_set_property(request, p->ctx->overlay_plane, "SRC_X", p->src.x0 << 16); + drm_object_set_property(request, p->ctx->overlay_plane, "SRC_Y", p->src.y0 << 16); + drm_object_set_property(request, p->ctx->overlay_plane, "SRC_W", srcw << 16); + drm_object_set_property(request, p->ctx->overlay_plane, "SRC_H", srch << 16); + drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_X", MP_ALIGN_DOWN(p->dst.x0, 2)); + drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_Y", MP_ALIGN_DOWN(p->dst.y0, 2)); + drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_W", dstw); + drm_object_set_property(request, p->ctx->overlay_plane, "CRTC_H", dsth); + drm_object_set_property(request, p->ctx->overlay_plane, "ZPOS", 0); + } else { + ret = drmModeSetPlane(p->ctx->fd, p->ctx->overlay_plane->id, p->ctx->crtc->id, next_frame.fb.fb_id, 0, + MP_ALIGN_DOWN(p->dst.x0, 2), MP_ALIGN_DOWN(p->dst.y0, 2), dstw, dsth, + p->src.x0 << 16, p->src.y0 << 16 , srcw << 16, srch << 16); + if (ret < 0) { + MP_ERR(hw, "Failed to set the plane %d (buffer %d).\n", p->ctx->overlay_plane->id, + next_frame.fb.fb_id); + goto fail; + } + } + } + } + + set_current_frame(hw, &next_frame); + return 0; + + fail: + drm_prime_destroy_framebuffer(p->log, p->ctx->fd, &next_frame.fb); + return ret; +} + +static void uninit(struct ra_hwdec *hw) +{ + struct priv *p = hw->priv; + + set_current_frame(hw, NULL); + + if (p->ctx) { + drm_atomic_destroy_context(p->ctx); + p->ctx = NULL; + } +} + +static int init(struct ra_hwdec *hw) +{ + struct priv *p = hw->priv; + int drm_overlay; + + p->log = hw->log; + + mp_read_option_raw(hw->global, "drm-overlay", &m_option_type_int, &drm_overlay); + + GL *gl = ra_gl_get(hw->ra); + struct mpv_opengl_cb_drm_params *params = + gl ? (struct mpv_opengl_cb_drm_params *) + mpgl_get_native_display(gl, "opengl-cb-drm-params") : NULL; + if (!params) { + MP_ERR(hw, "Could not get drm interop info.\n"); + goto err; + } + + if (params->fd) { + p->ctx = drm_atomic_create_context(p->log, params->fd, params->crtc_id, + drm_overlay); + if (!p->ctx) { + mp_err(p->log, "Failed to retrieve DRM atomic context.\n"); + goto err; + } + } else { + mp_err(p->log, "Failed to retrieve DRM fd from native display.\n"); + goto err; + } + + drmModeCrtcPtr crtc; + crtc = drmModeGetCrtc(p->ctx->fd, p->ctx->crtc->id); + if (crtc) { + p->display_w = crtc->mode.hdisplay; + p->display_h = crtc->mode.vdisplay; + drmModeFreeCrtc(crtc); + } + + + uint64_t has_prime; + if (drmGetCap(p->ctx->fd, DRM_CAP_PRIME, &has_prime) < 0) { + MP_ERR(hw, "Card does not support prime handles.\n"); + goto err; + } + + return 0; + +err: + uninit(hw); + return -1; +} + +const struct ra_hwdec_driver ra_hwdec_drmprime_drm = { + .name = "drmprime-drm", + .api = HWDEC_RKMPP, + .priv_size = sizeof(struct priv), + .imgfmts = {IMGFMT_DRMPRIME, 0}, + .init = init, + .overlay_frame = overlay_frame, + .uninit = uninit, +}; |