From daa0a3edba13cf3415c1dbb6c765667206e55861 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Mon, 20 Jan 2020 18:33:20 +0800 Subject: [PATCH 2/4] HACK: vo_xv: Support dma buffer rendering Send dma buffer to xv port when it supports dma port attributes. Depends on: 1/ ffmpeg with hw format AV_PIX_FMT_DRM_PRIME and sw format AV_PIX_FMT_NV12. 2/ xserver xv with NV12 dma buf rendering hacks. Tested with: mpv --hwdec=rkmpp --vd-lavc-software-fallback=no --vo=xv test.mp4 Change-Id: I446cb3061e745b7ef1d5496c228062050ce07064 Signed-off-by: Jeffy Chen --- video/out/vo_xv.c | 194 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 193 insertions(+), 1 deletion(-) diff --git a/video/out/vo_xv.c b/video/out/vo_xv.c index f37a8d1228..1e7211dee4 100644 --- a/video/out/vo_xv.c +++ b/video/out/vo_xv.c @@ -23,10 +23,15 @@ #include #include #include +#include +#include +#include #include #include #include +#include +#include #include "config.h" @@ -62,6 +67,18 @@ #define MAX_BUFFERS 10 +#define XV_DMA_CLIENT_PROP "XV_DMA_CLIENT_ID" +#define XV_DMA_VER_STRIDE_PROP "XV_DMA_VER_STRIDE" +#define XV_DMA_HOR_STRIDE_PROP "XV_DMA_HOR_STRIDE" +#define XV_DMA_CLIENT_PATH "/tmp/.xv_dma_client" + +struct dma_desc { + int hor_stride; + int ver_stride; + int dma_fd; + int valid; +}; + struct xvctx { struct xv_ck_info_s { int method; // CK_METHOD_* constants @@ -79,6 +96,8 @@ struct xvctx { int current_ip_buf; int num_buffers; XvImage *xvimage[MAX_BUFFERS]; + struct dma_desc dma_descs[MAX_BUFFERS]; + int dma_client_id; struct mp_image *original_image; uint32_t image_width; uint32_t image_height; @@ -111,6 +130,7 @@ static const struct fmt_entry fmt_table[] = { {IMGFMT_420P, MP_FOURCC_I420}, {IMGFMT_UYVY, MP_FOURCC_UYVY}, {IMGFMT_NV12, MP_FOURCC_NV12}, + {IMGFMT_DRMPRIME, MP_FOURCC_NV12}, {0} }; @@ -177,6 +197,144 @@ static int xv_find_atom(struct vo *vo, uint32_t xv_port, const char *name, return atom; } +static int xv_check_dma_client(struct vo *vo) +{ + struct xvctx *ctx = vo->priv; + Atom atom; + int xv_value = 0; + + if (!ctx->dma_client_id) + return -1; + + atom = XInternAtom(vo->x11->display, XV_DMA_CLIENT_PROP, True); + if (atom != None) + XvGetPortAttribute(vo->x11->display, ctx->xv_port, atom, &xv_value); + + if (xv_value) + return 0; + + ctx->dma_client_id = 0; + return -1; +} + +static void xv_flush_dma_client(struct vo *vo) +{ + struct xvctx *ctx = vo->priv; + Atom atom; + + if (!ctx->dma_client_id) + return; + + atom = XInternAtom(vo->x11->display, XV_DMA_CLIENT_PROP, True); + if (atom != None) { + XvSetPortAttribute(vo->x11->display, ctx->xv_port, + atom, ctx->dma_client_id); + XvGetPortAttribute(vo->x11->display, ctx->xv_port, atom, + &ctx->dma_client_id); + } +} + +static void xv_disable_dma_client(struct vo *vo) +{ + struct xvctx *ctx = vo->priv; + Atom atom; + + if (!ctx->dma_client_id) + return; + + atom = XInternAtom(vo->x11->display, XV_DMA_CLIENT_PROP, True); + if (atom != None) + XvSetPortAttribute(vo->x11->display, ctx->xv_port, atom, 0); + + ctx->dma_client_id = 0; +} + +static void xv_send_dma_params(struct vo *vo, int hor_stride, int ver_stride) +{ + struct xvctx *ctx = vo->priv; + Atom atom; + + if (!ctx->dma_client_id) + return; + + atom = XInternAtom(vo->x11->display, XV_DMA_HOR_STRIDE_PROP, True); + if (atom == None) + goto failed; + + XvSetPortAttribute(vo->x11->display, ctx->xv_port, atom, hor_stride); + + atom = XInternAtom(vo->x11->display, XV_DMA_VER_STRIDE_PROP, True); + if (atom == None) + goto failed; + + XvSetPortAttribute(vo->x11->display, ctx->xv_port, atom, ver_stride); + + return; + +failed: + xv_disable_dma_client(vo); + ctx->dma_client_id = 0; +} + +static void xv_send_dma_fd(struct vo *vo, int dma_fd) +{ + struct xvctx *ctx = vo->priv; + struct sockaddr_un addr; + struct iovec iov; + struct msghdr msg; + struct cmsghdr *header; + char buf[CMSG_SPACE(sizeof(int))]; + int socket_fd; + + if (!ctx->dma_client_id) + return; + + xv_flush_dma_client(vo); + + socket_fd = socket(PF_UNIX, SOCK_DGRAM, 0); + if (socket_fd < 0) + goto failed; + + addr.sun_family = AF_LOCAL; + snprintf(addr.sun_path, sizeof (addr.sun_path), + XV_DMA_CLIENT_PATH ".%d", ctx->dma_client_id); + addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; + + if (connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + goto failed; + + iov.iov_base = buf; + iov.iov_len = 1; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = buf; + msg.msg_controllen = sizeof(buf); + msg.msg_name = NULL; + msg.msg_namelen = 0; + + header = CMSG_FIRSTHDR(&msg); + header->cmsg_level = SOL_SOCKET; + header->cmsg_type = SCM_RIGHTS; + + header->cmsg_len = CMSG_LEN(sizeof(int)); + *((int *)CMSG_DATA(header)) = dma_fd; + sendmsg(socket_fd, &msg, 0); + + /* Send am empty msg at the end */ + header->cmsg_len = CMSG_LEN(0); + sendmsg(socket_fd, &msg, 0); + + close(socket_fd); + return; + +failed: + xv_disable_dma_client(vo); + + if (socket_fd >= 0) + close(socket_fd); +} + static int xv_set_eq(struct vo *vo, uint32_t xv_port, const char *name, int value) { @@ -508,8 +666,10 @@ static int reconfig(struct vo *vo, struct mp_image_params *params) MP_VERBOSE(vo, "using Xvideo port %d for hw scaling\n", ctx->xv_port); // In case config has been called before - for (i = 0; i < ctx->num_buffers; i++) + for (i = 0; i < ctx->num_buffers; i++) { deallocate_xvimage(vo, i); + ctx->dma_descs[i].valid = 0; + } ctx->num_buffers = ctx->cfg_buffers; @@ -683,6 +843,14 @@ static void wait_for_completion(struct vo *vo, int max_outstanding) static void flip_page(struct vo *vo) { struct xvctx *ctx = vo->priv; + struct dma_desc *dma_desc = &ctx->dma_descs[ctx->current_buf]; + + if (dma_desc->valid) { + xv_send_dma_fd(vo, dma_desc->dma_fd); + xv_send_dma_params(vo, dma_desc->hor_stride, dma_desc->ver_stride); + dma_desc->valid = 0; + } + put_xvimage(vo, ctx->xvimage[ctx->current_buf]); /* remember the currently visible buffer */ @@ -701,6 +869,26 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) struct mp_image xv_buffer = get_xv_buffer(vo, ctx->current_buf); if (mpi) { + if (mpi->hwctx && !xv_check_dma_client(vo)) { + AVHWFramesContext *fctx = (void *)mpi->hwctx->data; + if (fctx->format == AV_PIX_FMT_DRM_PRIME && + fctx->sw_format == AV_PIX_FMT_NV12) { + AVDRMFrameDescriptor *desc = + (AVDRMFrameDescriptor *)mpi->planes[0]; + AVDRMLayerDescriptor *layer = &desc->layers[0]; + struct dma_desc *dma_desc = &ctx->dma_descs[ctx->current_buf]; + + dma_desc->hor_stride = layer->planes[0].pitch; + dma_desc->ver_stride = + layer->planes[1].offset / dma_desc->hor_stride; + dma_desc->dma_fd = desc->objects[0].fd; + dma_desc->valid = 1; + + // TODO: Draw osd on mmapped hw frame + goto out; + } + } + mp_image_copy(&xv_buffer, mpi); } else { mp_image_clear(&xv_buffer, 0, 0, xv_buffer.w, xv_buffer.h); @@ -709,6 +897,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi) struct mp_osd_res res = osd_res_from_image_params(vo->params); osd_draw_on_image(vo->osd, res, mpi ? mpi->pts : 0, 0, &xv_buffer); +out: if (mpi != ctx->original_image) { talloc_free(ctx->original_image); ctx->original_image = mpi; @@ -847,6 +1036,9 @@ static int preinit(struct vo *vo) ctx->fo = XvListImageFormats(x11->display, ctx->xv_port, (int *) &ctx->formats); + ctx->dma_client_id = getpid(); + xv_flush_dma_client(vo); + MP_WARN(vo, "Warning: this legacy VO has bad quality and performance, " "and will in particular result in blurry OSD and subtitles. " "You should fix your graphics drivers, or not force the xv VO.\n"); -- 2.17.1