1From daa0a3edba13cf3415c1dbb6c765667206e55861 Mon Sep 17 00:00:00 2001
2From: Jeffy Chen <jeffy.chen@rock-chips.com>
3Date: Mon, 20 Jan 2020 18:33:20 +0800
4Subject: [PATCH 2/4] HACK: vo_xv: Support dma buffer rendering
5
6Send dma buffer to xv port when it supports dma port attributes.
7
8Depends on:
91/ ffmpeg with hw format AV_PIX_FMT_DRM_PRIME and sw format
10AV_PIX_FMT_NV12.
11
122/ xserver xv with NV12 dma buf rendering hacks.
13
14Tested with:
15mpv --hwdec=rkmpp --vd-lavc-software-fallback=no --vo=xv test.mp4
16
17Change-Id: I446cb3061e745b7ef1d5496c228062050ce07064
18Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
19---
20 video/out/vo_xv.c | 194 +++++++++++++++++++++++++++++++++++++++++++++-
21 1 file changed, 193 insertions(+), 1 deletion(-)
22
23diff --git a/video/out/vo_xv.c b/video/out/vo_xv.c
24index f37a8d1228..1e7211dee4 100644
25--- a/video/out/vo_xv.c
26+++ b/video/out/vo_xv.c
27@@ -23,10 +23,15 @@
28 #include <stdint.h>
29 #include <stdbool.h>
30 #include <errno.h>
31+#include <unistd.h>
32+#include <sys/socket.h>
33+#include <sys/un.h>
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h>
36
37 #include <libavutil/common.h>
38+#include <libavutil/hwcontext.h>
39+#include <libavutil/hwcontext_drm.h>
40
41 #include "config.h"
42
43@@ -62,6 +67,18 @@
44
45 #define MAX_BUFFERS 10
46
47+#define XV_DMA_CLIENT_PROP      "XV_DMA_CLIENT_ID"
48+#define XV_DMA_VER_STRIDE_PROP  "XV_DMA_VER_STRIDE"
49+#define XV_DMA_HOR_STRIDE_PROP  "XV_DMA_HOR_STRIDE"
50+#define XV_DMA_CLIENT_PATH      "/tmp/.xv_dma_client"
51+
52+struct dma_desc {
53+    int hor_stride;
54+    int ver_stride;
55+    int dma_fd;
56+    int valid;
57+};
58+
59 struct xvctx {
60     struct xv_ck_info_s {
61         int method; // CK_METHOD_* constants
62@@ -79,6 +96,8 @@ struct xvctx {
63     int current_ip_buf;
64     int num_buffers;
65     XvImage *xvimage[MAX_BUFFERS];
66+    struct dma_desc dma_descs[MAX_BUFFERS];
67+    int dma_client_id;
68     struct mp_image *original_image;
69     uint32_t image_width;
70     uint32_t image_height;
71@@ -111,6 +130,7 @@ static const struct fmt_entry fmt_table[] = {
72     {IMGFMT_420P,       MP_FOURCC_I420},
73     {IMGFMT_UYVY,       MP_FOURCC_UYVY},
74     {IMGFMT_NV12,       MP_FOURCC_NV12},
75+    {IMGFMT_DRMPRIME,   MP_FOURCC_NV12},
76     {0}
77 };
78
79@@ -177,6 +197,144 @@ static int xv_find_atom(struct vo *vo, uint32_t xv_port, const char *name,
80     return atom;
81 }
82
83+static int xv_check_dma_client(struct vo *vo)
84+{
85+    struct xvctx *ctx = vo->priv;
86+    Atom atom;
87+    int xv_value = 0;
88+
89+    if (!ctx->dma_client_id)
90+        return -1;
91+
92+    atom = XInternAtom(vo->x11->display, XV_DMA_CLIENT_PROP, True);
93+    if (atom != None)
94+        XvGetPortAttribute(vo->x11->display, ctx->xv_port, atom, &xv_value);
95+
96+    if (xv_value)
97+        return 0;
98+
99+    ctx->dma_client_id = 0;
100+    return -1;
101+}
102+
103+static void xv_flush_dma_client(struct vo *vo)
104+{
105+    struct xvctx *ctx = vo->priv;
106+    Atom atom;
107+
108+    if (!ctx->dma_client_id)
109+        return;
110+
111+    atom = XInternAtom(vo->x11->display, XV_DMA_CLIENT_PROP, True);
112+    if (atom != None) {
113+        XvSetPortAttribute(vo->x11->display, ctx->xv_port,
114+                           atom, ctx->dma_client_id);
115+        XvGetPortAttribute(vo->x11->display, ctx->xv_port, atom,
116+                           &ctx->dma_client_id);
117+    }
118+}
119+
120+static void xv_disable_dma_client(struct vo *vo)
121+{
122+    struct xvctx *ctx = vo->priv;
123+    Atom atom;
124+
125+    if (!ctx->dma_client_id)
126+        return;
127+
128+    atom = XInternAtom(vo->x11->display, XV_DMA_CLIENT_PROP, True);
129+    if (atom != None)
130+        XvSetPortAttribute(vo->x11->display, ctx->xv_port, atom, 0);
131+
132+    ctx->dma_client_id = 0;
133+}
134+
135+static void xv_send_dma_params(struct vo *vo, int hor_stride, int ver_stride)
136+{
137+    struct xvctx *ctx = vo->priv;
138+    Atom atom;
139+
140+    if (!ctx->dma_client_id)
141+        return;
142+
143+    atom = XInternAtom(vo->x11->display, XV_DMA_HOR_STRIDE_PROP, True);
144+    if (atom == None)
145+        goto failed;
146+
147+    XvSetPortAttribute(vo->x11->display, ctx->xv_port, atom, hor_stride);
148+
149+    atom = XInternAtom(vo->x11->display, XV_DMA_VER_STRIDE_PROP, True);
150+    if (atom == None)
151+        goto failed;
152+
153+    XvSetPortAttribute(vo->x11->display, ctx->xv_port, atom, ver_stride);
154+
155+    return;
156+
157+failed:
158+    xv_disable_dma_client(vo);
159+    ctx->dma_client_id = 0;
160+}
161+
162+static void xv_send_dma_fd(struct vo *vo, int dma_fd)
163+{
164+    struct xvctx *ctx = vo->priv;
165+    struct sockaddr_un addr;
166+    struct iovec iov;
167+    struct msghdr msg;
168+    struct cmsghdr *header;
169+    char buf[CMSG_SPACE(sizeof(int))];
170+    int socket_fd;
171+
172+    if (!ctx->dma_client_id)
173+        return;
174+
175+    xv_flush_dma_client(vo);
176+
177+    socket_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
178+    if (socket_fd < 0)
179+        goto failed;
180+
181+    addr.sun_family = AF_LOCAL;
182+    snprintf(addr.sun_path, sizeof (addr.sun_path),
183+             XV_DMA_CLIENT_PATH ".%d", ctx->dma_client_id);
184+    addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
185+
186+    if (connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
187+        goto failed;
188+
189+    iov.iov_base = buf;
190+    iov.iov_len = 1;
191+
192+    msg.msg_iov = &iov;
193+    msg.msg_iovlen = 1;
194+    msg.msg_control = buf;
195+    msg.msg_controllen = sizeof(buf);
196+    msg.msg_name = NULL;
197+    msg.msg_namelen = 0;
198+
199+    header = CMSG_FIRSTHDR(&msg);
200+    header->cmsg_level = SOL_SOCKET;
201+    header->cmsg_type = SCM_RIGHTS;
202+
203+    header->cmsg_len = CMSG_LEN(sizeof(int));
204+    *((int *)CMSG_DATA(header)) = dma_fd;
205+    sendmsg(socket_fd, &msg, 0);
206+
207+    /* Send am empty msg at the end */
208+    header->cmsg_len = CMSG_LEN(0);
209+    sendmsg(socket_fd, &msg, 0);
210+
211+    close(socket_fd);
212+    return;
213+
214+failed:
215+    xv_disable_dma_client(vo);
216+
217+    if (socket_fd >= 0)
218+        close(socket_fd);
219+}
220+
221 static int xv_set_eq(struct vo *vo, uint32_t xv_port, const char *name,
222                      int value)
223 {
224@@ -508,8 +666,10 @@ static int reconfig(struct vo *vo, struct mp_image_params *params)
225     MP_VERBOSE(vo, "using Xvideo port %d for hw scaling\n", ctx->xv_port);
226
227     // In case config has been called before
228-    for (i = 0; i < ctx->num_buffers; i++)
229+    for (i = 0; i < ctx->num_buffers; i++) {
230         deallocate_xvimage(vo, i);
231+        ctx->dma_descs[i].valid = 0;
232+    }
233
234     ctx->num_buffers = ctx->cfg_buffers;
235
236@@ -683,6 +843,14 @@ static void wait_for_completion(struct vo *vo, int max_outstanding)
237 static void flip_page(struct vo *vo)
238 {
239     struct xvctx *ctx = vo->priv;
240+    struct dma_desc *dma_desc = &ctx->dma_descs[ctx->current_buf];
241+
242+    if (dma_desc->valid) {
243+        xv_send_dma_fd(vo, dma_desc->dma_fd);
244+        xv_send_dma_params(vo, dma_desc->hor_stride, dma_desc->ver_stride);
245+        dma_desc->valid = 0;
246+    }
247+
248     put_xvimage(vo, ctx->xvimage[ctx->current_buf]);
249
250     /* remember the currently visible buffer */
251@@ -701,6 +869,26 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
252
253     struct mp_image xv_buffer = get_xv_buffer(vo, ctx->current_buf);
254     if (mpi) {
255+        if (mpi->hwctx && !xv_check_dma_client(vo)) {
256+            AVHWFramesContext *fctx = (void *)mpi->hwctx->data;
257+            if (fctx->format == AV_PIX_FMT_DRM_PRIME &&
258+                fctx->sw_format == AV_PIX_FMT_NV12) {
259+                AVDRMFrameDescriptor *desc =
260+                    (AVDRMFrameDescriptor *)mpi->planes[0];
261+                AVDRMLayerDescriptor *layer = &desc->layers[0];
262+                struct dma_desc *dma_desc = &ctx->dma_descs[ctx->current_buf];
263+
264+                dma_desc->hor_stride = layer->planes[0].pitch;
265+                dma_desc->ver_stride =
266+                    layer->planes[1].offset / dma_desc->hor_stride;
267+                dma_desc->dma_fd = desc->objects[0].fd;
268+                dma_desc->valid = 1;
269+
270+                // TODO: Draw osd on mmapped hw frame
271+                goto out;
272+            }
273+        }
274+
275         mp_image_copy(&xv_buffer, mpi);
276     } else {
277         mp_image_clear(&xv_buffer, 0, 0, xv_buffer.w, xv_buffer.h);
278@@ -709,6 +897,7 @@ static void draw_image(struct vo *vo, mp_image_t *mpi)
279     struct mp_osd_res res = osd_res_from_image_params(vo->params);
280     osd_draw_on_image(vo->osd, res, mpi ? mpi->pts : 0, 0, &xv_buffer);
281
282+out:
283     if (mpi != ctx->original_image) {
284         talloc_free(ctx->original_image);
285         ctx->original_image = mpi;
286@@ -847,6 +1036,9 @@ static int preinit(struct vo *vo)
287     ctx->fo = XvListImageFormats(x11->display, ctx->xv_port,
288                                  (int *) &ctx->formats);
289
290+    ctx->dma_client_id = getpid();
291+    xv_flush_dma_client(vo);
292+
293     MP_WARN(vo, "Warning: this legacy VO has bad quality and performance, "
294                 "and will in particular result in blurry OSD and subtitles. "
295                 "You should fix your graphics drivers, or not force the xv VO.\n");
296--
2972.17.1
298
299