1Upstream-Status: Inappropriate
2
3RPI-Distro repo forks original vlc and applies patches
4to enable raspiberry pi support.
5
6--- a/configure.ac
7+++ b/configure.ac
8@@ -3478,6 +3478,9 @@ dnl
9 AC_ARG_ENABLE(mmal,
10   AS_HELP_STRING([--enable-mmal],
11     [Multi-Media Abstraction Layer (MMAL) hardware plugin (default enable)]))
12+AC_ARG_ENABLE(mmal_avcodec,
13+  AS_HELP_STRING([--enable-mmal-avcodec],
14+    [Use MMAL enabled avcodec libs (default disable)]))
15 if test "${enable_mmal}" != "no"; then
16   VLC_SAVE_FLAGS
17   LDFLAGS="${LDFLAGS} -L/opt/vc/lib -lvchostif"
18@@ -3488,7 +3491,7 @@ if test "${enable_mmal}" != "no"; then
19         VLC_ADD_PLUGIN([mmal])
20         VLC_ADD_LDFLAGS([mmal],[ -L/opt/vc/lib ])
21         VLC_ADD_CFLAGS([mmal],[ -isystem /opt/vc/include -isystem /opt/vc/include/interface/vcos/pthreads -isystem /opt/vc/include/interface/vmcs_host/linux ])
22-        VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif ]) ], [
23+        VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif -lvchiq_arm -lvcsm ]) ], [
24           AS_IF([test "${enable_mmal}" = "yes"],
25             [ AC_MSG_ERROR([Cannot find bcm library...]) ],
26             [ AC_MSG_WARN([Cannot find bcm library...]) ])
27@@ -3500,6 +3503,7 @@ if test "${enable_mmal}" != "no"; then
28   VLC_RESTORE_FLAGS
29 fi
30 AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"])
31+AM_CONDITIONAL([HAVE_MMAL_AVCODEC], [test "${enable_mmal_avcodec}" = "yes"])
32
33 dnl
34 dnl evas plugin
35--- a/include/vlc_fourcc.h
36+++ b/include/vlc_fourcc.h
37@@ -365,6 +365,11 @@
38
39 /* Broadcom MMAL opaque buffer type */
40 #define VLC_CODEC_MMAL_OPAQUE     VLC_FOURCC('M','M','A','L')
41+#define VLC_CODEC_MMAL_ZC_SAND8   VLC_FOURCC('Z','S','D','8')
42+#define VLC_CODEC_MMAL_ZC_SAND10  VLC_FOURCC('Z','S','D','0')
43+#define VLC_CODEC_MMAL_ZC_SAND30  VLC_FOURCC('Z','S','D','3')
44+#define VLC_CODEC_MMAL_ZC_I420    VLC_FOURCC('Z','4','2','0')
45+#define VLC_CODEC_MMAL_ZC_RGB32   VLC_FOURCC('Z','R','G','B')
46
47 /* DXVA2 opaque video surface for use with D3D9 */
48 #define VLC_CODEC_D3D9_OPAQUE     VLC_FOURCC('D','X','A','9') /* 4:2:0  8 bpc */
49--- a/modules/hw/mmal/Makefile.am
50+++ b/modules/hw/mmal/Makefile.am
51@@ -1,23 +1,57 @@
52 include $(top_srcdir)/modules/common.am
53 mmaldir = $(pluginsdir)/mmal
54
55-AM_CFLAGS += $(CFLAGS_mmal)
56-AM_LDFLAGS += -rpath '$(mmaldir)' $(LDFLAGS_mmal)
57+AM_CFLAGS += -pthread $(CFLAGS_mmal)
58+AM_LDFLAGS += -pthread -rpath '$(mmaldir)' $(LDFLAGS_mmal)
59
60-libmmal_vout_plugin_la_SOURCES = vout.c mmal_picture.c mmal_picture.h
61+libmmal_vout_plugin_la_SOURCES = vout.c mmal_cma.c mmal_picture.c subpic.c\
62+  mmal_cma.h mmal_picture.h subpic.h transform_ops.h\
63+  mmal_piccpy_neon.S
64 libmmal_vout_plugin_la_CFLAGS = $(AM_CFLAGS)
65-libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm
66+libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm -lX11 -lXrandr
67 libmmal_vout_plugin_la_LIBADD = $(LIBS_mmal)
68 mmal_LTLIBRARIES = libmmal_vout_plugin.la
69
70-libmmal_codec_plugin_la_SOURCES = codec.c
71+libmmal_codec_plugin_la_SOURCES = codec.c mmal_cma.c mmal_picture.c subpic.c\
72+  mmal_cma.h mmal_picture.h subpic.h transform_ops.h\
73+  blend_rgba_neon.S mmal_piccpy_neon.S
74 libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS)
75 libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
76 libmmal_codec_plugin_la_LIBADD = $(LIBS_mmal)
77 mmal_LTLIBRARIES += libmmal_codec_plugin.la
78
79-libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c
80+libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c mmal_cma.c\
81+  mmal_cma.h mmal_picture.h transform_ops.h\
82+  mmal_piccpy_neon.S
83 libmmal_deinterlace_plugin_la_CFLAGS = $(AM_CFLAGS)
84 libmmal_deinterlace_plugin_la_LDFLAGS = $(AM_LDFLAGS)
85 libmmal_deinterlace_plugin_la_LIBADD = $(LIBS_mmal)
86 mmal_LTLIBRARIES += libmmal_deinterlace_plugin.la
87+
88+libmmal_xsplitter_plugin_la_SOURCES = xsplitter.c mmal_picture.c mmal_cma.c\
89+  mmal_cma.h mmal_picture.h transform_ops.h\
90+  mmal_piccpy_neon.S
91+libmmal_xsplitter_plugin_la_CFLAGS = $(AM_CFLAGS)
92+libmmal_xsplitter_plugin_la_LDFLAGS = $(AM_LDFLAGS)
93+libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal)
94+mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la
95+
96+libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c\
97+  mmal_cma.h mmal_picture.h transform_ops.h\
98+  mmal_piccpy_neon.S
99+libmmal_converter_plugin_la_CFLAGS = $(AM_CFLAGS)
100+libmmal_converter_plugin_la_LDFLAGS = $(AM_LDFLAGS)
101+libmmal_converter_plugin_la_LIBADD = $(LIBS_mmal)
102+mmal_LTLIBRARIES += libmmal_converter_plugin.la
103+
104+if HAVE_MMAL_AVCODEC
105+libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_cma.c mmal_picture.c\
106+  mmal_cma.h mmal_picture.h transform_ops.h\
107+  mmal_piccpy_neon.S
108+libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS)
109+libmmal_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
110+libmmal_avcodec_plugin_la_LIBADD = $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(LIBS_mmal)
111+mmal_LTLIBRARIES += libmmal_avcodec_plugin.la
112+endif
113+
114+
115--- /dev/null
116+++ b/modules/hw/mmal/blend_rgba_neon.S
117@@ -0,0 +1,197 @@
118+        .syntax unified
119+        .arm
120+//      .thumb
121+        .text
122+        .align 16
123+        .arch armv7-a
124+        .fpu neon-vfpv4
125+
126+@ blend_rgbx_rgba_neon
127+
128+@ Implements /255 as ((x * 257) + 0x8000) >> 16
129+@ This generates something in the range [(x+126)/255, (x+127)/255] which is good enough
130+
131+@ There is advantage to aligning src and/or dest - dest gives a bit more due to being used twice
132+
133+
134+
135+@ [r0] RGBx dest      loaded into d20-d23
136+@ [r1] RGBA src merge loaded into d16-d19
137+@ r2   plane alpha
138+@ r3   count (pixels)
139+
140+.macro blend_main sR, sG, sB, sA, dR, dG, dB, dA
141+
142+        push      { r4, lr }
143+
144+        vdup.u8    d7,  r2
145+
146+        subs       r3,  #8
147+        vmov.u8    d6,  #0xff
148+
149+        blt        2f
150+
151+        @ If < 16 bytes to move then don't bother trying to align
152+        @ (a) This means the the align doesn't need to worry about r3 underflow
153+        @ (b) The overhead would be greater than any gain
154+        cmp        r3,  #8
155+        mov        r4,  r3
156+        ble        1f
157+
158+        @ Align r1 on a 32 byte boundary
159+        neg        r3,  r0
160+        ubfx       r3,  r3,  #2,  #3
161+
162+        cmp        r3,  #0
163+        blne       10f
164+
165+        sub        r3,  r4,  r3
166+
167+1:
168+        vld4.8    {d16, d17, d18, d19}, [r1]
169+
170+1:
171+        vmull.u8   q15, \sA, d7
172+
173+        vld4.8    {d20, d21, d22, d23}, [r0]
174+
175+        vsra.u16   q15, q15, #8
176+        subs       r3,  #8
177+        vrshrn.u16 d31, q15, #8
178+        vsub.u8    d30, d6,  d31
179+
180+        vmull.u8   q12, \sR, d31
181+        vmull.u8   q13, \sG, d31
182+        vmull.u8   q14, \sB, d31
183+        addge      r1,  #32
184+
185+        vmlal.u8   q12, \dR, d30
186+        vmlal.u8   q13, \dG, d30
187+        vmlal.u8   q14, \dB, d30
188+        vld4.8    {d16, d17, d18, d19}, [r1]
189+
190+        vsra.u16   q12, q12, #8         @ * 257/256
191+        vsra.u16   q13, q13, #8
192+        vsra.u16   q14, q14, #8
193+
194+        vrshrn.u16 \dR, q12, #8
195+        vrshrn.u16 \dG, q13, #8
196+        vrshrn.u16 \dB, q14, #8
197+        vmov.u8    \dA, #0xff
198+
199+        vst4.8    {d20, d21, d22, d23}, [r0]!
200+        bge        1b
201+        add        r1,  #32
202+
203+2:
204+        cmp        r3,  #-8
205+        blgt       10f
206+
207+        pop       { r4, pc }
208+
209+
210+// Partial version
211+// Align @ start & deal with tail
212+10:
213+        lsls       r2,  r3,  #30        @ b2 -> C, b1 -> N
214+        mov        r2,  r0
215+        bcc        1f
216+        vld4.8    {d16[0], d17[0], d18[0], d19[0]}, [r1]!
217+        vld4.8    {d20[0], d21[0], d22[0], d23[0]}, [r2]!
218+        vld4.8    {d16[1], d17[1], d18[1], d19[1]}, [r1]!
219+        vld4.8    {d20[1], d21[1], d22[1], d23[1]}, [r2]!
220+        vld4.8    {d16[2], d17[2], d18[2], d19[2]}, [r1]!
221+        vld4.8    {d20[2], d21[2], d22[2], d23[2]}, [r2]!
222+        vld4.8    {d16[3], d17[3], d18[3], d19[3]}, [r1]!
223+        vld4.8    {d20[3], d21[3], d22[3], d23[3]}, [r2]!
224+1:
225+        bpl        1f
226+        vld4.8    {d16[4], d17[4], d18[4], d19[4]}, [r1]!
227+        vld4.8    {d20[4], d21[4], d22[4], d23[4]}, [r2]!
228+        vld4.8    {d16[5], d17[5], d18[5], d19[5]}, [r1]!
229+        vld4.8    {d20[5], d21[5], d22[5], d23[5]}, [r2]!
230+1:
231+        tst        r3,  #1
232+        beq        1f
233+        vld4.8    {d16[6], d17[6], d18[6], d19[6]}, [r1]!
234+        vld4.8    {d20[6], d21[6], d22[6], d23[6]}, [r2]!
235+1:
236+        @ Set conditions for later
237+        lsls       r2,  r3,  #30        @ b2 -> C, b1 -> N
238+
239+        vmull.u8   q15, \sA, d7
240+        vsra.u16   q15, q15, #8
241+        vrshrn.u16 d31, q15, #8
242+        vsub.u8    d30, d6,  d31
243+
244+        vmull.u8   q12, \sR, d31
245+        vmull.u8   q13, \sG, d31
246+        vmull.u8   q14, \sB, d31
247+
248+        vmlal.u8   q12, \dR, d30
249+        vmlal.u8   q13, \dG, d30
250+        vmlal.u8   q14, \dB, d30
251+
252+        vsra.u16   q12, q12, #8
253+        vsra.u16   q13, q13, #8
254+        vsra.u16   q14, q14, #8
255+
256+        vrshrn.u16 \dR, q12, #8
257+        vrshrn.u16 \dG, q13, #8
258+        vrshrn.u16 \dB, q14, #8
259+        vmov.u8    \dA, #0xff
260+
261+        bcc        1f
262+        vst4.8    {d20[0], d21[0], d22[0], d23[0]}, [r0]!
263+        vst4.8    {d20[1], d21[1], d22[1], d23[1]}, [r0]!
264+        vst4.8    {d20[2], d21[2], d22[2], d23[2]}, [r0]!
265+        vst4.8    {d20[3], d21[3], d22[3], d23[3]}, [r0]!
266+1:
267+        bpl        1f
268+        vst4.8    {d20[4], d21[4], d22[4], d23[4]}, [r0]!
269+        vst4.8    {d20[5], d21[5], d22[5], d23[5]}, [r0]!
270+1:
271+        tst        r3,  #1
272+        bxeq       lr
273+        vst4.8    {d20[6], d21[6], d22[6], d23[6]}, [r0]!
274+
275+        bx         lr
276+
277+.endm
278+
279+
280+@ [r0] RGBx dest      (Byte order: R, G, B, x)
281+@ [r1] RGBA src merge (Byte order: R, G, B, A)
282+@ r2   plane alpha
283+@ r3   count (pixels)
284+
285+@ Whilst specified as RGBx+RGBA the only important part is the position of
286+@ alpha, the other components are all treated the same
287+
288+@ [r0] RGBx dest      (Byte order: R, G, B, x)
289+@ [r1] RGBA src merge (Byte order: R, G, B, A) - same as above
290+@ r2   plane alpha
291+@ r3   count (pixels)
292+        .align  16
293+        .global blend_rgbx_rgba_neon
294+#ifdef __ELF__
295+        .type   blend_rgbx_rgba_neon, %function
296+#endif
297+blend_rgbx_rgba_neon:
298+        blend_main d16, d17, d18, d19, d20, d21, d22, d23
299+
300+
301+@ [r0] RGBx dest      (Byte order: R, G, B, x)
302+@ [r1] RGBA src merge (Byte order: B, G, R, A) - B / R swapped
303+@ r2   plane alpha
304+@ r3   count (pixels)
305+        .align  16
306+        .global blend_bgrx_rgba_neon
307+#ifdef __ELF__
308+        .type   blend_bgrx_rgba_neon, %function
309+#endif
310+blend_bgrx_rgba_neon:
311+        blend_main d18, d17, d16, d19, d20, d21, d22, d23
312+
313+
314+
315--- /dev/null
316+++ b/modules/hw/mmal/blend_rgba_neon.h
317@@ -0,0 +1,17 @@
318+#ifndef HW_MMAL_BLEND_RGBA_NEON_H
319+#define HW_MMAL_BLEND_RGBA_NEON_H
320+
321+#ifdef __cplusplus
322+extern "C" {
323+#endif
324+
325+typedef void blend_neon_fn(void * dest, const void * src, int alpha, unsigned int n);
326+extern blend_neon_fn blend_rgbx_rgba_neon;
327+extern blend_neon_fn blend_bgrx_rgba_neon;
328+
329+#ifdef __cplusplus
330+}
331+#endif
332+
333+#endif
334+
335--- /dev/null
336+++ b/modules/hw/mmal/blend_test.c
337@@ -0,0 +1,180 @@
338+#include <stdio.h>
339+#include <stdint.h>
340+#include <memory.h>
341+
342+#include "blend_rgba_neon.h"
343+
344+#define RPI_PROFILE 1
345+#define RPI_PROC_ALLOC 1
346+#include "rpi_prof.h"
347+
348+static inline unsigned div255(unsigned v)
349+{
350+    // This models what we we do in the asm for / 255
351+    // It generates something in the range [(i+126)/255, (i+127)/255] which is good enough
352+    return ((v * 257) + 0x8000) >> 16;
353+}
354+
355+static inline unsigned int a_merge(unsigned int dst, unsigned src, unsigned f)
356+{
357+    return div255((255 - f) * (dst) + src * f);
358+}
359+
360+
361+static void merge_line(void * dest, const void * src, int alpha, unsigned int n)
362+{
363+    unsigned int i;
364+    const uint8_t * s_data = src;
365+    uint8_t * d_data = dest;
366+
367+    for (i = 0; i != n; ++i) {
368+        const uint32_t s_pel = ((const uint32_t *)s_data)[i];
369+        const uint32_t d_pel = ((const uint32_t *)d_data)[i];
370+        const unsigned int a = div255(alpha * (s_pel >> 24));
371+        ((uint32_t *)d_data)[i] = 0xff000000 |
372+            (a_merge((d_pel >> 16) & 0xff, (s_pel >> 16) & 0xff, a) << 16) |
373+            (a_merge((d_pel >> 8)  & 0xff, (s_pel >> 8)  & 0xff, a) << 8 ) |
374+            (a_merge((d_pel >> 0)  & 0xff, (s_pel >> 0)  & 0xff, a) << 0 );
375+    }
376+}
377+
378+
379+// Merge RGBA with BGRA
380+static void merge_line2(void * dest, const void * src, int alpha, unsigned int n)
381+{
382+    unsigned int i;
383+    const uint8_t * s_data = src;
384+    uint8_t * d_data = dest;
385+
386+    for (i = 0; i != n; ++i) {
387+        const uint32_t s_pel = ((const uint32_t *)s_data)[i];
388+        const uint32_t d_pel = ((const uint32_t *)d_data)[i];
389+        const unsigned int a = div255(alpha * (s_pel >> 24));
390+        ((uint32_t *)d_data)[i] = 0xff000000 |
391+            (a_merge((d_pel >> 0)  & 0xff, (s_pel >> 16) & 0xff, a) << 0 ) |
392+            (a_merge((d_pel >> 8)  & 0xff, (s_pel >> 8)  & 0xff, a) << 8 ) |
393+            (a_merge((d_pel >> 16) & 0xff, (s_pel >> 0)  & 0xff, a) << 16);
394+    }
395+}
396+
397+#define BUF_SIZE   256
398+#define BUF_SLACK  16
399+#define BUF_ALIGN  64
400+#define BUF_ALLOC  (BUF_SIZE + 2*BUF_SLACK + BUF_ALIGN)
401+
402+static void test_line(const uint32_t * const dx, const unsigned int d_off,
403+                      const uint32_t * const sx, const unsigned int s_off,
404+                      const unsigned int alpha, const unsigned int len, const int prof_no)
405+{
406+    uint32_t d0_buf[BUF_ALLOC];
407+    uint32_t d1_buf[BUF_ALLOC];
408+    const uint32_t * const s0 = sx + s_off;
409+
410+    uint32_t * const d0 =  (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
411+    uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
412+    unsigned int i;
413+
414+    memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4);
415+    memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4);
416+
417+    merge_line(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
418+
419+    PROFILE_START();
420+    blend_rgbx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
421+    PROFILE_ACC_N(prof_no);
422+
423+    for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) {
424+        if (d0[i] != d1[i]) {
425+            printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len);
426+        }
427+    }
428+}
429+
430+static void test_line2(const uint32_t * const dx, const unsigned int d_off,
431+                      const uint32_t * const sx, const unsigned int s_off,
432+                      const unsigned int alpha, const unsigned int len, const int prof_no)
433+{
434+    uint32_t d0_buf[BUF_ALLOC];
435+    uint32_t d1_buf[BUF_ALLOC];
436+    const uint32_t * const s0 = sx + s_off;
437+
438+    uint32_t * const d0 =  (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
439+    uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
440+    unsigned int i;
441+
442+    memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4);
443+    memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4);
444+
445+    merge_line2(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
446+
447+    PROFILE_START();
448+    blend_bgrx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
449+    PROFILE_ACC_N(prof_no);
450+
451+    for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) {
452+        if (d0[i] != d1[i]) {
453+            printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len);
454+        }
455+    }
456+}
457+
458+
459+
460+int main(int argc, char *argv[])
461+{
462+    unsigned int i, j;
463+    uint32_t d0_buf[BUF_ALLOC];
464+    uint32_t s0_buf[BUF_ALLOC];
465+
466+    uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + 63) & ~63) + 0;
467+    uint32_t * const s0 = (uint32_t *)(((uintptr_t)s0_buf + 63) & ~63) + 0;
468+
469+    PROFILE_INIT();
470+
471+    for (i = 0; i != 255*255; ++i) {
472+        unsigned int a = div255(i);
473+        unsigned int b = (i + 127)/255;
474+        unsigned int c = (i + 126)/255;
475+        if (a != b && a != c)
476+            printf("%d/255: %d != %d/%d\n", i, a, b, c);
477+    }
478+
479+    for (i = 0; i != BUF_ALLOC; ++i) {
480+        d0_buf[i] = 0xff00 | i;
481+        s0_buf[i] = (i << 24) | 0x40ffc0;
482+    }
483+
484+    for (i = 0; i != 256; ++i) {
485+        test_line(d0, 0, s0, 0, i, 256, -1);
486+    }
487+    for (i = 0; i != 256; ++i) {
488+        test_line(d0, 0, s0, 0, 128, i, -1);
489+    }
490+
491+    for (j = 0; j != 16; ++j) {
492+        for (i = 0; i != 256; ++i) {
493+            test_line(d0, j & 3, s0, j >> 2, i, 256, j);
494+        }
495+        PROFILE_PRINTF_N(j);
496+        PROFILE_CLEAR_N(j);
497+    }
498+    printf("Done 1\n");
499+
500+    for (i = 0; i != 256; ++i) {
501+        test_line2(d0, 0, s0, 0, i, 256, -1);
502+    }
503+    for (i = 0; i != 256; ++i) {
504+        test_line2(d0, 0, s0, 0, 128, i, -1);
505+    }
506+
507+    for (j = 0; j != 16; ++j) {
508+        for (i = 0; i != 256; ++i) {
509+            test_line2(d0, j & 3, s0, j >> 2, i, 256, j);
510+        }
511+        PROFILE_PRINTF_N(j);
512+    }
513+    printf("Done 2\n");
514+
515+    return 0;
516+}
517+
518--- a/modules/hw/mmal/codec.c
519+++ b/modules/hw/mmal/codec.c
520@@ -26,267 +26,443 @@
521 #include "config.h"
522 #endif
523
524+#include <stdatomic.h>
525+
526 #include <vlc_common.h>
527-#include <vlc_atomic.h>
528 #include <vlc_plugin.h>
529 #include <vlc_codec.h>
530+#include <vlc_filter.h>
531 #include <vlc_threads.h>
532
533-#include <bcm_host.h>
534 #include <interface/mmal/mmal.h>
535 #include <interface/mmal/util/mmal_util.h>
536 #include <interface/mmal/util/mmal_default_components.h>
537
538+#include <interface/vcsm/user-vcsm.h>
539+
540+#include "mmal_cma.h"
541 #include "mmal_picture.h"
542
543+#include "subpic.h"
544+#include "blend_rgba_neon.h"
545+
546+#define TRACE_ALL 0
547+
548+#define OPT_TO_FROM_ZC 0
549+
550 /*
551  * This seems to be a bit high, but reducing it causes instabilities
552  */
553 #define NUM_EXTRA_BUFFERS 5
554+//#define NUM_EXTRA_BUFFERS 10
555 #define NUM_DECODER_BUFFER_HEADERS 30
556
557-#define MIN_NUM_BUFFERS_IN_TRANSIT 2
558+#define CONVERTER_BUFFERS 4  // Buffers on the output of the converter
559+
560+#define MMAL_SLICE_HEIGHT 16
561+#define MMAL_ALIGN_W      32
562+#define MMAL_ALIGN_H      16
563
564 #define MMAL_OPAQUE_NAME "mmal-opaque"
565 #define MMAL_OPAQUE_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.")
566 #define MMAL_OPAQUE_LONGTEXT N_("Decode frames directly into RPI VideoCore instead of host memory. This option must only be used with the MMAL video output plugin.")
567
568-static int OpenDecoder(decoder_t *dec);
569-static void CloseDecoder(decoder_t *dec);
570-
571-vlc_module_begin()
572-    set_shortname(N_("MMAL decoder"))
573-    set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
574-    set_capability("video decoder", 90)
575-    add_shortcut("mmal_decoder")
576-    add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
577-    set_callbacks(OpenDecoder, CloseDecoder)
578-vlc_module_end()
579+#define MMAL_RESIZE_NAME "mmal-resize"
580+#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.")
581+#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.")
582+
583+#define MMAL_ISP_NAME "mmal-isp"
584+#define MMAL_ISP_TEXT N_("Use mmal isp rather than hvs.")
585+#define MMAL_ISP_LONGTEXT N_("Use mmal isp rather than hvs. This may be faster but has no blend.")
586
587-struct decoder_sys_t {
588-    bool opaque;
589+typedef struct decoder_sys_t
590+{
591     MMAL_COMPONENT_T *component;
592     MMAL_PORT_T *input;
593     MMAL_POOL_T *input_pool;
594     MMAL_PORT_T *output;
595-    MMAL_POOL_T *output_pool; /* only used for non-opaque mode */
596+    hw_mmal_port_pool_ref_t *ppr;
597     MMAL_ES_FORMAT_T *output_format;
598-    vlc_sem_t sem;
599
600+    MMAL_STATUS_T err_stream;
601     bool b_top_field_first;
602     bool b_progressive;
603
604+    bool b_flushed;
605+
606+    vcsm_init_type_t vcsm_init_type;
607+
608+    // Lock to avoid pic update & allocate happenening simultainiously
609+    // * We should be able to arrange life s.t. this isn't needed
610+    //   but while we are confused apply belt & braces
611+    vlc_mutex_t pic_lock;
612+
613     /* statistics */
614-    int output_in_transit;
615-    int input_in_transit;
616     atomic_bool started;
617-};
618+} decoder_sys_t;
619
620-/* Utilities */
621-static int change_output_format(decoder_t *dec);
622-static int send_output_buffer(decoder_t *dec);
623-static void fill_output_port(decoder_t *dec);
624-
625-/* VLC decoder callback */
626-static int decode(decoder_t *dec, block_t *block);
627-static void flush_decoder(decoder_t *dec);
628-
629-/* MMAL callbacks */
630-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
631-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
632-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
633
634-static int OpenDecoder(decoder_t *dec)
635-{
636-    int ret = VLC_SUCCESS;
637-    decoder_sys_t *sys;
638-    MMAL_PARAMETER_UINT32_T extra_buffers;
639-    MMAL_STATUS_T status;
640+typedef struct supported_mmal_enc_s {
641+    struct {
642+       MMAL_PARAMETER_HEADER_T header;
643+       MMAL_FOURCC_T encodings[64];
644+    } supported;
645+    int n;
646+} supported_mmal_enc_t;
647+
648+#define SUPPORTED_MMAL_ENC_INIT \
649+{ \
650+    {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, \
651+    -1 \
652+}
653
654-    if (dec->fmt_in.i_codec != VLC_CODEC_MPGV &&
655-            dec->fmt_in.i_codec != VLC_CODEC_H264)
656-        return VLC_EGENERIC;
657+static supported_mmal_enc_t supported_decode_in_enc = SUPPORTED_MMAL_ENC_INIT;
658
659-    sys = calloc(1, sizeof(decoder_sys_t));
660-    if (!sys) {
661-        ret = VLC_ENOMEM;
662-        goto out;
663+static bool is_enc_supported(supported_mmal_enc_t * const support, const MMAL_FOURCC_T fcc)
664+{
665+    int i;
666+
667+    if (fcc == 0)
668+        return false;
669+    if (support->n == -1)
670+        return true;  // Unknown - say OK
671+    for (i = 0; i < support->n; ++i) {
672+        if (support->supported.encodings[i] == fcc)
673+            return true;
674     }
675-    dec->p_sys = sys;
676+    return false;
677+}
678
679-    sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME);
680-    bcm_host_init();
681+static bool set_and_test_enc_supported(supported_mmal_enc_t * const support, MMAL_PORT_T * port, const MMAL_FOURCC_T fcc)
682+{
683+    if (support->n >= 0)
684+        /* already done */;
685+    else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&support->supported) != MMAL_SUCCESS)
686+        support->n = 0;
687+    else
688+        support->n = (support->supported.header.size - sizeof(support->supported.header)) /
689+          sizeof(support->supported.encodings[0]);
690
691-    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
692-    if (status != MMAL_SUCCESS) {
693-        msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
694-                MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
695-        ret = VLC_EGENERIC;
696-        goto out;
697-    }
698+    return is_enc_supported(support, fcc);
699+}
700
701-    sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
702-    status = mmal_port_enable(sys->component->control, control_port_cb);
703-    if (status != MMAL_SUCCESS) {
704-        msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
705-                sys->component->control->name, status, mmal_status_to_string(status));
706-        ret = VLC_EGENERIC;
707-        goto out;
708+static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc)
709+{
710+    switch (fcc){
711+    case VLC_CODEC_MJPG:
712+        return MMAL_ENCODING_MJPEG;
713+    case VLC_CODEC_MP1V:
714+        return MMAL_ENCODING_MP1V;
715+    case VLC_CODEC_MPGV:
716+    case VLC_CODEC_MP2V:
717+        return MMAL_ENCODING_MP2V;
718+    case VLC_CODEC_H263:
719+        return MMAL_ENCODING_H263;
720+    case VLC_CODEC_MP4V:
721+        return MMAL_ENCODING_MP4V;
722+    case VLC_CODEC_H264:
723+        return MMAL_ENCODING_H264;
724+    case VLC_CODEC_VP6:
725+        return MMAL_ENCODING_VP6;
726+    case VLC_CODEC_VP8:
727+        return MMAL_ENCODING_VP8;
728+    case VLC_CODEC_WMV1:
729+        return MMAL_ENCODING_WMV1;
730+    case VLC_CODEC_WMV2:
731+        return MMAL_ENCODING_WMV2;
732+    case VLC_CODEC_WMV3:
733+        return MMAL_ENCODING_WMV3;
734+    case VLC_CODEC_VC1:
735+        return MMAL_ENCODING_WVC1;
736+    case VLC_CODEC_THEORA:
737+        return MMAL_ENCODING_THEORA;
738+    default:
739+        break;
740     }
741+    return 0;
742+}
743
744-    sys->input = sys->component->input[0];
745-    sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
746-    if (dec->fmt_in.i_codec == VLC_CODEC_MPGV)
747-        sys->input->format->encoding = MMAL_ENCODING_MP2V;
748-    else
749-        sys->input->format->encoding = MMAL_ENCODING_H264;
750+static MMAL_FOURCC_T pic_to_slice_mmal_fourcc(const MMAL_FOURCC_T fcc)
751+{
752+    switch (fcc){
753+    case MMAL_ENCODING_I420:
754+        return MMAL_ENCODING_I420_SLICE;
755+    case MMAL_ENCODING_I422:
756+        return MMAL_ENCODING_I422_SLICE;
757+    case MMAL_ENCODING_ARGB:
758+        return MMAL_ENCODING_ARGB_SLICE;
759+    case MMAL_ENCODING_RGBA:
760+        return MMAL_ENCODING_RGBA_SLICE;
761+    case MMAL_ENCODING_ABGR:
762+        return MMAL_ENCODING_ABGR_SLICE;
763+    case MMAL_ENCODING_BGRA:
764+        return MMAL_ENCODING_BGRA_SLICE;
765+    case MMAL_ENCODING_RGB16:
766+        return MMAL_ENCODING_RGB16_SLICE;
767+    case MMAL_ENCODING_RGB24:
768+        return MMAL_ENCODING_RGB24_SLICE;
769+    case MMAL_ENCODING_RGB32:
770+        return MMAL_ENCODING_RGB32_SLICE;
771+    case MMAL_ENCODING_BGR16:
772+        return MMAL_ENCODING_BGR16_SLICE;
773+    case MMAL_ENCODING_BGR24:
774+        return MMAL_ENCODING_BGR24_SLICE;
775+    case MMAL_ENCODING_BGR32:
776+        return MMAL_ENCODING_BGR32_SLICE;
777+    default:
778+        break;
779+    }
780+    return 0;
781+}
782
783-    if (dec->fmt_in.i_codec == VLC_CODEC_H264) {
784-        if (dec->fmt_in.i_extra > 0) {
785-            status = mmal_format_extradata_alloc(sys->input->format,
786-                    dec->fmt_in.i_extra);
787-            if (status == MMAL_SUCCESS) {
788-                memcpy(sys->input->format->extradata, dec->fmt_in.p_extra,
789-                        dec->fmt_in.i_extra);
790-                sys->input->format->extradata_size = dec->fmt_in.i_extra;
791-            } else {
792-                msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)",
793-                        sys->input->name, status, mmal_status_to_string(status));
794-            }
795+#define DEBUG_SQUARES 0
796+#if DEBUG_SQUARES
797+static void draw_square(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t val)
798+{
799+    uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x;
800+    unsigned int i;
801+    for (i = 0; i != h; ++i) {
802+        unsigned int j;
803+        for (j = 0; j != w; ++j) {
804+            p[j] = val;
805         }
806+        p += pic_stride;
807     }
808+}
809+#endif
810
811-    status = mmal_port_format_commit(sys->input);
812-    if (status != MMAL_SUCCESS) {
813-        msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
814-                sys->input->name, status, mmal_status_to_string(status));
815-        ret = VLC_EGENERIC;
816-        goto out;
817+#if 0
818+static inline void draw_line(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int len, int inc)
819+{
820+    uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x;
821+    while (len-- != 0) {
822+        *p = ~0U;
823+        p += inc;
824     }
825-    sys->input->buffer_size = sys->input->buffer_size_recommended;
826-    sys->input->buffer_num = sys->input->buffer_num_recommended;
827+}
828
829-    status = mmal_port_enable(sys->input, input_port_cb);
830-    if (status != MMAL_SUCCESS) {
831-        msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
832-                sys->input->name, status, mmal_status_to_string(status));
833-        ret = VLC_EGENERIC;
834-        goto out;
835-    }
836
837-    sys->output = sys->component->output[0];
838-    sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
839+static void draw_corners(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
840+{
841+    const unsigned int len = 20;
842+    draw_line(pic_buf, pic_stride, x, y, len, 1);
843+    draw_line(pic_buf, pic_stride, x, y, len, pic_stride);
844+    draw_line(pic_buf, pic_stride, x + w - 1, y, len, -1);
845+    draw_line(pic_buf, pic_stride, x + w - 1, y, len, pic_stride);
846+    draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -1);
847+    draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -(int)pic_stride);
848+    draw_line(pic_buf, pic_stride, x, y + h - 1, len, 1);
849+    draw_line(pic_buf, pic_stride, x, y + h - 1, len, -(int)pic_stride);
850+}
851+#endif
852
853-    if (sys->opaque) {
854-        extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
855-        extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
856-        extra_buffers.value = NUM_EXTRA_BUFFERS;
857-        status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
858-        if (status != MMAL_SUCCESS) {
859-            msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
860-                    status, mmal_status_to_string(status));
861-            ret = VLC_EGENERIC;
862-            goto out;
863-        }
864+static MMAL_RATIONAL_T
865+rationalize_sar(unsigned int num, unsigned int den)
866+{
867+    static const unsigned int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 0};
868+    const unsigned int * p = primes;
869
870-        msg_Dbg(dec, "Activate zero-copy for output port");
871-        MMAL_PARAMETER_BOOLEAN_T zero_copy = {
872-            { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
873-            1
874-        };
875+    // If either num or den is 0 then return a well formed "unknown"
876+    if (num == 0 || den == 0) {
877+        return (MMAL_RATIONAL_T){.num = 0, .den = 0};
878+    }
879
880-        status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
881-        if (status != MMAL_SUCCESS) {
882-           msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
883-                    sys->output->name, status, mmal_status_to_string(status));
884-           goto out;
885+    while (*p != 0 && num >= *p && den >= *p) {
886+        if (num % *p != 0 || den % *p != 0)
887+            ++p;
888+        else {
889+            num /= *p;
890+            den /= *p;
891         }
892     }
893+    return (MMAL_RATIONAL_T){.num = num, .den = den};
894+}
895
896-    status = mmal_port_enable(sys->output, output_port_cb);
897-    if (status != MMAL_SUCCESS) {
898-        msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)",
899-                sys->output->name, status, mmal_status_to_string(status));
900-        ret = VLC_EGENERIC;
901-        goto out;
902-    }
903+// Buffer either attached to pic or released
904+static picture_t * alloc_opaque_pic(decoder_t * const dec, MMAL_BUFFER_HEADER_T * const buf)
905+{
906+    decoder_sys_t *const dec_sys = dec->p_sys;
907
908-    status = mmal_component_enable(sys->component);
909-    if (status != MMAL_SUCCESS) {
910-        msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
911-                sys->component->name, status, mmal_status_to_string(status));
912-        ret = VLC_EGENERIC;
913-        goto out;
914+    vlc_mutex_lock(&dec_sys->pic_lock);
915+    picture_t * const pic = decoder_NewPicture(dec);
916+    vlc_mutex_unlock(&dec_sys->pic_lock);
917+
918+    if (pic == NULL)
919+        goto fail1;
920+
921+    if (buf->length == 0) {
922+        msg_Err(dec, "%s: Empty buffer", __func__);
923+        goto fail2;
924     }
925
926-    sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
927+    if ((pic->context = hw_mmal_gen_context(buf, dec_sys->ppr)) == NULL)
928+        goto fail2;
929
930-    if (sys->opaque) {
931-        dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
932-        dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
933-    } else {
934-        dec->fmt_out.i_codec = VLC_CODEC_I420;
935-        dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
936+    buf_to_pic_copy_props(pic, buf);
937+
938+#if TRACE_ALL
939+    msg_Dbg(dec, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
940+#endif
941+
942+    return pic;
943+
944+fail2:
945+    picture_Release(pic);
946+fail1:
947+    // Recycle rather than release to avoid buffer starvation if NewPic fails
948+    hw_mmal_port_pool_ref_recycle(dec_sys->ppr, buf);
949+    return NULL;
950+}
951+
952+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
953+{
954+    decoder_t *dec = (decoder_t *)port->userdata;
955+    MMAL_STATUS_T status;
956+
957+#if TRACE_ALL
958+    msg_Dbg(dec, "<<< %s: cmd=%d, data=%p", __func__, buffer->cmd, buffer->data);
959+#endif
960+
961+    if (buffer->cmd == MMAL_EVENT_ERROR) {
962+        status = *(uint32_t *)buffer->data;
963+        dec->p_sys->err_stream = status;
964+        msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
965+                mmal_status_to_string(status));
966     }
967
968-    dec->pf_decode = decode;
969-    dec->pf_flush  = flush_decoder;
970+    mmal_buffer_header_release(buffer);
971+}
972
973-    vlc_sem_init(&sys->sem, 0);
974+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
975+{
976+    block_t * const block = (block_t *)buffer->user_data;
977
978-out:
979-    if (ret != VLC_SUCCESS)
980-        CloseDecoder(dec);
981+    (void)port;  // Unused
982
983-    return ret;
984+#if TRACE_ALL
985+    msg_Dbg((decoder_t *)port->userdata, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
986+            buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
987+#endif
988+
989+    mmal_buffer_header_reset(buffer);
990+    mmal_buffer_header_release(buffer);
991+
992+    if (block != NULL)
993+        block_Release(block);
994 }
995
996-static void CloseDecoder(decoder_t *dec)
997+static void decoder_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
998 {
999-    decoder_sys_t *sys = dec->p_sys;
1000-    MMAL_BUFFER_HEADER_T *buffer;
1001+    decoder_t * const dec = (decoder_t *)port->userdata;
1002
1003-    if (!sys)
1004+    if (buffer->cmd == 0 && buffer->length != 0)
1005+    {
1006+#if TRACE_ALL
1007+        msg_Dbg(dec, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
1008+                buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
1009+#endif
1010+
1011+        picture_t *pic = alloc_opaque_pic(dec, buffer);
1012+#if TRACE_ALL
1013+        msg_Dbg(dec, "flags=%#x, video flags=%#x", buffer->flags, buffer->type->video.flags);
1014+#endif
1015+        if (pic == NULL)
1016+            msg_Err(dec, "Failed to allocate new picture");
1017+        else
1018+            decoder_QueueVideo(dec, pic);
1019+        // Buffer released or attached to pic - do not release again
1020         return;
1021+    }
1022
1023-    if (sys->component && sys->component->control->is_enabled)
1024-        mmal_port_disable(sys->component->control);
1025+    if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED)
1026+    {
1027+        decoder_sys_t * const sys = dec->p_sys;
1028+        MMAL_EVENT_FORMAT_CHANGED_T * const fmt = mmal_event_format_changed_get(buffer);
1029+        MMAL_ES_FORMAT_T * const format = mmal_format_alloc();
1030
1031-    if (sys->input && sys->input->is_enabled)
1032-        mmal_port_disable(sys->input);
1033+        if (format == NULL)
1034+            msg_Err(dec, "Failed to allocate new format");
1035+        else
1036+        {
1037+            mmal_format_full_copy(format, fmt->format);
1038+            format->encoding = MMAL_ENCODING_OPAQUE;
1039
1040-    if (sys->output && sys->output->is_enabled)
1041-        mmal_port_disable(sys->output);
1042+            // If no PAR in the stream - see if we've got one from the demux
1043+            if (format->es->video.par.den <= 0 || format->es->video.par.num <= 0) {
1044+                unsigned int n = dec->fmt_in.video.i_sar_num;
1045+                unsigned int d = dec->fmt_in.video.i_sar_den;
1046+
1047+                if (n == 0 || d == 0) {
1048+                    // Guesswork required
1049+                    const unsigned int w = format->es->video.width;
1050+                    const unsigned int h = format->es->video.height;
1051+                    if ((w == 704 || w == 720) && (h == 480 || h == 576)) {
1052+                        // Very likely SD 4:3
1053+                        n = w * 3;
1054+                        d = h * 4;
1055+                    }
1056+                    else
1057+                    {
1058+                        // Otherwise guess SAR 1:1
1059+                        n = 1;
1060+                        d = 1;
1061+                    }
1062+                }
1063
1064-    if (sys->component && sys->component->is_enabled)
1065-        mmal_component_disable(sys->component);
1066+                format->es->video.par = rationalize_sar(n, d);
1067+            }
1068
1069-    if (sys->input_pool)
1070-        mmal_pool_destroy(sys->input_pool);
1071+            if (sys->output_format != NULL)
1072+                mmal_format_free(sys->output_format);
1073
1074-    if (sys->output_format)
1075-        mmal_format_free(sys->output_format);
1076+            sys->output_format = format;
1077+        }
1078+    }
1079+    else if (buffer->cmd != 0) {
1080+        char buf0[5];
1081+        msg_Warn(dec, "Unexpected output cb event: %s", str_fourcc(buf0, buffer->cmd));
1082+    }
1083
1084-    if (sys->output_pool)
1085-        mmal_pool_destroy(sys->output_pool);
1086+    // If we get here then we were flushing (cmd == 0 && len == 0) or
1087+    // that was an EVENT - in either case we want to release the buffer
1088+    // back to its pool rather than recycle it.
1089+    mmal_buffer_header_reset(buffer);
1090+    buffer->user_data = NULL;
1091+    mmal_buffer_header_release(buffer);
1092+}
1093
1094-    if (sys->component)
1095-        mmal_component_release(sys->component);
1096
1097-    vlc_sem_destroy(&sys->sem);
1098-    free(sys);
1099
1100-    bcm_host_deinit();
1101+static void fill_output_port(decoder_t *dec)
1102+{
1103+    decoder_sys_t *sys = dec->p_sys;
1104+
1105+    if (decoder_UpdateVideoFormat(dec) != 0)
1106+    {
1107+        // If we have a new format don't bother stuffing the buffer
1108+        // We should get a reset RSN
1109+#if TRACE_ALL
1110+        msg_Dbg(dec, "%s: Updated", __func__);
1111+#endif
1112+
1113+        return;
1114+    }
1115+
1116+    hw_mmal_port_pool_ref_fill(sys->ppr);
1117+    return;
1118 }
1119
1120 static int change_output_format(decoder_t *dec)
1121 {
1122     MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type;
1123-    decoder_sys_t *sys = dec->p_sys;
1124+    decoder_sys_t * const sys = dec->p_sys;
1125     MMAL_STATUS_T status;
1126-    int pool_size;
1127     int ret = 0;
1128
1129+#if TRACE_ALL
1130+    msg_Dbg(dec, "%s: <<<", __func__);
1131+#endif
1132+
1133     if (atomic_load(&sys->started)) {
1134         mmal_format_full_copy(sys->output->format, sys->output_format);
1135         status = mmal_port_format_commit(sys->output);
1136@@ -300,7 +476,9 @@ static int change_output_format(decoder_
1137     }
1138
1139 port_reset:
1140+#if TRACE_ALL
1141     msg_Dbg(dec, "%s: Do full port reset", __func__);
1142+#endif
1143     status = mmal_port_disable(sys->output);
1144     if (status != MMAL_SUCCESS) {
1145         msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)",
1146@@ -310,6 +488,7 @@ port_reset:
1147     }
1148
1149     mmal_format_full_copy(sys->output->format, sys->output_format);
1150+
1151     status = mmal_port_format_commit(sys->output);
1152     if (status != MMAL_SUCCESS) {
1153         msg_Err(dec, "Failed to commit output format (status=%"PRIx32" %s)",
1154@@ -318,18 +497,10 @@ port_reset:
1155         goto out;
1156     }
1157
1158-    if (sys->opaque) {
1159-        sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
1160-        pool_size = NUM_DECODER_BUFFER_HEADERS;
1161-    } else {
1162-        sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended,
1163-                MIN_NUM_BUFFERS_IN_TRANSIT);
1164-        pool_size = sys->output->buffer_num;
1165-    }
1166-
1167+    sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
1168     sys->output->buffer_size = sys->output->buffer_size_recommended;
1169
1170-    status = mmal_port_enable(sys->output, output_port_cb);
1171+    status = mmal_port_enable(sys->output, decoder_output_cb);
1172     if (status != MMAL_SUCCESS) {
1173         msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)",
1174                 status, mmal_status_to_string(status));
1175@@ -338,25 +509,14 @@ port_reset:
1176     }
1177
1178     if (!atomic_load(&sys->started)) {
1179-        if (!sys->opaque) {
1180-            sys->output_pool = mmal_port_pool_create(sys->output, pool_size, 0);
1181-            msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num);
1182-        }
1183-
1184         atomic_store(&sys->started, true);
1185
1186         /* we need one picture from vout for each buffer header on the output
1187          * port */
1188-        dec->i_extra_picture_buffers = pool_size;
1189-
1190-        /* remove what VLC core reserves as it is part of the pool_size
1191-         * already */
1192-        if (dec->fmt_in.i_codec == VLC_CODEC_H264)
1193-            dec->i_extra_picture_buffers -= 19;
1194-        else
1195-            dec->i_extra_picture_buffers -= 3;
1196-
1197+        dec->i_extra_picture_buffers = 10;
1198+#if TRACE_ALL
1199         msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers);
1200+#endif
1201     }
1202
1203 apply_fmt:
1204@@ -366,8 +526,8 @@ apply_fmt:
1205     dec->fmt_out.video.i_y_offset = sys->output->format->es->video.crop.y;
1206     dec->fmt_out.video.i_visible_width = sys->output->format->es->video.crop.width;
1207     dec->fmt_out.video.i_visible_height = sys->output->format->es->video.crop.height;
1208-    dec->fmt_out.video.i_sar_num = sys->output->format->es->video.par.num;
1209-    dec->fmt_out.video.i_sar_den = sys->output->format->es->video.par.den;
1210+    dec->fmt_out.video.i_sar_num = sys->output_format->es->video.par.num;  // SAR can be killed by commit
1211+    dec->fmt_out.video.i_sar_den = sys->output_format->es->video.par.den;
1212     dec->fmt_out.video.i_frame_rate = sys->output->format->es->video.frame_rate.num;
1213     dec->fmt_out.video.i_frame_rate_base = sys->output->format->es->video.frame_rate.den;
1214
1215@@ -382,12 +542,19 @@ apply_fmt:
1216         sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive);
1217         sys->b_top_field_first = sys->b_progressive ? true :
1218             (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst);
1219+#if TRACE_ALL
1220         msg_Dbg(dec, "Detected %s%s video (%d)",
1221                 sys->b_progressive ? "progressive" : "interlaced",
1222                 sys->b_progressive ? "" : (sys->b_top_field_first ? " tff" : " bff"),
1223                 interlace_type.eMode);
1224+#endif
1225     }
1226
1227+    // Tell the rest of the world we have changed format
1228+    vlc_mutex_lock(&sys->pic_lock);
1229+    ret = decoder_UpdateVideoFormat(dec);
1230+    vlc_mutex_unlock(&sys->pic_lock);
1231+
1232 out:
1233     mmal_format_free(sys->output_format);
1234     sys->output_format = NULL;
1235@@ -395,144 +562,85 @@ out:
1236     return ret;
1237 }
1238
1239-static int send_output_buffer(decoder_t *dec)
1240+static MMAL_STATUS_T
1241+set_extradata_and_commit(decoder_t * const dec, decoder_sys_t * const sys)
1242 {
1243-    decoder_sys_t *sys = dec->p_sys;
1244-    MMAL_BUFFER_HEADER_T *buffer;
1245-    picture_sys_t *p_sys;
1246-    picture_t *picture = NULL;
1247     MMAL_STATUS_T status;
1248-    unsigned buffer_size = 0;
1249-    int ret = 0;
1250
1251-    if (!sys->output->is_enabled)
1252-        return VLC_EGENERIC;
1253-
1254-    /* If local output pool is allocated, use it - this is only the case for
1255-     * non-opaque modes */
1256-    if (sys->output_pool) {
1257-        buffer = mmal_queue_get(sys->output_pool->queue);
1258-        if (!buffer) {
1259-            msg_Warn(dec, "Failed to get new buffer");
1260-            return VLC_EGENERIC;
1261-        }
1262-    }
1263-
1264-    if (!decoder_UpdateVideoFormat(dec))
1265-        picture = decoder_NewPicture(dec);
1266-    if (!picture) {
1267-        msg_Warn(dec, "Failed to get new picture");
1268-        ret = -1;
1269-        goto err;
1270-    }
1271-
1272-    p_sys = picture->p_sys;
1273-    for (int i = 0; i < picture->i_planes; i++)
1274-        buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch;
1275-
1276-    if (sys->output_pool) {
1277-        mmal_buffer_header_reset(buffer);
1278-        buffer->alloc_size = sys->output->buffer_size;
1279-        if (buffer_size < sys->output->buffer_size) {
1280-            msg_Err(dec, "Retrieved picture with too small data block (%d < %d)",
1281-                    buffer_size, sys->output->buffer_size);
1282-            ret = VLC_EGENERIC;
1283-            goto err;
1284-        }
1285-
1286-        if (!sys->opaque)
1287-            buffer->data = picture->p[0].p_pixels;
1288-    } else {
1289-        buffer = p_sys->buffer;
1290-        if (!buffer) {
1291-            msg_Warn(dec, "Picture has no buffer attached");
1292-            picture_Release(picture);
1293-            return VLC_EGENERIC;
1294-        }
1295-        buffer->data = p_sys->buffer->data;
1296-    }
1297-    buffer->user_data = picture;
1298-    buffer->cmd = 0;
1299-
1300-    status = mmal_port_send_buffer(sys->output, buffer);
1301+    status = mmal_port_format_commit(sys->input);
1302     if (status != MMAL_SUCCESS) {
1303-        msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)",
1304-                status, mmal_status_to_string(status));
1305-        ret = -1;
1306-        goto err;
1307-    }
1308-    atomic_fetch_add(&sys->output_in_transit, 1);
1309-
1310-    return ret;
1311-
1312-err:
1313-    if (picture)
1314-        picture_Release(picture);
1315-    if (sys->output_pool && buffer) {
1316-        buffer->data = NULL;
1317-        mmal_buffer_header_release(buffer);
1318+        msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
1319+                sys->input->name, status, mmal_status_to_string(status));
1320     }
1321-    return ret;
1322+    return status;
1323 }
1324
1325-static void fill_output_port(decoder_t *dec)
1326+static MMAL_STATUS_T decoder_send_extradata(decoder_t * const dec, decoder_sys_t *const sys)
1327 {
1328-    decoder_sys_t *sys = dec->p_sys;
1329-
1330-    unsigned max_buffers_in_transit = 0;
1331-    int buffers_available = 0;
1332-    int buffers_to_send = 0;
1333-    int i;
1334+    if (dec->fmt_in.i_codec == VLC_CODEC_H264 &&
1335+        dec->fmt_in.i_extra > 0)
1336+    {
1337+        MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->input_pool->queue);
1338+        MMAL_STATUS_T status;
1339+
1340+        mmal_buffer_header_reset(buf);
1341+        buf->cmd = 0;
1342+        buf->user_data = NULL;
1343+        buf->alloc_size = sys->input->buffer_size;
1344+        buf->length = dec->fmt_in.i_extra;
1345+        buf->data = dec->fmt_in.p_extra;
1346+        buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG;
1347
1348-    if (sys->output_pool) {
1349-        max_buffers_in_transit = __MAX(sys->output_pool->headers_num,
1350-                MIN_NUM_BUFFERS_IN_TRANSIT);
1351-        buffers_available = mmal_queue_length(sys->output_pool->queue);
1352-    } else {
1353-        max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS;
1354-        buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit);
1355+        status = mmal_port_send_buffer(sys->input, buf);
1356+        if (status != MMAL_SUCCESS) {
1357+            msg_Err(dec, "Failed to send extradata buffer to input port (status=%"PRIx32" %s)",
1358+                    status, mmal_status_to_string(status));
1359+            return status;
1360+        }
1361     }
1362-    buffers_to_send = max_buffers_in_transit - atomic_load(&sys->output_in_transit);
1363
1364-    if (buffers_to_send > buffers_available)
1365-        buffers_to_send = buffers_available;
1366-
1367-#ifndef NDEBUG
1368-    msg_Dbg(dec, "Send %d buffers to output port (available: %d, "
1369-                    "in_transit: %d, buffer_num: %d)",
1370-                    buffers_to_send, buffers_available,
1371-                    atomic_load(&sys->output_in_transit),
1372-                    sys->output->buffer_num);
1373-#endif
1374-    for (i = 0; i < buffers_to_send; ++i)
1375-        if (send_output_buffer(dec) < 0)
1376-            break;
1377+    return MMAL_SUCCESS;
1378 }
1379
1380 static void flush_decoder(decoder_t *dec)
1381 {
1382-    decoder_sys_t *sys = dec->p_sys;
1383-    MMAL_BUFFER_HEADER_T *buffer;
1384-    MMAL_STATUS_T status;
1385+    decoder_sys_t *const sys = dec->p_sys;
1386
1387-    msg_Dbg(dec, "Flushing decoder ports...");
1388-    mmal_port_flush(sys->output);
1389-    mmal_port_flush(sys->input);
1390-
1391-    while (atomic_load(&sys->output_in_transit) ||
1392-           atomic_load(&sys->input_in_transit))
1393-        vlc_sem_wait(&sys->sem);
1394+#if TRACE_ALL
1395+    msg_Dbg(dec, "%s: <<<", __func__);
1396+#endif
1397+
1398+    if (!sys->b_flushed) {
1399+        mmal_port_disable(sys->input);
1400+        mmal_port_disable(sys->output);
1401+        // We can leave the input disabled, but we want the output enabled
1402+        // in order to sink any buffers returning from other modules
1403+        mmal_port_enable(sys->output, decoder_output_cb);
1404+        sys->b_flushed = true;
1405+    }
1406+#if TRACE_ALL
1407+    msg_Dbg(dec, "%s: >>>", __func__);
1408+#endif
1409 }
1410
1411 static int decode(decoder_t *dec, block_t *block)
1412 {
1413     decoder_sys_t *sys = dec->p_sys;
1414     MMAL_BUFFER_HEADER_T *buffer;
1415-    bool need_flush = false;
1416     uint32_t len;
1417-    uint32_t flags = 0;
1418+    uint32_t flags = MMAL_BUFFER_HEADER_FLAG_FRAME_START;
1419     MMAL_STATUS_T status;
1420
1421+#if TRACE_ALL
1422+    msg_Dbg(dec, "<<< %s: %lld/%lld", __func__, block == NULL ? -1LL : block->i_dts, block == NULL ? -1LL : block->i_pts);
1423+#endif
1424+
1425+    if (sys->err_stream != MMAL_SUCCESS) {
1426+        msg_Err(dec, "MMAL error reported by ctrl");
1427+        flush_decoder(dec);
1428+        return VLCDEC_ECRITICAL;  /// I think they are all fatal
1429+    }
1430+
1431     /*
1432      * Configure output port if necessary
1433      */
1434@@ -541,18 +649,50 @@ static int decode(decoder_t *dec, block_
1435             msg_Err(dec, "Failed to change output port format");
1436     }
1437
1438-    if (!block)
1439-        goto out;
1440+    if (block == NULL)
1441+        return VLCDEC_SUCCESS;
1442
1443     /*
1444      * Check whether full flush is required
1445      */
1446-    if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
1447+    if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
1448+#if TRACE_ALL
1449+        msg_Dbg(dec, "%s: >>> Discontinuity", __func__);
1450+#endif
1451         flush_decoder(dec);
1452+    }
1453+
1454+    if (block->i_buffer == 0)
1455+    {
1456         block_Release(block);
1457         return VLCDEC_SUCCESS;
1458     }
1459
1460+    // Reenable stuff if the last thing we did was flush
1461+    if (!sys->output->is_enabled &&
1462+        (status = mmal_port_enable(sys->output, decoder_output_cb)) != MMAL_SUCCESS)
1463+    {
1464+        msg_Err(dec, "Output port enable failed");
1465+        goto fail;
1466+    }
1467+
1468+    if (!sys->input->is_enabled)
1469+    {
1470+        if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
1471+            goto fail;
1472+
1473+        if ((status = mmal_port_enable(sys->input, input_port_cb)) != MMAL_SUCCESS)
1474+        {
1475+            msg_Err(dec, "Input port enable failed");
1476+            goto fail;
1477+        }
1478+
1479+        if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
1480+            goto fail;
1481+    }
1482+
1483+    // *** We cannot get a picture to put the result in 'till we have
1484+    // reported the size & the output stages have been set up
1485     if (atomic_load(&sys->started))
1486         fill_output_port(dec);
1487
1488@@ -563,18 +703,21 @@ static int decode(decoder_t *dec, block_
1489     if (block->i_flags & BLOCK_FLAG_CORRUPTED)
1490         flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED;
1491
1492-    while (block && block->i_buffer > 0) {
1493-        buffer = mmal_queue_timedwait(sys->input_pool->queue, 100);
1494+    while (block != NULL)
1495+    {
1496+        buffer = mmal_queue_wait(sys->input_pool->queue);
1497         if (!buffer) {
1498             msg_Err(dec, "Failed to retrieve buffer header for input data");
1499-            need_flush = true;
1500-            break;
1501+            goto fail;
1502         }
1503+
1504         mmal_buffer_header_reset(buffer);
1505         buffer->cmd = 0;
1506-        buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts;
1507+        buffer->pts = block->i_pts != VLC_TICK_INVALID ? block->i_pts :
1508+            block->i_dts != VLC_TICK_INVALID ? block->i_dts : MMAL_TIME_UNKNOWN;
1509         buffer->dts = block->i_dts;
1510         buffer->alloc_size = sys->input->buffer_size;
1511+        buffer->user_data = NULL;
1512
1513         len = block->i_buffer;
1514         if (len > buffer->alloc_size)
1515@@ -585,94 +728,1808 @@ static int decode(decoder_t *dec, block_
1516         block->i_buffer -= len;
1517         buffer->length = len;
1518         if (block->i_buffer == 0) {
1519+            flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
1520+            if (block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE) {
1521+                msg_Dbg(dec, "EOS sent");
1522+                flags |= MMAL_BUFFER_HEADER_FLAG_EOS;
1523+            }
1524             buffer->user_data = block;
1525             block = NULL;
1526         }
1527         buffer->flags = flags;
1528
1529+#if TRACE_ALL
1530+        msg_Dbg(dec, "%s: -- Send buffer: cmd=%d, data=%p, size=%d, len=%d, offset=%d, flags=%#x, pts=%lld, dts=%lld", __func__,\
1531+                buffer->cmd, buffer->data, buffer->alloc_size, buffer->length, buffer->offset,
1532+                buffer->flags, (long long)buffer->pts, (long long)buffer->dts);
1533+#endif
1534         status = mmal_port_send_buffer(sys->input, buffer);
1535         if (status != MMAL_SUCCESS) {
1536             msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)",
1537                     status, mmal_status_to_string(status));
1538-            break;
1539+            goto fail;
1540         }
1541-        atomic_fetch_add(&sys->input_in_transit, 1);
1542+
1543+        // Reset flushed flag once we have sent a buf
1544+        sys->b_flushed = false;
1545+        flags &= ~MMAL_BUFFER_HEADER_FLAG_FRAME_START;
1546     }
1547+    return VLCDEC_SUCCESS;
1548
1549-out:
1550-    if (need_flush)
1551-        flush_decoder(dec);
1552+fail:
1553+    flush_decoder(dec);
1554+    return VLCDEC_ECRITICAL;
1555
1556-    return VLCDEC_SUCCESS;
1557 }
1558
1559-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1560+
1561+static void CloseDecoder(decoder_t *dec)
1562 {
1563-    decoder_t *dec = (decoder_t *)port->userdata;
1564+    decoder_sys_t *sys = dec->p_sys;
1565+
1566+#if TRACE_ALL
1567+    msg_Dbg(dec, "%s: <<<", __func__);
1568+#endif
1569+
1570+    if (!sys)
1571+        return;
1572+
1573+    if (sys->component != NULL) {
1574+        if (sys->input->is_enabled)
1575+            mmal_port_disable(sys->input);
1576+
1577+        if (sys->output->is_enabled)
1578+            mmal_port_disable(sys->output);
1579+
1580+        if (sys->component->control->is_enabled)
1581+            mmal_port_disable(sys->component->control);
1582+
1583+        if (sys->component->is_enabled)
1584+            mmal_component_disable(sys->component);
1585+
1586+        mmal_component_release(sys->component);
1587+    }
1588+
1589+    if (sys->input_pool != NULL)
1590+        mmal_pool_destroy(sys->input_pool);
1591+
1592+    if (sys->output_format != NULL)
1593+        mmal_format_free(sys->output_format);
1594+
1595+    hw_mmal_port_pool_ref_release(sys->ppr, false);
1596+
1597+    cma_vcsm_exit(sys->vcsm_init_type);
1598+
1599+    vlc_mutex_destroy(&sys->pic_lock);
1600+    free(sys);
1601+}
1602+
1603+static int OpenDecoder(decoder_t *dec)
1604+{
1605+    int ret = VLC_EGENERIC;
1606+    decoder_sys_t *sys;
1607     MMAL_STATUS_T status;
1608+    const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec);
1609+
1610+#if TRACE_ALL || 1
1611+    {
1612+        char buf1[5], buf2[5], buf2a[5];
1613+        char buf3[5], buf4[5];
1614+        MMAL_RATIONAL_T r = rationalize_sar(dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den);
1615+
1616+        msg_Dbg(dec, "%s: <<< (%s/%s)[%s] %dx%d %d/%d=%d/%d o:%#x -> (%s/%s) %dx%d %d/%d o:%#x", __func__,
1617+                str_fourcc(buf1, dec->fmt_in.i_codec),
1618+                str_fourcc(buf2, dec->fmt_in.video.i_chroma),
1619+                str_fourcc(buf2a, in_fcc),
1620+                dec->fmt_in.video.i_width, dec->fmt_in.video.i_height,
1621+                dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den,
1622+                r.num, r.den,
1623+                (int)dec->fmt_in.video.orientation,
1624+                str_fourcc(buf3, dec->fmt_out.i_codec),
1625+                str_fourcc(buf4, dec->fmt_out.video.i_chroma),
1626+                dec->fmt_out.video.i_width, dec->fmt_out.video.i_height,
1627+                dec->fmt_out.video.i_sar_num, dec->fmt_out.video.i_sar_den,
1628+                (int)dec->fmt_out.video.orientation);
1629+    }
1630+#endif
1631+
1632+    if (!is_enc_supported(&supported_decode_in_enc, in_fcc))
1633+        return VLC_EGENERIC;
1634+
1635+    sys = calloc(1, sizeof(decoder_sys_t));
1636+    if (!sys) {
1637+        ret = VLC_ENOMEM;
1638+        goto fail;
1639+    }
1640+    dec->p_sys = sys;
1641+    vlc_mutex_init(&sys->pic_lock);
1642+
1643+    if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
1644+        msg_Err(dec, "VCSM init failed");
1645+        goto fail;
1646+    }
1647+    msg_Info(dec, "VCSM init succeeded: %s", cma_vcsm_init_str(sys->vcsm_init_type));
1648+
1649+    sys->err_stream = MMAL_SUCCESS;
1650+
1651+    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
1652+    if (status != MMAL_SUCCESS) {
1653+        msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
1654+                MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
1655+        goto fail;
1656+    }
1657+
1658+    sys->input = sys->component->input[0];
1659+    sys->output = sys->component->output[0];
1660+
1661+    sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
1662+    sys->input->format->encoding = in_fcc;
1663+
1664+    if (!set_and_test_enc_supported(&supported_decode_in_enc, sys->input, in_fcc)) {
1665+#if TRACE_ALL
1666+        char cbuf[5];
1667+        msg_Dbg(dec, "Format not supported: %s", str_fourcc(cbuf, in_fcc));
1668+#endif
1669+        goto fail;
1670+    }
1671+
1672+    sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
1673+    status = mmal_port_enable(sys->component->control, control_port_cb);
1674+    if (status != MMAL_SUCCESS) {
1675+        msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
1676+                sys->component->control->name, status, mmal_status_to_string(status));
1677+        goto fail;
1678+    }
1679+
1680+    if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
1681+        goto fail;
1682+
1683+    sys->input->buffer_size = sys->input->buffer_size_recommended;
1684+    sys->input->buffer_num = sys->input->buffer_num_recommended;
1685+
1686+    status = mmal_port_enable(sys->input, input_port_cb);
1687+    if (status != MMAL_SUCCESS) {
1688+        msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
1689+                sys->input->name, status, mmal_status_to_string(status));
1690+        goto fail;
1691+    }
1692+
1693+    // Set vanishingly unlikely shape (or at least crop)
1694+    // to ensure that we get a resolution changed event
1695+    // Small wxh are rejected (128x128 is rejected) so pick a
1696+    // plausible size.
1697+    // Crop doesn't seem to be checked for being constrained by wxh
1698+    // so we could place it outside the pic to be sure that it is
1699+    // never matched but stick with something legal in case it is ever
1700+    // actually checked
1701+    sys->output->format->es->video.height = 256;
1702+    sys->output->format->es->video.width = 256;
1703+    sys->output->format->es->video.crop.height = 4;
1704+    sys->output->format->es->video.crop.width = 2;
1705+    sys->output->format->es->video.crop.x = 66;
1706+    sys->output->format->es->video.crop.y = 88;
1707+
1708+    if ((status = hw_mmal_opaque_output(VLC_OBJECT(dec), &sys->ppr,
1709+                                        sys->output, NUM_EXTRA_BUFFERS, decoder_output_cb)) != MMAL_SUCCESS)
1710+        goto fail;
1711+
1712+    status = mmal_component_enable(sys->component);
1713+    if (status != MMAL_SUCCESS) {
1714+        msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
1715+                sys->component->name, status, mmal_status_to_string(status));
1716+        goto fail;
1717+    }
1718+
1719+    if ((sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
1720+    {
1721+        msg_Err(dec, "Failed to create input pool");
1722+        goto fail;
1723+    }
1724+
1725+    sys->b_flushed = true;
1726+
1727+    if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
1728+        goto fail;
1729+
1730+    // Given no better ideas at this point copy input format to output
1731+    // This also copies container stuff (such as orientation) that we do not
1732+    // decode from the ES but may be important to display
1733+    video_format_Copy(&dec->fmt_out.video, &dec->fmt_in.video);
1734+    dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
1735+    dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
1736+
1737+
1738+    dec->pf_decode = decode;
1739+    dec->pf_flush  = flush_decoder;
1740+
1741+#if TRACE_ALL
1742+    msg_Dbg(dec, ">>> %s: ok", __func__);
1743+#endif
1744+    return 0;
1745+
1746+fail:
1747+    CloseDecoder(dec);
1748+#if TRACE_ALL
1749+msg_Dbg(dec, ">>> %s: FAIL: ret=%d", __func__, ret);
1750+#endif
1751+    return ret;
1752+}
1753+
1754+// ----------------------------
1755+
1756+#define CONV_MAX_LATENCY 1  // In frames
1757+
1758+typedef struct pic_fifo_s {
1759+    picture_t * head;
1760+    picture_t * tail;
1761+} pic_fifo_t;
1762+
1763+static inline picture_t * pic_fifo_get(pic_fifo_t * const pf)
1764+{
1765+    picture_t * const pic = pf->head;;
1766+    if (pic != NULL) {
1767+        pf->head = pic->p_next;
1768+        pic->p_next = NULL;
1769+    }
1770+    return pic;
1771+}
1772+
1773+static inline picture_t * pic_fifo_get_all(pic_fifo_t * const pf)
1774+{
1775+    picture_t * const pic = pf->head;;
1776+    pf->head = NULL;
1777+    return pic;
1778+}
1779+
1780+static inline void pic_fifo_release_all(pic_fifo_t * const pf)
1781+{
1782+    picture_t * pic;
1783+    while ((pic = pic_fifo_get(pf)) != NULL) {
1784+        picture_Release(pic);
1785+    }
1786+}
1787+
1788+static inline void pic_fifo_init(pic_fifo_t * const pf)
1789+{
1790+    pf->head = NULL;
1791+    pf->tail = NULL;  // Not strictly needed
1792+}
1793+
1794+static inline void pic_fifo_put(pic_fifo_t * const pf, picture_t * pic)
1795+{
1796+    pic->p_next = NULL;
1797+    if (pf->head == NULL)
1798+        pf->head = pic;
1799+    else
1800+        pf->tail->p_next = pic;
1801+    pf->tail = pic;
1802+}
1803+
1804+#define SUBS_MAX 3
1805+
1806+typedef enum filter_resizer_e {
1807+    FILTER_RESIZER_RESIZER,
1808+    FILTER_RESIZER_ISP,
1809+    FILTER_RESIZER_HVS
1810+} filter_resizer_t;
1811+
1812+typedef struct conv_frame_stash_s
1813+{
1814+    mtime_t pts;
1815+    MMAL_BUFFER_HEADER_T * sub_bufs[SUBS_MAX];
1816+} conv_frame_stash_t;
1817+
1818+typedef struct filter_sys_t {
1819+    filter_resizer_t resizer_type;
1820+    MMAL_COMPONENT_T *component;
1821+    MMAL_PORT_T *input;
1822+    MMAL_PORT_T *output;
1823+    MMAL_POOL_T *out_pool;  // Free output buffers
1824+    MMAL_POOL_T *in_pool;   // Input pool to get BH for replication
1825+
1826+    cma_buf_pool_t * cma_in_pool;
1827+    cma_buf_pool_t * cma_out_pool;
1828+
1829+    subpic_reg_stash_t subs[SUBS_MAX];
1830+
1831+    pic_fifo_t ret_pics;
1832+
1833+    unsigned int pic_n;
1834+    vlc_sem_t sem;
1835+    vlc_mutex_t lock;
1836+
1837+    MMAL_STATUS_T err_stream;
1838+
1839+    bool needs_copy_in;
1840+    bool is_cma;
1841+    bool is_sliced;
1842+    bool out_fmt_set;
1843+    const char * component_name;
1844+    MMAL_PORT_BH_CB_T in_port_cb_fn;
1845+    MMAL_PORT_BH_CB_T out_port_cb_fn;
1846+
1847+    uint64_t frame_seq;
1848+    conv_frame_stash_t stash[16];
1849+
1850+    // Slice specific tracking stuff
1851+    struct {
1852+        pic_fifo_t pics;
1853+        unsigned int line;  // Lines filled
1854+    } slice;
1855+
1856+    vcsm_init_type_t vcsm_init_type;
1857+} filter_sys_t;
1858+
1859+
1860+static MMAL_STATUS_T pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic)
1861+{
1862+    unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3;
1863+    MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
1864+
1865+    es_fmt->type = MMAL_ES_TYPE_VIDEO;
1866+    es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format);
1867+    es_fmt->encoding_variant = 0;
1868+
1869+    // Fill in crop etc.
1870+    hw_mmal_vlc_fmt_to_mmal_fmt(es_fmt, &pic->format);
1871+    // Override width / height with strides if appropriate
1872+    if (bpp != 0) {
1873+        v_fmt->width = pic->p[0].i_pitch / bpp;
1874+        v_fmt->height = pic->p[0].i_lines;
1875+    }
1876+    return MMAL_SUCCESS;
1877+}
1878+
1879+
1880+static MMAL_STATUS_T conv_enable_in(filter_t * const p_filter, filter_sys_t * const sys)
1881+{
1882+    MMAL_STATUS_T err = MMAL_SUCCESS;
1883+
1884+    if (!sys->input->is_enabled &&
1885+        (err = mmal_port_enable(sys->input, sys->in_port_cb_fn)) != MMAL_SUCCESS)
1886+    {
1887+        msg_Err(p_filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
1888+                sys->input->name, err, mmal_status_to_string(err));
1889+    }
1890+    return err;
1891+}
1892+
1893+static MMAL_STATUS_T conv_enable_out(filter_t * const p_filter, filter_sys_t * const sys)
1894+{
1895+    MMAL_STATUS_T err = MMAL_SUCCESS;
1896+
1897+    if (sys->is_cma)
1898+    {
1899+        if (sys->cma_out_pool == NULL &&
1900+            (sys->cma_out_pool = cma_buf_pool_new(CONVERTER_BUFFERS, CONVERTER_BUFFERS, true, "mmal_resizer")) == NULL)
1901+        {
1902+            msg_Err(p_filter, "Failed to alloc cma buf pool");
1903+            return MMAL_ENOMEM;
1904+        }
1905+    }
1906+    else
1907+    {
1908+        cma_buf_pool_deletez(&sys->cma_out_pool);
1909+    }
1910+
1911+    if (!sys->output->is_enabled &&
1912+        (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS)
1913+    {
1914+        msg_Err(p_filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
1915+                sys->output->name, err, mmal_status_to_string(err));
1916+    }
1917+    return err;
1918+}
1919+
1920+static void conv_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1921+{
1922+    filter_t * const p_filter = (filter_t *)port->userdata;
1923+
1924+#if TRACE_ALL
1925+    msg_Dbg(p_filter, "%s: <<< cmd=%d, data=%p, pic=%p", __func__, buffer->cmd, buffer->data, buffer->user_data);
1926+#endif
1927
1928     if (buffer->cmd == MMAL_EVENT_ERROR) {
1929-        status = *(uint32_t *)buffer->data;
1930-        msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
1931+        MMAL_STATUS_T status = *(uint32_t *)buffer->data;
1932+
1933+        p_filter->p_sys->err_stream = status;
1934+
1935+        msg_Err(p_filter, "MMAL error %"PRIx32" \"%s\"", status,
1936                 mmal_status_to_string(status));
1937     }
1938
1939     mmal_buffer_header_release(buffer);
1940 }
1941
1942-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1943+static void conv_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
1944 {
1945-    block_t *block = (block_t *)buffer->user_data;
1946-    decoder_t *dec = (decoder_t *)port->userdata;
1947-    decoder_sys_t *sys = dec->p_sys;
1948-    buffer->user_data = NULL;
1949+#if TRACE_ALL
1950+    picture_context_t * ctx = buf->user_data;
1951+//    filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
1952+
1953+    msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, ctx=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
1954+            __func__, buf->cmd, ctx, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
1955+#else
1956+    VLC_UNUSED(port);
1957+#endif
1958+
1959+    mmal_buffer_header_release(buf);
1960+
1961+#if TRACE_ALL
1962+    msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
1963+#endif
1964+}
1965+
1966+static void conv_out_q_pic(filter_sys_t * const sys, picture_t * const pic)
1967+{
1968+    pic->p_next = NULL;
1969+
1970+    vlc_mutex_lock(&sys->lock);
1971+    pic_fifo_put(&sys->ret_pics, pic);
1972+    vlc_mutex_unlock(&sys->lock);
1973
1974-    mmal_buffer_header_release(buffer);
1975-    if (block)
1976-        block_Release(block);
1977-    atomic_fetch_sub(&sys->input_in_transit, 1);
1978     vlc_sem_post(&sys->sem);
1979 }
1980
1981-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1982+static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
1983 {
1984-    decoder_t *dec = (decoder_t *)port->userdata;
1985-    decoder_sys_t *sys = dec->p_sys;
1986-    picture_t *picture;
1987-    MMAL_EVENT_FORMAT_CHANGED_T *fmt;
1988-    MMAL_ES_FORMAT_T *format;
1989-
1990-    if (buffer->cmd == 0) {
1991-        picture = (picture_t *)buffer->user_data;
1992-        if (buffer->length > 0) {
1993-            picture->date = buffer->pts;
1994-            picture->b_progressive = sys->b_progressive;
1995-            picture->b_top_field_first = sys->b_top_field_first;
1996-            decoder_QueueVideo(dec, picture);
1997-        } else {
1998-            picture_Release(picture);
1999-            if (sys->output_pool) {
2000-                buffer->user_data = NULL;
2001-                buffer->alloc_size = 0;
2002-                buffer->data = NULL;
2003-                mmal_buffer_header_release(buffer);
2004-            }
2005-        }
2006-        atomic_fetch_sub(&sys->output_in_transit, 1);
2007-        vlc_sem_post(&sys->sem);
2008-    } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
2009-        fmt = mmal_event_format_changed_get(buffer);
2010+    filter_t * const p_filter = (filter_t *)port->userdata;
2011+    filter_sys_t * const sys = p_filter->p_sys;
2012
2013-        format = mmal_format_alloc();
2014-        mmal_format_full_copy(format, fmt->format);
2015+#if TRACE_ALL
2016+    msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld/%lld", __func__,
2017+            buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size,
2018+            (long long)buf->pts, (long long)sys->stash[(unsigned int)(buf->pts & 0xf)].pts);
2019+#endif
2020+    if (buf->cmd == 0) {
2021+        picture_t * const pic = (picture_t *)buf->user_data;
2022
2023-        if (sys->opaque)
2024-            format->encoding = MMAL_ENCODING_OPAQUE;
2025+        if (pic == NULL) {
2026+            msg_Err(p_filter, "%s: Buffer has no attached picture", __func__);
2027+        }
2028+        else if (buf->data == NULL || buf->length == 0)
2029+        {
2030+#if TRACE_ALL
2031+            msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
2032+#endif
2033+        }
2034+        else
2035+        {
2036+            buf_to_pic_copy_props(pic, buf);
2037+
2038+            // Set pic data pointers from buf aux info now it has it
2039+            if (sys->is_cma) {
2040+                if (cma_pic_set_data(pic, sys->output->format, buf) != VLC_SUCCESS)
2041+                    msg_Err(p_filter, "Failed to set data");
2042+            }
2043+
2044+//            draw_corners(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, pic->p[0].i_visible_pitch / 4, pic->p[0].i_visible_lines);
2045+#if DEBUG_SQUARES
2046+            draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4,  0, 0, 32, 32, 0xffff0000);
2047+            draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 32, 0, 32, 32, 0xff00ff00);
2048+            draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 64, 0, 32, 32, 0xff0000ff);
2049+#endif
2050+
2051+            buf->user_data = NULL;  // Responsability for this pic no longer with buffer
2052+            conv_out_q_pic(sys, pic);
2053+        }
2054+    }
2055+
2056+    mmal_buffer_header_release(buf);
2057+}
2058+
2059+
2060+static void slice_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
2061+{
2062+    filter_t * const p_filter = (filter_t *)port->userdata;
2063+    filter_sys_t * const sys = p_filter->p_sys;
2064+
2065+#if TRACE_ALL
2066+    msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld", __func__,
2067+            buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, (long long)buf->pts);
2068+#endif
2069+
2070+    if (buf->cmd != 0)
2071+    {
2072+        mmal_buffer_header_release(buf);
2073+        return;
2074+    }
2075+
2076+    if (buf->data == NULL || buf->length == 0)
2077+    {
2078+#if TRACE_ALL
2079+        msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
2080+#endif
2081+    }
2082+    else
2083+    {
2084+        // Got slice
2085+        picture_t *pic = sys->slice.pics.head;
2086+        const unsigned int scale_lines = sys->output->format->es->video.height;  // Expected lines of callback
2087+
2088+        if (pic == NULL) {
2089+            msg_Err(p_filter, "No output picture");
2090+            goto fail;
2091+        }
2092+
2093+        // Copy lines
2094+        // * single plane only - fix for I420
2095+        {
2096+            const unsigned int scale_n = __MIN(scale_lines - sys->slice.line, MMAL_SLICE_HEIGHT);
2097+            const unsigned int pic_lines = pic->p[0].i_lines;
2098+            const unsigned int copy_n = sys->slice.line + scale_n <= pic_lines ? scale_n :
2099+                sys->slice.line >= pic_lines ? 0 :
2100+                    pic_lines - sys->slice.line;
2101+
2102+            const unsigned int src_stride = buf->type->video.pitch[0];
2103+            const unsigned int dst_stride = pic->p[0].i_pitch;
2104+            uint8_t *dst = pic->p[0].p_pixels + sys->slice.line * dst_stride;
2105+            const uint8_t *src = buf->data + buf->type->video.offset[0];
2106+
2107+            if (src_stride == dst_stride) {
2108+                if (copy_n != 0)
2109+                    memcpy(dst, src, src_stride * copy_n);
2110+            }
2111+            else {
2112+                unsigned int i;
2113+                for (i = 0; i != copy_n; ++i) {
2114+                    memcpy(dst, src, __MIN(dst_stride, src_stride));
2115+                    dst += dst_stride;
2116+                    src += src_stride;
2117+                }
2118+            }
2119+            sys->slice.line += scale_n;
2120+        }
2121+
2122+        if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) {
2123+
2124+            if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) {
2125+                // Stuff doesn't add up...
2126+                msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags);
2127+                goto fail;
2128+            }
2129+            else {
2130+                sys->slice.line = 0;
2131+
2132+                vlc_mutex_lock(&sys->lock);
2133+                pic_fifo_get(&sys->slice.pics);  // Remove head from Q
2134+                vlc_mutex_unlock(&sys->lock);
2135+
2136+                buf_to_pic_copy_props(pic, buf);
2137+                conv_out_q_pic(sys, pic);
2138+            }
2139+        }
2140+    }
2141+
2142+    // Put back
2143+    buf->user_data = NULL; // Zap here to make sure we can't reuse later
2144+    mmal_buffer_header_reset(buf);
2145+
2146+    if (mmal_port_send_buffer(sys->output, buf) != MMAL_SUCCESS) {
2147+        mmal_buffer_header_release(buf);
2148+    }
2149+    return;
2150+
2151+fail:
2152+    sys->err_stream = MMAL_EIO;
2153+    vlc_sem_post(&sys->sem);  // If we were waiting then break us out - the flush should fix sem values
2154+}
2155+
2156+
2157+static void conv_flush(filter_t * p_filter)
2158+{
2159+    filter_sys_t * const sys = p_filter->p_sys;
2160+    unsigned int i;
2161+
2162+#if TRACE_ALL
2163+    msg_Dbg(p_filter, "<<< %s", __func__);
2164+#endif
2165+
2166+    if (sys->resizer_type == FILTER_RESIZER_HVS)
2167+    {
2168+        for (i = 0; i != SUBS_MAX; ++i) {
2169+            hw_mmal_subpic_flush(VLC_OBJECT(p_filter), sys->subs + i);
2170+        }
2171+    }
2172+
2173+    if (sys->input != NULL && sys->input->is_enabled)
2174+        mmal_port_disable(sys->input);
2175+
2176+    if (sys->output != NULL && sys->output->is_enabled)
2177+        mmal_port_disable(sys->output);
2178+
2179+//    cma_buf_pool_deletez(&sys->cma_out_pool);
2180+
2181+    // Free up anything we may have already lying around
2182+    // Don't need lock as the above disables should have prevented anything
2183+    // happening in the background
2184+
2185+    for (i = 0; i != 16; ++i) {
2186+        conv_frame_stash_t *const stash = sys->stash + i;
2187+        unsigned int sub_no;
2188+
2189+        stash->pts = MMAL_TIME_UNKNOWN;
2190+        for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2191+            if (stash->sub_bufs[sub_no] != NULL) {
2192+                mmal_buffer_header_release(stash->sub_bufs[sub_no]);
2193+                stash->sub_bufs[sub_no] = NULL;
2194+            }
2195+        }
2196+    }
2197+
2198+    pic_fifo_release_all(&sys->slice.pics);
2199+    pic_fifo_release_all(&sys->ret_pics);
2200+
2201+    // Reset sem values - easiest & most reliable way is to just kill & re-init
2202+    vlc_sem_destroy(&sys->sem);
2203+    vlc_sem_init(&sys->sem, 0);
2204+    sys->pic_n = 0;
2205+
2206+    // Reset error status
2207+    sys->err_stream = MMAL_SUCCESS;
2208+
2209+#if TRACE_ALL
2210+    msg_Dbg(p_filter, ">>> %s", __func__);
2211+#endif
2212+}
2213+
2214+static void conv_stash_fixup(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const p_pic)
2215+{
2216+    conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf);
2217+    unsigned int sub_no;
2218+    VLC_UNUSED(p_filter);
2219+
2220+    p_pic->date = stash->pts;
2221+    for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2222+        if (stash->sub_bufs[sub_no] != NULL) {
2223+            // **** Do stashed blend
2224+            // **** Aaargh, bother... need to rescale subs too
2225+
2226+            mmal_buffer_header_release(stash->sub_bufs[sub_no]);
2227+            stash->sub_bufs[sub_no] = NULL;
2228+        }
2229+    }
2230+}
2231+
2232+// Output buffers may contain a pic ref on error or flush
2233+// Free it
2234+static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
2235+{
2236+    VLC_UNUSED(userdata);
2237+
2238+    picture_t * const pic = header->user_data;
2239+    header->user_data = NULL;
2240+
2241+    if (pic != NULL)
2242+        picture_Release(pic);
2243+
2244+    return MMAL_FALSE;
2245+}
2246+
2247+static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const pic)
2248+{
2249+    MMAL_STATUS_T status;
2250+
2251+    sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
2252+    sys->output->format->type = MMAL_ES_TYPE_VIDEO;
2253+    sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
2254+    sys->output->format->encoding_variant = 0;
2255+    hw_mmal_vlc_fmt_to_mmal_fmt(sys->output->format, &p_filter->fmt_out.video);
2256+
2257+    if (pic != NULL)
2258+    {
2259+        // Override default format width/height if we have a pic we need to match
2260+        if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS)
2261+        {
2262+            char cbuf[5];
2263+            msg_Err(p_filter, "Bad format desc: %s, pic=%p, bits=%d", str_fourcc(cbuf, pic->format.i_chroma), pic, pic->format.i_bits_per_pixel);
2264+            return status;
2265+        }
2266+
2267+        MMAL_VIDEO_FORMAT_T *fmt = &sys->output->format->es->video;
2268+        msg_Dbg(p_filter, "%s: %dx%d [(0,0) %dx%d]", __func__, fmt->width, fmt->height, fmt->crop.width, fmt->crop.height);
2269+    }
2270+
2271+    if (sys->is_sliced) {
2272+        // Override height for slice
2273+        sys->output->format->es->video.height = MMAL_SLICE_HEIGHT;
2274+    }
2275+
2276+    mmal_log_dump_format(sys->output->format);
2277+
2278+    status = mmal_port_format_commit(sys->output);
2279+    if (status != MMAL_SUCCESS) {
2280+        msg_Err(p_filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
2281+                sys->output->name, status, mmal_status_to_string(status));
2282+        return status;
2283+    }
2284+
2285+    sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended);
2286+    sys->output->buffer_size = sys->output->buffer_size_recommended;
2287+
2288+    if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS)
2289+        return status;
2290+
2291+    return MMAL_SUCCESS;
2292+}
2293+
2294+
2295+static picture_t *conv_get_out_pics(filter_sys_t * const sys)
2296+{
2297+    picture_t * ret_pics;
2298+
2299+    vlc_sem_wait(&sys->sem);
2300+
2301+    // Return a single pending buffer
2302+    vlc_mutex_lock(&sys->lock);
2303+    ret_pics = pic_fifo_get(&sys->ret_pics);
2304+    vlc_mutex_unlock(&sys->lock);
2305+
2306+    return ret_pics;
2307+}
2308+
2309+static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic)
2310+{
2311+    filter_sys_t * const sys = p_filter->p_sys;
2312+    picture_t * ret_pics = NULL;
2313+    MMAL_STATUS_T err;
2314+    const uint64_t frame_seq = ++sys->frame_seq;
2315+    conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf);
2316+    MMAL_BUFFER_HEADER_T * out_buf = NULL;
2317+
2318+#if TRACE_ALL
2319+    {
2320+        char dbuf0[5], dbuf1[5];
2321+        msg_Dbg(p_filter, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] sar:%d/%d", __func__,
2322+                str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
2323+                p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
2324+                p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
2325+                p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
2326+                str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
2327+                p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
2328+                p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
2329+                p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
2330+    }
2331+#endif
2332+
2333+    if (sys->err_stream != MMAL_SUCCESS) {
2334+        goto stream_fail;
2335+    }
2336+
2337+    // Check pic fmt corresponds to what we have set up
2338+    if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
2339+    {
2340+        msg_Dbg(p_filter, "Reset input port format");
2341+
2342+        // HVS can take new formats without disable, others need it
2343+        if (sys->resizer_type != FILTER_RESIZER_HVS) {
2344+            // Extract any pending pic
2345+            if (sys->pic_n >= 2) {
2346+                ret_pics = conv_get_out_pics(sys);
2347+                // If pic_n == 1 then we return without trying to get stuff
2348+                sys->pic_n = 1;
2349+            }
2350+            if (sys->input->is_enabled) {
2351+                if ((err = mmal_port_disable(sys->input)) != MMAL_SUCCESS)
2352+                    msg_Warn(p_filter, "Format update disable failed: %s", mmal_status_to_string(err));
2353+            }
2354+        }
2355+
2356+//        mmal_log_dump_port(sys->input);
2357+        if ((err = mmal_port_format_commit(sys->input)) != MMAL_SUCCESS)
2358+            msg_Warn(p_filter, "Format update commit failed: %s", mmal_status_to_string(err));
2359+
2360+        // (Re)enable if required will be done later
2361+    }
2362+
2363+    if (p_pic->context == NULL) {
2364+        // Can't have stashed subpics if not one of our pics
2365+        if (!sys->needs_copy_in)
2366+            msg_Dbg(p_filter, "%s: No context", __func__);
2367+    }
2368+    else if (sys->resizer_type == FILTER_RESIZER_HVS)
2369+    {
2370+        unsigned int sub_no = 0;
2371+
2372+        for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2373+            int rv;
2374+            if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter),
2375+                                            hw_mmal_pic_sub_buf_get(p_pic, sub_no),
2376+                                            sys->subs + sub_no,
2377+                                            &p_pic->format,
2378+                                            &sys->output->format->es->video.crop,
2379+                                            MMAL_DISPLAY_ROT0,
2380+                                            frame_seq)) == 0)
2381+                break;
2382+            else if (rv < 0)
2383+                goto fail;
2384+        }
2385+    }
2386+    else
2387+    {
2388+        unsigned int sub_no = 0;
2389+        for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2390+            if ((stash->sub_bufs[sub_no] = hw_mmal_pic_sub_buf_get(p_pic, sub_no)) != NULL) {
2391+                mmal_buffer_header_acquire(stash->sub_bufs[sub_no]);
2392+            }
2393+        }
2394+    }
2395+
2396+    if (!sys->out_fmt_set) {
2397+        sys->out_fmt_set = true;
2398+
2399+        if (sys->is_sliced) {
2400+            // If zc then we will do stride conversion when we copy to arm side
2401+            // so no need to worry about actual pic dimensions here
2402+            if ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS)
2403+                goto fail;
2404+
2405+            sys->out_pool = mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size);
2406+        }
2407+        else {
2408+            picture_t *pic = filter_NewPicture(p_filter);
2409+            err = conv_set_output(p_filter, sys, pic);
2410+            picture_Release(pic);
2411+            if (err != MMAL_SUCCESS)
2412+                goto fail;
2413+
2414+            sys->out_pool = mmal_pool_create(sys->output->buffer_num, 0);
2415+        }
2416+
2417+        if (sys->out_pool == NULL) {
2418+            msg_Err(p_filter, "Failed to create output pool");
2419+            goto fail;
2420+        }
2421+    }
2422+
2423+    // Reenable stuff if the last thing we did was flush
2424+    if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS ||
2425+        (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
2426+        goto fail;
2427+
2428+    // We attach pic to buf before stuffing the output port
2429+    // We could attach the pic on output for cma, but it is a lot easier to keep
2430+    // the code common.
2431+    {
2432+        picture_t * const out_pic = filter_NewPicture(p_filter);
2433+
2434+        if (out_pic == NULL)
2435+        {
2436+            msg_Err(p_filter, "Failed to alloc required filter output pic");
2437+            goto fail;
2438+        }
2439+
2440+        out_pic->format.i_sar_den = p_filter->fmt_out.video.i_sar_den;
2441+        out_pic->format.i_sar_num = p_filter->fmt_out.video.i_sar_num;
2442+
2443+        if (sys->is_sliced) {
2444+            vlc_mutex_lock(&sys->lock);
2445+            pic_fifo_put(&sys->slice.pics, out_pic);
2446+            vlc_mutex_unlock(&sys->lock);
2447+
2448+            // Poke any returned pic buffers into output
2449+            // In general this should only happen immediately after enable
2450+            while ((out_buf = mmal_queue_get(sys->out_pool->queue)) != NULL)
2451+                mmal_port_send_buffer(sys->output, out_buf);
2452+        }
2453+        else
2454+        {
2455+            // 1 in - 1 out
2456+            if ((out_buf = mmal_queue_wait(sys->out_pool->queue)) == NULL)
2457+            {
2458+                msg_Err(p_filter, "Failed to get output buffer");
2459+                picture_Release(out_pic);
2460+                goto fail;
2461+            }
2462+            mmal_buffer_header_reset(out_buf);
2463+
2464+            // Attach out_pic to the buffer & ensure it is freed when the buffer is released
2465+            // On a good send callback the pic will be extracted to avoid this
2466+            out_buf->user_data = out_pic;
2467+            mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, NULL);
2468+
2469+#if 0
2470+            {
2471+                char dbuf0[5];
2472+                msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d",
2473+                        str_fourcc(dbuf0, out_pic->format.i_chroma),
2474+                        out_pic->format.i_width, out_pic->format.i_height,
2475+                        out_pic->format.i_x_offset, out_pic->format.i_y_offset,
2476+                        out_pic->format.i_visible_width, out_pic->format.i_visible_height,
2477+                        out_pic->format.i_sar_num, out_pic->format.i_sar_den);
2478+            }
2479+#endif
2480+
2481+            if (sys->is_cma) {
2482+                int rv;
2483+
2484+                cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size);
2485+                if (cb == NULL) {
2486+                    char dbuf0[5];
2487+                    msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d",
2488+                            str_fourcc(dbuf0, out_pic->format.i_chroma),
2489+                            sys->output->buffer_size);
2490+                    goto fail;
2491+                }
2492+                const unsigned int vc_h = cma_buf_vc_handle(cb);  // Cannot coerce without going via variable
2493+                out_buf->data = (uint8_t *)vc_h;
2494+                out_buf->alloc_size = sys->output->buffer_size;
2495+
2496+                if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS)
2497+                {
2498+                    char dbuf0[5];
2499+                    msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d",
2500+                            str_fourcc(dbuf0, out_pic->format.i_chroma),
2501+                            rv);
2502+                    cma_buf_unref(cb);
2503+                    goto fail;
2504+                }
2505+            }
2506+            else {
2507+                out_buf->data = out_pic->p[0].p_pixels;
2508+                out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines;
2509+                //**** stride ????
2510+            }
2511+
2512+#if TRACE_ALL
2513+            msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld",
2514+                    p_pic, out_buf->data, out_buf->user_data, out_buf->flags,
2515+                    out_buf->length, out_buf->alloc_size, (long long)out_buf->pts);
2516+#endif
2517+
2518+            if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
2519+            {
2520+                msg_Err(p_filter, "Send buffer to output failed");
2521+                goto fail;
2522+            }
2523+            out_buf = NULL;
2524+        }
2525+    }
2526+
2527+
2528+    // Stuff into input
2529+    // We assume the BH is already set up with values reflecting pic date etc.
2530+    stash->pts = p_pic->date;
2531+    {
2532+        MMAL_BUFFER_HEADER_T *const pic_buf = sys->needs_copy_in ?
2533+            hw_mmal_pic_buf_copied(p_pic, sys->in_pool, sys->input, sys->cma_in_pool) :
2534+            hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
2535+
2536+        // Whether or not we extracted the pic_buf we are done with the picture
2537+        picture_Release(p_pic);
2538+        p_pic = NULL;
2539+
2540+        if (pic_buf == NULL) {
2541+            msg_Err(p_filter, "Pic has no attached buffer");
2542+            goto fail;
2543+        }
2544+
2545+        pic_buf->pts = frame_seq;
2546+
2547+#if TRACE_ALL
2548+            msg_Dbg(p_filter, "In buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d/%d, pts=%lld",
2549+                    p_pic, pic_buf->data, pic_buf->user_data, pic_buf->flags,
2550+                    pic_buf->length, pic_buf->alloc_size, sys->input->buffer_size, (long long)pic_buf->pts);
2551+#endif
2552+
2553+        if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
2554+        {
2555+            msg_Err(p_filter, "Send buffer to input failed");
2556+            mmal_buffer_header_release(pic_buf);
2557+            goto fail;
2558+        }
2559+    }
2560+
2561+    // We have a 1 pic latency for everything except the 1st pic which we
2562+    // wait for.
2563+    // This means we get a single static pic out
2564+    if (sys->pic_n++ == 1) {
2565+#if TRACE_ALL
2566+        msg_Dbg(p_filter, ">>> %s: Pic1=%p", __func__, ret_pics);
2567+#endif
2568+        return ret_pics;
2569+    }
2570+
2571+    ret_pics = conv_get_out_pics(sys);
2572+
2573+    if (sys->err_stream != MMAL_SUCCESS)
2574+        goto stream_fail;
2575+
2576+    conv_stash_fixup(p_filter, sys, ret_pics);
2577+
2578+#if TRACE_ALL
2579+    msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
2580+#endif
2581+
2582+    return ret_pics;
2583+
2584+stream_fail:
2585+    msg_Err(p_filter, "MMAL error reported by callback");
2586+fail:
2587+#if TRACE_ALL
2588+    msg_Err(p_filter, ">>> %s: FAIL", __func__);
2589+#endif
2590+    if (ret_pics != NULL)
2591+        picture_Release(ret_pics);
2592+    if (out_buf != NULL)
2593+        mmal_buffer_header_release(out_buf);
2594+    if (p_pic != NULL)
2595+        picture_Release(p_pic);
2596+    conv_flush(p_filter);
2597+    return NULL;
2598+}
2599+
2600+static void CloseConverter(vlc_object_t * obj)
2601+{
2602+    filter_t * const p_filter = (filter_t *)obj;
2603+    filter_sys_t * const sys = p_filter->p_sys;
2604+    unsigned int i;
2605+
2606+#if TRACE_ALL
2607+    msg_Dbg(obj, "<<< %s", __func__);
2608+#endif
2609+
2610+    if (sys == NULL)
2611+        return;
2612+
2613+    // Disables input & output ports
2614+    conv_flush(p_filter);
2615+
2616+    cma_buf_pool_deletez(&sys->cma_in_pool);
2617+    cma_buf_pool_deletez(&sys->cma_out_pool);
2618+
2619+    if (sys->component && sys->component->control->is_enabled)
2620+        mmal_port_disable(sys->component->control);
2621+
2622+    if (sys->component && sys->component->is_enabled)
2623+        mmal_component_disable(sys->component);
2624+
2625+    if (sys->resizer_type == FILTER_RESIZER_HVS)
2626+    {
2627+        for (i = 0; i != SUBS_MAX; ++i) {
2628+            hw_mmal_subpic_close(VLC_OBJECT(p_filter), sys->subs + i);
2629+        }
2630+    }
2631+
2632+    if (sys->out_pool)
2633+    {
2634+        if (sys->is_sliced)
2635+            mmal_port_pool_destroy(sys->output, sys->out_pool);
2636+        else
2637+            mmal_pool_destroy(sys->out_pool);
2638+    }
2639+
2640+    if (sys->in_pool != NULL)
2641+        mmal_pool_destroy(sys->in_pool);
2642+
2643+    if (sys->component)
2644+        mmal_component_release(sys->component);
2645+
2646+    cma_vcsm_exit(sys->vcsm_init_type);
2647+
2648+    vlc_sem_destroy(&sys->sem);
2649+    vlc_mutex_destroy(&sys->lock);
2650+
2651+    p_filter->p_sys = NULL;
2652+    free(sys);
2653+}
2654+
2655+
2656+static inline MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt)
2657+{
2658+    if (hw_mmal_chroma_is_mmal(fmt->i_chroma))
2659+        return vlc_to_mmal_video_fourcc(fmt);
2660+
2661+    if (fmt->i_chroma == VLC_CODEC_I420 ||
2662+        fmt->i_chroma == VLC_CODEC_I420_10L)
2663+        return MMAL_ENCODING_I420;
2664+
2665+    return 0;
2666+}
2667+
2668+static inline MMAL_FOURCC_T filter_enc_out(const video_format_t * const fmt)
2669+{
2670+    const MMAL_FOURCC_T mmes = vlc_to_mmal_video_fourcc(fmt);
2671+    // Can only copy out single plane stuff currently - this could be fixed!
2672+    return hw_mmal_chroma_is_mmal(fmt->i_chroma) || mmes != MMAL_ENCODING_I420 ? mmes : 0;
2673+}
2674+
2675+
2676+static int OpenConverter(vlc_object_t * obj)
2677+{
2678+    filter_t * const p_filter = (filter_t *)obj;
2679+    int ret = VLC_EGENERIC;
2680+    filter_sys_t *sys;
2681+    MMAL_STATUS_T status;
2682+    MMAL_FOURCC_T enc_out = filter_enc_out(&p_filter->fmt_out.video);
2683+    const MMAL_FOURCC_T enc_in = filter_enc_in(&p_filter->fmt_in.video);
2684+    bool use_resizer;
2685+    bool use_isp;
2686+    int gpu_mem;
2687+
2688+    // At least in principle we should deal with any mmal format as input
2689+    if (enc_in == 0 || enc_out == 0)
2690+        return VLC_EGENERIC;
2691+
2692+    // Can't transform
2693+    if (p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation)
2694+        return VLC_EGENERIC;
2695+
2696+    use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME);
2697+    use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME);
2698+
2699+retry:
2700+    // ** Make more generic by checking supported encs
2701+    //
2702+    // Must use ISP - HVS can't do this, nor can resizer
2703+    if (enc_in == MMAL_ENCODING_YUVUV64_10) {
2704+        // If resizer selected then just give up
2705+        if (use_resizer)
2706+            return VLC_EGENERIC;
2707+        // otherwise downgrade HVS to ISP
2708+        use_isp = true;
2709+    }
2710+    // HVS can't do I420
2711+    if (enc_out == MMAL_ENCODING_I420) {
2712+        use_isp = true;
2713+    }
2714+    // Only HVS can deal with SAND30
2715+    if (enc_in == MMAL_ENCODING_YUV10_COL) {
2716+        if (use_isp || use_resizer)
2717+            return VLC_EGENERIC;
2718+    }
2719
2720-        sys->output_format = format;
2721
2722-        mmal_buffer_header_release(buffer);
2723+    if (use_resizer) {
2724+        // use resizer overrides use_isp
2725+        use_isp = false;
2726+    }
2727+
2728+    // Check we have a sliced version of the fourcc if we want the resizer
2729+    if (use_resizer &&
2730+        (enc_out = pic_to_slice_mmal_fourcc(enc_out)) == 0) {
2731+        return VLC_EGENERIC;
2732+    }
2733+
2734+    gpu_mem = hw_mmal_get_gpu_mem();
2735+
2736+    {
2737+        char dbuf0[5], dbuf1[5], dbuf2[5], dbuf3[5];
2738+        msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s/%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d (gpu=%d)", __func__,
2739+                use_resizer ? "resize" : use_isp ? "isp" : "hvs",
2740+                str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), str_fourcc(dbuf2, enc_in),
2741+                p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
2742+                p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
2743+                p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
2744+                p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
2745+                str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), str_fourcc(dbuf3, enc_out),
2746+                p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
2747+                p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
2748+                p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
2749+                p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
2750+                p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den,
2751+                gpu_mem);
2752+    }
2753+
2754+    sys = calloc(1, sizeof(filter_sys_t));
2755+    if (!sys) {
2756+        ret = VLC_ENOMEM;
2757+        goto fail;
2758+    }
2759+    p_filter->p_sys = sys;
2760+
2761+    // Init stuff the we destroy unconditionaly in Close first
2762+    vlc_mutex_init(&sys->lock);
2763+    vlc_sem_init(&sys->sem, 0);
2764+    sys->err_stream = MMAL_SUCCESS;
2765+    pic_fifo_init(&sys->ret_pics);
2766+    pic_fifo_init(&sys->slice.pics);
2767+
2768+    sys->needs_copy_in = !hw_mmal_chroma_is_mmal(p_filter->fmt_in.video.i_chroma);
2769+    sys->in_port_cb_fn = conv_input_port_cb;
2770+
2771+    if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
2772+        msg_Err(p_filter, "VCSM init failed");
2773+        goto fail;
2774+    }
2775+
2776+    if (use_resizer) {
2777+        sys->resizer_type = FILTER_RESIZER_RESIZER;
2778+        sys->is_sliced = true;
2779+        sys->component_name = MMAL_COMPONENT_DEFAULT_RESIZER;
2780+        sys->out_port_cb_fn = slice_output_port_cb;
2781+    }
2782+    else if (use_isp) {
2783+        sys->resizer_type = FILTER_RESIZER_ISP;
2784+        sys->is_sliced = false;  // Copy directly into filter picture
2785+        sys->component_name = MMAL_COMPONENT_ISP_RESIZER;
2786+        sys->out_port_cb_fn = conv_output_port_cb;
2787     } else {
2788-        mmal_buffer_header_release(buffer);
2789+        sys->resizer_type = FILTER_RESIZER_HVS;
2790+        sys->is_sliced = false;  // Copy directly into filter picture
2791+        sys->component_name = MMAL_COMPONENT_HVS;
2792+        sys->out_port_cb_fn = conv_output_port_cb;
2793+    }
2794+    sys->is_cma = is_cma_buf_pic_chroma(p_filter->fmt_out.video.i_chroma);
2795+
2796+    status = mmal_component_create(sys->component_name, &sys->component);
2797+    if (status != MMAL_SUCCESS) {
2798+        if (!use_isp && !use_resizer) {
2799+            msg_Warn(p_filter, "Failed to rcreate HVS resizer - retrying with ISP");
2800+            CloseConverter(obj);
2801+            use_isp = true;
2802+            goto retry;
2803+        }
2804+        msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
2805+                MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
2806+        goto fail;
2807     }
2808+    sys->output = sys->component->output[0];
2809+    sys->input  = sys->component->input[0];
2810+
2811+    sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
2812+    status = mmal_port_enable(sys->component->control, conv_control_port_cb);
2813+    if (status != MMAL_SUCCESS) {
2814+        msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
2815+                sys->component->control->name, status, mmal_status_to_string(status));
2816+        goto fail;
2817+    }
2818+
2819+    if (sys->needs_copy_in &&
2820+        (sys->cma_in_pool = cma_buf_pool_new(2, 2, true, "conv-copy-in")) == NULL)
2821+    {
2822+        msg_Err(p_filter, "Failed to allocate input CMA pool");
2823+        goto fail;
2824+    }
2825+
2826+    sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
2827+    sys->input->format->type = MMAL_ES_TYPE_VIDEO;
2828+    sys->input->format->encoding = enc_in;
2829+    sys->input->format->encoding_variant = MMAL_ENCODING_I420;
2830+    hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &p_filter->fmt_in.video);
2831+    port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, 1);
2832+
2833+    mmal_log_dump_format(sys->input->format);
2834+
2835+    status = mmal_port_format_commit(sys->input);
2836+    if (status != MMAL_SUCCESS) {
2837+        msg_Err(p_filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
2838+                sys->input->name, status, mmal_status_to_string(status));
2839+        goto fail;
2840+    }
2841+    sys->input->buffer_size = sys->input->buffer_size_recommended;
2842+    sys->input->buffer_num = NUM_DECODER_BUFFER_HEADERS;
2843+
2844+    if ((status = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
2845+        goto fail;
2846+
2847+    port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced || sys->is_cma);
2848+
2849+    status = mmal_component_enable(sys->component);
2850+    if (status != MMAL_SUCCESS) {
2851+        msg_Err(p_filter, "Failed to enable component %s (status=%"PRIx32" %s)",
2852+                sys->component->name, status, mmal_status_to_string(status));
2853+        goto fail;
2854+    }
2855+
2856+    if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
2857+    {
2858+        msg_Err(p_filter, "Failed to create input pool");
2859+        goto fail;
2860+    }
2861+
2862+    if (sys->resizer_type == FILTER_RESIZER_HVS)
2863+    {
2864+        unsigned int i;
2865+        for (i = 0; i != SUBS_MAX; ++i) {
2866+            if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], -1, i + 1) != MMAL_SUCCESS)
2867+            {
2868+                msg_Err(p_filter, "Failed to open subpic %d", i);
2869+                goto fail;
2870+            }
2871+        }
2872+    }
2873+
2874+    p_filter->pf_video_filter = conv_filter;
2875+    p_filter->pf_flush = conv_flush;
2876+    // video_drain NIF in filter structure
2877+
2878+#if TRACE_ALL
2879+    msg_Dbg(p_filter, ">>> %s: ok", __func__);
2880+#endif
2881+
2882+    return VLC_SUCCESS;
2883+
2884+fail:
2885+    CloseConverter(obj);
2886+
2887+    if (!use_resizer && status == MMAL_ENOMEM) {
2888+        use_resizer = true;
2889+        msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer");
2890+        goto retry;
2891+    }
2892+
2893+#if TRACE_ALL
2894+    msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret);
2895+#endif
2896+    return ret;
2897+}
2898+
2899+#if OPT_TO_FROM_ZC
2900+//----------------------------------------------------------------------------
2901+//
2902+// Simple copy in to ZC
2903+
2904+typedef struct to_zc_sys_s {
2905+    vcsm_init_type_t vcsm_init_type;
2906+    cma_buf_pool_t * cma_out_pool;
2907+} to_zc_sys_t;
2908+
2909+
2910+static size_t buf_alloc_size(const vlc_fourcc_t i_chroma, const unsigned int width, const unsigned int height)
2911+{
2912+    const unsigned int pels = width * height;
2913+
2914+    switch (i_chroma)
2915+    {
2916+        case VLC_CODEC_MMAL_ZC_RGB32:
2917+            return pels * 4;
2918+        case VLC_CODEC_MMAL_ZC_I420:
2919+            return pels * 3 / 2;
2920+        default:
2921+            break;
2922+    }
2923+    return 0;
2924+}
2925+
2926+
2927+static picture_t *
2928+to_zc_filter(filter_t *p_filter, picture_t *in_pic)
2929+{
2930+    to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys;
2931+#if TRACE_ALL
2932+    msg_Dbg(p_filter, "<<< %s", __func__);
2933+#endif
2934+
2935+    assert(p_filter->fmt_out.video.i_chroma == VLC_CODEC_MMAL_ZC_I420);
2936+
2937+    picture_t * const out_pic = filter_NewPicture(p_filter);
2938+    if (out_pic == NULL)
2939+        goto fail0;
2940+
2941+    MMAL_ES_SPECIFIC_FORMAT_T mm_vfmt = {.video={0}};
2942+    MMAL_ES_FORMAT_T mm_esfmt = {
2943+        .encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video),
2944+        .es = &mm_vfmt};
2945+
2946+    hw_mmal_vlc_fmt_to_mmal_fmt(&mm_esfmt, &p_filter->fmt_out.video);
2947+
2948+    const size_t buf_alloc = buf_alloc_size(p_filter->fmt_out.video.i_chroma,
2949+                                            mm_vfmt.video.width, mm_vfmt.video.height);
2950+    if (buf_alloc == 0)
2951+        goto fail1;
2952+    cma_buf_t *const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, buf_alloc);
2953+    if (cb == NULL)
2954+        goto fail1;
2955+
2956+    if (cma_buf_pic_attach(cb, out_pic) != VLC_SUCCESS)
2957+        goto fail2;
2958+    cma_pic_set_data(out_pic, &mm_esfmt, NULL);
2959+
2960+    hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), NULL, &mm_esfmt, in_pic);
2961+
2962+    // Copy pic properties
2963+    out_pic->date              = in_pic->date;
2964+    out_pic->b_force           = in_pic->b_force;
2965+    out_pic->b_progressive     = in_pic->b_progressive;
2966+    out_pic->b_top_field_first = in_pic->b_top_field_first;
2967+    out_pic->i_nb_fields       = in_pic->i_nb_fields;
2968+
2969+    picture_Release(in_pic);
2970+
2971+    return out_pic;
2972+
2973+fail2:
2974+    cma_buf_unref(cb);
2975+fail1:
2976+    picture_Release(out_pic);
2977+fail0:
2978+    picture_Release(in_pic);
2979+    return NULL;
2980+}
2981+
2982+static void to_zc_flush(filter_t * p_filter)
2983+{
2984+    VLC_UNUSED(p_filter);
2985 }
2986+
2987+static void CloseConverterToZc(vlc_object_t * obj)
2988+{
2989+    filter_t * const p_filter = (filter_t *)obj;
2990+    to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys;
2991+
2992+    if (sys == NULL)
2993+        return;
2994+
2995+    p_filter->p_sys = NULL;
2996+
2997+    cma_buf_pool_deletez(&sys->cma_out_pool);
2998+    cma_vcsm_exit(sys->vcsm_init_type);
2999+
3000+    free(sys);
3001+}
3002+
3003+static bool to_zc_validate_fmt(const video_format_t * const f_in, const video_format_t * const f_out)
3004+{
3005+    if (!((f_in->i_chroma == VLC_CODEC_I420 || f_in->i_chroma == VLC_CODEC_I420_10L) &&
3006+          f_out->i_chroma == VLC_CODEC_MMAL_ZC_I420))
3007+    {
3008+        return false;
3009+    }
3010+    if (f_in->i_height != f_out->i_height ||
3011+        f_in->i_width  != f_out->i_width)
3012+    {
3013+        return false;
3014+    }
3015+
3016+    return true;
3017+}
3018+
3019+static int OpenConverterToZc(vlc_object_t * obj)
3020+{
3021+    int ret = VLC_EGENERIC;
3022+    filter_t * const p_filter = (filter_t *)obj;
3023+
3024+    if (!to_zc_validate_fmt(&p_filter->fmt_in.video, &p_filter->fmt_out.video))
3025+        goto fail;
3026+
3027+    {
3028+        char dbuf0[5], dbuf1[5];
3029+        msg_Dbg(p_filter, "%s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d", __func__,
3030+                str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma),
3031+                p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
3032+                p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
3033+                p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
3034+                p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
3035+                str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma),
3036+                p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
3037+                p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
3038+                p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
3039+                p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
3040+                p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
3041+    }
3042+
3043+    to_zc_sys_t * const sys = calloc(1, sizeof(*sys));
3044+    if (!sys) {
3045+        ret = VLC_ENOMEM;
3046+        goto fail;
3047+    }
3048+    p_filter->p_sys = (filter_sys_t *)sys;
3049+
3050+    if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
3051+        msg_Err(p_filter, "VCSM init failed");
3052+        goto fail;
3053+    }
3054+
3055+    if ((sys->cma_out_pool = cma_buf_pool_new(5, 5, true, "conv-to-zc")) == NULL)
3056+    {
3057+        msg_Err(p_filter, "Failed to allocate input CMA pool");
3058+        goto fail;
3059+    }
3060+
3061+    p_filter->pf_video_filter = to_zc_filter;
3062+    p_filter->pf_flush = to_zc_flush;
3063+    return VLC_SUCCESS;
3064+
3065+fail:
3066+    CloseConverterToZc(obj);
3067+    return ret;
3068+}
3069+
3070+//----------------------------------------------------------------------------
3071+//
3072+// Simple "copy" from ZC
3073+
3074+static void CloseConverterFromZc(vlc_object_t * obj)
3075+{
3076+    VLC_UNUSED(obj);
3077+}
3078+
3079+static int OpenConverterFromZc(vlc_object_t * obj)
3080+{
3081+    return VLC_EGENERIC;
3082+}
3083+#endif
3084+//----------------------------------------------------------------------------
3085+
3086+typedef struct blend_sys_s {
3087+    vzc_pool_ctl_t * vzc;
3088+    const picture_t * last_dst;  // Not a ref, just a hint that we have a new pic
3089+    vcsm_init_type_t vcsm_init_type;
3090+} blend_sys_t;
3091+
3092+static void FilterBlendMmal(filter_t *p_filter,
3093+                  picture_t *dst, const picture_t * src,
3094+                  int x_offset, int y_offset, int alpha)
3095+{
3096+    blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
3097+#if TRACE_ALL
3098+    msg_Dbg(p_filter, "%s (%d,%d:%d) pic=%p, pts=%lld, force=%d", __func__, x_offset, y_offset, alpha, src, src->date, src->b_force);
3099+#endif
3100+    // If nothing to do then do nothing
3101+    if (alpha == 0 ||
3102+        src->format.i_visible_height == 0 ||
3103+        src->format.i_visible_width == 0)
3104+    {
3105+        return;
3106+    }
3107+
3108+    if (dst->context == NULL)
3109+        msg_Err(p_filter, "MMAL pic missing context");
3110+    else
3111+    {
3112+        // cast away src const so we can ref it
3113+        MMAL_BUFFER_HEADER_T *buf = hw_mmal_vzc_buf_from_pic(sys->vzc, (picture_t *)src,
3114+                                                             vis_mmal_rect(&dst->format),
3115+                                                             x_offset, y_offset,
3116+                                                             alpha,
3117+                                                             dst != sys->last_dst || !hw_mmal_pic_has_sub_bufs(dst));
3118+        if (buf == NULL) {
3119+            msg_Err(p_filter, "Failed to allocate vzc buffer for subpic");
3120+            return;
3121+        }
3122+
3123+        hw_mmal_pic_sub_buf_add(dst, buf);
3124+
3125+        sys->last_dst = dst;
3126+    }
3127+}
3128+
3129+static void FlushBlendMmal(filter_t * p_filter)
3130+{
3131+    blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
3132+    sys->last_dst = NULL;
3133+    hw_mmal_vzc_pool_flush(sys->vzc);
3134+}
3135+
3136+static void CloseBlendMmal(vlc_object_t *object)
3137+{
3138+    filter_t * const p_filter = (filter_t *)object;
3139+    blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
3140+
3141+    if (sys != NULL) {
3142+        p_filter->p_sys = NULL;
3143+
3144+        hw_mmal_vzc_pool_release(sys->vzc);
3145+        cma_vcsm_exit(sys->vcsm_init_type);
3146+        free(sys);
3147+    }
3148+}
3149+
3150+static int OpenBlendMmal(vlc_object_t *object)
3151+{
3152+    filter_t * const p_filter = (filter_t *)object;
3153+    const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
3154+
3155+    if (!hw_mmal_chroma_is_mmal(vfcc_dst) ||
3156+        !hw_mmal_vzc_subpic_fmt_valid(&p_filter->fmt_in.video))
3157+    {
3158+        return VLC_EGENERIC;
3159+    }
3160+
3161+    {
3162+        char dbuf0[5], dbuf1[5];
3163+        msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %dx%d]->%s,%dx%d [(%d,%d) %dx%d]", __func__,
3164+                "blend",
3165+                str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
3166+                p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
3167+                p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
3168+                str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
3169+                p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
3170+                p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
3171+    }
3172+
3173+    {
3174+        blend_sys_t * const sys = calloc(1, sizeof (*sys));
3175+        if (sys == NULL)
3176+            return VLC_ENOMEM;
3177+
3178+        p_filter->p_sys = (filter_sys_t *)sys;
3179+
3180+        if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
3181+            msg_Err(p_filter, "VCSM init failed");
3182+            goto fail;
3183+        }
3184+
3185+        if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
3186+            goto fail;
3187+    }
3188+
3189+    p_filter->pf_video_blend = FilterBlendMmal;
3190+    p_filter->pf_flush = FlushBlendMmal;
3191+
3192+    return VLC_SUCCESS;
3193+
3194+fail:
3195+    CloseBlendMmal(VLC_OBJECT(p_filter));
3196+    return VLC_ENOMEM;
3197+}
3198+
3199+// ---------------------------------------------------------------------------
3200+
3201+static void FilterBlendNeon(filter_t *p_filter,
3202+                  picture_t *dst_pic, const picture_t * src_pic,
3203+                  int x_offset, int y_offset, int alpha)
3204+{
3205+    const uint8_t * s_data;
3206+    uint8_t * d_data;
3207+    int width = src_pic->format.i_visible_width;
3208+    int height = src_pic->format.i_visible_height;
3209+    blend_neon_fn *const blend_fn = (blend_neon_fn * )p_filter->p_sys;
3210+
3211+#if TRACE_ALL
3212+    msg_Dbg(p_filter, "%s (%d,%d:%d) pic=%p, pts=%lld, force=%d", __func__, x_offset, y_offset, alpha, src_pic, src_pic->date, src_pic->b_force);
3213+#endif
3214+
3215+    if (alpha == 0 ||
3216+        src_pic->format.i_visible_height == 0 ||
3217+        src_pic->format.i_visible_width == 0)
3218+    {
3219+        return;
3220+    }
3221+
3222+    x_offset += dst_pic->format.i_x_offset;
3223+    y_offset += dst_pic->format.i_y_offset;
3224+
3225+    // Deal with R/B overrun
3226+    if (x_offset + width >= (int)(dst_pic->format.i_x_offset + dst_pic->format.i_visible_width))
3227+        width = dst_pic->format.i_x_offset + dst_pic->format.i_visible_width - x_offset;
3228+    if (y_offset + height >= (int)(dst_pic->format.i_y_offset + dst_pic->format.i_visible_height))
3229+        height = dst_pic->format.i_y_offset + dst_pic->format.i_visible_height - y_offset;
3230+
3231+    if (width <= 0 || height <= 0) {
3232+        return;
3233+    }
3234+
3235+    // *** L/U overrun
3236+
3237+    s_data = src_pic->p[0].p_pixels +
3238+        src_pic->p[0].i_pixel_pitch * src_pic->format.i_x_offset +
3239+        src_pic->p[0].i_pitch * src_pic->format.i_y_offset;
3240+    d_data = dst_pic->p[0].p_pixels +
3241+        dst_pic->p[0].i_pixel_pitch * x_offset +
3242+        dst_pic->p[0].i_pitch * y_offset;
3243+
3244+
3245+    do {
3246+        blend_fn(d_data, s_data, alpha, width);
3247+        s_data += src_pic->p[0].i_pitch;
3248+        d_data += dst_pic->p[0].i_pitch;
3249+    } while (--height > 0);
3250+}
3251+
3252+static void CloseBlendNeon(vlc_object_t *object)
3253+{
3254+    VLC_UNUSED(object);
3255+}
3256+
3257+static int OpenBlendNeon(vlc_object_t *object)
3258+{
3259+    filter_t * const p_filter = (filter_t *)object;
3260+    const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
3261+    MMAL_FOURCC_T mfcc_src = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video);
3262+    MMAL_FOURCC_T mfcc_dst = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
3263+    blend_neon_fn * blend_fn = (blend_neon_fn *)0;
3264+
3265+    // Non-alpha RGB only for dest
3266+    if (vfcc_dst != VLC_CODEC_RGB32)
3267+        return VLC_EGENERIC;
3268+
3269+    // Check we have appropriate blend fn (mmal doesn't have a non-alpha RGB32)
3270+    switch (mfcc_src) {
3271+    case MMAL_ENCODING_RGBA:
3272+        if (mfcc_dst == MMAL_ENCODING_RGBA)
3273+            blend_fn = blend_rgbx_rgba_neon;
3274+        else if (mfcc_dst == MMAL_ENCODING_BGRA)
3275+            blend_fn = blend_bgrx_rgba_neon;
3276+        break;
3277+
3278+    case MMAL_ENCODING_BGRA:
3279+        if (mfcc_dst == MMAL_ENCODING_BGRA)
3280+            blend_fn = blend_rgbx_rgba_neon;
3281+        else if (mfcc_dst == MMAL_ENCODING_RGBA)
3282+            blend_fn = blend_bgrx_rgba_neon;
3283+        break;
3284+
3285+    default:
3286+        break;
3287+    }
3288+
3289+    if (blend_fn == (blend_neon_fn *)0)
3290+    {
3291+        return VLC_EGENERIC;
3292+    }
3293+
3294+    p_filter->p_sys = (void *)blend_fn;
3295+    p_filter->pf_video_blend = FilterBlendNeon;
3296+
3297+    {
3298+        char dbuf0[5], dbuf1[5];
3299+        char dbuf0a[5], dbuf1a[5];
3300+        msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %dx%d]->%s/%s,%dx%d [(%d,%d) %dx%d]", __func__,
3301+                "blend",
3302+                str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma),
3303+                str_fourcc(dbuf0a, mfcc_src),
3304+                p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
3305+                p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
3306+                p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
3307+                str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma),
3308+                str_fourcc(dbuf1a, mfcc_dst),
3309+                p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
3310+                p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
3311+                p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
3312+    }
3313+
3314+    return VLC_SUCCESS;
3315+}
3316+
3317+vlc_module_begin()
3318+    set_category( CAT_INPUT )
3319+    set_subcategory( SUBCAT_INPUT_VCODEC )
3320+    set_shortname(N_("MMAL decoder"))
3321+    set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
3322+    set_capability("video decoder", 90)
3323+    add_shortcut("mmal_decoder")
3324+    add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
3325+    set_callbacks(OpenDecoder, CloseDecoder)
3326+
3327+    add_submodule()
3328+    set_category( CAT_VIDEO )
3329+    set_subcategory( SUBCAT_VIDEO_VFILTER )
3330+    set_shortname(N_("MMAL resizer"))
3331+    set_description(N_("MMAL resizing conversion filter"))
3332+    add_shortcut("mmal_converter")
3333+    set_capability( "video converter", 900 )
3334+    add_bool(MMAL_RESIZE_NAME, /* default */ false, MMAL_RESIZE_TEXT, MMAL_RESIZE_LONGTEXT, /* advanced option */ false)
3335+    add_bool(MMAL_ISP_NAME, /* default */ false, MMAL_ISP_TEXT, MMAL_ISP_LONGTEXT, /* advanced option */ false)
3336+    set_callbacks(OpenConverter, CloseConverter)
3337+
3338+#if OPT_TO_FROM_ZC
3339+    add_submodule()
3340+    set_category( CAT_VIDEO )
3341+    set_subcategory( SUBCAT_VIDEO_VFILTER )
3342+    set_shortname(N_("MMAL to ZC"))
3343+    set_description(N_("MMAL conversion to ZC filter"))
3344+    add_shortcut("mmal_to_zc")
3345+    set_capability( "video converter", 901 )
3346+    set_callbacks(OpenConverterToZc, CloseConverterToZc)
3347+
3348+    add_submodule()
3349+    set_category( CAT_VIDEO )
3350+    set_subcategory( SUBCAT_VIDEO_VFILTER )
3351+    set_shortname(N_("MMAL from ZC"))
3352+    set_description(N_("MMAL conversion from ZC filter"))
3353+    add_shortcut("mmal_from_zc")
3354+    set_capability( "video converter", 902 )
3355+    set_callbacks(OpenConverterFromZc, CloseConverterFromZc)
3356+#endif
3357+
3358+    add_submodule()
3359+    set_category( CAT_VIDEO )
3360+    set_subcategory( SUBCAT_VIDEO_VFILTER )
3361+    set_description(N_("Video pictures blending for MMAL"))
3362+    add_shortcut("mmal_blend")
3363+    set_capability("video blending", 120)
3364+    set_callbacks(OpenBlendMmal, CloseBlendMmal)
3365+
3366+    add_submodule()
3367+    set_category( CAT_VIDEO )
3368+    set_subcategory( SUBCAT_VIDEO_VFILTER )
3369+    set_description(N_("Video pictures blending for neon"))
3370+    add_shortcut("neon_blend")
3371+    set_capability("video blending", 110)
3372+    set_callbacks(OpenBlendNeon, CloseBlendNeon)
3373+
3374+vlc_module_end()
3375+
3376+
3377--- /dev/null
3378+++ b/modules/hw/mmal/converter_mmal.c
3379@@ -0,0 +1,479 @@
3380+#ifdef HAVE_CONFIG_H
3381+# include "config.h"
3382+#endif
3383+
3384+#include <unistd.h>
3385+#include <fcntl.h>
3386+#include <sys/ioctl.h>
3387+#include <sys/mman.h>
3388+
3389+#include <interface/vcsm/user-vcsm.h>
3390+
3391+#include <vlc_common.h>
3392+#include <vlc_picture.h>
3393+
3394+#include <libdrm/drm_fourcc.h>
3395+#include <EGL/egl.h>
3396+#include <EGL/eglext.h>
3397+#include <GLES2/gl2.h>
3398+#include <GLES2/gl2ext.h>
3399+
3400+#include "mmal_cma.h"
3401+
3402+#include "../../video_output/opengl/converter.h"
3403+
3404+#include "mmal_picture.h"
3405+
3406+#include <assert.h>
3407+
3408+#define TRACE_ALL 0
3409+
3410+typedef struct mmal_gl_converter_s
3411+{
3412+    EGLint drm_fourcc;
3413+    vcsm_init_type_t vcsm_init_type;
3414+    cma_buf_t * last_cb;
3415+
3416+    PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
3417+} mmal_gl_converter_t;
3418+
3419+
3420+static EGLint vlc_to_gl_fourcc(const video_format_t * const fmt)
3421+{
3422+    // Converting to mmal selects the right RGB32 varient
3423+    switch(vlc_to_mmal_video_fourcc(fmt))
3424+    {
3425+       case MMAL_ENCODING_I420:
3426+          return MMAL_FOURCC('Y','U','1','2');
3427+       case MMAL_ENCODING_YV12:
3428+          return MMAL_FOURCC('Y','V','1','2');
3429+       case MMAL_ENCODING_I422:
3430+          return MMAL_FOURCC('Y','U','1','6');
3431+//       case MMAL_ENCODING_YUVUV128:  // Doesn't actually work yet
3432+       case MMAL_ENCODING_NV12:
3433+          return MMAL_FOURCC('N','V','1','2');
3434+       case MMAL_ENCODING_NV21:
3435+          return MMAL_FOURCC('N','V','2','1');
3436+       case MMAL_ENCODING_RGB16:
3437+          return MMAL_FOURCC('R','G','1','6');
3438+       case MMAL_ENCODING_RGB24:
3439+          return MMAL_FOURCC('B','G','2','4');
3440+       case MMAL_ENCODING_BGR24:
3441+          return MMAL_FOURCC('R','G','2','4');
3442+       case MMAL_ENCODING_BGR32:
3443+       case MMAL_ENCODING_BGRA:
3444+          return MMAL_FOURCC('X','R','2','4');
3445+       case MMAL_ENCODING_RGB32:
3446+       case MMAL_ENCODING_RGBA:
3447+          return MMAL_FOURCC('X','B','2','4');
3448+       default:
3449+          break;
3450+    }
3451+    return 0;
3452+}
3453+
3454+typedef struct tex_context_s {
3455+    picture_context_t cmn;
3456+    GLuint texture;
3457+
3458+    PFNGLDELETETEXTURESPROC DeleteTextures;  // Copy fn pointer so we don't need tc on delete
3459+} tex_context_t;
3460+
3461+static void tex_context_delete(tex_context_t * const tex)
3462+{
3463+    tex->DeleteTextures(1, &tex->texture);
3464+    free(tex);
3465+}
3466+
3467+static void tex_context_destroy(picture_context_t * pic_ctx)
3468+{
3469+    tex_context_delete((tex_context_t *)pic_ctx);
3470+}
3471+
3472+static picture_context_t * tex_context_copy(picture_context_t * pic_ctx)
3473+{
3474+    return pic_ctx;
3475+}
3476+
3477+static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic, cma_buf_t * const cb)
3478+{
3479+    mmal_gl_converter_t * const sys = tc->priv;
3480+    tex_context_t * tex = (tex_context_t *)cma_buf_context2(cb);
3481+    if (tex != NULL)
3482+        return tex;
3483+
3484+    if ((tex = malloc(sizeof(*tex))) == NULL)
3485+        return NULL;
3486+
3487+    *tex = (tex_context_t){
3488+        .cmn = {
3489+            .destroy = tex_context_destroy,
3490+            .copy = tex_context_copy
3491+        },
3492+        .texture = 0,
3493+        .DeleteTextures = tc->vt->DeleteTextures
3494+    };
3495+
3496+    {
3497+        EGLint attribs[30];
3498+        EGLint * a = attribs;
3499+        const int fd = cma_buf_fd(cb);
3500+        uint8_t * base_addr = cma_buf_addr(cb);
3501+
3502+        if (pic->i_planes >= 4 || pic->i_planes <= 0)
3503+        {
3504+            msg_Err(tc, "%s: Bad planes: %d", __func__, pic->i_planes);
3505+            goto fail;
3506+        }
3507+
3508+        *a++ = EGL_WIDTH;
3509+        *a++ = pic->format.i_visible_width;
3510+        *a++ = EGL_HEIGHT;
3511+        *a++ = pic->format.i_visible_height;
3512+        *a++ = EGL_LINUX_DRM_FOURCC_EXT;
3513+        *a++ = sys->drm_fourcc;
3514+
3515+        if (pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8)
3516+        {
3517+            // Sand is its own very special bunny :-(
3518+            static const EGLint attnames[] = {
3519+                EGL_DMA_BUF_PLANE0_FD_EXT,
3520+                EGL_DMA_BUF_PLANE0_OFFSET_EXT,
3521+                EGL_DMA_BUF_PLANE0_PITCH_EXT,
3522+                EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
3523+                EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
3524+                EGL_DMA_BUF_PLANE1_FD_EXT,
3525+                EGL_DMA_BUF_PLANE1_OFFSET_EXT,
3526+                EGL_DMA_BUF_PLANE1_PITCH_EXT,
3527+                EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
3528+                EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT
3529+            };
3530+
3531+            const EGLint * n = attnames;
3532+
3533+            for (int i = 0; i < pic->i_planes; ++i)
3534+            {
3535+                const uint64_t mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(pic->p[i].i_pitch >> 7);
3536+
3537+                *a++ = *n++;
3538+                *a++ = fd;
3539+                *a++ = *n++;
3540+                *a++ = pic->p[i].p_pixels - base_addr;
3541+                *a++ = *n++;
3542+                *a++ = pic->format.i_width;
3543+                *a++ = *n++;
3544+                *a++ = (EGLint)(mod >> 32);
3545+                *a++ = *n++;
3546+                *a++ = (EGLint)(mod & 0xffffffff);
3547+            }
3548+        }
3549+        else
3550+        {
3551+            static const EGLint attnames[] = {
3552+                EGL_DMA_BUF_PLANE0_FD_EXT,
3553+                EGL_DMA_BUF_PLANE0_OFFSET_EXT,
3554+                EGL_DMA_BUF_PLANE0_PITCH_EXT,
3555+                EGL_DMA_BUF_PLANE1_FD_EXT,
3556+                EGL_DMA_BUF_PLANE1_OFFSET_EXT,
3557+                EGL_DMA_BUF_PLANE1_PITCH_EXT,
3558+                EGL_DMA_BUF_PLANE2_FD_EXT,
3559+                EGL_DMA_BUF_PLANE2_OFFSET_EXT,
3560+                EGL_DMA_BUF_PLANE2_PITCH_EXT,
3561+                EGL_DMA_BUF_PLANE3_FD_EXT,
3562+                EGL_DMA_BUF_PLANE3_OFFSET_EXT,
3563+                EGL_DMA_BUF_PLANE3_PITCH_EXT
3564+            };
3565+
3566+            const EGLint * n = attnames;
3567+
3568+            for (int i = 0; i < pic->i_planes; ++i)
3569+            {
3570+                *a++ = *n++;
3571+                *a++ = fd;
3572+                *a++ = *n++;
3573+                *a++ = pic->p[i].p_pixels - base_addr;
3574+                *a++ = *n++;
3575+                *a++ = pic->p[i].i_pitch;
3576+            }
3577+        }
3578+
3579+        *a = EGL_NONE;
3580+
3581+        const EGLImage image = tc->gl->egl.createImageKHR(tc->gl, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
3582+        if (!image) {
3583+           msg_Err(tc, "Failed to import fd %d: Err=%#x", fd, tc->vt->GetError());
3584+           goto fail;
3585+        }
3586+
3587+        // ** ?? tc->tex_target
3588+        tc->vt->GenTextures(1, &tex->texture);
3589+        tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture);
3590+        tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3591+        tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3592+        sys->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
3593+
3594+        tc->gl->egl.destroyImageKHR(tc->gl, image);
3595+    }
3596+
3597+    if (cma_buf_add_context2(cb, &tex->cmn) != VLC_SUCCESS)
3598+    {
3599+        msg_Err(tc, "%s: add_context2 failed", __func__);
3600+        goto fail;
3601+    }
3602+    return tex;
3603+
3604+fail:
3605+    tex_context_delete(tex);
3606+    return NULL;
3607+}
3608+
3609+
3610+static int
3611+tc_mmal_update(const opengl_tex_converter_t *tc, GLuint *textures,
3612+                const GLsizei *tex_width, const GLsizei *tex_height,
3613+                picture_t *pic, const size_t *plane_offset)
3614+{
3615+    mmal_gl_converter_t * const sys = tc->priv;
3616+#if TRACE_ALL
3617+    {
3618+        char cbuf[5];
3619+        msg_Dbg(tc, "%s: %s %d*%dx%d : %d*%dx%d", __func__,
3620+                str_fourcc(cbuf, pic->format.i_chroma),
3621+                tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines);
3622+    }
3623+#endif
3624+    VLC_UNUSED(tex_width);
3625+    VLC_UNUSED(tex_height);
3626+    VLC_UNUSED(plane_offset);
3627+
3628+    if (!is_cma_buf_pic_chroma(pic->format.i_chroma))
3629+    {
3630+        char cbuf[5];
3631+        msg_Err(tc, "Pic with unexpected chroma: %s", str_fourcc(cbuf, pic->format.i_chroma));
3632+        return VLC_EGENERIC;
3633+    }
3634+
3635+    cma_buf_t * const cb = cma_buf_pic_get(pic);
3636+    if (cb == NULL)
3637+    {
3638+        msg_Err(tc, "Pic missing cma buf");
3639+        return VLC_EGENERIC;
3640+    }
3641+
3642+    tex_context_t * const tex = get_tex_context(tc, pic, cb);
3643+    if (tex == NULL)
3644+        return VLC_EGENERIC;
3645+
3646+//    tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture);
3647+
3648+    cma_buf_unref(sys->last_cb);
3649+    sys->last_cb = cma_buf_ref(cb);
3650+
3651+    textures[0] = tex->texture;
3652+    return VLC_SUCCESS;
3653+}
3654+
3655+static int
3656+tc_mmal_fetch_locations(opengl_tex_converter_t *tc, GLuint program)
3657+{
3658+    tc->uloc.Texture[0] = tc->vt->GetUniformLocation(program, "Texture0");
3659+    return tc->uloc.Texture[0] != -1 ? VLC_SUCCESS : VLC_EGENERIC;
3660+}
3661+
3662+static void
3663+tc_mmal_prepare_shader(const opengl_tex_converter_t *tc,
3664+                        const GLsizei *tex_width, const GLsizei *tex_height,
3665+                        float alpha)
3666+{
3667+    (void) tex_width; (void) tex_height; (void) alpha;
3668+    VLC_UNUSED(tc);
3669+//    tc->vt->Uniform1i(tc->uloc.Texture[0], 0);
3670+}
3671+
3672+static GLuint
3673+tc_fragment_shader_init(opengl_tex_converter_t * const tc, const GLenum tex_target,
3674+                        const vlc_fourcc_t chroma, const video_color_space_t yuv_space)
3675+{
3676+    VLC_UNUSED(yuv_space);
3677+
3678+    tc->tex_count = 1;
3679+    tc->tex_target = tex_target;
3680+    tc->texs[0] = (struct opengl_tex_cfg) {
3681+        { 1, 1 }, { 1, 1 }, GL_RGB, chroma, GL_UNSIGNED_SHORT  //** ??
3682+    };
3683+
3684+    tc->pf_fetch_locations = tc_mmal_fetch_locations;
3685+    tc->pf_prepare_shader = tc_mmal_prepare_shader;
3686+
3687+
3688+    const char fs[] =
3689+       "#extension GL_OES_EGL_image_external : enable\n"
3690+       "precision mediump float;\n"
3691+       "uniform samplerExternalOES Texture0;\n"
3692+       "varying vec2 TexCoord0;\n"
3693+       "void main() {\n"
3694+       "  gl_FragColor = texture2D(Texture0, TexCoord0);\n"
3695+       "}\n";
3696+
3697+
3698+    const char *code = fs;
3699+
3700+    GLuint fragment_shader = tc->vt->CreateShader(GL_FRAGMENT_SHADER);
3701+    tc->vt->ShaderSource(fragment_shader, 1, &code, NULL);
3702+    tc->vt->CompileShader(fragment_shader);
3703+    return fragment_shader;
3704+}
3705+
3706+
3707+static void
3708+CloseGLConverter(vlc_object_t *obj)
3709+{
3710+    opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj;
3711+    mmal_gl_converter_t * const sys = tc->priv;
3712+
3713+    if (sys == NULL)
3714+        return;
3715+
3716+    cma_buf_unref(sys->last_cb);
3717+    cma_vcsm_exit(sys->vcsm_init_type);
3718+    free(sys);
3719+}
3720+
3721+
3722+// Pick a chroma that we can convert to
3723+// Prefer I420 as smallest
3724+static vlc_fourcc_t chroma_in_out(const vlc_fourcc_t chroma_in)
3725+{
3726+    switch (chroma_in)
3727+    {
3728+        case VLC_CODEC_MMAL_OPAQUE:
3729+        case VLC_CODEC_MMAL_ZC_I420:
3730+        case VLC_CODEC_MMAL_ZC_SAND8:
3731+        case VLC_CODEC_MMAL_ZC_SAND10:          // ISP only
3732+            return VLC_CODEC_MMAL_ZC_I420;
3733+        case VLC_CODEC_MMAL_ZC_SAND30:          // HVS only
3734+        case VLC_CODEC_MMAL_ZC_RGB32:
3735+            return VLC_CODEC_MMAL_ZC_RGB32;     // HVS can't generate YUV of any sort
3736+        default:
3737+            break;
3738+    }
3739+    return 0;
3740+}
3741+
3742+
3743+static int
3744+OpenGLConverter(vlc_object_t *obj)
3745+{
3746+    opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj;
3747+    int rv = VLC_EGENERIC;
3748+    const EGLint eglfmt = vlc_to_gl_fourcc(&tc->fmt);
3749+    const vlc_fourcc_t chroma_out = chroma_in_out(tc->fmt.i_chroma);
3750+
3751+    // Do we know what to do with this?
3752+    if (chroma_out == 0)
3753+        return rv;
3754+
3755+    {
3756+        char dbuf0[5], dbuf1[5], dbuf2[5];
3757+        msg_Dbg(tc, "<<< %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__,
3758+                str_fourcc(dbuf0, tc->fmt.i_chroma),
3759+                str_fourcc(dbuf1, eglfmt),
3760+                tc->fmt.i_width, tc->fmt.i_height,
3761+                tc->fmt.i_x_offset, tc->fmt.i_y_offset,
3762+                tc->fmt.i_visible_width, tc->fmt.i_visible_height,
3763+                tc->fmt.i_sar_num, tc->fmt.i_sar_den,
3764+                str_fourcc(dbuf2, chroma_out));
3765+    }
3766+
3767+    if (tc->gl->ext != VLC_GL_EXT_EGL ||
3768+        !tc->gl->egl.createImageKHR || !tc->gl->egl.destroyImageKHR)
3769+    {
3770+        // Missing an important callback
3771+        msg_Dbg(tc, "Missing EGL xxxImageKHR calls");
3772+        return rv;
3773+    }
3774+
3775+    if ((tc->priv = calloc(1, sizeof(mmal_gl_converter_t))) == NULL)
3776+    {
3777+        msg_Err(tc, "priv alloc failure");
3778+        rv = VLC_ENOMEM;
3779+        goto fail;
3780+    }
3781+    mmal_gl_converter_t * const sys = tc->priv;
3782+
3783+    sys->drm_fourcc = eglfmt;
3784+
3785+    if ((sys->vcsm_init_type = cma_vcsm_init()) != VCSM_INIT_CMA) {
3786+        msg_Dbg(tc, "VCSM init failed");
3787+        goto fail;
3788+    }
3789+
3790+    if ((sys->glEGLImageTargetTexture2DOES = vlc_gl_GetProcAddress(tc->gl, "glEGLImageTargetTexture2DOES")) == NULL)
3791+    {
3792+        msg_Err(tc, "Failed to bind GL fns");
3793+        goto fail;
3794+    }
3795+
3796+    if ((tc->fshader = tc_fragment_shader_init(tc, GL_TEXTURE_EXTERNAL_OES,
3797+                                                   eglfmt == 0 ? VLC_CODEC_RGB32 : tc->fmt.i_chroma,
3798+                                                   eglfmt == 0 ? COLOR_SPACE_SRGB : tc->fmt.space)) == 0)
3799+    {
3800+        msg_Err(tc, "Failed to make shader");
3801+        goto fail;
3802+    }
3803+
3804+    if (eglfmt == 0)
3805+    {
3806+        tc->fmt.i_chroma = chroma_out;
3807+        tc->fmt.i_bits_per_pixel = 8;
3808+        if (tc->fmt.i_chroma == VLC_CODEC_MMAL_ZC_RGB32)
3809+        {
3810+            tc->fmt.i_rmask = 0xff0000;
3811+            tc->fmt.i_gmask = 0xff00;
3812+            tc->fmt.i_bmask = 0xff;
3813+            tc->fmt.space = COLOR_SPACE_SRGB;
3814+        }
3815+        else
3816+        {
3817+            tc->fmt.i_rmask = 0;
3818+            tc->fmt.i_gmask = 0;
3819+            tc->fmt.i_bmask = 0;
3820+            tc->fmt.space = COLOR_SPACE_UNDEF;
3821+        }
3822+        sys->drm_fourcc = vlc_to_gl_fourcc(&tc->fmt);
3823+    }
3824+
3825+    tc->handle_texs_gen = true;  // We manage the texs
3826+    tc->pf_update  = tc_mmal_update;
3827+
3828+#if TRACE_ALL
3829+    {
3830+        char dbuf0[5], dbuf1[5], dbuf2[5];
3831+        msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__,
3832+                str_fourcc(dbuf0, tc->fmt.i_chroma),
3833+                str_fourcc(dbuf1, sys->drm_fourcc),
3834+                tc->fmt.i_width, tc->fmt.i_height,
3835+                tc->fmt.i_x_offset, tc->fmt.i_y_offset,
3836+                tc->fmt.i_visible_width, tc->fmt.i_visible_height,
3837+                tc->fmt.i_sar_num, tc->fmt.i_sar_den,
3838+                str_fourcc(dbuf2, chroma_out));
3839+    }
3840+#endif
3841+
3842+    return VLC_SUCCESS;
3843+
3844+fail:
3845+    CloseGLConverter(obj);
3846+    return rv;
3847+}
3848+
3849+vlc_module_begin ()
3850+    set_description("MMAL OpenGL surface converter")
3851+    set_shortname (N_("MMALGLConverter"))
3852+    set_capability("glconv", 900)
3853+    set_callbacks(OpenGLConverter, CloseGLConverter)
3854+    set_category(CAT_VIDEO)
3855+    set_subcategory(SUBCAT_VIDEO_VOUT)
3856+    add_shortcut("mmal_gl_converter")
3857+vlc_module_end ()
3858+
3859--- a/modules/hw/mmal/deinterlace.c
3860+++ b/modules/hw/mmal/deinterlace.c
3861@@ -26,11 +26,12 @@
3862 #include "config.h"
3863 #endif
3864
3865-#include <vlc_picture_pool.h>
3866+#include <stdatomic.h>
3867+
3868 #include <vlc_common.h>
3869+#include <vlc_picture_pool.h>
3870 #include <vlc_plugin.h>
3871 #include <vlc_filter.h>
3872-#include <vlc_atomic.h>
3873
3874 #include "mmal_picture.h"
3875
3876@@ -39,468 +40,814 @@
3877 #include <interface/mmal/util/mmal_util.h>
3878 #include <interface/mmal/util/mmal_default_components.h>
3879
3880-#define MIN_NUM_BUFFERS_IN_TRANSIT 2
3881+#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu"
3882+#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.")
3883+#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.")
3884
3885-#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu"
3886-#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.")
3887-#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.")
3888+#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv"
3889+#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace")
3890+#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace")
3891
3892-static int Open(filter_t *filter);
3893-static void Close(filter_t *filter);
3894+#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast"
3895+#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace")
3896+#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace")
3897
3898-vlc_module_begin()
3899-    set_shortname(N_("MMAL deinterlace"))
3900-    set_description(N_("MMAL-based deinterlace filter plugin"))
3901-    set_capability("video filter", 0)
3902-    set_category(CAT_VIDEO)
3903-    set_subcategory(SUBCAT_VIDEO_VFILTER)
3904-    set_callbacks(Open, Close)
3905-    add_shortcut("deinterlace")
3906-    add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT,
3907-                    MMAL_DEINTERLACE_QPU_LONGTEXT, true);
3908-vlc_module_end()
3909+#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none"
3910+#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace")
3911+#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\
3912+    "This is the default for > SD if < 96M gpu-mem")
3913+
3914+#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate"
3915+#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate")
3916+#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input")
3917+
3918+#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate"
3919+#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate")
3920+#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input")
3921
3922-struct filter_sys_t {
3923+
3924+typedef struct filter_sys_t
3925+{
3926     MMAL_COMPONENT_T *component;
3927     MMAL_PORT_T *input;
3928     MMAL_PORT_T *output;
3929+    MMAL_POOL_T *in_pool;
3930+
3931+    MMAL_QUEUE_T * out_q;
3932+
3933+    // Bind this lot somehow into ppr????
3934+    bool is_cma;
3935+    cma_buf_pool_t * cma_out_pool;
3936+    MMAL_POOL_T * out_pool;
3937+
3938+    hw_mmal_port_pool_ref_t *out_ppr;
3939+
3940+    bool half_rate;
3941+    bool use_qpu;
3942+    bool use_fast;
3943+    bool use_passthrough;
3944+    unsigned int seq_in;    // Seq of next frame to submit (1-15) [Init=1]
3945+    unsigned int seq_out;   // Seq of last frame received  (1-15) [Init=15]
3946
3947-    MMAL_QUEUE_T *filtered_pictures;
3948-    vlc_sem_t sem;
3949+    vcsm_init_type_t vcsm_init_type;
3950
3951-    atomic_bool started;
3952+} filter_sys_t;
3953
3954-    /* statistics */
3955-    int output_in_transit;
3956-    int input_in_transit;
3957-};
3958-
3959-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
3960-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
3961-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
3962-static picture_t *deinterlace(filter_t *filter, picture_t *picture);
3963-static void flush(filter_t *filter);
3964
3965 #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
3966
3967-static int Open(filter_t *filter)
3968+#define TRACE_ALL 0
3969+
3970+
3971+
3972+// Buffer attached to pic on success, is still valid on failure
3973+static picture_t * di_alloc_opaque(filter_t * const p_filter, MMAL_BUFFER_HEADER_T * const buf)
3974 {
3975-    int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
3976-            (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base /
3977-            filter->fmt_in.video.i_frame_rate : 0;
3978-    bool use_qpu = var_InheritBool(filter, MMAL_DEINTERLACE_QPU);
3979+    filter_sys_t *const filter_sys = p_filter->p_sys;
3980+    picture_t * const pic = filter_NewPicture(p_filter);
3981
3982-    MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
3983-            { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
3984-            MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
3985-            4,
3986-            { 3, frame_duration, 0, use_qpu }
3987-    };
3988+    if (pic == NULL)
3989+        goto fail1;
3990
3991-    int ret = VLC_SUCCESS;
3992-    MMAL_STATUS_T status;
3993-    filter_sys_t *sys;
3994+    if (buf->length == 0) {
3995+        msg_Err(p_filter, "%s: Empty buffer", __func__);
3996+        goto fail2;
3997+    }
3998
3999-    msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!",
4000-            frame_duration, use_qpu ? "used" : "unused");
4001+    if ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL)
4002+        goto fail2;
4003
4004-    if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
4005-        return VLC_EGENERIC;
4006+    buf_to_pic_copy_props(pic, buf);
4007
4008-    if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
4009-        return VLC_EGENERIC;
4010+#if TRACE_ALL
4011+    msg_Dbg(p_filter, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
4012+#endif
4013
4014-    sys = calloc(1, sizeof(filter_sys_t));
4015-    if (!sys)
4016-        return VLC_ENOMEM;
4017-    filter->p_sys = sys;
4018+    return pic;
4019
4020-    bcm_host_init();
4021+fail2:
4022+    picture_Release(pic);
4023+fail1:
4024+//    mmal_buffer_header_release(buf);
4025+    return NULL;
4026+}
4027
4028-    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
4029-    if (status != MMAL_SUCCESS) {
4030-        msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
4031-                MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4032-        ret = VLC_EGENERIC;
4033-        goto out;
4034-    }
4035+static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4036+{
4037+#if TRACE_ALL
4038+    pic_ctx_mmal_t * ctx = buffer->user_data;
4039+//    filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
4040+
4041+    msg_Dbg((filter_t *)port->userdata, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buffer->cmd, ctx, buffer,
4042+            buffer->flags, (long long)buffer->pts);
4043+#else
4044+    VLC_UNUSED(port);
4045+#endif
4046
4047-    status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
4048-    if (status != MMAL_SUCCESS) {
4049-        msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
4050-                MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4051-        ret = VLC_EGENERIC;
4052-        goto out;
4053-    }
4054+    mmal_buffer_header_release(buffer);
4055
4056-    sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4057-    status = mmal_port_enable(sys->component->control, control_port_cb);
4058-    if (status != MMAL_SUCCESS) {
4059-        msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
4060-                sys->component->control->name, status, mmal_status_to_string(status));
4061-        ret = VLC_EGENERIC;
4062-        goto out;
4063+#if TRACE_ALL
4064+    msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
4065+#endif
4066+}
4067+
4068+static void di_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
4069+{
4070+    if (buf->cmd == 0 && buf->length != 0)
4071+    {
4072+        // The filter structure etc. should always exist if we have contents
4073+        // but might not on later flushes as we shut down
4074+        filter_t * const p_filter = (filter_t *)port->userdata;
4075+        filter_sys_t * const sys = p_filter->p_sys;
4076+
4077+#if TRACE_ALL
4078+        msg_Dbg(p_filter, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts);
4079+#endif
4080+        mmal_queue_put(sys->out_q, buf);
4081+#if TRACE_ALL
4082+        msg_Dbg(p_filter, ">>> %s: out Q len=%d", __func__, mmal_queue_length(sys->out_q));
4083+#endif
4084+        return;
4085     }
4086
4087-    sys->input = sys->component->input[0];
4088-    sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4089-    if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
4090-        sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
4091-    sys->input->format->es->video.width = filter->fmt_in.video.i_width;
4092-    sys->input->format->es->video.height = filter->fmt_in.video.i_height;
4093-    sys->input->format->es->video.crop.x = 0;
4094-    sys->input->format->es->video.crop.y = 0;
4095-    sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width;
4096-    sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height;
4097-    sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num;
4098-    sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den;
4099+    mmal_buffer_header_reset(buf);   // User data stays intact so release will kill pic
4100+    mmal_buffer_header_release(buf);
4101+}
4102
4103-    es_format_Copy(&filter->fmt_out, &filter->fmt_in);
4104-    filter->fmt_out.video.i_frame_rate *= 2;
4105
4106-    status = mmal_port_format_commit(sys->input);
4107-    if (status != MMAL_SUCCESS) {
4108-        msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
4109-                        sys->input->name, status, mmal_status_to_string(status));
4110-        ret = VLC_EGENERIC;
4111-        goto out;
4112-    }
4113-    sys->input->buffer_size = sys->input->buffer_size_recommended;
4114-    sys->input->buffer_num = sys->input->buffer_num_recommended;
4115
4116-    if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
4117-        MMAL_PARAMETER_BOOLEAN_T zero_copy = {
4118-            { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
4119-            1
4120-        };
4121+static MMAL_STATUS_T fill_output_from_q(filter_t * const p_filter, filter_sys_t * const sys, MMAL_QUEUE_T * const q)
4122+{
4123+    MMAL_BUFFER_HEADER_T * out_buf;
4124
4125-        status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
4126-        if (status != MMAL_SUCCESS) {
4127-           msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
4128-                    sys->input->name, status, mmal_status_to_string(status));
4129-           goto out;
4130+    while ((out_buf = mmal_queue_get(q)) != NULL)
4131+    {
4132+        MMAL_STATUS_T err;
4133+        if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
4134+        {
4135+            msg_Err(p_filter, "Send buffer to output failed");
4136+            mmal_queue_put_back(q, out_buf);
4137+            return err;
4138         }
4139     }
4140+    return MMAL_SUCCESS;
4141+}
4142
4143-    status = mmal_port_enable(sys->input, input_port_cb);
4144-    if (status != MMAL_SUCCESS) {
4145-        msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
4146-                sys->input->name, status, mmal_status_to_string(status));
4147-        ret = VLC_EGENERIC;
4148-        goto out;
4149-    }
4150+// Output buffers may contain a pic ref on error or flush
4151+// Free it
4152+static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
4153+{
4154+    VLC_UNUSED(userdata);
4155
4156-    sys->output = sys->component->output[0];
4157-    sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4158-    mmal_format_full_copy(sys->output->format, sys->input->format);
4159+    cma_buf_t * const cb = header->user_data;
4160+    header->user_data = NULL;
4161+    cma_buf_unref(cb);  // Copes fine with NULL
4162
4163-    status = mmal_port_format_commit(sys->output);
4164-    if (status != MMAL_SUCCESS) {
4165-        msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
4166-                        sys->input->name, status, mmal_status_to_string(status));
4167-        ret = VLC_EGENERIC;
4168-        goto out;
4169+    return MMAL_FALSE;
4170+}
4171+
4172+static inline unsigned int seq_inc(unsigned int x)
4173+{
4174+    return x + 1 >= 16 ? 1 : x + 1;
4175+}
4176+
4177+static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq)
4178+{
4179+    return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq);
4180+}
4181+
4182+static picture_t *deinterlace(filter_t * p_filter, picture_t * p_pic)
4183+{
4184+    filter_sys_t * const sys = p_filter->p_sys;
4185+    picture_t *ret_pics = NULL;
4186+    MMAL_STATUS_T err;
4187+    MMAL_BUFFER_HEADER_T * out_buf = NULL;
4188+
4189+#if TRACE_ALL
4190+    msg_Dbg(p_filter, "<<< %s", __func__);
4191+#endif
4192+
4193+    if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
4194+    {
4195+        // ****** Breaks on opaque (at least)
4196+
4197+        if (sys->input->is_enabled)
4198+            mmal_port_disable(sys->input);
4199+#if 0
4200+        if (sys->output->is_enabled)
4201+            mmal_port_disable(sys->output);
4202+
4203+        mmal_format_full_copy(sys->output->format, sys->input->format);
4204+        mmal_port_format_commit(sys->output);
4205+        sys->output->buffer_num = 30;
4206+        sys->output->buffer_size = sys->input->buffer_size_recommended;
4207+        mmal_port_enable(sys->output, di_output_port_cb);
4208+#endif
4209+        if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
4210+            msg_Err(p_filter, "Failed to update pic format");
4211+        sys->input->buffer_num = 30;
4212+        sys->input->buffer_size = sys->input->buffer_size_recommended;
4213+        mmal_log_dump_format(sys->input->format);
4214+    }
4215+
4216+    // Reenable stuff if the last thing we did was flush
4217+    // Output should always be enabled
4218+    if (!sys->input->is_enabled &&
4219+        (err = mmal_port_enable(sys->input, di_input_port_cb)) != MMAL_SUCCESS)
4220+    {
4221+        msg_Err(p_filter, "Input port reenable failed");
4222+        goto fail;
4223+    }
4224+
4225+    if (!sys->is_cma)
4226+    {
4227+        // Fill output from anything that has turned up in pool Q
4228+        if (hw_mmal_port_pool_ref_fill(sys->out_ppr) != MMAL_SUCCESS)
4229+        {
4230+            msg_Err(p_filter, "Out port fill fail");
4231+            goto fail;
4232+        }
4233     }
4234+    else
4235+    {
4236+        // We are expecting one in - one out so simply wedge a new bufer
4237+        // into the output port.  Flow control will happen on cma alloc.
4238+
4239+        if ((out_buf = mmal_queue_get(sys->out_pool->queue)) == NULL)
4240+        {
4241+            // Should never happen
4242+            msg_Err(p_filter, "Failed to get output buffer");
4243+            goto fail;
4244+        }
4245+        mmal_buffer_header_reset(out_buf);
4246
4247-    sys->output->buffer_num = 3;
4248+        // Attach cma_buf to the buffer & ensure it is freed when the buffer is released
4249+        // On a good send callback the pic will be extracted to avoid this
4250+        mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, p_filter);
4251+
4252+        cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size);
4253+        if ((out_buf->user_data = cb) == NULL)  // Check & attach cb to buf
4254+        {
4255+            char dbuf0[5];
4256+            msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d",
4257+                    str_fourcc(dbuf0, p_pic->format.i_chroma),
4258+                    sys->output->buffer_size);
4259+            goto fail;
4260+        }
4261+        const unsigned int vc_h = cma_buf_vc_handle(cb);  // Cannot coerce without going via variable
4262+        out_buf->data = (uint8_t *)vc_h;
4263+        out_buf->alloc_size = sys->output->buffer_size;
4264+
4265+#if TRACE_ALL
4266+        msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld",
4267+                p_pic, out_buf->data, out_buf->user_data, out_buf->flags,
4268+                out_buf->length, out_buf->alloc_size, (long long)out_buf->pts);
4269+#endif
4270
4271-    if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
4272-        MMAL_PARAMETER_UINT32_T extra_buffers = {
4273-            { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) },
4274-            5
4275-        };
4276-        status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
4277-        if (status != MMAL_SUCCESS) {
4278-            msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
4279-                    status, mmal_status_to_string(status));
4280-            goto out;
4281+        if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
4282+        {
4283+            msg_Err(p_filter, "Send buffer to output failed");
4284+            goto fail;
4285         }
4286+        out_buf = NULL;
4287+    }
4288
4289-        MMAL_PARAMETER_BOOLEAN_T zero_copy = {
4290-            { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
4291-            1
4292-        };
4293+    // Stuff into input
4294+    // We assume the BH is already set up with values reflecting pic date etc.
4295+    {
4296+        MMAL_BUFFER_HEADER_T * const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
4297+
4298+        if (pic_buf == NULL)
4299+        {
4300+            msg_Err(p_filter, "Pic has not attached buffer");
4301+            goto fail;
4302+        }
4303
4304-        status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
4305-        if (status != MMAL_SUCCESS) {
4306-           msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
4307-                    sys->output->name, status, mmal_status_to_string(status));
4308-           goto out;
4309+        picture_Release(p_pic);
4310+
4311+        // Add a sequence to the flags so we can track what we have actually
4312+        // deinterlaced
4313+        pic_buf->flags = (pic_buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0));
4314+        sys->seq_in = seq_inc(sys->seq_in);
4315+
4316+        if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
4317+        {
4318+            msg_Err(p_filter, "Send buffer to input failed");
4319+            mmal_buffer_header_release(pic_buf);
4320+            goto fail;
4321         }
4322     }
4323
4324-    status = mmal_port_enable(sys->output, output_port_cb);
4325-    if (status != MMAL_SUCCESS) {
4326-        msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
4327-                sys->output->name, status, mmal_status_to_string(status));
4328-        ret = VLC_EGENERIC;
4329-        goto out;
4330+    // Return anything that is in the out Q
4331+    {
4332+        picture_t ** pp_pic = &ret_pics;
4333+
4334+        // Advanced di has a 3 frame latency, so if the seq delta is greater
4335+        // than that then we are expecting at least two frames of output. Wait
4336+        // for one of those.
4337+        // seq_in is seq of the next frame we are going to submit (1-15, no 0)
4338+        // seq_out is last frame we removed from Q
4339+        // So after 4 frames sent (1st time we want to wait), 0 rx seq_in=5, seq_out=15, delta=5
4340+
4341+        while ((out_buf = (seq_delta(sys->seq_in, sys->seq_out) >= 5 ? mmal_queue_timedwait(sys->out_q, 1000) : mmal_queue_get(sys->out_q))) != NULL)
4342+        {
4343+            const unsigned int seq_out = (out_buf->flags / MMAL_BUFFER_HEADER_FLAG_USER0) & 0xf;
4344+            int rv;
4345+
4346+            picture_t * out_pic;
4347+
4348+            if (sys->is_cma)
4349+            {
4350+                // Alloc pic
4351+                if ((out_pic = filter_NewPicture(p_filter)) == NULL)
4352+                {
4353+                    // Can't alloc pic - just stop extraction
4354+                    mmal_queue_put_back(sys->out_q, out_buf);
4355+                    out_buf = NULL;
4356+                    msg_Warn(p_filter, "Failed to alloc new filter output pic");
4357+                    break;
4358+                }
4359+
4360+                // Extract cma_buf from buf & attach to pic
4361+                cma_buf_t * const cb = (cma_buf_t *)out_buf->user_data;
4362+                if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS)
4363+                {
4364+                    char dbuf0[5];
4365+                    msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d",
4366+                            str_fourcc(dbuf0, out_pic->format.i_chroma),
4367+                            rv);
4368+                    // cb still attached to buffer and will be freed with it
4369+                    goto fail;
4370+                }
4371+                out_buf->user_data = NULL;
4372+
4373+                buf_to_pic_copy_props(out_pic, out_buf);
4374+
4375+                // Set pic data pointers from buf aux info now it has it
4376+                if ((rv = cma_pic_set_data(out_pic, sys->output->format, out_buf)) != VLC_SUCCESS)
4377+                {
4378+                    char dbuf0[5];
4379+                    msg_Err(p_filter, "Failed to set data: fmt=%s, rv=%d",
4380+                            str_fourcc(dbuf0, sys->output->format->encoding),
4381+                            rv);
4382+                }
4383+
4384+                out_buf->user_data = NULL;  // Responsability for this pic no longer with buffer
4385+                mmal_buffer_header_release(out_buf);
4386+            }
4387+            else
4388+            {
4389+                out_pic = di_alloc_opaque(p_filter, out_buf);
4390+
4391+                if (out_pic == NULL) {
4392+                    msg_Warn(p_filter, "Failed to alloc new filter output pic");
4393+                    mmal_queue_put_back(sys->out_q, out_buf);  // Wedge buf back into Q in the hope we can alloc a pic later
4394+                    out_buf = NULL;
4395+                    break;
4396+                }
4397+            }
4398+            out_buf = NULL;  // Now attached to pic or recycled
4399+
4400+#if TRACE_ALL
4401+            msg_Dbg(p_filter, "-- %s: Q pic=%p: seq_in=%d, seq_out=%d, delta=%d", __func__, out_pic, sys->seq_in, seq_out, seq_delta(sys->seq_in, seq_out));
4402+#endif
4403+
4404+            *pp_pic = out_pic;
4405+            pp_pic = &out_pic->p_next;
4406+
4407+            // Ignore 0 seqs
4408+            // Don't think these should actually happen
4409+            if (seq_out != 0)
4410+                sys->seq_out = seq_out;
4411+        }
4412+
4413+        // Crash on lockup
4414+        assert(ret_pics != NULL || seq_delta(sys->seq_in, sys->seq_out) < 5);
4415     }
4416
4417-    status = mmal_component_enable(sys->component);
4418-    if (status != MMAL_SUCCESS) {
4419-        msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
4420-                sys->component->name, status, mmal_status_to_string(status));
4421-        ret = VLC_EGENERIC;
4422-        goto out;
4423+#if TRACE_ALL
4424+    msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
4425+#endif
4426+
4427+    return ret_pics;
4428+
4429+fail:
4430+    if (out_buf != NULL)
4431+        mmal_buffer_header_release(out_buf);
4432+    picture_Release(p_pic);
4433+    return NULL;
4434+}
4435+
4436+static void di_flush(filter_t *p_filter)
4437+{
4438+    filter_sys_t * const sys = p_filter->p_sys;
4439+
4440+#if TRACE_ALL
4441+    msg_Dbg(p_filter, "<<< %s", __func__);
4442+#endif
4443+
4444+    if (sys->input != NULL && sys->input->is_enabled)
4445+        mmal_port_disable(sys->input);
4446+
4447+    if (sys->output != NULL && sys->output->is_enabled)
4448+    {
4449+        if (sys->is_cma)
4450+        {
4451+            MMAL_BUFFER_HEADER_T * buf;
4452+            mmal_port_disable(sys->output);
4453+            while ((buf = mmal_queue_get(sys->out_q)) != NULL)
4454+                mmal_buffer_header_release(buf);
4455+        }
4456+        else
4457+        {
4458+            // Wedge anything we've got into the output port as that will free the underlying buffers
4459+            fill_output_from_q(p_filter, sys, sys->out_q);
4460+
4461+            mmal_port_disable(sys->output);
4462+
4463+            // If that dumped anything real into the out_q then have another go
4464+            if (mmal_queue_length(sys->out_q) != 0)
4465+            {
4466+                mmal_port_enable(sys->output, di_output_port_cb);
4467+                fill_output_from_q(p_filter, sys, sys->out_q);
4468+                mmal_port_disable(sys->output);
4469+                // Out q should now be empty & should remain so until the input is reenabled
4470+            }
4471+        }
4472+        mmal_port_enable(sys->output, di_output_port_cb);
4473+
4474+        // Leaving the input disabled is fine - but we want to leave the output enabled
4475+        // so we can retrieve buffers that are still bound to pictures
4476     }
4477
4478-    sys->filtered_pictures = mmal_queue_create();
4479+    sys->seq_in = 1;
4480+    sys->seq_out = 15;
4481
4482-    filter->pf_video_filter = deinterlace;
4483-    filter->pf_flush = flush;
4484+#if TRACE_ALL
4485+    msg_Dbg(p_filter, ">>> %s", __func__);
4486+#endif
4487+}
4488
4489-    vlc_sem_init(&sys->sem, 0);
4490
4491-out:
4492-    if (ret != VLC_SUCCESS)
4493-        Close(filter);
4494+static void pass_flush(filter_t *p_filter)
4495+{
4496+    // Nothing to do
4497+    VLC_UNUSED(p_filter);
4498+}
4499
4500-    return ret;
4501+static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic)
4502+{
4503+    VLC_UNUSED(p_filter);
4504+
4505+    p_pic->b_progressive = true;
4506+    return p_pic;
4507 }
4508
4509-static void Close(filter_t *filter)
4510+
4511+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4512 {
4513-    filter_sys_t *sys = filter->p_sys;
4514-    MMAL_BUFFER_HEADER_T *buffer;
4515+    filter_t *filter = (filter_t *)port->userdata;
4516+    MMAL_STATUS_T status;
4517
4518-    if (!sys)
4519+    if (buffer->cmd == MMAL_EVENT_ERROR) {
4520+        status = *(uint32_t *)buffer->data;
4521+        msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
4522+                mmal_status_to_string(status));
4523+    }
4524+
4525+    mmal_buffer_header_reset(buffer);
4526+    mmal_buffer_header_release(buffer);
4527+}
4528+
4529+static void CloseMmalDeinterlace(filter_t *filter)
4530+{
4531+    filter_sys_t * const sys = filter->p_sys;
4532+
4533+#if TRACE_ALL
4534+    msg_Dbg(filter, "<<< %s", __func__);
4535+#endif
4536+
4537+    if (sys == NULL)
4538         return;
4539
4540-    if (sys->component && sys->component->control->is_enabled)
4541-        mmal_port_disable(sys->component->control);
4542+    if (sys->use_passthrough)
4543+    {
4544+        free(sys);
4545+        return;
4546+    }
4547
4548-    if (sys->input && sys->input->is_enabled)
4549-        mmal_port_disable(sys->input);
4550+    di_flush(filter);
4551
4552-    if (sys->output && sys->output->is_enabled)
4553-        mmal_port_disable(sys->output);
4554+    if (sys->component && sys->component->control->is_enabled)
4555+        mmal_port_disable(sys->component->control);
4556
4557     if (sys->component && sys->component->is_enabled)
4558         mmal_component_disable(sys->component);
4559
4560-    while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
4561-        picture_t *pic = (picture_t *)buffer->user_data;
4562-        picture_Release(pic);
4563+    if (sys->in_pool != NULL)
4564+        mmal_pool_destroy(sys->in_pool);
4565+
4566+    hw_mmal_port_pool_ref_release(sys->out_ppr, false);
4567+    // Once we exit filter & sys are invalid so mark as such
4568+    if (sys->output != NULL)
4569+        sys->output->userdata = NULL;
4570+
4571+    if (sys->is_cma)
4572+    {
4573+        if (sys->output && sys->output->is_enabled)
4574+            mmal_port_disable(sys->output);
4575+
4576+        cma_buf_pool_deletez(&sys->cma_out_pool);
4577+
4578+        if (sys->out_pool != NULL)
4579+            mmal_pool_destroy(sys->out_pool);
4580     }
4581
4582-    if (sys->filtered_pictures)
4583-        mmal_queue_destroy(sys->filtered_pictures);
4584+    if (sys->out_q != NULL)
4585+        mmal_queue_destroy(sys->out_q);
4586
4587     if (sys->component)
4588         mmal_component_release(sys->component);
4589
4590-    vlc_sem_destroy(&sys->sem);
4591+    cma_vcsm_exit(sys->vcsm_init_type);
4592+
4593     free(sys);
4594+}
4595+
4596
4597-    bcm_host_deinit();
4598+static bool is_fmt_valid_in(const vlc_fourcc_t fmt)
4599+{
4600+    return fmt == VLC_CODEC_MMAL_OPAQUE ||
4601+           fmt == VLC_CODEC_MMAL_ZC_I420 ||
4602+           fmt == VLC_CODEC_MMAL_ZC_SAND8;
4603 }
4604
4605-static int send_output_buffer(filter_t *filter)
4606+static int OpenMmalDeinterlace(filter_t *filter)
4607 {
4608-    filter_sys_t *sys = filter->p_sys;
4609-    MMAL_BUFFER_HEADER_T *buffer;
4610+    int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
4611+            CLOCK_FREQ * filter->fmt_in.video.i_frame_rate_base /
4612+            filter->fmt_in.video.i_frame_rate : 0;
4613+
4614+    int ret = VLC_EGENERIC;
4615     MMAL_STATUS_T status;
4616-    picture_t *picture;
4617-    int ret = 0;
4618+    filter_sys_t *sys;
4619+
4620+    msg_Dbg(filter, "<<< %s", __func__);
4621+
4622+    if (!is_fmt_valid_in(filter->fmt_in.video.i_chroma) ||
4623+        filter->fmt_out.video.i_chroma != filter->fmt_in.video.i_chroma)
4624+        return VLC_EGENERIC;
4625
4626-    if (!sys->output->is_enabled) {
4627-        ret = VLC_EGENERIC;
4628-        goto out;
4629+    sys = calloc(1, sizeof(filter_sys_t));
4630+    if (!sys)
4631+        return VLC_ENOMEM;
4632+    filter->p_sys = sys;
4633+
4634+    sys->seq_in = 1;
4635+    sys->seq_out = 15;
4636+    sys->is_cma = is_cma_buf_pic_chroma(filter->fmt_out.video.i_chroma);
4637+
4638+    if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
4639+        msg_Err(filter, "VCSM init failed");
4640+        goto fail;
4641+    }
4642+
4643+    if (rpi_is_model_pi4())
4644+    {
4645+        sys->half_rate = true;
4646+        sys->use_qpu = false;
4647+        sys->use_fast = true;
4648+    }
4649+    else
4650+    {
4651+        sys->half_rate = false;
4652+        sys->use_qpu = true;
4653+        sys->use_fast = false;
4654+    }
4655+    sys->use_passthrough = false;
4656+
4657+    if (filter->fmt_in.video.i_width * filter->fmt_in.video.i_height > 768 * 576)
4658+    {
4659+        // We get stressed if we have to try too hard - so make life easier
4660+        sys->half_rate = true;
4661+        // Also check we actually have enough memory to do this
4662+        // Memory always comes from GPU if Opaque
4663+        // Assume we have plenty of memory if it comes from CMA
4664+        if ((!sys->is_cma || sys->vcsm_init_type == VCSM_INIT_LEGACY) &&
4665+            hw_mmal_get_gpu_mem() < (96 << 20))
4666+        {
4667+            sys->use_passthrough = true;
4668+            msg_Warn(filter, "Deinterlace bypassed due to lack of GPU memory");
4669+        }
4670     }
4671
4672-    picture = filter_NewPicture(filter);
4673-    if (!picture) {
4674-        msg_Warn(filter, "Failed to get new picture");
4675-        ret = -1;
4676-        goto out;
4677+    if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU))
4678+        sys->use_qpu = false;
4679+    if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV))
4680+    {
4681+        sys->use_fast = false;
4682+        sys->use_passthrough = false;
4683+    }
4684+    if (var_InheritBool(filter, MMAL_DEINTERLACE_FAST))
4685+    {
4686+        sys->use_fast = true;
4687+        sys->use_passthrough = false;
4688+    }
4689+    if (var_InheritBool(filter, MMAL_DEINTERLACE_NONE))
4690+        sys->use_passthrough = true;
4691+    if (var_InheritBool(filter, MMAL_DEINTERLACE_FULL_RATE))
4692+        sys->half_rate = false;
4693+    if (var_InheritBool(filter, MMAL_DEINTERLACE_HALF_RATE))
4694+        sys->half_rate = true;
4695+
4696+    if (sys->use_passthrough)
4697+    {
4698+        filter->pf_video_filter = pass_deinterlace;
4699+        filter->pf_flush = pass_flush;
4700+        // Don't need VCSM - get rid of it now
4701+        cma_vcsm_exit(sys->vcsm_init_type);
4702+        sys->vcsm_init_type = VCSM_INIT_NONE;
4703+        return 0;
4704+    }
4705+
4706+    {
4707+        char dbuf0[5], dbuf1[5];
4708+        msg_Dbg(filter, "%s: %s,%dx%d [(%d,%d) %d/%d] -> %s,%dx%d [(%d,%d) %dx%d]: %s %s %s", __func__,
4709+                str_fourcc(dbuf0, filter->fmt_in.video.i_chroma),
4710+                filter->fmt_in.video.i_width, filter->fmt_in.video.i_height,
4711+                filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset,
4712+                filter->fmt_in.video.i_visible_width, filter->fmt_in.video.i_visible_height,
4713+                str_fourcc(dbuf1, filter->fmt_out.video.i_chroma),
4714+                filter->fmt_out.video.i_width, filter->fmt_out.video.i_height,
4715+                filter->fmt_out.video.i_x_offset, filter->fmt_out.video.i_y_offset,
4716+                filter->fmt_out.video.i_visible_width, filter->fmt_out.video.i_visible_height,
4717+                sys->use_qpu ? "QPU" : "VPU",
4718+                sys->use_fast ? "FAST" : "ADV",
4719+                sys->use_passthrough ? "PASS" : sys->half_rate ? "HALF" : "FULL");
4720     }
4721-    picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate;
4722-    picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base;
4723
4724-    buffer = picture->p_sys->buffer;
4725-    buffer->user_data = picture;
4726-    buffer->cmd = 0;
4727+    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
4728+    if (status != MMAL_SUCCESS) {
4729+        msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
4730+                MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4731+        goto fail;
4732+    }
4733
4734-    mmal_picture_lock(picture);
4735+    {
4736+        const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
4737+            { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
4738+            sys->use_fast ?
4739+                MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST :
4740+                MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
4741+            4,
4742+            { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu }
4743+        };
4744
4745-    status = mmal_port_send_buffer(sys->output, buffer);
4746+        status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
4747+        if (status != MMAL_SUCCESS) {
4748+            msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
4749+                    MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4750+            goto fail;
4751+        }
4752+    }
4753+
4754+    sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4755+    status = mmal_port_enable(sys->component->control, control_port_cb);
4756     if (status != MMAL_SUCCESS) {
4757-        msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)",
4758-                status, mmal_status_to_string(status));
4759-        mmal_buffer_header_release(buffer);
4760-        picture_Release(picture);
4761-        ret = -1;
4762-    } else {
4763-        atomic_fetch_add(&sys->output_in_transit, 1);
4764-        vlc_sem_post(&sys->sem);
4765+        msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
4766+                sys->component->control->name, status, mmal_status_to_string(status));
4767+        goto fail;
4768     }
4769
4770-out:
4771-    return ret;
4772-}
4773+    sys->input = sys->component->input[0];
4774+    sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4775+    sys->input->format->encoding = vlc_to_mmal_video_fourcc(&filter->fmt_in.video);
4776+    hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &filter->fmt_in.video);
4777
4778-static void fill_output_port(filter_t *filter)
4779-{
4780-    filter_sys_t *sys = filter->p_sys;
4781-    /* allow at least 2 buffers in transit */
4782-    unsigned max_buffers_in_transit = __MAX(2, MIN_NUM_BUFFERS_IN_TRANSIT);
4783-    int buffers_available = sys->output->buffer_num -
4784-        atomic_load(&sys->output_in_transit) -
4785-        mmal_queue_length(sys->filtered_pictures);
4786-    int buffers_to_send = max_buffers_in_transit - sys->output_in_transit;
4787-    int i;
4788+    es_format_Copy(&filter->fmt_out, &filter->fmt_in);
4789+    if (!sys->half_rate)
4790+        filter->fmt_out.video.i_frame_rate *= 2;
4791
4792-    if (buffers_to_send > buffers_available)
4793-        buffers_to_send = buffers_available;
4794+    status = mmal_port_format_commit(sys->input);
4795+    if (status != MMAL_SUCCESS) {
4796+        msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
4797+                        sys->input->name, status, mmal_status_to_string(status));
4798+        goto fail;
4799+    }
4800+    sys->input->buffer_size = sys->input->buffer_size_recommended;
4801+    sys->input->buffer_num = 30;
4802+//    sys->input->buffer_num = sys->input->buffer_num_recommended;
4803
4804-#ifndef NDEBUG
4805-    msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
4806-                    buffers_to_send, buffers_available, sys->output_in_transit,
4807-                    sys->output->buffer_num);
4808-#endif
4809-    for (i = 0; i < buffers_to_send; ++i) {
4810-        if (send_output_buffer(filter) < 0)
4811-            break;
4812+    if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
4813+    {
4814+        msg_Err(filter, "Failed to create input pool");
4815+        goto fail;
4816     }
4817-}
4818
4819-static picture_t *deinterlace(filter_t *filter, picture_t *picture)
4820-{
4821-    filter_sys_t *sys = filter->p_sys;
4822-    MMAL_BUFFER_HEADER_T *buffer;
4823-    picture_t *out_picture = NULL;
4824-    picture_t *ret = NULL;
4825-    MMAL_STATUS_T status;
4826-    unsigned i = 0;
4827+    status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
4828+    if (status != MMAL_SUCCESS) {
4829+       msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
4830+                sys->input->name, status, mmal_status_to_string(status));
4831+       goto fail;
4832+    }
4833
4834-    fill_output_port(filter);
4835+    status = mmal_port_enable(sys->input, di_input_port_cb);
4836+    if (status != MMAL_SUCCESS) {
4837+        msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
4838+                sys->input->name, status, mmal_status_to_string(status));
4839+        goto fail;
4840+    }
4841
4842-    buffer = picture->p_sys->buffer;
4843-    buffer->user_data = picture;
4844-    buffer->pts = picture->date;
4845-    buffer->cmd = 0;
4846
4847-    if (!picture->p_sys->displayed) {
4848-        status = mmal_port_send_buffer(sys->input, buffer);
4849-        if (status != MMAL_SUCCESS) {
4850-            msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)",
4851-                    status, mmal_status_to_string(status));
4852-            picture_Release(picture);
4853-        } else {
4854-            picture->p_sys->displayed = true;
4855-            atomic_fetch_add(&sys->input_in_transit, 1);
4856-            vlc_sem_post(&sys->sem);
4857-        }
4858-    } else {
4859-        picture_Release(picture);
4860-    }
4861-
4862-    /*
4863-     * Send output buffers
4864-     */
4865-    while(atomic_load(&sys->started) && i < 2) {
4866-        if (buffer = mmal_queue_timedwait(sys->filtered_pictures, 2000)) {
4867-            i++;
4868-            if (!out_picture) {
4869-                out_picture = (picture_t *)buffer->user_data;
4870-                ret = out_picture;
4871-            } else {
4872-                out_picture->p_next = (picture_t *)buffer->user_data;
4873-                out_picture = out_picture->p_next;
4874-            }
4875-            out_picture->date = buffer->pts;
4876-        } else {
4877-            msg_Dbg(filter, "Failed waiting for filtered picture");
4878-            break;
4879-        }
4880+    if ((sys->out_q = mmal_queue_create()) == NULL)
4881+    {
4882+        msg_Err(filter, "Failed to create out Q");
4883+        goto fail;
4884     }
4885-    if (out_picture)
4886-        out_picture->p_next = NULL;
4887
4888-    return ret;
4889-}
4890-
4891-static void flush(filter_t *filter)
4892-{
4893-    filter_sys_t *sys = filter->p_sys;
4894-    MMAL_BUFFER_HEADER_T *buffer;
4895+    sys->output = sys->component->output[0];
4896+    mmal_format_full_copy(sys->output->format, sys->input->format);
4897
4898-    msg_Dbg(filter, "flush deinterlace filter");
4899+    if (!sys->is_cma)
4900+    {
4901+        if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS)
4902+            goto fail;
4903+    }
4904+    else
4905+    {
4906+        // CMA stuff
4907+        sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4908+
4909+        if ((sys->cma_out_pool = cma_buf_pool_new(8, 8, true, "deinterlace")) == NULL)
4910+        {
4911+            msg_Err(filter, "Failed to alloc cma buf pool");
4912+            goto fail;
4913+        }
4914
4915-    msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)",
4916-            sys->input_in_transit, sys->output_in_transit);
4917-    mmal_port_flush(sys->output);
4918-    mmal_port_flush(sys->input);
4919-
4920-    msg_Dbg(filter, "flush: wait for all buffers to be returned");
4921-    while (atomic_load(&sys->input_in_transit) ||
4922-            atomic_load(&sys->output_in_transit))
4923-        vlc_sem_wait(&sys->sem);
4924-
4925-    while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
4926-        picture_t *pic = (picture_t *)buffer->user_data;
4927-        msg_Dbg(filter, "flush: release already filtered pic %p",
4928-                (void *)pic);
4929-        picture_Release(pic);
4930-    }
4931-    atomic_store(&sys->started, false);
4932-    msg_Dbg(filter, "flush: done");
4933-}
4934+        // Rate control done by CMA in flight logic, so have "inexhaustable" pool here
4935+        if ((sys->out_pool = mmal_pool_create(30, 0)) == NULL)
4936+        {
4937+            msg_Err(filter, "Failed to alloc out pool");
4938+            goto fail;
4939+        }
4940
4941-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4942-{
4943-    filter_t *filter = (filter_t *)port->userdata;
4944-    MMAL_STATUS_T status;
4945+        port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, true);
4946
4947-    if (buffer->cmd == MMAL_EVENT_ERROR) {
4948-        status = *(uint32_t *)buffer->data;
4949-        msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
4950-                mmal_status_to_string(status));
4951-    }
4952+        if ((status = mmal_port_format_commit(sys->output)) != MMAL_SUCCESS)
4953+        {
4954+            msg_Err(filter, "Output port format commit failed");
4955+            goto fail;
4956+        }
4957
4958-    mmal_buffer_header_release(buffer);
4959-}
4960+        sys->output->buffer_num = 30;
4961+        sys->output->buffer_size = sys->output->buffer_size_recommended;
4962
4963-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4964-{
4965-    picture_t *picture = (picture_t *)buffer->user_data;
4966-    filter_t *filter = (filter_t *)port->userdata;
4967-    filter_sys_t *sys = filter->p_sys;
4968+        // CB just drops all bufs into out_q
4969+        if ((status = mmal_port_enable(sys->output, di_output_port_cb)) != MMAL_SUCCESS)
4970+        {
4971+            msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
4972+                    sys->output->name, status, mmal_status_to_string(status));
4973+            goto fail;
4974+        }
4975+    }
4976
4977-    if (picture) {
4978-        picture_Release(picture);
4979-    } else {
4980-        msg_Warn(filter, "Got buffer without picture on input port - OOOPS");
4981-        mmal_buffer_header_release(buffer);
4982+    status = mmal_component_enable(sys->component);
4983+    if (status != MMAL_SUCCESS) {
4984+        msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
4985+                sys->component->name, status, mmal_status_to_string(status));
4986+        goto fail;
4987     }
4988
4989-    atomic_fetch_sub(&sys->input_in_transit, 1);
4990-    vlc_sem_post(&sys->sem);
4991+    filter->pf_video_filter = deinterlace;
4992+    filter->pf_flush = di_flush;
4993+    return 0;
4994+
4995+fail:
4996+    CloseMmalDeinterlace(filter);
4997+    return ret;
4998 }
4999
5000-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
5001-{
5002-    filter_t *filter = (filter_t *)port->userdata;
5003-    filter_sys_t *sys = filter->p_sys;
5004-    picture_t *picture;
5005+vlc_module_begin()
5006+    set_shortname(N_("MMAL deinterlace"))
5007+    set_description(N_("MMAL-based deinterlace filter plugin"))
5008+    set_capability("video filter", 900)
5009+    set_category(CAT_VIDEO)
5010+    set_subcategory(SUBCAT_VIDEO_VFILTER)
5011+    set_callbacks(OpenMmalDeinterlace, CloseMmalDeinterlace)
5012+    add_shortcut("deinterlace")
5013+    add_bool(MMAL_DEINTERLACE_NO_QPU, false, MMAL_DEINTERLACE_NO_QPU_TEXT,
5014+                    MMAL_DEINTERLACE_NO_QPU_LONGTEXT, true);
5015+    add_bool(MMAL_DEINTERLACE_ADV, false, MMAL_DEINTERLACE_ADV_TEXT,
5016+                    MMAL_DEINTERLACE_ADV_LONGTEXT, true);
5017+    add_bool(MMAL_DEINTERLACE_FAST, false, MMAL_DEINTERLACE_FAST_TEXT,
5018+                    MMAL_DEINTERLACE_FAST_LONGTEXT, true);
5019+    add_bool(MMAL_DEINTERLACE_NONE, false, MMAL_DEINTERLACE_NONE_TEXT,
5020+                    MMAL_DEINTERLACE_NONE_LONGTEXT, true);
5021+    add_bool(MMAL_DEINTERLACE_HALF_RATE, false, MMAL_DEINTERLACE_HALF_RATE_TEXT,
5022+                    MMAL_DEINTERLACE_HALF_RATE_LONGTEXT, true);
5023+    add_bool(MMAL_DEINTERLACE_FULL_RATE, false, MMAL_DEINTERLACE_FULL_RATE_TEXT,
5024+                    MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true);
5025+
5026+vlc_module_end()
5027+
5028
5029-    if (buffer->cmd == 0) {
5030-        if (buffer->length > 0) {
5031-            atomic_store(&sys->started, true);
5032-            mmal_queue_put(sys->filtered_pictures, buffer);
5033-            picture = (picture_t *)buffer->user_data;
5034-        } else {
5035-            picture = (picture_t *)buffer->user_data;
5036-            picture_Release(picture);
5037-        }
5038-
5039-        atomic_fetch_sub(&sys->output_in_transit, 1);
5040-        vlc_sem_post(&sys->sem);
5041-    } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
5042-        msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled");
5043-        mmal_buffer_header_release(buffer);
5044-    } else {
5045-        mmal_buffer_header_release(buffer);
5046-    }
5047-}
5048--- /dev/null
5049+++ b/modules/hw/mmal/mmal_avcodec.c
5050@@ -0,0 +1,2175 @@
5051+/*****************************************************************************
5052+ * video.c: video decoder using the libavcodec library
5053+ *****************************************************************************
5054+ * Copyright (C) 1999-2001 VLC authors and VideoLAN
5055+ * $Id$
5056+ *
5057+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>
5058+ *          Gildas Bazin <gbazin@videolan.org>
5059+ *
5060+ * This program is free software; you can redistribute it and/or modify it
5061+ * under the terms of the GNU Lesser General Public License as published by
5062+ * the Free Software Foundation; either version 2.1 of the License, or
5063+ * (at your option) any later version.
5064+ *
5065+ * This program is distributed in the hope that it will be useful,
5066+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
5067+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5068+ * GNU Lesser General Public License for more details.
5069+ *
5070+ * You should have received a copy of the GNU Lesser General Public License
5071+ * along with this program; if not, write to the Free Software Foundation,
5072+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
5073+ *****************************************************************************/
5074+
5075+/*****************************************************************************
5076+ * Preamble
5077+ *****************************************************************************/
5078+#include "config.h"
5079+
5080+#include <vlc_common.h>
5081+#include <vlc_codec.h>
5082+#include <vlc_avcodec.h>
5083+#include <vlc_cpu.h>
5084+#include <vlc_atomic.h>
5085+#include <assert.h>
5086+
5087+#include <libavcodec/avcodec.h>
5088+#include <libavutil/mem.h>
5089+#include <libavutil/pixdesc.h>
5090+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
5091+#include <libavutil/mastering_display_metadata.h>
5092+#endif
5093+
5094+//#include "avcodec.h"
5095+//#include "va.h"
5096+
5097+#include <vlc_plugin.h>
5098+#include <libavutil/rpi_sand_fns.h>
5099+#include <libavcodec/rpi_zc.h>
5100+#include "../../codec/cc.h"
5101+#include "../../codec/avcodec/avcommon.h"  // ??? Beware over inclusion
5102+#include "mmal_cma.h"
5103+#include "mmal_picture.h"
5104+
5105+#define TRACE_ALL 0
5106+
5107+#define BUFFERS_IN_FLIGHT       5       // Default max value for in flight buffers
5108+#define BUFFERS_IN_FLIGHT_UHD   3       // Fewer if very big
5109+
5110+#define MMAL_AVCODEC_BUFFERS "mmal-avcodec-buffers"
5111+#define MMAL_AVCODEC_BUFFERS_TEXT N_("In flight buffer count before blocking.")
5112+#define MMAL_AVCODEC_BUFFERS_LONGTEXT N_("In flight buffer count before blocking. " \
5113+"Beware that incautious changing of this can lead to lockup. " \
5114+"Zero will disable the module.")
5115+
5116+
5117+// Fwd declarations required due to wanting to avoid reworking the original
5118+// code too much
5119+static void MmalAvcodecCloseDecoder( vlc_object_t *obj );
5120+
5121+
5122+/*****************************************************************************
5123+ * decoder_sys_t : decoder descriptor
5124+ *****************************************************************************/
5125+struct decoder_sys_t
5126+{
5127+    AVCodecContext *p_context;
5128+    const AVCodec  *p_codec;
5129+
5130+    /* Video decoder specific part */
5131+    date_t  pts;
5132+
5133+    /* Closed captions for decoders */
5134+    cc_data_t cc;
5135+
5136+    /* for frame skipping algo */
5137+    bool b_hurry_up;
5138+    bool b_show_corrupted;
5139+    bool b_from_preroll;
5140+    enum AVDiscard i_skip_frame;
5141+
5142+    /* how many decoded frames are late */
5143+    int     i_late_frames;
5144+    mtime_t i_late_frames_start;
5145+    mtime_t i_last_late_delay;
5146+
5147+    /* for direct rendering */
5148+    bool        b_direct_rendering;
5149+    atomic_bool b_dr_failure;
5150+
5151+    /* Hack to force display of still pictures */
5152+    bool b_first_frame;
5153+
5154+
5155+    /* */
5156+    bool palette_sent;
5157+
5158+    /* VA API */
5159+//    vlc_va_t *p_va;
5160+    enum PixelFormat pix_fmt;
5161+    int profile;
5162+    int level;
5163+
5164+    vlc_sem_t sem_mt;
5165+
5166+    // Rpi vars
5167+    cma_buf_pool_t * cma_pool;
5168+    bool pool_alloc_1;
5169+    vcsm_init_type_t vcsm_init_type;
5170+    int cma_in_flight_max;
5171+    // Debug
5172+    decoder_t * p_dec;
5173+};
5174+
5175+
5176+static vlc_fourcc_t
5177+ZcFindVlcChroma(const int i_ffmpeg_chroma)
5178+{
5179+    switch (i_ffmpeg_chroma)
5180+    {
5181+        // This is all we claim to deal with
5182+        // In theory RGB should be doable within our current framework
5183+        case AV_PIX_FMT_YUV420P:
5184+            return VLC_CODEC_MMAL_ZC_I420;
5185+        case AV_PIX_FMT_SAND128:
5186+        case AV_PIX_FMT_RPI4_8:
5187+            return VLC_CODEC_MMAL_ZC_SAND8;
5188+        case AV_PIX_FMT_SAND64_10:
5189+            return VLC_CODEC_MMAL_ZC_SAND10;
5190+        case AV_PIX_FMT_RPI4_10:
5191+            return VLC_CODEC_MMAL_ZC_SAND30;
5192+        default:
5193+            break;
5194+    }
5195+    return 0;
5196+}
5197+
5198+// Pix Fmt conv for MMal
5199+// video_fromat from ffmpeg pic_fmt
5200+static int
5201+ZcGetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma )
5202+{
5203+    fmt->i_rmask = 0;
5204+    fmt->i_gmask = 0;
5205+    fmt->i_bmask = 0;
5206+    fmt->i_chroma = ZcFindVlcChroma(i_ffmpeg_chroma);
5207+
5208+    return fmt->i_chroma == 0 ? -1 : 0;
5209+}
5210+
5211+
5212+// Format chooser is way simpler than vlc
5213+static enum PixelFormat
5214+ZcGetFormat(AVCodecContext *p_context, const enum PixelFormat *pi_fmt)
5215+{
5216+    enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt);
5217+    for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++)
5218+    {
5219+        if (ZcFindVlcChroma(pi_fmt[i]) != 0)
5220+            return pi_fmt[i];
5221+    }
5222+    return swfmt;
5223+}
5224+
5225+
5226+static void cma_avbuf_pool_free(void * v)
5227+{
5228+    cma_buf_unref(v);
5229+}
5230+
5231+static unsigned int zc_buf_vcsm_handle(void * v)
5232+{
5233+    return cma_buf_vcsm_handle(v);
5234+}
5235+
5236+static unsigned int zc_buf_vc_handle(void * v)
5237+{
5238+    return cma_buf_vc_handle(v);
5239+}
5240+
5241+static void * zc_buf_map_arm(void * v)
5242+{
5243+    return cma_buf_addr(v);
5244+}
5245+
5246+static unsigned int zc_buf_map_vc(void * v)
5247+{
5248+    return cma_buf_vc_addr(v);
5249+}
5250+
5251+
5252+
5253+static const av_rpi_zc_buf_fn_tab_t zc_buf_fn_tab = {
5254+    .free = cma_avbuf_pool_free,
5255+
5256+    .vcsm_handle = zc_buf_vcsm_handle,
5257+    .vc_handle = zc_buf_vc_handle,
5258+    .map_arm = zc_buf_map_arm,
5259+    .map_vc = zc_buf_map_vc
5260+};
5261+
5262+
5263+static AVBufferRef *
5264+zc_alloc_buf(void * v, size_t size, const AVRpiZcFrameGeometry * geo)
5265+{
5266+    decoder_t * const dec = v;
5267+    decoder_sys_t * const sys = dec->p_sys;
5268+
5269+    VLC_UNUSED(geo);
5270+
5271+    assert(sys != NULL);
5272+
5273+    const unsigned int dec_pool_req = av_rpi_zc_get_decoder_pool_size(sys->p_context->opaque);
5274+    if (dec_pool_req != 0)
5275+    {
5276+        cma_buf_pool_resize(sys->cma_pool, dec_pool_req + sys->cma_in_flight_max, sys->cma_in_flight_max);
5277+
5278+        if (!sys->pool_alloc_1)
5279+        {
5280+            sys->pool_alloc_1 = true;
5281+            msg_Dbg(dec, "Pool size: (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size);
5282+            if (cma_buf_pool_fill(sys->cma_pool, size) != 0)
5283+                msg_Warn(dec, "Failed to preallocate decoder pool (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size);
5284+        }
5285+    }
5286+
5287+    void * const cmabuf = cma_buf_pool_alloc_buf(sys->cma_pool, size);
5288+
5289+    if (cmabuf == NULL)
5290+    {
5291+        msg_Err(dec, "CMA buf pool alloc buf failed");
5292+        return NULL;
5293+    }
5294+
5295+    AVBufferRef *const avbuf = av_rpi_zc_buf(cma_buf_size(cmabuf), 0, cmabuf, &zc_buf_fn_tab);
5296+
5297+    if (avbuf == NULL)
5298+    {
5299+        msg_Err(dec, "av_rpi_zc_buf failed");
5300+        cma_buf_unref(cmabuf);
5301+        return NULL;
5302+    }
5303+
5304+    return avbuf;
5305+}
5306+
5307+static void
5308+zc_free_pool(void * v)
5309+{
5310+    decoder_t * const dec = v;
5311+    cma_buf_pool_delete(dec->p_sys->cma_pool);
5312+}
5313+
5314+
5315+static const uint8_t shift_01[] = {0,1,1,1};
5316+static const uint8_t pb_1[] = {1,1,1,1};
5317+static const uint8_t pb_12[] = {1,2,2,2};
5318+static const uint8_t pb_24[] = {2,4,4,4};
5319+static const uint8_t pb_4[] = {4,4,4,4};
5320+
5321+static int set_pic_from_frame(picture_t * const pic, const AVFrame * const frame)
5322+{
5323+    const uint8_t * hs = shift_01;
5324+    const uint8_t * ws = shift_01;
5325+    const uint8_t * pb = pb_1;
5326+
5327+    switch (pic->format.i_chroma)
5328+    {
5329+        case VLC_CODEC_MMAL_ZC_RGB32:
5330+            pic->i_planes = 1;
5331+            pb = pb_4;
5332+            break;
5333+        case VLC_CODEC_MMAL_ZC_I420:
5334+            pic->i_planes = 3;
5335+            break;
5336+        case VLC_CODEC_MMAL_ZC_SAND8:
5337+            pic->i_planes = 2;
5338+            pb = pb_12;
5339+            break;
5340+        case VLC_CODEC_MMAL_ZC_SAND10:
5341+        case VLC_CODEC_MMAL_ZC_SAND30:  // Lies: SAND30 is "special"
5342+            pic->i_planes = 2;
5343+            pb = pb_24;
5344+            break;
5345+        default:
5346+            return VLC_EGENERIC;
5347+    }
5348+
5349+    const cma_buf_t * const cb = cma_buf_pic_get(pic);
5350+    uint8_t * const data = cma_buf_addr(cb);
5351+    if (data == NULL) {
5352+        return VLC_ENOMEM;
5353+    }
5354+
5355+    uint8_t * frame_end = frame->data[0] + cma_buf_size(cb);
5356+    for (int i = 0; i != pic->i_planes; ++i) {
5357+        // Calculate lines from gap between planes
5358+        // This will give us an accurate "height" for later use by MMAL
5359+        const int lines = ((i + 1 == pic->i_planes ? frame_end : frame->data[i + 1]) -
5360+                           frame->data[i]) / frame->linesize[i];
5361+        pic->p[i] = (plane_t){
5362+            .p_pixels = data + (frame->data[i] - frame->data[0]),
5363+            .i_lines = lines,
5364+            .i_pitch = frame->linesize[i],
5365+            .i_pixel_pitch = pb[i],
5366+            .i_visible_lines = av_frame_cropped_height(frame) >> hs[i],
5367+            .i_visible_pitch = av_frame_cropped_width(frame) >> ws[i]
5368+        };
5369+    }
5370+    return 0;
5371+}
5372+
5373+
5374+//============================================================================
5375+//
5376+// Nicked from avcodec/fourcc.c
5377+//
5378+// * Really we should probably use that directly
5379+
5380+/*
5381+ * Video Codecs
5382+ */
5383+
5384+struct vlc_avcodec_fourcc
5385+{
5386+    vlc_fourcc_t i_fourcc;
5387+    unsigned i_codec;
5388+};
5389+
5390+
5391+static const struct vlc_avcodec_fourcc video_codecs[] =
5392+{
5393+    { VLC_CODEC_MP1V, AV_CODEC_ID_MPEG1VIDEO },
5394+    { VLC_CODEC_MP2V, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */
5395+    { VLC_CODEC_MPGV, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */
5396+    /* AV_CODEC_ID_MPEG2VIDEO_XVMC */
5397+    { VLC_CODEC_H261, AV_CODEC_ID_H261 },
5398+    { VLC_CODEC_H263, AV_CODEC_ID_H263 },
5399+    { VLC_CODEC_RV10, AV_CODEC_ID_RV10 },
5400+    { VLC_CODEC_RV13, AV_CODEC_ID_RV10 },
5401+    { VLC_CODEC_RV20, AV_CODEC_ID_RV20 },
5402+    { VLC_CODEC_MJPG, AV_CODEC_ID_MJPEG },
5403+    { VLC_CODEC_MJPGB, AV_CODEC_ID_MJPEGB },
5404+    { VLC_CODEC_LJPG, AV_CODEC_ID_LJPEG },
5405+    { VLC_CODEC_SP5X, AV_CODEC_ID_SP5X },
5406+    { VLC_CODEC_JPEGLS, AV_CODEC_ID_JPEGLS },
5407+    { VLC_CODEC_MP4V, AV_CODEC_ID_MPEG4 },
5408+    /* AV_CODEC_ID_RAWVIDEO */
5409+    { VLC_CODEC_DIV1, AV_CODEC_ID_MSMPEG4V1 },
5410+    { VLC_CODEC_DIV2, AV_CODEC_ID_MSMPEG4V2 },
5411+    { VLC_CODEC_DIV3, AV_CODEC_ID_MSMPEG4V3 },
5412+    { VLC_CODEC_WMV1, AV_CODEC_ID_WMV1 },
5413+    { VLC_CODEC_WMV2, AV_CODEC_ID_WMV2 },
5414+    { VLC_CODEC_H263P, AV_CODEC_ID_H263P },
5415+    { VLC_CODEC_H263I, AV_CODEC_ID_H263I },
5416+    { VLC_CODEC_FLV1, AV_CODEC_ID_FLV1 },
5417+    { VLC_CODEC_SVQ1, AV_CODEC_ID_SVQ1 },
5418+    { VLC_CODEC_SVQ3, AV_CODEC_ID_SVQ3 },
5419+    { VLC_CODEC_DV, AV_CODEC_ID_DVVIDEO },
5420+    { VLC_CODEC_HUFFYUV, AV_CODEC_ID_HUFFYUV },
5421+    { VLC_CODEC_CYUV, AV_CODEC_ID_CYUV },
5422+    { VLC_CODEC_H264, AV_CODEC_ID_H264 },
5423+    { VLC_CODEC_INDEO3, AV_CODEC_ID_INDEO3 },
5424+    { VLC_CODEC_VP3, AV_CODEC_ID_VP3 },
5425+    { VLC_CODEC_THEORA, AV_CODEC_ID_THEORA },
5426+#if ( !defined( WORDS_BIGENDIAN ) )
5427+    /* Asus Video (Another thing that doesn't work on PPC) */
5428+    { VLC_CODEC_ASV1, AV_CODEC_ID_ASV1 },
5429+    { VLC_CODEC_ASV2, AV_CODEC_ID_ASV2 },
5430+#endif
5431+    { VLC_CODEC_FFV1, AV_CODEC_ID_FFV1 },
5432+    { VLC_CODEC_4XM, AV_CODEC_ID_4XM },
5433+    { VLC_CODEC_VCR1, AV_CODEC_ID_VCR1 },
5434+    { VLC_CODEC_CLJR, AV_CODEC_ID_CLJR },
5435+    { VLC_CODEC_MDEC, AV_CODEC_ID_MDEC },
5436+    { VLC_CODEC_ROQ, AV_CODEC_ID_ROQ },
5437+    { VLC_CODEC_INTERPLAY, AV_CODEC_ID_INTERPLAY_VIDEO },
5438+    { VLC_CODEC_XAN_WC3, AV_CODEC_ID_XAN_WC3 },
5439+    { VLC_CODEC_XAN_WC4, AV_CODEC_ID_XAN_WC4 },
5440+    { VLC_CODEC_RPZA, AV_CODEC_ID_RPZA },
5441+    { VLC_CODEC_CINEPAK, AV_CODEC_ID_CINEPAK },
5442+    { VLC_CODEC_WS_VQA, AV_CODEC_ID_WS_VQA },
5443+    { VLC_CODEC_MSRLE, AV_CODEC_ID_MSRLE },
5444+    { VLC_CODEC_MSVIDEO1, AV_CODEC_ID_MSVIDEO1 },
5445+    { VLC_CODEC_IDCIN, AV_CODEC_ID_IDCIN },
5446+    { VLC_CODEC_8BPS, AV_CODEC_ID_8BPS },
5447+    { VLC_CODEC_SMC, AV_CODEC_ID_SMC },
5448+    { VLC_CODEC_FLIC, AV_CODEC_ID_FLIC },
5449+    { VLC_CODEC_TRUEMOTION1, AV_CODEC_ID_TRUEMOTION1 },
5450+    { VLC_CODEC_VMDVIDEO, AV_CODEC_ID_VMDVIDEO },
5451+    { VLC_CODEC_LCL_MSZH, AV_CODEC_ID_MSZH },
5452+    { VLC_CODEC_LCL_ZLIB, AV_CODEC_ID_ZLIB },
5453+    { VLC_CODEC_QTRLE, AV_CODEC_ID_QTRLE },
5454+    { VLC_CODEC_TSCC, AV_CODEC_ID_TSCC },
5455+    { VLC_CODEC_ULTI, AV_CODEC_ID_ULTI },
5456+    { VLC_CODEC_QDRAW, AV_CODEC_ID_QDRAW },
5457+    { VLC_CODEC_VIXL, AV_CODEC_ID_VIXL },
5458+    { VLC_CODEC_QPEG, AV_CODEC_ID_QPEG },
5459+    { VLC_CODEC_PNG, AV_CODEC_ID_PNG },
5460+    { VLC_CODEC_PPM, AV_CODEC_ID_PPM },
5461+    /* AV_CODEC_ID_PBM */
5462+    { VLC_CODEC_PGM, AV_CODEC_ID_PGM },
5463+    { VLC_CODEC_PGMYUV, AV_CODEC_ID_PGMYUV },
5464+    { VLC_CODEC_PAM, AV_CODEC_ID_PAM },
5465+    { VLC_CODEC_FFVHUFF, AV_CODEC_ID_FFVHUFF },
5466+    { VLC_CODEC_RV30, AV_CODEC_ID_RV30 },
5467+    { VLC_CODEC_RV40, AV_CODEC_ID_RV40 },
5468+    { VLC_CODEC_VC1,  AV_CODEC_ID_VC1 },
5469+    { VLC_CODEC_WMVA, AV_CODEC_ID_VC1 },
5470+    { VLC_CODEC_WMV3, AV_CODEC_ID_WMV3 },
5471+    { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3 },
5472+    { VLC_CODEC_LOCO, AV_CODEC_ID_LOCO },
5473+    { VLC_CODEC_WNV1, AV_CODEC_ID_WNV1 },
5474+    { VLC_CODEC_AASC, AV_CODEC_ID_AASC },
5475+    { VLC_CODEC_INDEO2, AV_CODEC_ID_INDEO2 },
5476+    { VLC_CODEC_FRAPS, AV_CODEC_ID_FRAPS },
5477+    { VLC_CODEC_TRUEMOTION2, AV_CODEC_ID_TRUEMOTION2 },
5478+    { VLC_CODEC_BMP, AV_CODEC_ID_BMP },
5479+    { VLC_CODEC_CSCD, AV_CODEC_ID_CSCD },
5480+    { VLC_CODEC_MMVIDEO, AV_CODEC_ID_MMVIDEO },
5481+    { VLC_CODEC_ZMBV, AV_CODEC_ID_ZMBV },
5482+    { VLC_CODEC_AVS, AV_CODEC_ID_AVS },
5483+    { VLC_CODEC_SMACKVIDEO, AV_CODEC_ID_SMACKVIDEO },
5484+    { VLC_CODEC_NUV, AV_CODEC_ID_NUV },
5485+    { VLC_CODEC_KMVC, AV_CODEC_ID_KMVC },
5486+    { VLC_CODEC_FLASHSV, AV_CODEC_ID_FLASHSV },
5487+    { VLC_CODEC_CAVS, AV_CODEC_ID_CAVS },
5488+    { VLC_CODEC_JPEG2000, AV_CODEC_ID_JPEG2000 },
5489+    { VLC_CODEC_VMNC, AV_CODEC_ID_VMNC },
5490+    { VLC_CODEC_VP5, AV_CODEC_ID_VP5 },
5491+    { VLC_CODEC_VP6, AV_CODEC_ID_VP6 },
5492+    { VLC_CODEC_VP6F, AV_CODEC_ID_VP6F },
5493+    { VLC_CODEC_TARGA, AV_CODEC_ID_TARGA },
5494+    { VLC_CODEC_DSICINVIDEO, AV_CODEC_ID_DSICINVIDEO },
5495+    { VLC_CODEC_TIERTEXSEQVIDEO, AV_CODEC_ID_TIERTEXSEQVIDEO },
5496+    { VLC_CODEC_TIFF, AV_CODEC_ID_TIFF },
5497+    { VLC_CODEC_GIF, AV_CODEC_ID_GIF },
5498+    { VLC_CODEC_DXA, AV_CODEC_ID_DXA },
5499+    { VLC_CODEC_DNXHD, AV_CODEC_ID_DNXHD },
5500+    { VLC_CODEC_THP, AV_CODEC_ID_THP },
5501+    { VLC_CODEC_SGI, AV_CODEC_ID_SGI },
5502+    { VLC_CODEC_C93, AV_CODEC_ID_C93 },
5503+    { VLC_CODEC_BETHSOFTVID, AV_CODEC_ID_BETHSOFTVID },
5504+    /* AV_CODEC_ID_PTX */
5505+    { VLC_CODEC_TXD, AV_CODEC_ID_TXD },
5506+    { VLC_CODEC_VP6A, AV_CODEC_ID_VP6A },
5507+    { VLC_CODEC_AMV, AV_CODEC_ID_AMV },
5508+    { VLC_CODEC_VB, AV_CODEC_ID_VB },
5509+    { VLC_CODEC_PCX, AV_CODEC_ID_PCX },
5510+    /* AV_CODEC_ID_SUNRAST */
5511+    { VLC_CODEC_INDEO4, AV_CODEC_ID_INDEO4 },
5512+    { VLC_CODEC_INDEO5, AV_CODEC_ID_INDEO5 },
5513+    { VLC_CODEC_MIMIC, AV_CODEC_ID_MIMIC },
5514+    { VLC_CODEC_RL2, AV_CODEC_ID_RL2 },
5515+    { VLC_CODEC_ESCAPE124, AV_CODEC_ID_ESCAPE124 },
5516+    { VLC_CODEC_DIRAC, AV_CODEC_ID_DIRAC },
5517+    { VLC_CODEC_BFI, AV_CODEC_ID_BFI },
5518+    { VLC_CODEC_CMV, AV_CODEC_ID_CMV },
5519+    { VLC_CODEC_MOTIONPIXELS, AV_CODEC_ID_MOTIONPIXELS },
5520+    { VLC_CODEC_TGV, AV_CODEC_ID_TGV },
5521+    { VLC_CODEC_TGQ, AV_CODEC_ID_TGQ },
5522+    { VLC_CODEC_TQI, AV_CODEC_ID_TQI },
5523+    { VLC_CODEC_AURA, AV_CODEC_ID_AURA },
5524+    /* AV_CODEC_ID_AURA2 */
5525+    /* AV_CODEC_ID_V210X */
5526+    { VLC_CODEC_TMV, AV_CODEC_ID_TMV },
5527+    { VLC_CODEC_V210, AV_CODEC_ID_V210 },
5528+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 54, 50, 100 ) && LIBAVCODEC_VERSION_MICRO >= 100
5529+    { VLC_CODEC_VUYA, AV_CODEC_ID_AYUV },
5530+#endif
5531+    /* AV_CODEC_ID_DPX */
5532+    { VLC_CODEC_MAD, AV_CODEC_ID_MAD },
5533+    { VLC_CODEC_FRWU, AV_CODEC_ID_FRWU },
5534+    { VLC_CODEC_FLASHSV2, AV_CODEC_ID_FLASHSV2 },
5535+    /* AV_CODEC_ID_CDGRAPHICS */
5536+    /* AV_CODEC_ID_R210 */
5537+    { VLC_CODEC_ANM, AV_CODEC_ID_ANM },
5538+    { VLC_CODEC_BINKVIDEO, AV_CODEC_ID_BINKVIDEO },
5539+    /* AV_CODEC_ID_IFF_ILBM */
5540+    /* AV_CODEC_ID_IFF_BYTERUN1 */
5541+    { VLC_CODEC_KGV1, AV_CODEC_ID_KGV1 },
5542+    { VLC_CODEC_YOP, AV_CODEC_ID_YOP },
5543+    { VLC_CODEC_VP8, AV_CODEC_ID_VP8 },
5544+    /* AV_CODEC_ID_PICTOR */
5545+    /* AV_CODEC_ID_ANSI */
5546+    /* AV_CODEC_ID_A64_MULTI */
5547+    /* AV_CODEC_ID_A64_MULTI5 */
5548+    /* AV_CODEC_ID_R10K */
5549+    { VLC_CODEC_MXPEG, AV_CODEC_ID_MXPEG },
5550+    { VLC_CODEC_LAGARITH, AV_CODEC_ID_LAGARITH },
5551+    { VLC_CODEC_PRORES, AV_CODEC_ID_PRORES },
5552+    { VLC_CODEC_JV, AV_CODEC_ID_JV },
5553+    { VLC_CODEC_DFA, AV_CODEC_ID_DFA },
5554+    { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3IMAGE },
5555+    { VLC_CODEC_WMVP2, AV_CODEC_ID_VC1IMAGE },
5556+    { VLC_CODEC_UTVIDEO, AV_CODEC_ID_UTVIDEO },
5557+    { VLC_CODEC_BMVVIDEO, AV_CODEC_ID_BMV_VIDEO },
5558+    { VLC_CODEC_VBLE, AV_CODEC_ID_VBLE },
5559+    { VLC_CODEC_DXTORY, AV_CODEC_ID_DXTORY },
5560+    /* AV_CODEC_ID_V410 */
5561+    /* AV_CODEC_ID_XWD */
5562+    { VLC_CODEC_CDXL, AV_CODEC_ID_CDXL },
5563+    /* AV_CODEC_ID_XBM */
5564+    /* AV_CODEC_ID_ZEROCODEC */
5565+    { VLC_CODEC_MSS1, AV_CODEC_ID_MSS1 },
5566+    { VLC_CODEC_MSA1, AV_CODEC_ID_MSA1 },
5567+    { VLC_CODEC_TSC2, AV_CODEC_ID_TSCC2 },
5568+    { VLC_CODEC_MTS2, AV_CODEC_ID_MTS2 },
5569+    { VLC_CODEC_CLLC, AV_CODEC_ID_CLLC },
5570+    { VLC_CODEC_MSS2, AV_CODEC_ID_MSS2 },
5571+    { VLC_CODEC_VP9, AV_CODEC_ID_VP9 },
5572+#if LIBAVCODEC_VERSION_CHECK( 57, 26, 0, 83, 101 )
5573+    { VLC_CODEC_AV1, AV_CODEC_ID_AV1 },
5574+#endif
5575+    { VLC_CODEC_ICOD, AV_CODEC_ID_AIC },
5576+    /* AV_CODEC_ID_ESCAPE130 */
5577+    { VLC_CODEC_G2M4, AV_CODEC_ID_G2M },
5578+    { VLC_CODEC_G2M2, AV_CODEC_ID_G2M },
5579+    { VLC_CODEC_G2M3, AV_CODEC_ID_G2M },
5580+    /* AV_CODEC_ID_WEBP */
5581+    { VLC_CODEC_HNM4_VIDEO, AV_CODEC_ID_HNM4_VIDEO },
5582+    { VLC_CODEC_HEVC, AV_CODEC_ID_HEVC },
5583+
5584+    { VLC_CODEC_FIC , AV_CODEC_ID_FIC },
5585+    /* AV_CODEC_ID_ALIAS_PIX */
5586+    /* AV_CODEC_ID_BRENDER_PIX */
5587+    /* AV_CODEC_ID_PAF_VIDEO */
5588+    /* AV_CODEC_ID_EXR */
5589+
5590+    { VLC_CODEC_VP7 , AV_CODEC_ID_VP7 },
5591+    /* AV_CODEC_ID_SANM */
5592+    /* AV_CODEC_ID_SGIRLE */
5593+    /* AV_CODEC_ID_MVC1 */
5594+    /* AV_CODEC_ID_MVC2 */
5595+    { VLC_CODEC_HQX, AV_CODEC_ID_HQX },
5596+
5597+    { VLC_CODEC_TDSC, AV_CODEC_ID_TDSC },
5598+
5599+    { VLC_CODEC_HQ_HQA, AV_CODEC_ID_HQ_HQA },
5600+
5601+    { VLC_CODEC_HAP, AV_CODEC_ID_HAP },
5602+    /* AV_CODEC_ID_DDS */
5603+
5604+    { VLC_CODEC_DXV, AV_CODEC_ID_DXV },
5605+
5606+    /* ffmpeg only: AV_CODEC_ID_BRENDER_PIX */
5607+    /* ffmpeg only: AV_CODEC_ID_Y41P */
5608+    /* ffmpeg only: AV_CODEC_ID_EXR */
5609+    /* ffmpeg only: AV_CODEC_ID_AVRP */
5610+    /* ffmpeg only: AV_CODEC_ID_012V */
5611+    /* ffmpeg only: AV_CODEC_ID_AVUI */
5612+    /* ffmpeg only: AV_CODEC_ID_TARGA_Y216 */
5613+    /* ffmpeg only: AV_CODEC_ID_V308 */
5614+    /* ffmpeg only: AV_CODEC_ID_V408 */
5615+    /* ffmpeg only: AV_CODEC_ID_YUV4 */
5616+    /* ffmpeg only: AV_CODEC_ID_SANM */
5617+    /* ffmpeg only: AV_CODEC_ID_PAF_VIDEO */
5618+    /* ffmpeg only: AV_CODEC_ID_AVRN */
5619+    /* ffmpeg only: AV_CODEC_ID_CPIA */
5620+    /* ffmpeg only: AV_CODEC_ID_XFACE */
5621+    /* ffmpeg only: AV_CODEC_ID_SGIRLE */
5622+    /* ffmpeg only: AV_CODEC_ID_MVC1 */
5623+    /* ffmpeg only: AV_CODEC_ID_MVC2 */
5624+    /* ffmpeg only: AV_CODEC_ID_SNOW */
5625+    /* ffmpeg only: AV_CODEC_ID_SMVJPEG */
5626+
5627+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 24, 102 )
5628+    { VLC_CODEC_CINEFORM, AV_CODEC_ID_CFHD },
5629+#endif
5630+
5631+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 70, 100 )
5632+    { VLC_CODEC_PIXLET, AV_CODEC_ID_PIXLET },
5633+#endif
5634+
5635+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 101 )
5636+    { VLC_CODEC_SPEEDHQ, AV_CODEC_ID_SPEEDHQ },
5637+#endif
5638+
5639+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 79, 100 )
5640+    { VLC_CODEC_FMVC, AV_CODEC_ID_FMVC },
5641+#endif
5642+};
5643+
5644+// *** Really we should probably use GetFfmpegCodec with a pre-kludge for the bits we care about
5645+static bool
5646+ZcGetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc,
5647+                     unsigned *pi_ffmpeg_codec, const char **ppsz_name )
5648+{
5649+    const struct vlc_avcodec_fourcc *base;
5650+    size_t count;
5651+
5652+    base = video_codecs;
5653+    count = ARRAY_SIZE(video_codecs);
5654+    i_fourcc = vlc_fourcc_GetCodec( cat, i_fourcc );
5655+
5656+    for( size_t i = 0; i < count; i++ )
5657+    {
5658+        if( base[i].i_fourcc == i_fourcc )
5659+        {
5660+            if( pi_ffmpeg_codec != NULL )
5661+                *pi_ffmpeg_codec = base[i].i_codec;
5662+            if( ppsz_name )
5663+                *ppsz_name = vlc_fourcc_GetDescription( cat, i_fourcc );
5664+            return true;
5665+        }
5666+    }
5667+    return false;
5668+}
5669+
5670+
5671+
5672+//============================================================================
5673+// Derived from codec/avcodec/avcodec.c
5674+
5675+static AVCodecContext *
5676+ZcFfmpeg_AllocContext( decoder_t *p_dec,
5677+                                     const AVCodec **restrict codecp )
5678+{
5679+    unsigned i_codec_id;
5680+    const char *psz_namecodec;
5681+    const AVCodec *p_codec = NULL;
5682+
5683+    /* *** determine codec type *** */
5684+    if( !ZcGetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec,
5685+                         &i_codec_id, &psz_namecodec ) )
5686+         return NULL;
5687+
5688+    msg_Dbg( p_dec, "using %s %s", AVPROVIDER(LIBAVCODEC), LIBAVCODEC_IDENT );
5689+
5690+    /* Initialization must be done before avcodec_find_decoder() */
5691+    vlc_init_avcodec(VLC_OBJECT(p_dec));
5692+
5693+    /* *** ask ffmpeg for a decoder *** */
5694+    char *psz_decoder = var_InheritString( p_dec, "avcodec-codec" );
5695+    if( psz_decoder != NULL )
5696+    {
5697+        p_codec = avcodec_find_decoder_by_name( psz_decoder );
5698+        if( !p_codec )
5699+            msg_Err( p_dec, "Decoder `%s' not found", psz_decoder );
5700+        else if( p_codec->id != i_codec_id )
5701+        {
5702+            msg_Err( p_dec, "Decoder `%s' can't handle %4.4s",
5703+                    psz_decoder, (char*)&p_dec->fmt_in.i_codec );
5704+            p_codec = NULL;
5705+        }
5706+        free( psz_decoder );
5707+    }
5708+    if( !p_codec )
5709+//        p_codec = avcodec_find_decoder( i_codec_id );
5710+    {
5711+        if( p_dec->fmt_in.i_codec != VLC_CODEC_HEVC )
5712+            p_codec = avcodec_find_decoder(i_codec_id);
5713+        else
5714+        {
5715+            psz_namecodec = rpi_is_model_pi4() ? "hevc" : "hevc_rpi";
5716+            msg_Info(p_dec, "Looking for HEVC decoder '%s'", psz_namecodec);
5717+            p_codec = avcodec_find_decoder_by_name(psz_namecodec);
5718+        }
5719+    }
5720+
5721+    if( !p_codec )
5722+    {
5723+        msg_Dbg( p_dec, "codec not found (%s)", psz_namecodec );
5724+        return NULL;
5725+    }
5726+
5727+    *codecp = p_codec;
5728+
5729+    /* *** get a p_context *** */
5730+    AVCodecContext *avctx = avcodec_alloc_context3(p_codec);
5731+    if( unlikely(avctx == NULL) )
5732+        return NULL;
5733+
5734+    avctx->debug = var_InheritInteger( p_dec, "avcodec-debug" );
5735+    avctx->opaque = p_dec;
5736+    return avctx;
5737+}
5738+
5739+/*****************************************************************************
5740+ * ffmpeg_OpenCodec:
5741+ *****************************************************************************/
5742+
5743+static int
5744+ZcFfmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx,
5745+                      const AVCodec *codec )
5746+{
5747+    char *psz_opts = var_InheritString( p_dec, "avcodec-options" );
5748+    AVDictionary *options = NULL;
5749+    int ret;
5750+
5751+    if (psz_opts) {
5752+        vlc_av_get_options(psz_opts, &options);
5753+        free(psz_opts);
5754+    }
5755+
5756+    if (av_rpi_zc_init2(ctx, p_dec, zc_alloc_buf, zc_free_pool) != 0)
5757+    {
5758+        msg_Err(p_dec, "Failed to init AV ZC");
5759+        return VLC_EGENERIC;
5760+    }
5761+
5762+    vlc_avcodec_lock();
5763+    ret = avcodec_open2( ctx, codec, options ? &options : NULL );
5764+    vlc_avcodec_unlock();
5765+
5766+    AVDictionaryEntry *t = NULL;
5767+    while ((t = av_dict_get(options, "", t, AV_DICT_IGNORE_SUFFIX))) {
5768+        msg_Err( p_dec, "Unknown option \"%s\"", t->key );
5769+    }
5770+    av_dict_free(&options);
5771+
5772+    if( ret < 0 )
5773+    {
5774+        msg_Err( p_dec, "cannot start codec (%s)", codec->name );
5775+        return VLC_EGENERIC;
5776+    }
5777+
5778+    msg_Dbg( p_dec, "codec (%s) started", codec->name );
5779+    return VLC_SUCCESS;
5780+}
5781+
5782+//============================================================================
5783+// Derived from 3.0.7.1 codec/avcodec/video.c
5784+
5785+static inline void wait_mt(decoder_sys_t *sys)
5786+{
5787+#if 1
5788+    // As we only ever update the output in our main thread this lock is
5789+    // redundant
5790+    VLC_UNUSED(sys);
5791+#else
5792+    vlc_sem_wait(&sys->sem_mt);
5793+#endif
5794+}
5795+
5796+static inline void post_mt(decoder_sys_t *sys)
5797+{
5798+#if 1
5799+    // As we only ever update the output in our main thread this lock is
5800+    // redundant
5801+    VLC_UNUSED(sys);
5802+#else
5803+    vlc_sem_post(&sys->sem_mt);
5804+#endif
5805+}
5806+
5807+/*****************************************************************************
5808+ * Local prototypes
5809+ *****************************************************************************/
5810+static void ffmpeg_InitCodec      ( decoder_t * );
5811+static int  DecodeVideo( decoder_t *, block_t * );
5812+static void Flush( decoder_t * );
5813+
5814+static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc )
5815+{
5816+    uint8_t *p = (uint8_t*)&fcc;
5817+    return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
5818+}
5819+
5820+/*****************************************************************************
5821+ * Local Functions
5822+ *****************************************************************************/
5823+
5824+/**
5825+ * Sets the decoder output format.
5826+ */
5827+static int lavc_GetVideoFormat(decoder_t *dec, video_format_t *restrict fmt,
5828+                               AVCodecContext *ctx, enum AVPixelFormat pix_fmt,
5829+                               enum AVPixelFormat sw_pix_fmt)
5830+{
5831+    int width = ctx->coded_width;
5832+    int height = ctx->coded_height;
5833+
5834+    video_format_Init(fmt, 0);
5835+
5836+#if 1
5837+    VLC_UNUSED(sw_pix_fmt);
5838+    if ((fmt->i_chroma = ZcFindVlcChroma(pix_fmt)) == 0)
5839+        return -1;
5840+#else
5841+    if (pix_fmt == sw_pix_fmt)
5842+    {   /* software decoding */
5843+        int aligns[AV_NUM_DATA_POINTERS];
5844+
5845+        if (GetVlcChroma(fmt, pix_fmt))
5846+            return -1;
5847+
5848+        /* The libavcodec palette can only be fetched when the first output
5849+         * frame is decoded. Assume that the current chroma is RGB32 while we
5850+         * are waiting for a valid palette. Indeed, fmt_out.video.p_palette
5851+         * doesn't trigger a new vout request, but a new chroma yes. */
5852+        if (pix_fmt == AV_PIX_FMT_PAL8 && !dec->fmt_out.video.p_palette)
5853+            fmt->i_chroma = VLC_CODEC_RGB32;
5854+
5855+        avcodec_align_dimensions2(ctx, &width, &height, aligns);
5856+    }
5857+    else /* hardware decoding */
5858+        fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt);
5859+#endif
5860+
5861+    if( width == 0 || height == 0 || width > 8192 || height > 8192 ||
5862+        width < ctx->width || height < ctx->height )
5863+    {
5864+        msg_Err(dec, "Invalid frame size %dx%d vsz %dx%d",
5865+                     width, height, ctx->width, ctx->height );
5866+        return -1; /* invalid display size */
5867+    }
5868+
5869+    fmt->i_width = width;
5870+    fmt->i_height = height;
5871+    fmt->i_visible_width = ctx->width;
5872+    fmt->i_visible_height = ctx->height;
5873+
5874+    /* If an aspect-ratio was specified in the input format then force it */
5875+    if (dec->fmt_in.video.i_sar_num > 0 && dec->fmt_in.video.i_sar_den > 0)
5876+    {
5877+        fmt->i_sar_num = dec->fmt_in.video.i_sar_num;
5878+        fmt->i_sar_den = dec->fmt_in.video.i_sar_den;
5879+    }
5880+    else
5881+    {
5882+        fmt->i_sar_num = ctx->sample_aspect_ratio.num;
5883+        fmt->i_sar_den = ctx->sample_aspect_ratio.den;
5884+
5885+        if (fmt->i_sar_num == 0 || fmt->i_sar_den == 0)
5886+            fmt->i_sar_num = fmt->i_sar_den = 1;
5887+    }
5888+
5889+    if (dec->fmt_in.video.i_frame_rate > 0
5890+     && dec->fmt_in.video.i_frame_rate_base > 0)
5891+    {
5892+        fmt->i_frame_rate = dec->fmt_in.video.i_frame_rate;
5893+        fmt->i_frame_rate_base = dec->fmt_in.video.i_frame_rate_base;
5894+    }
5895+    else if (ctx->framerate.num > 0 && ctx->framerate.den > 0)
5896+    {
5897+        fmt->i_frame_rate = ctx->framerate.num;
5898+        fmt->i_frame_rate_base = ctx->framerate.den;
5899+# if LIBAVCODEC_VERSION_MICRO <  100
5900+        // for some reason libav don't thinkg framerate presents actually same thing as in ffmpeg
5901+        fmt->i_frame_rate_base *= __MAX(ctx->ticks_per_frame, 1);
5902+# endif
5903+    }
5904+    else if (ctx->time_base.num > 0 && ctx->time_base.den > 0)
5905+    {
5906+        fmt->i_frame_rate = ctx->time_base.den;
5907+        fmt->i_frame_rate_base = ctx->time_base.num
5908+                                 * __MAX(ctx->ticks_per_frame, 1);
5909+    }
5910+
5911+    /* FIXME we should only set the known values and let the core decide
5912+     * later of fallbacks, but we can't do that with a boolean */
5913+    switch ( ctx->color_range )
5914+    {
5915+    case AVCOL_RANGE_JPEG:
5916+        fmt->b_color_range_full = true;
5917+        break;
5918+    case AVCOL_RANGE_UNSPECIFIED:
5919+        fmt->b_color_range_full = !vlc_fourcc_IsYUV( fmt->i_chroma );
5920+        break;
5921+    case AVCOL_RANGE_MPEG:
5922+    default:
5923+        fmt->b_color_range_full = false;
5924+        break;
5925+    }
5926+
5927+    switch( ctx->colorspace )
5928+    {
5929+        case AVCOL_SPC_BT709:
5930+            fmt->space = COLOR_SPACE_BT709;
5931+            break;
5932+        case AVCOL_SPC_SMPTE170M:
5933+        case AVCOL_SPC_BT470BG:
5934+            fmt->space = COLOR_SPACE_BT601;
5935+            break;
5936+        case AVCOL_SPC_BT2020_NCL:
5937+        case AVCOL_SPC_BT2020_CL:
5938+            fmt->space = COLOR_SPACE_BT2020;
5939+            break;
5940+        default:
5941+            break;
5942+    }
5943+
5944+    switch( ctx->color_trc )
5945+    {
5946+        case AVCOL_TRC_LINEAR:
5947+            fmt->transfer = TRANSFER_FUNC_LINEAR;
5948+            break;
5949+        case AVCOL_TRC_GAMMA22:
5950+            fmt->transfer = TRANSFER_FUNC_SRGB;
5951+            break;
5952+        case AVCOL_TRC_BT709:
5953+            fmt->transfer = TRANSFER_FUNC_BT709;
5954+            break;
5955+        case AVCOL_TRC_SMPTE170M:
5956+        case AVCOL_TRC_BT2020_10:
5957+        case AVCOL_TRC_BT2020_12:
5958+            fmt->transfer = TRANSFER_FUNC_BT2020;
5959+            break;
5960+#if LIBAVUTIL_VERSION_CHECK( 55, 14, 0, 31, 100)
5961+        case AVCOL_TRC_ARIB_STD_B67:
5962+            fmt->transfer = TRANSFER_FUNC_ARIB_B67;
5963+            break;
5964+#endif
5965+#if LIBAVUTIL_VERSION_CHECK( 55, 17, 0, 37, 100)
5966+        case AVCOL_TRC_SMPTE2084:
5967+            fmt->transfer = TRANSFER_FUNC_SMPTE_ST2084;
5968+            break;
5969+        case AVCOL_TRC_SMPTE240M:
5970+            fmt->transfer = TRANSFER_FUNC_SMPTE_240;
5971+            break;
5972+        case AVCOL_TRC_GAMMA28:
5973+            fmt->transfer = TRANSFER_FUNC_BT470_BG;
5974+            break;
5975+#endif
5976+        default:
5977+            break;
5978+    }
5979+
5980+    switch( ctx->color_primaries )
5981+    {
5982+        case AVCOL_PRI_BT709:
5983+            fmt->primaries = COLOR_PRIMARIES_BT709;
5984+            break;
5985+        case AVCOL_PRI_BT470BG:
5986+            fmt->primaries = COLOR_PRIMARIES_BT601_625;
5987+            break;
5988+        case AVCOL_PRI_SMPTE170M:
5989+        case AVCOL_PRI_SMPTE240M:
5990+            fmt->primaries = COLOR_PRIMARIES_BT601_525;
5991+            break;
5992+        case AVCOL_PRI_BT2020:
5993+            fmt->primaries = COLOR_PRIMARIES_BT2020;
5994+            break;
5995+        default:
5996+            break;
5997+    }
5998+
5999+    switch( ctx->chroma_sample_location )
6000+    {
6001+        case AVCHROMA_LOC_LEFT:
6002+            fmt->chroma_location = CHROMA_LOCATION_LEFT;
6003+            break;
6004+        case AVCHROMA_LOC_CENTER:
6005+            fmt->chroma_location = CHROMA_LOCATION_CENTER;
6006+            break;
6007+        case AVCHROMA_LOC_TOPLEFT:
6008+            fmt->chroma_location = CHROMA_LOCATION_TOP_LEFT;
6009+            break;
6010+        default:
6011+            break;
6012+    }
6013+
6014+    return 0;
6015+}
6016+
6017+static int lavc_UpdateVideoFormat(decoder_t *dec, AVCodecContext *ctx,
6018+                                  enum AVPixelFormat fmt,
6019+                                  enum AVPixelFormat swfmt)
6020+{
6021+    video_format_t fmt_out;
6022+    int val;
6023+#if TRACE_ALL
6024+    msg_Dbg(dec, "<<< %s", __func__);
6025+#endif
6026+    val = lavc_GetVideoFormat(dec, &fmt_out, ctx, fmt, swfmt);
6027+    if (val)
6028+    {
6029+        msg_Dbg(dec, "Failed to get format");
6030+        return val;
6031+    }
6032+
6033+    /* always have date in fields/ticks units */
6034+    if(dec->p_sys->pts.i_divider_num)
6035+        date_Change(&dec->p_sys->pts, fmt_out.i_frame_rate *
6036+                                      __MAX(ctx->ticks_per_frame, 1),
6037+                                      fmt_out.i_frame_rate_base);
6038+    else
6039+        date_Init(&dec->p_sys->pts, fmt_out.i_frame_rate *
6040+                                    __MAX(ctx->ticks_per_frame, 1),
6041+                                    fmt_out.i_frame_rate_base);
6042+
6043+    fmt_out.p_palette = dec-> fmt_out.video.p_palette;
6044+    dec->fmt_out.video.p_palette = NULL;
6045+
6046+    es_format_Change(&dec->fmt_out, VIDEO_ES, fmt_out.i_chroma);
6047+    dec->fmt_out.video = fmt_out;
6048+    dec->fmt_out.video.orientation = dec->fmt_in.video.orientation;
6049+    dec->fmt_out.video.projection_mode = dec->fmt_in.video.projection_mode;
6050+    dec->fmt_out.video.multiview_mode = dec->fmt_in.video.multiview_mode;
6051+    dec->fmt_out.video.pose = dec->fmt_in.video.pose;
6052+    if ( dec->fmt_in.video.mastering.max_luminance )
6053+        dec->fmt_out.video.mastering = dec->fmt_in.video.mastering;
6054+    dec->fmt_out.video.lighting = dec->fmt_in.video.lighting;
6055+
6056+    val = decoder_UpdateVideoFormat(dec);
6057+#if TRACE_ALL
6058+    msg_Dbg(dec, ">>> %s: rv=%d", __func__, val);
6059+#endif
6060+    return val;
6061+}
6062+
6063+static int OpenVideoCodec( decoder_t *p_dec )
6064+{
6065+    decoder_sys_t *p_sys = p_dec->p_sys;
6066+    AVCodecContext *ctx = p_sys->p_context;
6067+    const AVCodec *codec = p_sys->p_codec;
6068+    int ret;
6069+
6070+    if( ctx->extradata_size <= 0 )
6071+    {
6072+        if( codec->id == AV_CODEC_ID_VC1 ||
6073+            codec->id == AV_CODEC_ID_THEORA )
6074+        {
6075+            msg_Warn( p_dec, "waiting for extra data for codec %s",
6076+                      codec->name );
6077+            return 1;
6078+        }
6079+    }
6080+
6081+    ctx->width  = p_dec->fmt_in.video.i_visible_width;
6082+    ctx->height = p_dec->fmt_in.video.i_visible_height;
6083+
6084+    ctx->coded_width = p_dec->fmt_in.video.i_width;
6085+    ctx->coded_height = p_dec->fmt_in.video.i_height;
6086+
6087+    ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel;
6088+    p_sys->pix_fmt = AV_PIX_FMT_NONE;
6089+    p_sys->profile = -1;
6090+    p_sys->level = -1;
6091+    cc_Init( &p_sys->cc );
6092+
6093+    set_video_color_settings( &p_dec->fmt_in.video, ctx );
6094+    if( p_dec->fmt_in.video.i_frame_rate_base &&
6095+        p_dec->fmt_in.video.i_frame_rate &&
6096+        (double) p_dec->fmt_in.video.i_frame_rate /
6097+                 p_dec->fmt_in.video.i_frame_rate_base < 6 )
6098+    {
6099+        ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
6100+    }
6101+
6102+    post_mt( p_sys );
6103+    ret = ZcFfmpeg_OpenCodec( p_dec, ctx, codec );
6104+    wait_mt( p_sys );
6105+    if( ret < 0 )
6106+        return ret;
6107+
6108+    switch( ctx->active_thread_type )
6109+    {
6110+        case FF_THREAD_FRAME:
6111+            msg_Dbg( p_dec, "using frame thread mode with %d threads",
6112+                     ctx->thread_count );
6113+            break;
6114+        case FF_THREAD_SLICE:
6115+            msg_Dbg( p_dec, "using slice thread mode with %d threads",
6116+                     ctx->thread_count );
6117+            break;
6118+        case 0:
6119+            if( ctx->thread_count > 1 )
6120+                msg_Warn( p_dec, "failed to enable threaded decoding" );
6121+            break;
6122+        default:
6123+            msg_Warn( p_dec, "using unknown thread mode with %d threads",
6124+                      ctx->thread_count );
6125+            break;
6126+    }
6127+    return 0;
6128+}
6129+
6130+/*****************************************************************************
6131+ * InitVideo: initialize the video decoder
6132+ *****************************************************************************
6133+ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet
6134+ * opened (done after the first decoded frame).
6135+ *****************************************************************************/
6136+static int MmalAvcodecOpenDecoder( vlc_object_t *obj )
6137+{
6138+    decoder_t *p_dec = (decoder_t *)obj;
6139+    const AVCodec *p_codec;
6140+
6141+    int extra_buffers = var_InheritInteger(p_dec, MMAL_AVCODEC_BUFFERS);
6142+
6143+    if (extra_buffers < 0)
6144+    {
6145+        extra_buffers = p_dec->fmt_in.video.i_height * p_dec->fmt_in.video.i_width >= 1920 * 1088 ?
6146+            BUFFERS_IN_FLIGHT_UHD : BUFFERS_IN_FLIGHT;
6147+    }
6148+
6149+    if (extra_buffers <= 0)
6150+    {
6151+        msg_Dbg(p_dec, "%s: extra_buffers=%d - cannot use module", __func__, extra_buffers);
6152+        return VLC_EGENERIC;
6153+    }
6154+
6155+    const vcsm_init_type_t vcsm_type = cma_vcsm_init();
6156+    const int vcsm_size =
6157+        vcsm_type == VCSM_INIT_LEGACY ? hw_mmal_get_gpu_mem() : 512 << 20;
6158+
6159+#if 1
6160+    {
6161+        char buf1[5], buf2[5], buf2a[5];
6162+        char buf3[5], buf4[5];
6163+        uint32_t in_fcc = 0;
6164+        msg_Dbg(p_dec, "%s: <<< (%s/%s)[%s] %dx%d -> (%s/%s) %dx%d [%s/%d] xb:%d", __func__,
6165+                str_fourcc(buf1, p_dec->fmt_in.i_codec),
6166+                str_fourcc(buf2, p_dec->fmt_in.video.i_chroma),
6167+                str_fourcc(buf2a, in_fcc),
6168+                p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height,
6169+                str_fourcc(buf3, p_dec->fmt_out.i_codec),
6170+                str_fourcc(buf4, p_dec->fmt_out.video.i_chroma),
6171+                p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height,
6172+                cma_vcsm_init_str(vcsm_type), vcsm_size, extra_buffers);
6173+    }
6174+#endif
6175+
6176+    if( vcsm_type == VCSM_INIT_NONE )
6177+        return VLC_EGENERIC;
6178+#if 1
6179+    if( (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC &&
6180+         (vcsm_type == VCSM_INIT_CMA || vcsm_size < (96 << 20))) ||
6181+        (p_dec->fmt_in.i_codec == VLC_CODEC_HEVC &&
6182+         vcsm_size < (128 << 20)))
6183+    {
6184+        cma_vcsm_exit(vcsm_type);
6185+        return VLC_EGENERIC;
6186+    }
6187+#endif
6188+
6189+    AVCodecContext *p_context = ZcFfmpeg_AllocContext( p_dec, &p_codec );
6190+    if( p_context == NULL )
6191+    {
6192+        cma_vcsm_exit(vcsm_type);
6193+        return VLC_EGENERIC;
6194+    }
6195+
6196+    int i_val;
6197+
6198+    /* Allocate the memory needed to store the decoder's structure */
6199+    decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) );
6200+    if( unlikely(p_sys == NULL) )
6201+    {
6202+        avcodec_free_context( &p_context );
6203+        cma_vcsm_exit(vcsm_type);
6204+        return VLC_ENOMEM;
6205+    }
6206+
6207+    p_dec->p_sys = p_sys;
6208+    p_sys->p_context = p_context;
6209+    p_sys->p_codec = p_codec;
6210+    p_sys->p_dec = p_dec;
6211+//    p_sys->p_va = NULL;
6212+    p_sys->cma_in_flight_max = extra_buffers;
6213+    p_sys->vcsm_init_type = vcsm_type;
6214+    vlc_sem_init( &p_sys->sem_mt, 0 );
6215+
6216+    /* ***** Fill p_context with init values ***** */
6217+    p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ?
6218+                                p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec );
6219+
6220+    /*  ***** Get configuration of ffmpeg plugin ***** */
6221+    p_context->workaround_bugs =
6222+        var_InheritInteger( p_dec, "avcodec-workaround-bugs" );
6223+    p_context->err_recognition =
6224+        var_InheritInteger( p_dec, "avcodec-error-resilience" );
6225+
6226+    if( var_CreateGetBool( p_dec, "grayscale" ) )
6227+        p_context->flags |= AV_CODEC_FLAG_GRAY;
6228+
6229+    /* ***** Output always the frames ***** */
6230+    p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
6231+
6232+    i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" );
6233+    if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL;
6234+    else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY;
6235+    else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR;
6236+    else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF;
6237+    else p_context->skip_loop_filter = AVDISCARD_DEFAULT;
6238+
6239+    if( var_CreateGetBool( p_dec, "avcodec-fast" ) )
6240+        p_context->flags2 |= AV_CODEC_FLAG2_FAST;
6241+
6242+    /* ***** libavcodec frame skipping ***** */
6243+    p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" );
6244+    p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" );
6245+
6246+    i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" );
6247+    if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL;
6248+    else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY;
6249+    else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR;
6250+    else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF;
6251+    else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE;
6252+    else p_sys->i_skip_frame = AVDISCARD_DEFAULT;
6253+    p_context->skip_frame = p_sys->i_skip_frame;
6254+
6255+    i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" );
6256+    if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL;
6257+    else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY;
6258+    else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR;
6259+    else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF;
6260+    else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE;
6261+    else p_context->skip_idct = AVDISCARD_DEFAULT;
6262+
6263+    /* ***** libavcodec direct rendering ***** */
6264+    p_sys->b_direct_rendering = false;
6265+    atomic_init(&p_sys->b_dr_failure, false);
6266+    if( var_CreateGetBool( p_dec, "avcodec-dr" ) &&
6267+       (p_codec->capabilities & AV_CODEC_CAP_DR1) &&
6268+        /* No idea why ... but this fixes flickering on some TSCC streams */
6269+        p_sys->p_codec->id != AV_CODEC_ID_TSCC &&
6270+        p_sys->p_codec->id != AV_CODEC_ID_CSCD &&
6271+        p_sys->p_codec->id != AV_CODEC_ID_CINEPAK )
6272+    {
6273+        /* Some codecs set pix_fmt only after the 1st frame has been decoded,
6274+         * so we need to do another check in ffmpeg_GetFrameBuf() */
6275+        p_sys->b_direct_rendering = true;
6276+    }
6277+
6278+    p_context->get_format = ZcGetFormat;
6279+#if 0
6280+    p_context->get_format = ffmpeg_GetFormat;
6281+    /* Always use our get_buffer wrapper so we can calculate the
6282+     * PTS correctly */
6283+    p_context->get_buffer2 = lavc_GetFrame;
6284+    p_context->opaque = p_dec;
6285+#endif
6286+
6287+    int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" );
6288+    if( i_thread_count <= 0 )
6289+#if 1
6290+    {
6291+        // Pick 5 threads for everything on Pi except for HEVC where the h/w
6292+        // really limits the useful size to 3
6293+        i_thread_count = p_codec->id == AV_CODEC_ID_HEVC ? 3 : 5;
6294+    }
6295+#else
6296+    {
6297+        i_thread_count = vlc_GetCPUCount();
6298+        if( i_thread_count > 1 )
6299+            i_thread_count++;
6300+
6301+        //FIXME: take in count the decoding time
6302+#if VLC_WINSTORE_APP
6303+        i_thread_count = __MIN( i_thread_count, 6 );
6304+#else
6305+        i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 );
6306+#endif
6307+    }
6308+    i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 );
6309+#endif
6310+    msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count );
6311+    p_context->thread_count = i_thread_count;
6312+    p_context->thread_safe_callbacks = true;
6313+
6314+    switch( p_codec->id )
6315+    {
6316+        case AV_CODEC_ID_MPEG4:
6317+        case AV_CODEC_ID_H263:
6318+            p_context->thread_type = 0;
6319+            break;
6320+        case AV_CODEC_ID_MPEG1VIDEO:
6321+        case AV_CODEC_ID_MPEG2VIDEO:
6322+            p_context->thread_type &= ~FF_THREAD_SLICE;
6323+            /* fall through */
6324+# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0))
6325+        case AV_CODEC_ID_H264:
6326+        case AV_CODEC_ID_VC1:
6327+        case AV_CODEC_ID_WMV3:
6328+            p_context->thread_type &= ~FF_THREAD_FRAME;
6329+# endif
6330+        default:
6331+            break;
6332+    }
6333+
6334+    if( p_context->thread_type & FF_THREAD_FRAME )
6335+        p_dec->i_extra_picture_buffers = 2 * p_context->thread_count;
6336+
6337+    /* ***** misc init ***** */
6338+    date_Init(&p_sys->pts, 1, 30001);
6339+    date_Set(&p_sys->pts, VLC_TS_INVALID);
6340+    p_sys->b_first_frame = true;
6341+    p_sys->i_late_frames = 0;
6342+    p_sys->b_from_preroll = false;
6343+
6344+    /* Set output properties */
6345+    if( ZcGetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS )
6346+    {
6347+        /* we are doomed. but not really, because most codecs set their pix_fmt later on */
6348+//        p_dec->fmt_out.i_codec = VLC_CODEC_I420;
6349+        p_dec->fmt_out.i_codec = VLC_CODEC_MMAL_ZC_I420;
6350+    }
6351+    p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma;
6352+
6353+    p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation;
6354+
6355+    if( p_dec->fmt_in.video.p_palette ) {
6356+        p_sys->palette_sent = false;
6357+        p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) );
6358+        if( p_dec->fmt_out.video.p_palette )
6359+            *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette;
6360+    } else
6361+        p_sys->palette_sent = true;
6362+
6363+    if ((p_sys->cma_pool = cma_buf_pool_new(p_sys->cma_in_flight_max, p_sys->cma_in_flight_max, false, "mmal_avcodec")) == NULL)
6364+    {
6365+        msg_Err(p_dec, "CMA pool alloc failure");
6366+        goto fail;
6367+    }
6368+
6369+    /* ***** init this codec with special data ***** */
6370+    ffmpeg_InitCodec( p_dec );
6371+
6372+    /* ***** Open the codec ***** */
6373+    if( OpenVideoCodec( p_dec ) < 0 )
6374+    {
6375+        vlc_sem_destroy( &p_sys->sem_mt );
6376+        free( p_sys );
6377+        avcodec_free_context( &p_context );
6378+        return VLC_EGENERIC;
6379+    }
6380+
6381+    p_dec->pf_decode = DecodeVideo;
6382+    p_dec->pf_flush  = Flush;
6383+
6384+    /* XXX: Writing input format makes little sense. */
6385+    if( p_context->profile != FF_PROFILE_UNKNOWN )
6386+        p_dec->fmt_in.i_profile = p_context->profile;
6387+    if( p_context->level != FF_LEVEL_UNKNOWN )
6388+        p_dec->fmt_in.i_level = p_context->level;
6389+
6390+#if 1
6391+    // Most of the time we have nothing useful by way of a format here
6392+    // wait till we've decoded something
6393+#else
6394+    // Update output format
6395+    if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
6396+                               p_context->pix_fmt) != 0)
6397+    {
6398+        msg_Err(p_dec, "Unable to update format: pix_fmt=%d", p_context->pix_fmt);
6399+//        goto fail;
6400+    }
6401+#endif
6402+
6403+#if TRACE_ALL
6404+    msg_Dbg(p_dec, "<<< %s: OK", __func__);
6405+#endif
6406+    return VLC_SUCCESS;
6407+
6408+fail:
6409+    MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec));
6410+
6411+#if TRACE_ALL
6412+    msg_Dbg(p_dec, "<<< %s: FAIL", __func__);
6413+#endif
6414+
6415+    return VLC_EGENERIC;
6416+}
6417+
6418+/*****************************************************************************
6419+ * Flush:
6420+ *****************************************************************************/
6421+static void Flush( decoder_t *p_dec )
6422+{
6423+    decoder_sys_t *p_sys = p_dec->p_sys;
6424+    AVCodecContext *p_context = p_sys->p_context;
6425+
6426+#if TRACE_ALL
6427+    msg_Dbg(p_dec, "<<< %s", __func__);
6428+#endif
6429+
6430+    date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */
6431+    p_sys->i_late_frames = 0;
6432+    cc_Flush( &p_sys->cc );
6433+
6434+    /* Abort pictures in order to unblock all avcodec workers threads waiting
6435+     * for a picture. This will avoid a deadlock between avcodec_flush_buffers
6436+     * and workers threads */
6437+// It would probably be good to use AbortPicture but that often deadlocks on close
6438+// and given that we wait for pics in the main thread it should be unneeded (whereas
6439+// cma is alloced in the depths of ffmpeg on its own threads)
6440+//    decoder_AbortPictures( p_dec, true );
6441+    cma_buf_pool_cancel(p_sys->cma_pool);
6442+
6443+    post_mt( p_sys );
6444+    /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
6445+    if( avcodec_is_open( p_context ) )
6446+        avcodec_flush_buffers( p_context );
6447+    wait_mt( p_sys );
6448+
6449+    /* Reset cancel state to false */
6450+    cma_buf_pool_uncancel(p_sys->cma_pool);
6451+//    decoder_AbortPictures( p_dec, false );
6452+
6453+#if TRACE_ALL
6454+    msg_Dbg(p_dec, ">>> %s", __func__);
6455+#endif
6456+
6457+}
6458+
6459+static bool check_block_validity( decoder_sys_t *p_sys, block_t *block )
6460+{
6461+    if( !block)
6462+        return true;
6463+
6464+    if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )
6465+    {
6466+        date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
6467+        cc_Flush( &p_sys->cc );
6468+
6469+        p_sys->i_late_frames = 0;
6470+        if( block->i_flags & BLOCK_FLAG_CORRUPTED )
6471+        {
6472+            block_Release( block );
6473+            return false;
6474+        }
6475+    }
6476+    return true;
6477+}
6478+
6479+static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time)
6480+{
6481+    if( !block )
6482+        return false;
6483+    if( block->i_flags & BLOCK_FLAG_PREROLL )
6484+    {
6485+        /* Do not care about late frames when prerolling
6486+         * TODO avoid decoding of non reference frame
6487+         * (ie all B except for H264 where it depends only on nal_ref_idc) */
6488+        p_sys->i_late_frames = 0;
6489+        p_sys->b_from_preroll = true;
6490+        p_sys->i_last_late_delay = INT64_MAX;
6491+    }
6492+
6493+    if( p_sys->i_late_frames <= 0 )
6494+        return false;
6495+
6496+    if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ))
6497+    {
6498+        date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
6499+        block_Release( block );
6500+        p_sys->i_late_frames--;
6501+        return true;
6502+    }
6503+    return false;
6504+}
6505+
6506+static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture )
6507+{
6508+    if( p_sys->i_late_frames <= 4)
6509+        return false;
6510+
6511+    *b_need_output_picture = false;
6512+    if( p_sys->i_late_frames < 12 )
6513+    {
6514+        p_context->skip_frame =
6515+                (p_sys->i_skip_frame <= AVDISCARD_NONREF) ?
6516+                AVDISCARD_NONREF : p_sys->i_skip_frame;
6517+    }
6518+    else
6519+    {
6520+        /* picture too late, won't decode
6521+         * but break picture until a new I, and for mpeg4 ...*/
6522+        p_sys->i_late_frames--; /* needed else it will never be decrease */
6523+        return true;
6524+    }
6525+    return false;
6526+}
6527+
6528+static mtime_t interpolate_next_pts( decoder_t *p_dec, AVFrame *frame )
6529+{
6530+    decoder_sys_t *p_sys = p_dec->p_sys;
6531+    AVCodecContext *p_context = p_sys->p_context;
6532+
6533+    if( date_Get( &p_sys->pts ) == VLC_TS_INVALID ||
6534+        p_sys->pts.i_divider_num == 0 )
6535+        return VLC_TS_INVALID;
6536+
6537+    int i_tick = p_context->ticks_per_frame;
6538+    if( i_tick <= 0 )
6539+        i_tick = 1;
6540+
6541+    /* interpolate the next PTS */
6542+    return date_Increment( &p_sys->pts, i_tick + frame->repeat_pict );
6543+}
6544+
6545+static void update_late_frame_count( decoder_t *p_dec, block_t *p_block,
6546+                                     mtime_t current_time, mtime_t i_pts,
6547+                                     mtime_t i_next_pts )
6548+{
6549+    decoder_sys_t *p_sys = p_dec->p_sys;
6550+   /* Update frame late count (except when doing preroll) */
6551+   mtime_t i_display_date = VLC_TS_INVALID;
6552+   if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
6553+       i_display_date = decoder_GetDisplayDate( p_dec, i_pts );
6554+
6555+   mtime_t i_threshold = i_next_pts != VLC_TS_INVALID ? (i_next_pts - i_pts) / 2 : 20000;
6556+
6557+   if( i_display_date > VLC_TS_INVALID && i_display_date + i_threshold <= current_time )
6558+   {
6559+       /* Out of preroll, consider only late frames on rising delay */
6560+       if( p_sys->b_from_preroll )
6561+       {
6562+           if( p_sys->i_last_late_delay > current_time - i_display_date )
6563+           {
6564+               p_sys->i_last_late_delay = current_time - i_display_date;
6565+               return;
6566+           }
6567+           p_sys->b_from_preroll = false;
6568+       }
6569+
6570+       p_sys->i_late_frames++;
6571+       if( p_sys->i_late_frames == 1 )
6572+           p_sys->i_late_frames_start = current_time;
6573+
6574+   }
6575+   else
6576+   {
6577+       p_sys->i_late_frames = 0;
6578+   }
6579+}
6580+
6581+
6582+static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic )
6583+{
6584+    decoder_sys_t *p_sys = p_dec->p_sys;
6585+    bool format_changed = false;
6586+
6587+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
6588+#define FROM_AVRAT(default_factor, avrat) \
6589+(uint64_t)(default_factor) * (avrat).num / (avrat).den
6590+    const AVFrameSideData *metadata =
6591+            av_frame_get_side_data( frame,
6592+                                    AV_FRAME_DATA_MASTERING_DISPLAY_METADATA );
6593+    if ( metadata )
6594+    {
6595+        const AVMasteringDisplayMetadata *hdr_meta =
6596+                (const AVMasteringDisplayMetadata *) metadata->data;
6597+        if ( hdr_meta->has_luminance )
6598+        {
6599+#define ST2086_LUMA_FACTOR 10000
6600+            p_pic->format.mastering.max_luminance =
6601+                    FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance);
6602+            p_pic->format.mastering.min_luminance =
6603+                    FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance);
6604+        }
6605+        if ( hdr_meta->has_primaries )
6606+        {
6607+#define ST2086_RED   2
6608+#define ST2086_GREEN 0
6609+#define ST2086_BLUE  1
6610+#define LAV_RED    0
6611+#define LAV_GREEN  1
6612+#define LAV_BLUE   2
6613+#define ST2086_PRIM_FACTOR 50000
6614+            p_pic->format.mastering.primaries[ST2086_RED*2   + 0] =
6615+                    FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]);
6616+            p_pic->format.mastering.primaries[ST2086_RED*2   + 1] =
6617+                    FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]);
6618+            p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] =
6619+                    FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]);
6620+            p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] =
6621+                    FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]);
6622+            p_pic->format.mastering.primaries[ST2086_BLUE*2  + 0] =
6623+                    FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]);
6624+            p_pic->format.mastering.primaries[ST2086_BLUE*2  + 1] =
6625+                    FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]);
6626+            p_pic->format.mastering.white_point[0] =
6627+                    FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]);
6628+            p_pic->format.mastering.white_point[1] =
6629+                    FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]);
6630+        }
6631+
6632+        if ( memcmp( &p_dec->fmt_out.video.mastering,
6633+                     &p_pic->format.mastering,
6634+                     sizeof(p_pic->format.mastering) ) )
6635+        {
6636+            p_dec->fmt_out.video.mastering = p_pic->format.mastering;
6637+            format_changed = true;
6638+        }
6639+#undef FROM_AVRAT
6640+    }
6641+#endif
6642+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) )
6643+    const AVFrameSideData *metadata_lt =
6644+            av_frame_get_side_data( frame,
6645+                                    AV_FRAME_DATA_CONTENT_LIGHT_LEVEL );
6646+    if ( metadata_lt )
6647+    {
6648+        const AVContentLightMetadata *light_meta =
6649+                (const AVContentLightMetadata *) metadata_lt->data;
6650+        p_pic->format.lighting.MaxCLL = light_meta->MaxCLL;
6651+        p_pic->format.lighting.MaxFALL = light_meta->MaxFALL;
6652+        if ( memcmp( &p_dec->fmt_out.video.lighting,
6653+                     &p_pic->format.lighting,
6654+                     sizeof(p_pic->format.lighting) ) )
6655+        {
6656+            p_dec->fmt_out.video.lighting  = p_pic->format.lighting;
6657+            format_changed = true;
6658+        }
6659+    }
6660+#endif
6661+
6662+    if (format_changed && decoder_UpdateVideoFormat( p_dec ))
6663+        return -1;
6664+
6665+    const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC );
6666+    if( p_avcc )
6667+    {
6668+        cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size );
6669+        if( p_sys->cc.b_reorder || p_sys->cc.i_data )
6670+        {
6671+            block_t *p_cc = block_Alloc( p_sys->cc.i_data );
6672+            if( p_cc )
6673+            {
6674+                memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
6675+                if( p_sys->cc.b_reorder )
6676+                    p_cc->i_dts = p_cc->i_pts = p_pic->date;
6677+                else
6678+                    p_cc->i_pts = p_cc->i_dts;
6679+                decoder_cc_desc_t desc;
6680+                desc.i_608_channels = p_sys->cc.i_608channels;
6681+                desc.i_708_channels = p_sys->cc.i_708channels;
6682+                desc.i_reorder_depth = 4;
6683+                decoder_QueueCc( p_dec, p_cc, &desc );
6684+            }
6685+            cc_Flush( &p_sys->cc );
6686+        }
6687+    }
6688+    return 0;
6689+}
6690+
6691+/*****************************************************************************
6692+ * DecodeBlock: Called to decode one or more frames
6693+ *****************************************************************************/
6694+
6695+static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error )
6696+{
6697+    decoder_sys_t *p_sys = p_dec->p_sys;
6698+    AVCodecContext *p_context = p_sys->p_context;
6699+    /* Boolean if we assume that we should get valid pic as result */
6700+    bool b_need_output_picture = true;
6701+
6702+    /* Boolean for END_OF_SEQUENCE */
6703+    bool eos_spotted = false;
6704+
6705+#if TRACE_ALL
6706+    msg_Dbg(p_dec, "<<< %s: (buf_size=%d)", __func__, pp_block == NULL || *pp_block == NULL ? 0 : (*pp_block)->i_buffer);
6707+#endif
6708+
6709+    block_t *p_block;
6710+    mtime_t current_time;
6711+    picture_t *p_pic = NULL;
6712+    AVFrame *frame = NULL;
6713+
6714+    // By default we are OK
6715+    *error = false;
6716+
6717+    if( !p_context->extradata_size && p_dec->fmt_in.i_extra )
6718+    {
6719+        ffmpeg_InitCodec( p_dec );
6720+        if( !avcodec_is_open( p_context ) )
6721+            OpenVideoCodec( p_dec );
6722+    }
6723+
6724+    p_block = pp_block ? *pp_block : NULL;
6725+    if(!p_block && !(p_sys->p_codec->capabilities & AV_CODEC_CAP_DELAY) )
6726+        return NULL;
6727+
6728+    if( !avcodec_is_open( p_context ) )
6729+    {
6730+        if( p_block )
6731+            block_Release( p_block );
6732+        return NULL;
6733+    }
6734+
6735+    if( !check_block_validity( p_sys, p_block ) )
6736+        return NULL;
6737+
6738+    current_time = mdate();
6739+    if( p_dec->b_frame_drop_allowed &&  check_block_being_late( p_sys, p_block, current_time) )
6740+    {
6741+        msg_Err( p_dec, "more than 5 seconds of late video -> "
6742+                 "dropping frame (computer too slow ?)" );
6743+        return NULL;
6744+    }
6745+
6746+
6747+    /* A good idea could be to decode all I pictures and see for the other */
6748+
6749+    /* Defaults that if we aren't in prerolling, we want output picture
6750+       same for if we are flushing (p_block==NULL) */
6751+    if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
6752+        b_need_output_picture = true;
6753+    else
6754+        b_need_output_picture = false;
6755+
6756+    /* Change skip_frame config only if hurry_up is enabled */
6757+    if( p_sys->b_hurry_up )
6758+    {
6759+        p_context->skip_frame = p_sys->i_skip_frame;
6760+
6761+        /* Check also if we should/can drop the block and move to next block
6762+            as trying to catchup the speed*/
6763+        if( p_dec->b_frame_drop_allowed &&
6764+            check_frame_should_be_dropped( p_sys, p_context, &b_need_output_picture ) )
6765+        {
6766+            if( p_block )
6767+                block_Release( p_block );
6768+            msg_Warn( p_dec, "More than 11 late frames, dropping frame" );
6769+            return NULL;
6770+        }
6771+    }
6772+    if( !b_need_output_picture )
6773+    {
6774+        p_context->skip_frame = __MAX( p_context->skip_frame,
6775+                                              AVDISCARD_NONREF );
6776+    }
6777+
6778+    /*
6779+     * Do the actual decoding now */
6780+
6781+    /* Don't forget that libavcodec requires a little more bytes
6782+     * that the real frame size */
6783+    if( p_block && p_block->i_buffer > 0 )
6784+    {
6785+        eos_spotted = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0;
6786+
6787+        p_block = block_Realloc( p_block, 0,
6788+                            p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE );
6789+        if( !p_block )
6790+            return NULL;
6791+        p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
6792+        *pp_block = p_block;
6793+        memset( p_block->p_buffer + p_block->i_buffer, 0,
6794+                FF_INPUT_BUFFER_PADDING_SIZE );
6795+    }
6796+
6797+    while( !p_block || p_block->i_buffer > 0 || eos_spotted )
6798+    {
6799+        int i_used;
6800+        AVPacket pkt;
6801+
6802+        post_mt( p_sys );
6803+
6804+        av_init_packet( &pkt );
6805+        if( p_block && p_block->i_buffer > 0 )
6806+        {
6807+            pkt.data = p_block->p_buffer;
6808+            pkt.size = p_block->i_buffer;
6809+            pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE;
6810+            pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE;
6811+        }
6812+        else
6813+        {
6814+            /* Return delayed frames if codec has CODEC_CAP_DELAY */
6815+            pkt.data = NULL;
6816+            pkt.size = 0;
6817+        }
6818+
6819+        if( !p_sys->palette_sent )
6820+        {
6821+            uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
6822+            if (pal) {
6823+                memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE);
6824+                p_sys->palette_sent = true;
6825+            }
6826+        }
6827+
6828+        /* Make sure we don't reuse the same timestamps twice */
6829+        if( p_block )
6830+        {
6831+            p_block->i_pts =
6832+            p_block->i_dts = VLC_TS_INVALID;
6833+        }
6834+
6835+        int ret = avcodec_send_packet(p_context, &pkt);
6836+        if( ret != 0 && ret != AVERROR(EAGAIN) )
6837+        {
6838+            if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
6839+            {
6840+                msg_Err(p_dec, "avcodec_send_packet critical error");
6841+                *error = true;
6842+            }
6843+            av_packet_unref( &pkt );
6844+            break;
6845+        }
6846+        i_used = ret != AVERROR(EAGAIN) ? pkt.size : 0;
6847+        av_packet_unref( &pkt );
6848+
6849+        frame = av_frame_alloc();
6850+        if (unlikely(frame == NULL))
6851+        {
6852+            *error = true;
6853+            break;
6854+        }
6855+
6856+        ret = avcodec_receive_frame(p_context, frame);
6857+        if( ret != 0 && ret != AVERROR(EAGAIN) )
6858+        {
6859+            msg_Dbg(p_dec, "No receive");
6860+            if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
6861+            {
6862+                msg_Err(p_dec, "avcodec_receive_frame critical error");
6863+                *error = true;
6864+            }
6865+            av_frame_free(&frame);
6866+            /* After draining, we need to reset decoder with a flush */
6867+            if( ret == AVERROR_EOF )
6868+                avcodec_flush_buffers( p_sys->p_context );
6869+            break;
6870+        }
6871+        bool not_received_frame = ret;
6872+
6873+        wait_mt( p_sys );
6874+
6875+        if( eos_spotted )
6876+            p_sys->b_first_frame = true;
6877+
6878+        if( p_block )
6879+        {
6880+            if( p_block->i_buffer <= 0 )
6881+                eos_spotted = false;
6882+
6883+            /* Consumed bytes */
6884+            p_block->p_buffer += i_used;
6885+            p_block->i_buffer -= i_used;
6886+        }
6887+
6888+        /* Nothing to display */
6889+        if( not_received_frame )
6890+        {
6891+//            msg_Dbg(p_dec, "No rx: used=%d", i_used);
6892+            av_frame_free(&frame);
6893+            if( i_used == 0 ) break;
6894+            continue;
6895+        }
6896+
6897+        /* Compute the PTS */
6898+#ifdef FF_API_PKT_PTS
6899+        mtime_t i_pts = frame->pts;
6900+#else
6901+        mtime_t i_pts = frame->pkt_pts;
6902+#endif
6903+        if (i_pts == AV_NOPTS_VALUE )
6904+            i_pts = frame->pkt_dts;
6905+
6906+        if( i_pts == AV_NOPTS_VALUE )
6907+            i_pts = date_Get( &p_sys->pts );
6908+
6909+        /* Interpolate the next PTS */
6910+        if( i_pts > VLC_TS_INVALID )
6911+            date_Set( &p_sys->pts, i_pts );
6912+
6913+        const mtime_t i_next_pts = interpolate_next_pts(p_dec, frame);
6914+
6915+        update_late_frame_count( p_dec, p_block, current_time, i_pts, i_next_pts);
6916+
6917+        if( !b_need_output_picture ||
6918+//            ( !p_sys->p_va && !frame->linesize[0] ) ||
6919+           ( !frame->linesize[0] ) ||
6920+           ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) &&
6921+             !p_sys->b_show_corrupted ) )
6922+        {
6923+            av_frame_free(&frame);
6924+//            msg_Dbg(p_dec, "Bad frame");
6925+            continue;
6926+        }
6927+
6928+        if( p_context->pix_fmt == AV_PIX_FMT_PAL8
6929+         && !p_dec->fmt_out.video.p_palette )
6930+        {
6931+            /* See AV_PIX_FMT_PAL8 comment in avc_GetVideoFormat(): update the
6932+             * fmt_out palette and change the fmt_out chroma to request a new
6933+             * vout */
6934+            assert( p_dec->fmt_out.video.i_chroma != VLC_CODEC_RGBP );
6935+
6936+            video_palette_t *p_palette;
6937+            p_palette = p_dec->fmt_out.video.p_palette
6938+                      = malloc( sizeof(video_palette_t) );
6939+            if( !p_palette )
6940+            {
6941+                *error = true;
6942+                av_frame_free(&frame);
6943+                break;
6944+            }
6945+            static_assert( sizeof(p_palette->palette) == AVPALETTE_SIZE,
6946+                           "Palette size mismatch between vlc and libavutil" );
6947+            assert( frame->data[1] != NULL );
6948+            memcpy( p_palette->palette, frame->data[1], AVPALETTE_SIZE );
6949+            p_palette->i_entries = AVPALETTE_COUNT;
6950+            p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBP;
6951+            if( decoder_UpdateVideoFormat( p_dec ) )
6952+            {
6953+                av_frame_free(&frame);
6954+                continue;
6955+            }
6956+        }
6957+
6958+#if 1
6959+        {
6960+            cma_buf_t * const cb = av_rpi_zc_buf_v(frame->buf[0]);
6961+
6962+            if (cb == NULL)
6963+            {
6964+                msg_Err(p_dec, "Frame has no attached CMA buffer");
6965+                goto fail;
6966+            }
6967+
6968+            if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
6969+                                       p_context->pix_fmt) != 0)
6970+            {
6971+                msg_Err(p_dec, "Failed to update format");
6972+                goto fail;
6973+            }
6974+
6975+            if ((p_pic = decoder_NewPicture(p_dec)) == NULL)
6976+            {
6977+                msg_Err(p_dec, "Failed to allocate pic");
6978+                goto fail;
6979+            }
6980+
6981+            if (cma_buf_pic_attach(cma_buf_ref(cb), p_pic) != 0)
6982+            {
6983+                cma_buf_unref(cb);  // Undo the in_flight
6984+                char dbuf0[5];
6985+                msg_Err(p_dec, "Failed to attach bufs to pic: fmt=%s", str_fourcc(dbuf0, p_pic->format.i_chroma));
6986+                goto fail;
6987+            }
6988+
6989+            // ****** Set planes etc.
6990+            set_pic_from_frame(p_pic, frame);
6991+        }
6992+#else
6993+        picture_t *p_pic = frame->opaque;
6994+        if( p_pic == NULL )
6995+        {   /* When direct rendering is not used, get_format() and get_buffer()
6996+             * might not be called. The output video format must be set here
6997+             * then picture buffer can be allocated. */
6998+            if (p_sys->p_va == NULL
6999+             && lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
7000+                                       p_context->pix_fmt) == 0)
7001+                p_pic = decoder_NewPicture(p_dec);
7002+
7003+            if( !p_pic )
7004+            {
7005+                av_frame_free(&frame);
7006+                break;
7007+            }
7008+
7009+            /* Fill picture_t from AVFrame */
7010+            if( lavc_CopyPicture( p_dec, p_pic, frame ) != VLC_SUCCESS )
7011+            {
7012+                av_frame_free(&frame);
7013+                picture_Release( p_pic );
7014+                break;
7015+            }
7016+        }
7017+        else
7018+        {
7019+            /* Some codecs can return the same frame multiple times. By the
7020+             * time that the same frame is returned a second time, it will be
7021+             * too late to clone the underlying picture. So clone proactively.
7022+             * A single picture CANNOT be queued multiple times.
7023+             */
7024+            p_pic = picture_Clone( p_pic );
7025+            if( unlikely(p_pic == NULL) )
7026+            {
7027+                av_frame_free(&frame);
7028+                break;
7029+            }
7030+        }
7031+#endif
7032+
7033+        if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den )
7034+        {
7035+            /* Fetch again the aspect ratio in case it changed */
7036+            p_dec->fmt_out.video.i_sar_num
7037+                = p_context->sample_aspect_ratio.num;
7038+            p_dec->fmt_out.video.i_sar_den
7039+                = p_context->sample_aspect_ratio.den;
7040+
7041+            if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den )
7042+            {
7043+                p_dec->fmt_out.video.i_sar_num = 1;
7044+                p_dec->fmt_out.video.i_sar_den = 1;
7045+            }
7046+        }
7047+
7048+        p_pic->date = i_pts;
7049+        /* Hack to force display of still pictures */
7050+        p_pic->b_force = p_sys->b_first_frame;
7051+        p_pic->i_nb_fields = 2 + frame->repeat_pict;
7052+        p_pic->b_progressive = !frame->interlaced_frame;
7053+        p_pic->b_top_field_first = frame->top_field_first;
7054+
7055+        if (DecodeSidedata(p_dec, frame, p_pic))
7056+            i_pts = VLC_TS_INVALID;
7057+
7058+        av_frame_free(&frame);
7059+
7060+        /* Send decoded frame to vout */
7061+        if (i_pts > VLC_TS_INVALID)
7062+        {
7063+            p_sys->b_first_frame = false;
7064+#if TRACE_ALL
7065+            msg_Dbg(p_dec, ">>> %s: Got pic", __func__);
7066+#endif
7067+            return p_pic;
7068+        }
7069+        else
7070+            picture_Release( p_pic );
7071+    }
7072+
7073+    if( p_block )
7074+        block_Release( p_block );
7075+
7076+#if TRACE_ALL
7077+     msg_Dbg(p_dec, ">>> %s: NULL", __func__);
7078+#endif
7079+    return NULL;
7080+
7081+fail:
7082+#if TRACE_ALL
7083+     msg_Dbg(p_dec, ">>> %s: FAIL", __func__);
7084+#endif
7085+    av_frame_free(&frame);
7086+    if (p_pic != NULL)
7087+        picture_Release(p_pic);
7088+    if (p_block != NULL)
7089+        block_Release(p_block);
7090+    *error = true;
7091+    return NULL;
7092+}
7093+
7094+static int DecodeVideo( decoder_t *p_dec, block_t *p_block )
7095+{
7096+    block_t **pp_block = p_block ? &p_block : NULL;
7097+    picture_t *p_pic;
7098+    bool error = false;
7099+    while( ( p_pic = DecodeBlock( p_dec, pp_block, &error ) ) != NULL )
7100+        decoder_QueueVideo( p_dec, p_pic );
7101+    return VLCDEC_SUCCESS;
7102+// Easiest to just ignore all errors - returning a real error seems to
7103+// kill output forever
7104+//    return error ? VLCDEC_ECRITICAL : VLCDEC_SUCCESS;
7105+}
7106+
7107+/*****************************************************************************
7108+ * EndVideo: decoder destruction
7109+ *****************************************************************************
7110+ * This function is called when the thread ends after a successful
7111+ * initialization.
7112+ *****************************************************************************/
7113+static void MmalAvcodecCloseDecoder( vlc_object_t *obj )
7114+{
7115+    decoder_t *p_dec = (decoder_t *)obj;
7116+    decoder_sys_t *p_sys = p_dec->p_sys;
7117+    AVCodecContext *ctx = p_sys->p_context;
7118+//    void *hwaccel_context;
7119+
7120+    msg_Dbg(obj, "<<< %s", __func__);
7121+
7122+    post_mt( p_sys );
7123+
7124+    cma_buf_pool_cancel(p_sys->cma_pool);  // Abort any pending frame allocs
7125+
7126+    /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
7127+    if( avcodec_is_open( ctx ) )
7128+        avcodec_flush_buffers( ctx );
7129+
7130+    av_rpi_zc_uninit2(ctx);
7131+
7132+    wait_mt( p_sys );
7133+
7134+    cc_Flush( &p_sys->cc );
7135+
7136+//    hwaccel_context = ctx->hwaccel_context;
7137+    avcodec_free_context( &ctx );
7138+
7139+//    if( p_sys->p_va )
7140+//        vlc_va_Delete( p_sys->p_va, &hwaccel_context );
7141+
7142+    cma_vcsm_exit(p_sys->vcsm_init_type);
7143+
7144+    vlc_sem_destroy( &p_sys->sem_mt );
7145+    free( p_sys );
7146+}
7147+
7148+/*****************************************************************************
7149+ * ffmpeg_InitCodec: setup codec extra initialization data for ffmpeg
7150+ *****************************************************************************/
7151+static void ffmpeg_InitCodec( decoder_t *p_dec )
7152+{
7153+    decoder_sys_t *p_sys = p_dec->p_sys;
7154+    size_t i_size = p_dec->fmt_in.i_extra;
7155+
7156+    if( !i_size ) return;
7157+
7158+    if( p_sys->p_codec->id == AV_CODEC_ID_SVQ3 )
7159+    {
7160+        uint8_t *p;
7161+
7162+        p_sys->p_context->extradata_size = i_size + 12;
7163+        p = p_sys->p_context->extradata =
7164+            av_malloc( p_sys->p_context->extradata_size +
7165+                       FF_INPUT_BUFFER_PADDING_SIZE );
7166+        if( !p )
7167+            return;
7168+
7169+        memcpy( &p[0],  "SVQ3", 4 );
7170+        memset( &p[4], 0, 8 );
7171+        memcpy( &p[12], p_dec->fmt_in.p_extra, i_size );
7172+
7173+        /* Now remove all atoms before the SMI one */
7174+        if( p_sys->p_context->extradata_size > 0x5a &&
7175+            strncmp( (char*)&p[0x56], "SMI ", 4 ) )
7176+        {
7177+            uint8_t *psz = &p[0x52];
7178+
7179+            while( psz < &p[p_sys->p_context->extradata_size - 8] )
7180+            {
7181+                uint_fast32_t atom_size = GetDWBE( psz );
7182+                if( atom_size <= 1 )
7183+                {
7184+                    /* FIXME handle 1 as long size */
7185+                    break;
7186+                }
7187+                if( !strncmp( (char*)&psz[4], "SMI ", 4 ) )
7188+                {
7189+                    memmove( &p[0x52], psz,
7190+                             &p[p_sys->p_context->extradata_size] - psz );
7191+                    break;
7192+                }
7193+
7194+                psz += atom_size;
7195+            }
7196+        }
7197+    }
7198+    else
7199+    {
7200+        p_sys->p_context->extradata_size = i_size;
7201+        p_sys->p_context->extradata =
7202+            av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );
7203+        if( p_sys->p_context->extradata )
7204+        {
7205+            memcpy( p_sys->p_context->extradata,
7206+                    p_dec->fmt_in.p_extra, i_size );
7207+            memset( p_sys->p_context->extradata + i_size,
7208+                    0, FF_INPUT_BUFFER_PADDING_SIZE );
7209+        }
7210+    }
7211+}
7212+
7213+
7214+vlc_module_begin()
7215+    set_category( CAT_INPUT )
7216+    set_subcategory( SUBCAT_INPUT_VCODEC )
7217+    set_shortname(N_("MMAL avcodec"))
7218+    set_description(N_("MMAL buffered avcodec "))
7219+    set_capability("video decoder", 80)
7220+    add_shortcut("mmal_avcodec")
7221+    add_integer(MMAL_AVCODEC_BUFFERS, -1, MMAL_AVCODEC_BUFFERS_TEXT,
7222+                    MMAL_AVCODEC_BUFFERS_LONGTEXT, true)
7223+    set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder)
7224+vlc_module_end()
7225+
7226--- /dev/null
7227+++ b/modules/hw/mmal/mmal_cma.c
7228@@ -0,0 +1,668 @@
7229+#ifdef HAVE_CONFIG_H
7230+# include "config.h"
7231+#endif
7232+
7233+#include <stdatomic.h>
7234+#include <unistd.h>
7235+#include <fcntl.h>
7236+#include <sys/ioctl.h>
7237+#include <sys/mman.h>
7238+
7239+#include <interface/vcsm/user-vcsm.h>
7240+
7241+#include <vlc_common.h>
7242+#include <vlc_picture.h>
7243+
7244+#include "mmal_cma.h"
7245+#include "mmal_picture.h"
7246+
7247+#include <assert.h>
7248+
7249+#define TRACE_ALL 0
7250+
7251+//-----------------------------------------------------------------------------
7252+//
7253+// Generic pool functions
7254+// Knows nothing about pool entries
7255+
7256+typedef void * cma_pool_alloc_fn(void * v, size_t size);
7257+typedef void cma_pool_free_fn(void * v, void * el, size_t size);
7258+
7259+#if TRACE_ALL
7260+static atomic_int pool_seq;
7261+#endif
7262+
7263+// Pool structure
7264+// Ref count is held by pool owner and pool els that have been got
7265+// Els in the pool do not count towards its ref count
7266+struct cma_pool_fixed_s
7267+{
7268+    atomic_int ref_count;
7269+
7270+    vlc_mutex_t lock;
7271+    unsigned int n_in;
7272+    unsigned int n_out;
7273+    unsigned int pool_size;
7274+    int flight_size;
7275+    size_t el_size;
7276+    void ** pool;
7277+
7278+    bool cancel;
7279+    int in_flight;
7280+    vlc_cond_t flight_cond;
7281+
7282+    void * alloc_v;
7283+    cma_pool_alloc_fn * el_alloc_fn;
7284+    cma_pool_free_fn * el_free_fn;
7285+    cma_pool_on_delete_fn * on_delete_fn;
7286+
7287+    const char * name;
7288+#if TRACE_ALL
7289+    int seq;
7290+#endif
7291+};
7292+
7293+static inline unsigned int inc_mod(const unsigned int n, const unsigned int m)
7294+{
7295+    return n + 1 >= m ? 0 : n + 1;
7296+}
7297+
7298+static void free_pool(const cma_pool_fixed_t * const p, void ** const pool,
7299+                      const unsigned int pool_size, const size_t el_size)
7300+{
7301+    if (pool == NULL)
7302+        return;
7303+
7304+    for (unsigned int n = 0; n != pool_size; ++n)
7305+        if (pool[n] != NULL)
7306+            p->el_free_fn(p->alloc_v, pool[n], el_size);
7307+    free(pool);
7308+}
7309+
7310+// Just kill this - no checks
7311+static void cma_pool_fixed_delete(cma_pool_fixed_t * const p)
7312+{
7313+    cma_pool_on_delete_fn *const on_delete_fn = p->on_delete_fn;
7314+    void *const v = p->alloc_v;
7315+
7316+    free_pool(p, p->pool, p->pool_size, p->el_size);
7317+
7318+    if (p->name != NULL)
7319+        free((void *)p->name);  // Discard const
7320+
7321+    vlc_cond_destroy(&p->flight_cond);
7322+    vlc_mutex_destroy(&p->lock);
7323+    free(p);
7324+
7325+    // Inform our container that we are dead (if it cares)
7326+    if (on_delete_fn)
7327+        on_delete_fn(v);
7328+}
7329+
7330+static void cma_pool_fixed_unref(cma_pool_fixed_t * const p)
7331+{
7332+    if (atomic_fetch_sub(&p->ref_count, 1) <= 1)
7333+        cma_pool_fixed_delete(p);
7334+}
7335+
7336+static void cma_pool_fixed_ref(cma_pool_fixed_t * const p)
7337+{
7338+    atomic_fetch_add(&p->ref_count, 1);
7339+}
7340+
7341+static void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p)
7342+{
7343+    vlc_mutex_lock(&p->lock);
7344+    ++p->in_flight;
7345+    vlc_mutex_unlock(&p->lock);
7346+}
7347+
7348+static void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p)
7349+{
7350+    vlc_mutex_lock(&p->lock);
7351+    if (--p->in_flight == 0)
7352+        vlc_cond_signal(&p->flight_cond);
7353+    vlc_mutex_unlock(&p->lock);
7354+}
7355+
7356+static void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool inc_flight, const bool no_pool)
7357+{
7358+    void * v = NULL;
7359+
7360+    vlc_mutex_lock(&p->lock);
7361+
7362+    for (;;)
7363+    {
7364+        if (req_el_size != p->el_size)
7365+        {
7366+            void ** const deadpool = p->pool;
7367+            const size_t dead_size = p->el_size;
7368+            const unsigned int dead_n = p->pool_size;
7369+
7370+            p->pool = NULL;
7371+            p->n_in = 0;
7372+            p->n_out = 0;
7373+            p->el_size = req_el_size;
7374+
7375+            if (deadpool != NULL)
7376+            {
7377+                vlc_mutex_unlock(&p->lock);
7378+                // Do the free old op outside the mutex in case the free is slow
7379+                free_pool(p, deadpool, dead_n, dead_size);
7380+                vlc_mutex_lock(&p->lock);
7381+                continue;
7382+            }
7383+        }
7384+
7385+        // Late abort if flush or cancel so we can still kill the pool
7386+        if (req_el_size == 0 || p->cancel)
7387+        {
7388+            vlc_mutex_unlock(&p->lock);
7389+            return NULL;
7390+        }
7391+
7392+        if (p->pool != NULL && !no_pool)
7393+        {
7394+            v = p->pool[p->n_in];
7395+            if (v != NULL)
7396+            {
7397+                p->pool[p->n_in] = NULL;
7398+                p->n_in = inc_mod(p->n_in, p->pool_size);
7399+                break;
7400+            }
7401+        }
7402+
7403+        if (p->in_flight <= 0)
7404+            break;
7405+
7406+        vlc_cond_wait(&p->flight_cond, &p->lock);
7407+    }
7408+
7409+    if (inc_flight)
7410+        ++p->in_flight;
7411+
7412+    vlc_mutex_unlock(&p->lock);
7413+
7414+    if (v == NULL && req_el_size != 0)
7415+        v = p->el_alloc_fn(p->alloc_v, req_el_size);
7416+
7417+    // Tag ref
7418+    if (v != NULL)
7419+        cma_pool_fixed_ref(p);
7420+    // Remove flight if we set it and error
7421+    else if (inc_flight)
7422+        cma_pool_fixed_dec_in_flight(p);
7423+
7424+    return v;
7425+}
7426+
7427+static void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight)
7428+{
7429+    vlc_mutex_lock(&p->lock);
7430+
7431+    if (el_size == p->el_size && (p->pool == NULL || p->pool[p->n_out] == NULL))
7432+    {
7433+        if (p->pool == NULL)
7434+            p->pool = calloc(p->pool_size, sizeof(void*));
7435+
7436+        p->pool[p->n_out] = v;
7437+        p->n_out = inc_mod(p->n_out, p->pool_size);
7438+        v = NULL;
7439+    }
7440+
7441+    if (was_in_flight)
7442+        --p->in_flight;
7443+
7444+    vlc_mutex_unlock(&p->lock);
7445+
7446+    vlc_cond_signal(&p->flight_cond);
7447+
7448+    if (v != NULL)
7449+        p->el_free_fn(p->alloc_v, v, el_size);
7450+
7451+    cma_pool_fixed_unref(p);
7452+}
7453+
7454+static int cma_pool_fixed_resize(cma_pool_fixed_t * const p,
7455+                           const unsigned int new_pool_size, const int new_flight_size)
7456+{
7457+    void ** dead_pool = NULL;
7458+    size_t dead_size = 0;
7459+    unsigned int dead_n = 0;
7460+
7461+    // This makes this non-reentrant but saves us a lot of time in the normal
7462+    // "nothing happens" case
7463+    if (p->pool_size == new_pool_size && p->flight_size == new_flight_size)
7464+        return 0;
7465+
7466+    vlc_mutex_lock(&p->lock);
7467+
7468+    if (p->pool != NULL && new_pool_size != p->pool_size)
7469+    {
7470+        void ** const new_pool = calloc(new_pool_size, sizeof(void*));
7471+        unsigned int d, s;
7472+        dead_pool = p->pool;
7473+        dead_size = p->el_size;
7474+        dead_n = p->pool_size;
7475+
7476+        if (new_pool == NULL)
7477+        {
7478+            vlc_mutex_unlock(&p->lock);
7479+            return -1;
7480+        }
7481+
7482+        for (d = 0, s = p->n_in; d != new_pool_size && (new_pool[d] = dead_pool[s]) != NULL; ++d, s = inc_mod(s, dead_n))
7483+            dead_pool[s] = NULL;
7484+
7485+        p->n_out = 0;
7486+        p->n_in = (d != new_pool_size) ? d : 0;
7487+        p->pool = new_pool;
7488+    }
7489+
7490+    p->pool_size = new_pool_size;
7491+    if (new_flight_size > p->flight_size)
7492+        vlc_cond_broadcast(&p->flight_cond);  // Lock still active so nothing happens till we release it
7493+    p->in_flight += p->flight_size - new_flight_size;
7494+    p->flight_size = new_flight_size;
7495+
7496+    vlc_mutex_unlock(&p->lock);
7497+
7498+    free_pool(p, dead_pool, dead_n, dead_size);
7499+    return 0;
7500+}
7501+
7502+static int cma_pool_fixed_fill(cma_pool_fixed_t * const p, const size_t el_size)
7503+{
7504+    for (;;)
7505+    {
7506+        vlc_mutex_lock(&p->lock);
7507+        bool done = el_size == p->el_size && p->pool != NULL && p->pool[p->n_out] != NULL;
7508+        vlc_mutex_unlock(&p->lock);
7509+        if (done)
7510+            break;
7511+        void * buf = cma_pool_fixed_get(p, el_size, false, true);
7512+        if (buf == NULL)
7513+            return -ENOMEM;
7514+        cma_pool_fixed_put(p, buf, el_size, false);
7515+    }
7516+    return 0;
7517+}
7518+
7519+static void cma_pool_fixed_cancel(cma_pool_fixed_t * const p)
7520+{
7521+    vlc_mutex_lock(&p->lock);
7522+    p->cancel = true;
7523+    vlc_cond_broadcast(&p->flight_cond);
7524+    vlc_mutex_unlock(&p->lock);
7525+}
7526+
7527+static void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p)
7528+{
7529+    vlc_mutex_lock(&p->lock);
7530+    p->cancel = false;
7531+    vlc_mutex_unlock(&p->lock);
7532+}
7533+
7534+
7535+// Purge pool & unref
7536+static void cma_pool_fixed_kill(cma_pool_fixed_t * const p)
7537+{
7538+    if (p == NULL)
7539+        return;
7540+
7541+    // This flush is not strictly needed but it reclaims what memory we can reclaim asap
7542+    cma_pool_fixed_get(p, 0, false, false);
7543+    cma_pool_fixed_unref(p);
7544+}
7545+
7546+// Create a new pool
7547+static cma_pool_fixed_t*
7548+cma_pool_fixed_new(const unsigned int pool_size,
7549+                   const int flight_size,
7550+                   void * const alloc_v,
7551+                   cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn,
7552+                   cma_pool_on_delete_fn * const on_delete_fn,
7553+                   const char * const name)
7554+{
7555+    cma_pool_fixed_t* const p = calloc(1, sizeof(cma_pool_fixed_t));
7556+    if (p == NULL)
7557+        return NULL;
7558+
7559+    atomic_store(&p->ref_count, 1);
7560+    vlc_mutex_init(&p->lock);
7561+    vlc_cond_init(&p->flight_cond);
7562+
7563+    p->pool_size = pool_size;
7564+    p->flight_size = flight_size;
7565+    p->in_flight = -flight_size;
7566+
7567+    p->alloc_v = alloc_v;
7568+    p->el_alloc_fn = alloc_fn;
7569+    p->el_free_fn = free_fn;
7570+    p->on_delete_fn = on_delete_fn;
7571+    p->name = name == NULL ? NULL : strdup(name);
7572+#if TRACE_ALL
7573+    p->seq = atomic_fetch_add(&pool_seq, 1);
7574+#endif
7575+
7576+    return p;
7577+}
7578+
7579+// ---------------------------------------------------------------------------
7580+//
7581+// CMA buffer functions - uses cma_pool_fixed for pooling
7582+
7583+struct cma_buf_pool_s {
7584+    cma_pool_fixed_t * pool;
7585+    vcsm_init_type_t init_type;
7586+
7587+    bool all_in_flight;
7588+#if TRACE_ALL
7589+    size_t alloc_n;
7590+    size_t alloc_size;
7591+#endif
7592+};
7593+
7594+typedef struct cma_buf_s {
7595+    atomic_int ref_count;
7596+    cma_buf_pool_t * cbp;
7597+    bool in_flight;
7598+    size_t size;
7599+    unsigned int vcsm_h;   // VCSM handle from initial alloc
7600+    unsigned int vc_h;     // VC handle for ZC mmal buffers
7601+    unsigned int vc_addr;  // VC addr - unused by us but wanted by FFmpeg
7602+    int fd;                // dmabuf handle for GL
7603+    void * mmap;           // ARM mapped address
7604+    picture_context_t *ctx2;
7605+} cma_buf_t;
7606+
7607+static void cma_pool_delete(cma_buf_t * const cb)
7608+{
7609+    assert(atomic_load(&cb->ref_count) == 0);
7610+#if TRACE_ALL
7611+    cb->cbp->alloc_size -= cb->size;
7612+    --cb->cbp->alloc_n;
7613+    fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cb->cbp->pool->seq, cb->cbp->pool->name, cb->cbp->alloc_n, cb->cbp->alloc_size);
7614+#endif
7615+
7616+    if (cb->ctx2 != NULL)
7617+        cb->ctx2->destroy(cb->ctx2);
7618+
7619+    if (cb->mmap != MAP_FAILED)
7620+    {
7621+        if (cb->cbp->init_type == VCSM_INIT_CMA)
7622+            munmap(cb->mmap, cb->size);
7623+        else
7624+            vcsm_unlock_hdl(cb->vcsm_h);
7625+    }
7626+    if (cb->fd != -1)
7627+        close(cb->fd);
7628+    if (cb->vcsm_h != 0)
7629+        vcsm_free(cb->vcsm_h);
7630+    free(cb);
7631+}
7632+
7633+static void cma_pool_free_cb(void * v, void * el, size_t size)
7634+{
7635+    VLC_UNUSED(v);
7636+    VLC_UNUSED(size);
7637+
7638+    cma_pool_delete(el);
7639+}
7640+
7641+static void * cma_pool_alloc_cb(void * v, size_t size)
7642+{
7643+    cma_buf_pool_t * const cbp = v;
7644+
7645+    cma_buf_t * const cb = malloc(sizeof(cma_buf_t));
7646+    if (cb == NULL)
7647+        return NULL;
7648+
7649+    *cb = (cma_buf_t){
7650+        .ref_count = ATOMIC_VAR_INIT(0),
7651+        .cbp = cbp,
7652+        .in_flight = 0,
7653+        .size = size,
7654+        .vcsm_h = 0,
7655+        .vc_h = 0,
7656+        .fd = -1,
7657+        .mmap = MAP_FAILED,
7658+        .ctx2 = NULL
7659+    };
7660+#if TRACE_ALL
7661+    cb->cbp->alloc_size += cb->size;
7662+    ++cb->cbp->alloc_n;
7663+    fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cbp->pool->seq, cbp->pool->name, cbp->alloc_n, cbp->alloc_size);
7664+#endif
7665+
7666+    // 0x80 is magic value to force full ARM-side mapping - otherwise
7667+    // cache requests can cause kernel crashes
7668+    if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST | 0x80, "VLC frame")) == 0)
7669+    {
7670+#if TRACE_ALL
7671+        fprintf(stderr, "vcsm_malloc_cache fail\n");
7672+#endif
7673+        goto fail;
7674+    }
7675+
7676+    if ((cb->vc_h = vcsm_vc_hdl_from_hdl(cb->vcsm_h)) == 0)
7677+    {
7678+#if TRACE_ALL
7679+        fprintf(stderr, "vcsm_vc_hdl_from_hdl fail\n");
7680+#endif
7681+        goto fail;
7682+    }
7683+
7684+    if (cbp->init_type == VCSM_INIT_CMA)
7685+    {
7686+        if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1)
7687+        {
7688+#if TRACE_ALL
7689+            fprintf(stderr, "vcsm_export_dmabuf fail\n");
7690+#endif
7691+            goto fail;
7692+        }
7693+
7694+        if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED)
7695+            goto fail;
7696+    }
7697+    else
7698+    {
7699+        void * arm_addr;
7700+        if ((arm_addr = vcsm_lock(cb->vcsm_h)) == NULL)
7701+        {
7702+#if TRACE_ALL
7703+            fprintf(stderr, "vcsm_lock fail\n");
7704+#endif
7705+            goto fail;
7706+        }
7707+        cb->mmap = arm_addr;
7708+    }
7709+
7710+    cb->vc_addr = vcsm_vc_addr_from_hdl(cb->vcsm_h);
7711+
7712+    return cb;
7713+
7714+fail:
7715+    cma_pool_delete(cb);
7716+    return NULL;
7717+}
7718+
7719+// Pool has died - safe now to exit vcsm
7720+static void cma_buf_pool_on_delete_cb(void * v)
7721+{
7722+    cma_buf_pool_t * const cbp = v;
7723+
7724+    cma_vcsm_exit(cbp->init_type);
7725+    free(cbp);
7726+}
7727+
7728+void cma_buf_pool_cancel(cma_buf_pool_t * const cbp)
7729+{
7730+    if (cbp == NULL || cbp->pool == NULL)
7731+        return;
7732+
7733+    cma_pool_fixed_cancel(cbp->pool);
7734+}
7735+
7736+void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp)
7737+{
7738+    if (cbp == NULL || cbp->pool == NULL)
7739+        return;
7740+
7741+    cma_pool_fixed_uncancel(cbp->pool);
7742+}
7743+
7744+// User finished with pool
7745+void cma_buf_pool_delete(cma_buf_pool_t * const cbp)
7746+{
7747+    if (cbp == NULL)
7748+        return;
7749+
7750+    if (cbp->pool != NULL)
7751+    {
7752+        // We will call cma_buf_pool_on_delete_cb when the pool finally dies
7753+        // (might be now) which will free up our env.
7754+        cma_pool_fixed_kill(cbp->pool);
7755+    }
7756+    else
7757+    {
7758+        // Had no pool for some reason (error) but must still finish cleanup
7759+        cma_buf_pool_on_delete_cb(cbp);
7760+    }
7761+}
7762+
7763+int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size)
7764+{
7765+    return cma_pool_fixed_fill(cbp->pool, el_size);
7766+}
7767+
7768+int cma_buf_pool_resize(cma_buf_pool_t * const cbp,
7769+                        const unsigned int new_pool_size, const int new_flight_size)
7770+{
7771+    return cma_pool_fixed_resize(cbp->pool, new_pool_size, new_flight_size);
7772+}
7773+
7774+cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size, const bool all_in_flight, const char * const name)
7775+{
7776+    vcsm_init_type_t const init_type = cma_vcsm_init();
7777+    if (init_type == VCSM_INIT_NONE)
7778+        return NULL;
7779+
7780+    cma_buf_pool_t * const cbp = calloc(1, sizeof(cma_buf_pool_t));
7781+    if (cbp == NULL)
7782+        return NULL;
7783+
7784+    cbp->init_type = init_type;
7785+    cbp->all_in_flight = all_in_flight;
7786+
7787+    if ((cbp->pool = cma_pool_fixed_new(pool_size, flight_size, cbp, cma_pool_alloc_cb, cma_pool_free_cb, cma_buf_pool_on_delete_cb, name)) == NULL)
7788+        goto fail;
7789+    return cbp;
7790+
7791+fail:
7792+    cma_buf_pool_delete(cbp);
7793+    return NULL;
7794+}
7795+
7796+
7797+void cma_buf_in_flight(cma_buf_t * const cb)
7798+{
7799+    if (!cb->cbp->all_in_flight)
7800+    {
7801+        assert(!cb->in_flight);
7802+        cb->in_flight = true;
7803+        cma_pool_fixed_inc_in_flight(cb->cbp->pool);
7804+    }
7805+}
7806+
7807+void cma_buf_end_flight(cma_buf_t * const cb)
7808+{
7809+    if (cb != NULL && !cb->cbp->all_in_flight && cb->in_flight)
7810+    {
7811+        cb->in_flight = false;
7812+        cma_pool_fixed_dec_in_flight(cb->cbp->pool);
7813+    }
7814+}
7815+
7816+
7817+// Return vcsm handle
7818+unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb)
7819+{
7820+    return cb->vcsm_h;
7821+}
7822+
7823+size_t cma_buf_size(const cma_buf_t * const cb)
7824+{
7825+    return cb->size;
7826+}
7827+
7828+int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2)
7829+{
7830+    if (cb->ctx2 != NULL)
7831+        return VLC_EGENERIC;
7832+
7833+    cb->ctx2 = ctx2;
7834+    return VLC_SUCCESS;
7835+}
7836+
7837+unsigned int cma_buf_vc_handle(const cma_buf_t *const cb)
7838+{
7839+    return cb->vc_h;
7840+}
7841+
7842+int cma_buf_fd(const cma_buf_t *const cb)
7843+{
7844+    return cb->fd;
7845+}
7846+
7847+void * cma_buf_addr(const cma_buf_t *const cb)
7848+{
7849+    return cb->mmap;
7850+}
7851+
7852+unsigned int cma_buf_vc_addr(const cma_buf_t *const cb)
7853+{
7854+    return cb->vc_addr;
7855+}
7856+
7857+
7858+picture_context_t * cma_buf_context2(const cma_buf_t *const cb)
7859+{
7860+    return cb->ctx2;
7861+}
7862+
7863+
7864+void cma_buf_unref(cma_buf_t * const cb)
7865+{
7866+    if (cb == NULL)
7867+        return;
7868+    if (atomic_fetch_sub(&cb->ref_count, 1) <= 1)
7869+    {
7870+        const bool was_in_flight = cb->in_flight;
7871+        cb->in_flight = false;
7872+        cma_pool_fixed_put(cb->cbp->pool, cb, cb->size, was_in_flight);
7873+    }
7874+}
7875+
7876+cma_buf_t * cma_buf_ref(cma_buf_t * const cb)
7877+{
7878+    if (cb == NULL)
7879+        return NULL;
7880+    atomic_fetch_add(&cb->ref_count, 1);
7881+    return cb;
7882+}
7883+
7884+cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const cbp, const size_t size)
7885+{
7886+    cma_buf_t *const cb = cma_pool_fixed_get(cbp->pool, size, cbp->all_in_flight, false);
7887+
7888+    if (cb == NULL)
7889+        return NULL;
7890+
7891+    cb->in_flight = cbp->all_in_flight;
7892+    // When 1st allocated or retrieved from the pool the block will have a
7893+    // ref count of 0 so ref here
7894+    return cma_buf_ref(cb);
7895+}
7896+
7897--- /dev/null
7898+++ b/modules/hw/mmal/mmal_cma.h
7899@@ -0,0 +1,71 @@
7900+#ifndef VLC_MMAL_MMAL_CMA_H_
7901+#define VLC_MMAL_MMAL_CMA_H_
7902+
7903+
7904+struct cma_pool_fixed_s;
7905+typedef struct cma_pool_fixed_s cma_pool_fixed_t;
7906+
7907+typedef void * cma_pool_alloc_fn(void * v, size_t size);
7908+typedef void cma_pool_free_fn(void * v, void * el, size_t size);
7909+typedef void cma_pool_on_delete_fn(void * v);
7910+
7911+#if 0
7912+void cma_pool_fixed_unref(cma_pool_fixed_t * const p);
7913+void cma_pool_fixed_ref(cma_pool_fixed_t * const p);
7914+void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool in_flight);
7915+void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight);
7916+void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p);
7917+void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p);
7918+void cma_pool_fixed_cancel(cma_pool_fixed_t * const p);
7919+void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p);
7920+void cma_pool_fixed_kill(cma_pool_fixed_t * const p);
7921+int cma_pool_fixed_resize(cma_pool_fixed_t * const p,
7922+                          const unsigned int new_pool_size, const int new_flight_size);
7923+cma_pool_fixed_t * cma_pool_fixed_new(const unsigned int pool_size,
7924+                   const int flight_size,
7925+                   void * const alloc_v,
7926+                   cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn,
7927+                   cma_pool_on_delete_fn * const on_delete_fn,
7928+                   const char * const name);
7929+#endif
7930+
7931+struct cma_buf_s;
7932+typedef struct cma_buf_s cma_buf_t;
7933+
7934+void cma_buf_in_flight(cma_buf_t * const cb);
7935+void cma_buf_end_flight(cma_buf_t * const cb);
7936+unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb);
7937+size_t cma_buf_size(const cma_buf_t * const cb);
7938+int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2);
7939+unsigned int cma_buf_vc_handle(const cma_buf_t *const cb);
7940+int cma_buf_fd(const cma_buf_t *const cb);
7941+void * cma_buf_addr(const cma_buf_t *const cb);
7942+unsigned int cma_buf_vc_addr(const cma_buf_t *const cb);
7943+picture_context_t * cma_buf_context2(const cma_buf_t *const cb);
7944+
7945+void cma_buf_unref(cma_buf_t * const cb);
7946+cma_buf_t * cma_buf_ref(cma_buf_t * const cb);
7947+
7948+struct cma_buf_pool_s;
7949+typedef struct cma_buf_pool_s cma_buf_pool_t;
7950+
7951+cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const p, const size_t size);
7952+void cma_buf_pool_cancel(cma_buf_pool_t * const cbp);
7953+void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp);
7954+void cma_buf_pool_delete(cma_buf_pool_t * const p);
7955+int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size);
7956+int cma_buf_pool_resize(cma_buf_pool_t * const cbp,
7957+                          const unsigned int new_pool_size, const int new_flight_size);
7958+cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size,
7959+                                  const bool all_in_flight, const char * const name);
7960+
7961+static inline void cma_buf_pool_deletez(cma_buf_pool_t ** const pp)
7962+{
7963+    cma_buf_pool_t * const p = *pp;
7964+    if (p != NULL) {
7965+        *pp = NULL;
7966+        cma_buf_pool_delete(p);
7967+    }
7968+}
7969+
7970+#endif // VLC_MMAL_MMAL_CMA_H_
7971--- /dev/null
7972+++ b/modules/hw/mmal/mmal_gl.h
7973@@ -0,0 +1,45 @@
7974+// Trim this include list!
7975+
7976+#include <libdrm/drm.h>
7977+#include <libdrm/drm_mode.h>
7978+#include <libdrm/drm_fourcc.h>
7979+//#include <xf86drm.h>
7980+//#include <xf86drmMode.h>
7981+#include <X11/Xlib.h>
7982+#include <X11/Xutil.h>
7983+#include <X11/Xlib-xcb.h>
7984+#include <epoxy/gl.h>
7985+#include <epoxy/egl.h>
7986+#include <xcb/xcb.h>
7987+#include <xcb/dri3.h>
7988+
7989+struct mmal_gl_converter_s;
7990+
7991+typedef struct cma_buf_s {
7992+    struct mmal_gl_converter_s * sys;
7993+
7994+    size_t size;
7995+    __u32 h_dumb;
7996+    int fd;
7997+    unsigned int h_vcsm;
7998+    void * mapped_addr;
7999+    GLuint texture;
8000+} cma_buf_t;
8001+
8002+typedef struct cma_pic_sys_s {
8003+    cma_buf_t * cmabuf;
8004+} cma_pic_sys_t;
8005+
8006+static inline unsigned int
8007+hw_mmal_h_vcsm(const picture_t * const pic)
8008+{
8009+    const cma_pic_sys_t *const pic_sys = (cma_pic_sys_t *)pic->p_sys;
8010+
8011+    if (pic->format.i_chroma != VLC_CODEC_MMAL_GL_RGB32 ||
8012+        pic_sys == NULL || pic_sys->cmabuf == NULL) {
8013+        return 0;
8014+    }
8015+
8016+    return pic_sys->cmabuf->h_vcsm;
8017+}
8018+
8019--- /dev/null
8020+++ b/modules/hw/mmal/mmal_piccpy_neon.S
8021@@ -0,0 +1,105 @@
8022+// Copy pix
8023+
8024+        .syntax unified
8025+        .arm
8026+//      .thumb
8027+        .text
8028+        .align 16
8029+        .arch armv7-a
8030+        .fpu neon-vfpv4
8031+
8032+
8033+.macro  function name
8034+        .global  \name
8035+#ifdef __ELF__
8036+        .type    \name, %function
8037+#endif
8038+\name:
8039+.endm
8040+
8041+
8042+.macro  piccpy_to_8, bit_depth
8043+        subs     r2, #128
8044+        vpush    {q4-q7}
8045+        blt      2f
8046+1:
8047+        vldm     r1!, {q0-q7}
8048+        subs     r2, #128
8049+        vqrshrn.u16 d0,  q0,  #\bit_depth - 8
8050+        vqrshrn.u16 d1,  q1,  #\bit_depth - 8
8051+        vqrshrn.u16 d2,  q2,  #\bit_depth - 8
8052+        vqrshrn.u16 d3,  q3,  #\bit_depth - 8
8053+        vldm     r1!, {q8-q15}
8054+        vqrshrn.u16 d4,  q4,  #\bit_depth - 8
8055+        vqrshrn.u16 d5,  q5,  #\bit_depth - 8
8056+        vqrshrn.u16 d6,  q6,  #\bit_depth - 8
8057+        vqrshrn.u16 d7,  q7,  #\bit_depth - 8
8058+        vqrshrn.u16 d8,  q8,  #\bit_depth - 8
8059+        vqrshrn.u16 d9,  q9,  #\bit_depth - 8
8060+        vqrshrn.u16 d10, q10, #\bit_depth - 8
8061+        vqrshrn.u16 d11, q11, #\bit_depth - 8
8062+        vqrshrn.u16 d12, q12, #\bit_depth - 8
8063+        vqrshrn.u16 d13, q13, #\bit_depth - 8
8064+        vqrshrn.u16 d14, q14, #\bit_depth - 8
8065+        vqrshrn.u16 d15, q15, #\bit_depth - 8
8066+        vstm     r0!, {q0-q7}
8067+        bge      1b
8068+2:
8069+        adds     r2, #64
8070+        blt      1f
8071+
8072+        vldm     r1!, {q0-q7}
8073+        vqrshrn.u16 d0,  q0,  #\bit_depth - 8
8074+        vqrshrn.u16 d1,  q1,  #\bit_depth - 8
8075+        vqrshrn.u16 d2,  q2,  #\bit_depth - 8
8076+        vqrshrn.u16 d3,  q3,  #\bit_depth - 8
8077+        vqrshrn.u16 d4,  q4,  #\bit_depth - 8
8078+        vqrshrn.u16 d5,  q5,  #\bit_depth - 8
8079+        vqrshrn.u16 d6,  q6,  #\bit_depth - 8
8080+        vqrshrn.u16 d7,  q7,  #\bit_depth - 8
8081+        vstm     r0!, {q0-q3}
8082+1:
8083+        adds     r2, #32
8084+        blt      1f
8085+
8086+        vldm     r1!, {q0-q3}
8087+        vqrshrn.u16 d0,  q0,  #\bit_depth - 8
8088+        vqrshrn.u16 d1,  q1,  #\bit_depth - 8
8089+        vqrshrn.u16 d2,  q2,  #\bit_depth - 8
8090+        vqrshrn.u16 d3,  q3,  #\bit_depth - 8
8091+        vstm     r0!, {q0-q1}
8092+1:
8093+        adds     r2, #16
8094+        blt      1f
8095+
8096+        vldm     r1!, {q0-q1}
8097+        vqrshrn.u16 d0,  q0,  #\bit_depth - 8
8098+        vqrshrn.u16 d1,  q1,  #\bit_depth - 8
8099+        vstm     r0!, {q0}
8100+1:
8101+        adds     r2, #8
8102+        blt      1f
8103+
8104+        vldm     r1!, {q0}
8105+        vqrshrn.u16 d0,  q0,  #\bit_depth - 8
8106+        vstr     d0, [r0]
8107+        add      r0, #8
8108+1:
8109+        adds     r2, #4
8110+        blt      1f
8111+
8112+        vldr     d0, [r1]
8113+        vqrshrn.u16 d0,  q0,  #\bit_depth - 8
8114+        vstr     s0, [r0]
8115+1:
8116+        vpop     {q4-q7}
8117+        bx       lr
8118+.endm
8119+
8120+
8121+@ [r0] Dest
8122+@ [r1] Src
8123+@  r2  Pels
8124+function mmal_piccpy_10_to_8_neon
8125+        piccpy_to_8 10
8126+
8127--- a/modules/hw/mmal/mmal_picture.c
8128+++ b/modules/hw/mmal/mmal_picture.c
8129@@ -21,25 +21,1542 @@
8130  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
8131  *****************************************************************************/
8132
8133+// We would really like to use vlc_thread.h but the detach thread stuff can't be
8134+// used here :-(
8135+#include <pthread.h>
8136+
8137+#include <stdatomic.h>
8138+#include <unistd.h>
8139+#include <fcntl.h>
8140+
8141 #include <vlc_common.h>
8142+#include <vlc_cpu.h>
8143 #include <vlc_picture.h>
8144+
8145+#pragma GCC diagnostic push
8146+#pragma GCC diagnostic ignored "-Wbad-function-cast"
8147+#include <bcm_host.h>
8148+#pragma GCC diagnostic pop
8149 #include <interface/mmal/mmal.h>
8150+#include <interface/mmal/util/mmal_util.h>
8151+#include <interface/mmal/util/mmal_default_components.h>
8152+#include <interface/vmcs_host/vcgencmd.h>
8153+#include <interface/vcsm/user-vcsm.h>
8154
8155+#include "mmal_cma.h"
8156 #include "mmal_picture.h"
8157+#include "transform_ops.h"
8158+
8159+#define TRACE_TRANSFORMS 0
8160+
8161+#define UINT64_SIZE(s) (((s) + sizeof(uint64_t) - 1)/sizeof(uint64_t))
8162+
8163+static inline char safe_char(const unsigned int c0)
8164+{
8165+    const unsigned int c = c0 & 0xff;
8166+    return c > ' ' && c < 0x7f ? c : '.';
8167+}
8168+
8169+const char * str_fourcc(char * const buf, const unsigned int fcc)
8170+{
8171+    if (fcc == 0)
8172+        return "----";
8173+    buf[0] = safe_char(fcc >> 0);
8174+    buf[1] = safe_char(fcc >> 8);
8175+    buf[2] = safe_char(fcc >> 16);
8176+    buf[3] = safe_char(fcc >> 24);
8177+    buf[4] = 0;
8178+    return buf;
8179+}
8180+
8181+// WB + Inv
8182+static inline void flush_range(void * const start, const size_t len)
8183+{
8184+    uint64_t buf[UINT64_SIZE(sizeof(struct vcsm_user_clean_invalid2_s) + sizeof(struct vcsm_user_clean_invalid2_block_s))];
8185+    struct vcsm_user_clean_invalid2_s * const b = (struct vcsm_user_clean_invalid2_s *)buf;
8186+
8187+    *b = (struct vcsm_user_clean_invalid2_s){
8188+        .op_count = 1
8189+    };
8190+
8191+    b->s[0] = (struct vcsm_user_clean_invalid2_block_s){
8192+        .invalidate_mode = 3,   // wb + invalidate
8193+        .block_count = 1,
8194+        .start_address = start, // Rely on clean inv to fix up align & size boundries
8195+        .block_size = len,
8196+        .inter_block_stride = 0
8197+    };
8198+
8199+    vcsm_clean_invalid2(b);
8200+}
8201+
8202+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs)
8203+{
8204+    switch (vlc_cs)
8205+    {
8206+        case COLOR_SPACE_BT601:
8207+            return MMAL_COLOR_SPACE_ITUR_BT601;
8208+        case COLOR_SPACE_BT709:
8209+            return MMAL_COLOR_SPACE_ITUR_BT709;
8210+        default:
8211+            break;
8212+    }
8213+    return MMAL_COLOR_SPACE_UNKNOWN;
8214+}
8215+
8216+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc)
8217+{
8218+    switch (vf_vlc->i_chroma) {
8219+        case VLC_CODEC_MMAL_ZC_RGB32:
8220+        case VLC_CODEC_RGB32:
8221+        {
8222+            // VLC RGB32 aka RV32 means we have to look at the mask values
8223+            const uint32_t r = vf_vlc->i_rmask;
8224+            const uint32_t g = vf_vlc->i_gmask;
8225+            const uint32_t b = vf_vlc->i_bmask;
8226+            if (r == 0xff0000 && g == 0xff00 && b == 0xff)
8227+                return MMAL_ENCODING_BGRA;
8228+            if (r == 0xff && g == 0xff00 && b == 0xff0000)
8229+                return MMAL_ENCODING_RGBA;
8230+            if (r == 0xff000000 && g == 0xff0000 && b == 0xff00)
8231+                return MMAL_ENCODING_ABGR;
8232+            if (r == 0xff00 && g == 0xff0000 && b == 0xff000000)
8233+                return MMAL_ENCODING_ARGB;
8234+            break;
8235+        }
8236+        case VLC_CODEC_RGB16:
8237+        {
8238+            // VLC RGB16 aka RV16 means we have to look at the mask values
8239+            const uint32_t r = vf_vlc->i_rmask;
8240+            const uint32_t g = vf_vlc->i_gmask;
8241+            const uint32_t b = vf_vlc->i_bmask;
8242+            if (r == 0xf800 && g == 0x7e0 && b == 0x1f)
8243+                return MMAL_ENCODING_RGB16;
8244+            break;
8245+        }
8246+        case VLC_CODEC_I420:
8247+        case VLC_CODEC_MMAL_ZC_I420:
8248+            return MMAL_ENCODING_I420;
8249+        case VLC_CODEC_RGBA:
8250+            return MMAL_ENCODING_RGBA;
8251+        case VLC_CODEC_BGRA:
8252+            return MMAL_ENCODING_BGRA;
8253+        case VLC_CODEC_ARGB:
8254+            return MMAL_ENCODING_ARGB;
8255+        // VLC_CODEC_ABGR does not exist in VLC
8256+        case VLC_CODEC_MMAL_OPAQUE:
8257+            return MMAL_ENCODING_OPAQUE;
8258+        case VLC_CODEC_MMAL_ZC_SAND8:
8259+            return MMAL_ENCODING_YUVUV128;
8260+        case VLC_CODEC_MMAL_ZC_SAND10:
8261+            return MMAL_ENCODING_YUVUV64_10;
8262+        case VLC_CODEC_MMAL_ZC_SAND30:
8263+            return MMAL_ENCODING_YUV10_COL;
8264+        default:
8265+            break;
8266+    }
8267+    return 0;
8268+}
8269+
8270+static void vlc_fmt_to_video_format(MMAL_VIDEO_FORMAT_T *const vf_mmal, const video_frame_format_t * const vf_vlc)
8271+{
8272+    const unsigned int wmask = (vf_vlc->i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
8273+                                vf_vlc->i_chroma == VLC_CODEC_I420) ? 31 : 15;
8274+
8275+    vf_mmal->width          = (vf_vlc->i_width + wmask) & ~wmask;
8276+    vf_mmal->height         = (vf_vlc->i_height + 15) & ~15;
8277+    vf_mmal->crop.x         = vf_vlc->i_x_offset;
8278+    vf_mmal->crop.y         = vf_vlc->i_y_offset;
8279+    vf_mmal->crop.width     = vf_vlc->i_visible_width;
8280+    vf_mmal->crop.height    = vf_vlc->i_visible_height;
8281+    if (vf_vlc->i_sar_num == 0 || vf_vlc->i_sar_den == 0) {
8282+        vf_mmal->par.num        = 1;
8283+        vf_mmal->par.den        = 1;
8284+    } else {
8285+        vf_mmal->par.num        = vf_vlc->i_sar_num;
8286+        vf_mmal->par.den        = vf_vlc->i_sar_den;
8287+    }
8288+    vf_mmal->frame_rate.num = vf_vlc->i_frame_rate;
8289+    vf_mmal->frame_rate.den = vf_vlc->i_frame_rate_base;
8290+    vf_mmal->color_space    = vlc_to_mmal_color_space(vf_vlc->space);
8291+}
8292+
8293+
8294+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc)
8295+{
8296+    vlc_fmt_to_video_format(&es_fmt->es->video, vf_vlc);
8297+}
8298+
8299+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic)
8300+{
8301+    MMAL_VIDEO_FORMAT_T vf_new_ss;
8302+    MMAL_VIDEO_FORMAT_T *const vf_old = &es_fmt->es->video;
8303+    MMAL_VIDEO_FORMAT_T *const vf_new = &vf_new_ss;
8304+
8305+    vlc_fmt_to_video_format(vf_new, &pic->format);
8306+
8307+    // If we have a format that might have come from ffmpeg then rework for
8308+    // a better guess as to layout. All sand stuff is "special" with regards to
8309+    // width/height vs real layout so leave as is if that
8310+    if ((pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
8311+         pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) &&
8312+        pic->p[0].i_pixel_pitch != 0)
8313+    {
8314+        // Now overwrite width/height with a better guess as to actual layout info
8315+        vf_new->height = pic->p[0].i_lines;
8316+        vf_new->width = pic->p[0].i_pitch / pic->p[0].i_pixel_pitch;
8317+    }
8318+
8319+    if (
8320+        vf_new->width          != vf_old->width          ||
8321+        vf_new->height         != vf_old->height         ||
8322+        vf_new->crop.x         != vf_old->crop.x         ||
8323+        vf_new->crop.y         != vf_old->crop.y         ||
8324+        vf_new->crop.width     != vf_old->crop.width     ||
8325+        vf_new->crop.height    != vf_old->crop.height    ||
8326+        vf_new->par.num        != vf_old->par.num        ||
8327+        vf_new->par.den        != vf_old->par.den        ||
8328+        // Frame rate ignored
8329+        vf_new->color_space    != vf_old->color_space)
8330+    {
8331+#if 0
8332+        char dbuf0[5], dbuf1[5];
8333+        printf("%dx%d (%d,%d %dx%d) par:%d/%d %s -> %dx%d (%d,%d %dx%d) par:%d/%d %s\n",
8334+               vf_old->width          ,
8335+               vf_old->height         ,
8336+               vf_old->crop.x         ,
8337+               vf_old->crop.y         ,
8338+               vf_old->crop.width     ,
8339+               vf_old->crop.height    ,
8340+               vf_old->par.num        ,
8341+               vf_old->par.den        ,
8342+               str_fourcc(dbuf0, vf_old->color_space)    ,
8343+               vf_new->width          ,
8344+               vf_new->height         ,
8345+               vf_new->crop.x         ,
8346+               vf_new->crop.y         ,
8347+               vf_new->crop.width     ,
8348+               vf_new->crop.height    ,
8349+               vf_new->par.num        ,
8350+               vf_new->par.den        ,
8351+               str_fourcc(dbuf1, vf_new->color_space)    );
8352+#endif
8353+        *vf_old = *vf_new;
8354+        return true;
8355+    }
8356+    return false;
8357+}
8358+
8359+
8360+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
8361+   const unsigned int headers, const uint32_t payload_size)
8362+{
8363+    hw_mmal_port_pool_ref_t * ppr = calloc(1, sizeof(hw_mmal_port_pool_ref_t));
8364+    if (ppr == NULL)
8365+        return NULL;
8366+
8367+    if ((ppr->pool = mmal_port_pool_create(port, headers, payload_size)) == NULL)
8368+        goto fail;
8369+
8370+    ppr->port = port;
8371+    atomic_store(&ppr->refs, 1);
8372+    return ppr;
8373+
8374+fail:
8375+    free(ppr);
8376+    return NULL;
8377+}
8378+
8379+static void do_detached(void *(*fn)(void *), void * v)
8380+{
8381+    pthread_t dothread;
8382+    pthread_create(&dothread, NULL, fn, v);
8383+    pthread_detach(dothread);
8384+}
8385+
8386+// Destroy a ppr - aranged s.t. it has the correct prototype for a pthread
8387+static void * kill_ppr(void * v)
8388+{
8389+    hw_mmal_port_pool_ref_t * const ppr = v;
8390+    if (ppr->port->is_enabled)
8391+        mmal_port_disable(ppr->port);  // Avoid annoyed messages from MMAL when we kill the pool
8392+    mmal_port_pool_destroy(ppr->port, ppr->pool);
8393+    free(ppr);
8394+    return NULL;
8395+}
8396+
8397+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb)
8398+{
8399+    if (ppr == NULL)
8400+        return;
8401+    if (atomic_fetch_sub(&ppr->refs, 1) != 1)
8402+        return;
8403+    if (in_cb)
8404+        do_detached(kill_ppr, ppr);
8405+    else
8406+        kill_ppr(ppr);
8407+}
8408+
8409+// Put buffer in port if possible - if not then release to pool
8410+// Returns true if sent, false if recycled
8411+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf)
8412+{
8413+    mmal_buffer_header_reset(buf);
8414+    buf->user_data = NULL;
8415+
8416+    if (mmal_port_send_buffer(ppr->port, buf) == MMAL_SUCCESS)
8417+        return true;
8418+    mmal_buffer_header_release(buf);
8419+    return false;
8420+}
8421+
8422+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr)
8423+{
8424+    MMAL_BUFFER_HEADER_T * buf;
8425+    MMAL_STATUS_T err = MMAL_SUCCESS;
8426+
8427+    while ((buf = mmal_queue_get(ppr->pool->queue)) != NULL) {
8428+        if ((err = mmal_port_send_buffer(ppr->port, buf)) != MMAL_SUCCESS)
8429+        {
8430+            mmal_queue_put_back(ppr->pool->queue, buf);
8431+            break;
8432+        }
8433+    }
8434+    return err;
8435+}
8436+
8437+
8438+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
8439+                                    hw_mmal_port_pool_ref_t ** pppr,
8440+                                    MMAL_PORT_T * const port,
8441+                                    const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback)
8442+{
8443+    MMAL_STATUS_T status;
8444+
8445+    port->userdata = (struct MMAL_PORT_USERDATA_T *)obj;
8446+
8447+    status = port_parameter_set_uint32(port, MMAL_PARAMETER_EXTRA_BUFFERS, extra_buffers);
8448+    if (status != MMAL_SUCCESS) {
8449+        msg_Err(obj, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
8450+                status, mmal_status_to_string(status));
8451+        return status;
8452+    }
8453+
8454+    status = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, 1);
8455+    if (status != MMAL_SUCCESS) {
8456+       msg_Err(obj, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
8457+                port->name, status, mmal_status_to_string(status));
8458+       return status;
8459+    }
8460+
8461+    port->format->encoding = MMAL_ENCODING_OPAQUE;
8462+    port->format->encoding_variant = 0;
8463+    if ((status = mmal_port_format_commit(port)) != MMAL_SUCCESS)
8464+    {
8465+        msg_Err(obj, "Failed to commit format on port %s (status=%"PRIx32" %s)",
8466+                 port->name, status, mmal_status_to_string(status));
8467+        return status;
8468+    }
8469+
8470+    port->buffer_num = 30;
8471+    port->buffer_size = port->buffer_size_recommended;
8472+
8473+    if ((*pppr = hw_mmal_port_pool_ref_create(port, port->buffer_num, port->buffer_size)) == NULL) {
8474+        msg_Err(obj, "Failed to create output pool");
8475+        return status;
8476+    }
8477+
8478+    status = mmal_port_enable(port, callback);
8479+    if (status != MMAL_SUCCESS) {
8480+        hw_mmal_port_pool_ref_release(*pppr, false);
8481+        *pppr = NULL;
8482+        msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)",
8483+                port->name, status, mmal_status_to_string(status));
8484+        return status;
8485+    }
8486+
8487+    return MMAL_SUCCESS;
8488+}
8489+
8490+
8491+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn)
8492+{
8493+    pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
8494+    unsigned int i;
8495+
8496+    for (i = 0; i != ctx->buf_count; ++i) {
8497+        if (ctx->bufs[i] != NULL)
8498+            mmal_buffer_header_release(ctx->bufs[i]);
8499+    }
8500+
8501+    cma_buf_end_flight(ctx->cb);
8502+    cma_buf_unref(ctx->cb);
8503+
8504+    free(ctx);
8505+}
8506+
8507+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn)
8508+{
8509+    const pic_ctx_mmal_t * const src_ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
8510+    pic_ctx_mmal_t * const dst_ctx = calloc(1, sizeof(*dst_ctx));
8511+    unsigned int i;
8512+
8513+    if (dst_ctx == NULL)
8514+        return NULL;
8515+
8516+    // Copy
8517+    dst_ctx->cmn = src_ctx->cmn;
8518+
8519+    dst_ctx->cb = cma_buf_ref(src_ctx->cb);
8520+
8521+    dst_ctx->buf_count = src_ctx->buf_count;
8522+    for (i = 0; i != src_ctx->buf_count; ++i) {
8523+        dst_ctx->bufs[i] = src_ctx->bufs[i];
8524+        if (dst_ctx->bufs[i] != NULL)
8525+            mmal_buffer_header_acquire(dst_ctx->bufs[i]);
8526+    }
8527+
8528+    return &dst_ctx->cmn;
8529+}
8530+
8531+static MMAL_BOOL_T
8532+buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata)
8533+{
8534+    hw_mmal_port_pool_ref_t * const ppr = userdata;
8535+
8536+    // Kill the callback - otherwise we will go in circles!
8537+    mmal_buffer_header_pre_release_cb_set(buf, (MMAL_BH_PRE_RELEASE_CB_T)0, NULL);
8538+    mmal_buffer_header_acquire(buf);  // Ref it again
8539+
8540+    // As we have re-acquired the buffer we need a full release
8541+    // (not continue) to zap the ref count back to zero
8542+    // This is "safe" 'cos we have already reset the cb
8543+    hw_mmal_port_pool_ref_recycle(ppr, buf);
8544+    hw_mmal_port_pool_ref_release(ppr, true); // Assume in callback
8545+
8546+    return MMAL_TRUE;
8547+}
8548+
8549+// Buffer belongs to context on successful return from this fn
8550+// is still valid on failure
8551+picture_context_t *
8552+hw_mmal_gen_context(MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr)
8553+{
8554+    pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
8555+
8556+    if (ctx == NULL)
8557+        return NULL;
8558+
8559+    // If we have an associated ppr then ref & set appropriate callbacks
8560+    if (ppr != NULL) {
8561+        hw_mmal_port_pool_ref_acquire(ppr);
8562+        mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr);
8563+        buf->user_data = NULL;
8564+    }
8565+
8566+    ctx->cmn.copy = hw_mmal_pic_ctx_copy;
8567+    ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
8568+
8569+    ctx->buf_count = 1;
8570+    ctx->bufs[0] = buf;
8571+
8572+    return &ctx->cmn;
8573+}
8574+
8575+// n is els
8576+// * Make NEON!
8577+typedef void piccpy_fn(void * dest, const void * src, size_t n);
8578+
8579+extern piccpy_fn mmal_piccpy_10_to_8_neon;
8580+
8581+static void piccpy_10_to_8_c(void * dest, const void * src, size_t n)
8582+{
8583+    uint8_t * d = dest;
8584+    const uint16_t * s = src;
8585+    while (n-- != 0)
8586+        *d++ = *s++ >> 2;
8587+}
8588+
8589+// Do a stride converting copy - if the strides are the same and line_len is
8590+// close then do a single block copy - we don't expect to have to preserve
8591+// pixels in the output frame
8592+static void mem_copy_2d(uint8_t * d_ptr, const size_t d_stride,
8593+                        const uint8_t * s_ptr, const size_t s_stride,
8594+                        size_t lines, const size_t line_len)
8595+{
8596+    if (s_stride == d_stride && d_stride < line_len + 32)
8597+    {
8598+        memcpy(d_ptr, s_ptr, d_stride * lines);
8599+    }
8600+    else
8601+    {
8602+        while (lines-- != 0) {
8603+            memcpy(d_ptr, s_ptr, line_len);
8604+            d_ptr += d_stride;
8605+            s_ptr += s_stride;
8606+        }
8607+    }
8608+}
8609+
8610+// line_len in D units
8611+static void mem_copy_2d_10_to_8(uint8_t * d_ptr, const size_t d_stride,
8612+                        const uint8_t * s_ptr, const size_t s_stride,
8613+                        size_t lines, const size_t line_len)
8614+{
8615+    piccpy_fn * const docpy = vlc_CPU_ARM_NEON() ? mmal_piccpy_10_to_8_neon : piccpy_10_to_8_c;
8616+    if (s_stride == d_stride * 2 && d_stride < line_len + 32)
8617+    {
8618+        docpy(d_ptr, s_ptr, d_stride * lines);
8619+    }
8620+    else
8621+    {
8622+        while (lines-- != 0) {
8623+            docpy(d_ptr, s_ptr, line_len);
8624+            d_ptr += d_stride;
8625+            s_ptr += s_stride;
8626+        }
8627+    }
8628+}
8629+
8630+
8631+int hw_mmal_copy_pic_to_buf(void * const buf_data,
8632+                            uint32_t * const pLength,
8633+                            const MMAL_ES_FORMAT_T * const fmt,
8634+                            const picture_t * const pic)
8635+{
8636+    const MMAL_VIDEO_FORMAT_T *const video = &fmt->es->video;
8637+    uint8_t * const dest = buf_data;
8638+    size_t length = 0;
8639+
8640+    //**** Worry about x/y_offsets
8641+
8642+    assert(fmt->encoding == MMAL_ENCODING_I420);
8643+
8644+    switch (pic->format.i_chroma) {
8645+        case VLC_CODEC_I420:
8646+        {
8647+            const size_t y_size = video->width * video->height;
8648+            mem_copy_2d(dest, video->width,
8649+                 pic->p[0].p_pixels, pic->p[0].i_pitch,
8650+                 video->crop.height,
8651+                 video->crop.width);
8652+
8653+            mem_copy_2d(dest + y_size, video->width / 2,
8654+                 pic->p[1].p_pixels, pic->p[1].i_pitch,
8655+                 video->crop.height / 2,
8656+                 video->crop.width / 2);
8657+
8658+            mem_copy_2d(dest + y_size + y_size / 4, video->width / 2,
8659+                 pic->p[2].p_pixels, pic->p[2].i_pitch,
8660+                 video->crop.height / 2,
8661+                 video->crop.width / 2);
8662+
8663+            // And make sure it is actually in memory
8664+            length = y_size + y_size / 2;
8665+            break;
8666+        }
8667+
8668+        case VLC_CODEC_I420_10L:
8669+        {
8670+            const size_t y_size = video->width * video->height;
8671+            mem_copy_2d_10_to_8(dest, video->width,
8672+                 pic->p[0].p_pixels, pic->p[0].i_pitch,
8673+                 video->crop.height,
8674+                 video->crop.width);
8675+
8676+            mem_copy_2d_10_to_8(dest + y_size, video->width / 2,
8677+                 pic->p[1].p_pixels, pic->p[1].i_pitch,
8678+                 video->crop.height / 2,
8679+                 video->crop.width / 2);
8680+
8681+            mem_copy_2d_10_to_8(dest + y_size + y_size / 4, video->width / 2,
8682+                 pic->p[2].p_pixels, pic->p[2].i_pitch,
8683+                 video->crop.height / 2,
8684+                 video->crop.width / 2);
8685+
8686+            // And make sure it is actually in memory
8687+            length = y_size + y_size / 2;
8688+            break;
8689+        }
8690+
8691+        default:
8692+            if (pLength != NULL)
8693+                *pLength = 0;
8694+            return VLC_EBADVAR;
8695+    }
8696+
8697+    if (cma_vcsm_type() == VCSM_INIT_LEGACY) {  // ** CMA is currently always uncached
8698+        flush_range(dest, length);
8699+    }
8700+
8701+    if (pLength != NULL)
8702+        *pLength = (uint32_t)length;
8703+
8704+    return VLC_SUCCESS;
8705+}
8706+
8707+
8708+static MMAL_BOOL_T rep_buf_free_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
8709+{
8710+    cma_buf_t * const cb = userdata;
8711+    VLC_UNUSED(header);
8712+
8713+    cma_buf_unref(cb);
8714+    return MMAL_FALSE;
8715+}
8716+
8717+static int cma_buf_buf_attach(MMAL_BUFFER_HEADER_T * const buf, cma_buf_t * const cb)
8718+{
8719+    // Just a CMA buffer - fill in new buffer
8720+    const uintptr_t vc_h = cma_buf_vc_handle(cb);
8721+    if (vc_h == 0)
8722+        return VLC_EGENERIC;
8723+
8724+    mmal_buffer_header_reset(buf);
8725+    buf->data       = (uint8_t *)vc_h;
8726+    buf->alloc_size = cma_buf_size(cb);
8727+    buf->length     = buf->alloc_size;
8728+    // Ensure cb remains valid for the duration of this buffer
8729+    mmal_buffer_header_pre_release_cb_set(buf, rep_buf_free_cb, cma_buf_ref(cb));
8730+    return VLC_SUCCESS;
8731+}
8732+
8733+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic,
8734+                                              MMAL_POOL_T * const rep_pool,
8735+                                              MMAL_PORT_T * const port,
8736+                                              cma_buf_pool_t * const cbp)
8737+{
8738+    MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(rep_pool->queue);
8739+    if (buf == NULL)
8740+        goto fail0;
8741+
8742+    cma_buf_t * const cb = cma_buf_pool_alloc_buf(cbp, port->buffer_size);
8743+    if (cb == NULL)
8744+        goto fail1;
8745+
8746+    if (cma_buf_buf_attach(buf, cb) != VLC_SUCCESS)
8747+        goto fail2;
8748+
8749+    pic_to_buf_copy_props(buf, pic);
8750+
8751+    if (hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), &buf->length, port->format, pic) != VLC_SUCCESS)
8752+        goto fail2;
8753+    buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
8754+
8755+    cma_buf_unref(cb);
8756+    return buf;
8757+
8758+fail2:
8759+    cma_buf_unref(cb);
8760+fail1:
8761+    mmal_buffer_header_release(buf);
8762+fail0:
8763+    return NULL;
8764+}
8765+
8766+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool)
8767+{
8768+    pic_ctx_mmal_t *const ctx = (pic_ctx_mmal_t *)pic->context;
8769+    MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
8770+
8771+    if (rep_buf == NULL)
8772+        return NULL;
8773+
8774+    if (ctx->bufs[0] != NULL)
8775+    {
8776+        // Existing buffer - replicate it
8777+        if (mmal_buffer_header_replicate(rep_buf, ctx->bufs[0]) != MMAL_SUCCESS)
8778+            goto fail;
8779+    }
8780+    else if (ctx->cb != NULL)
8781+    {
8782+        // Just a CMA buffer - fill in new buffer
8783+        if (cma_buf_buf_attach(rep_buf, ctx->cb) != 0)
8784+            goto fail;
8785+    }
8786+    else
8787+        goto fail;
8788+
8789+    pic_to_buf_copy_props(rep_buf, pic);
8790+    return rep_buf;
8791+
8792+fail:
8793+    mmal_buffer_header_release(rep_buf);
8794+    return NULL;
8795+}
8796+
8797+
8798+
8799+
8800+int hw_mmal_get_gpu_mem(void) {
8801+    static int stashed_val = -2;
8802+    VCHI_INSTANCE_T vchi_instance;
8803+    VCHI_CONNECTION_T *vchi_connection = NULL;
8804+    char rbuf[1024] = { 0 };
8805+
8806+    if (stashed_val >= -1)
8807+        return stashed_val;
8808+
8809+    if (vchi_initialise(&vchi_instance) != 0)
8810+        goto fail0;
8811+
8812+    //create a vchi connection
8813+    if (vchi_connect(NULL, 0, vchi_instance) != 0)
8814+        goto fail0;
8815+
8816+    vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1);
8817+
8818+    //send the gencmd for the argument
8819+    if (vc_gencmd_send("get_mem gpu") != 0)
8820+        goto fail;
8821+
8822+    if (vc_gencmd_read_response(rbuf, sizeof(rbuf) - 1) != 0)
8823+        goto fail;
8824+
8825+    if (strncmp(rbuf, "gpu=", 4) != 0)
8826+        goto fail;
8827+
8828+    char *p;
8829+    unsigned long m = strtoul(rbuf + 4, &p, 10);
8830+
8831+    if (p[0] != 'M' || p[1] != '\0')
8832+        stashed_val = -1;
8833+    else
8834+        stashed_val = (int)m << 20;
8835+
8836+    vc_gencmd_stop();
8837+
8838+    //close the vchi connection
8839+    vchi_disconnect(vchi_instance);
8840+
8841+    return stashed_val;
8842+
8843+fail:
8844+    vc_gencmd_stop();
8845+    vchi_disconnect(vchi_instance);
8846+fail0:
8847+    stashed_val = -1;
8848+    return -1;
8849+};
8850+
8851+// ===========================================================================
8852+
8853+typedef struct pool_ent_s
8854+{
8855+    struct pool_ent_s * next;
8856+    struct pool_ent_s * prev;
8857+
8858+    atomic_int ref_count;
8859+    unsigned int seq;
8860+
8861+    size_t size;
8862+
8863+    int vcsm_hdl;
8864+    int vc_hdl;
8865+    void * buf;
8866+
8867+    unsigned int width;
8868+    unsigned int height;
8869+    MMAL_FOURCC_T enc_type;
8870+
8871+    picture_t * pic;
8872+} pool_ent_t;
8873+
8874+
8875+typedef struct ent_list_hdr_s
8876+{
8877+    pool_ent_t * ents;
8878+    pool_ent_t * tail;
8879+    unsigned int n;
8880+} ent_list_hdr_t;
8881+
8882+#define ENT_LIST_HDR_INIT (ent_list_hdr_t){ \
8883+   .ents = NULL, \
8884+   .tail = NULL, \
8885+   .n = 0 \
8886+}
8887+
8888+struct vzc_pool_ctl_s
8889+{
8890+    atomic_int ref_count;
8891+
8892+    ent_list_hdr_t ent_pool;
8893+    ent_list_hdr_t ents_cur;
8894+    ent_list_hdr_t ents_prev;
8895+
8896+    unsigned int max_n;
8897+    unsigned int seq;
8898+
8899+    vlc_mutex_t lock;
8900+
8901+    MMAL_POOL_T * buf_pool;
8902+
8903+    vcsm_init_type_t vcsm_init_type;
8904+};
8905+
8906+typedef struct vzc_subbuf_ent_s
8907+{
8908+    pool_ent_t * ent;
8909+    MMAL_RECT_T pic_rect;
8910+    MMAL_RECT_T orig_dest_rect;
8911+    MMAL_DISPLAYREGION_T dreg;
8912+} vzc_subbuf_ent_t;
8913+
8914+
8915+static pool_ent_t * ent_extract(ent_list_hdr_t * const elh, pool_ent_t * const ent)
8916+{
8917+//    printf("List %p [%d]: Ext %p\n", elh, elh->n, ent);
8918+
8919+    if (ent == NULL)
8920+        return NULL;
8921+
8922+    if (ent->next == NULL)
8923+        elh->tail = ent->prev;
8924+    else
8925+        ent->next->prev = ent->prev;
8926+
8927+    if (ent->prev == NULL)
8928+        elh->ents = ent->next;
8929+    else
8930+        ent->prev->next = ent->next;
8931+
8932+    ent->prev = ent->next = NULL;
8933+
8934+    --elh->n;
8935+
8936+    return ent;  // For convienience
8937+}
8938+
8939+static inline pool_ent_t * ent_extract_tail(ent_list_hdr_t * const elh)
8940+{
8941+    return ent_extract(elh, elh->tail);
8942+}
8943+
8944+static void ent_add_head(ent_list_hdr_t * const elh, pool_ent_t * const ent)
8945+{
8946+//    printf("List %p [%d]: Add %p\n", elh, elh->n, ent);
8947+
8948+    if ((ent->next = elh->ents) == NULL)
8949+        elh->tail = ent;
8950+    else
8951+        ent->next->prev = ent;
8952+
8953+    ent->prev = NULL;
8954+    elh->ents = ent;
8955+    ++elh->n;
8956+}
8957+
8958+static void ent_free(pool_ent_t * const ent)
8959+{
8960+//    printf("Free ent: %p\n", ent);
8961+    if (ent != NULL) {
8962+        // If we still have a ref to a pic - kill it now
8963+        if (ent->pic != NULL)
8964+            picture_Release(ent->pic);
8965+
8966+        // Free contents
8967+        vcsm_unlock_hdl(ent->vcsm_hdl);
8968+
8969+        vcsm_free(ent->vcsm_hdl);
8970+
8971+        free(ent);
8972+    }
8973+}
8974+
8975+static void ent_free_list(ent_list_hdr_t * const elh)
8976+{
8977+    pool_ent_t * ent = elh->ents;
8978+
8979+//    printf("Free list: %p [%d]\n", elh, elh->n);
8980+
8981+    *elh = ENT_LIST_HDR_INIT;
8982+
8983+    while (ent != NULL) {
8984+        pool_ent_t * const t = ent;
8985+        ent = t->next;
8986+        ent_free(t);
8987+    }
8988+}
8989+
8990+static void ent_list_move(ent_list_hdr_t * const dst, ent_list_hdr_t * const src)
8991+{
8992+//    printf("Move %p->%p\n", src, dst);
8993+
8994+    *dst = *src;
8995+    *src = ENT_LIST_HDR_INIT;
8996+}
8997+
8998+// Scans "backwards" as that should give us the fastest match if we are
8999+// presented with pics in the same order each time
9000+static pool_ent_t * ent_list_extract_pic_ent(ent_list_hdr_t * const elh, picture_t * const pic)
9001+{
9002+    pool_ent_t *ent = elh->tail;
9003+
9004+//    printf("Find list: %p [%d]; pic:%p\n", elh, elh->n, pic);
9005+
9006+    while (ent != NULL) {
9007+//        printf("Check ent: %p, pic:%p\n", ent, ent->pic);
9008+
9009+        if (ent->pic == pic)
9010+            return ent_extract(elh, ent);
9011+        ent = ent->prev;
9012+    }
9013+    return NULL;
9014+}
9015+
9016+#define POOL_ENT_ALLOC_BLOCK  0x10000
9017+
9018+static pool_ent_t * pool_ent_alloc_new(size_t req_size)
9019+{
9020+    pool_ent_t * ent = calloc(1, sizeof(*ent));
9021+    const size_t alloc_size = (req_size + POOL_ENT_ALLOC_BLOCK - 1) & ~(POOL_ENT_ALLOC_BLOCK - 1);
9022+
9023+    if (ent == NULL)
9024+        return NULL;
9025+
9026+    ent->next = ent->prev = NULL;
9027+
9028+    // Alloc from vcsm
9029+    if ((ent->vcsm_hdl = vcsm_malloc_cache(alloc_size, VCSM_CACHE_TYPE_HOST, (char *)"vlc-subpic")) == -1)
9030+        goto fail1;
9031+    if ((ent->vc_hdl = vcsm_vc_hdl_from_hdl(ent->vcsm_hdl)) == 0)
9032+        goto fail2;
9033+    if ((ent->buf = vcsm_lock(ent->vcsm_hdl)) == NULL)
9034+        goto fail2;
9035+
9036+    ent->size = alloc_size;
9037+    return ent;
9038+
9039+fail2:
9040+    vcsm_free(ent->vcsm_hdl);
9041+fail1:
9042+    free(ent);
9043+    return NULL;
9044+}
9045+
9046+static inline pool_ent_t * pool_ent_ref(pool_ent_t * const ent)
9047+{
9048+//    int n = atomic_fetch_add(&ent->ref_count, 1) + 1;
9049+//    printf("Ref: %p: %d\n", ent, n);
9050+    atomic_fetch_add(&ent->ref_count, 1);
9051+    return ent;
9052+}
9053+
9054+static void pool_recycle(vzc_pool_ctl_t * const pc, pool_ent_t * const ent)
9055+{
9056+    pool_ent_t * xs = NULL;
9057+    int n;
9058+
9059+    if (ent == NULL)
9060+        return;
9061+
9062+    n = atomic_fetch_sub(&ent->ref_count, 1) - 1;
9063+
9064+//    printf("%s: Pool: %p: Ent: %p: %d\n", __func__, &pc->ent_pool, ent, n);
9065+
9066+    if (n != 0)
9067+        return;
9068+
9069+    if (ent->pic != NULL) {
9070+        picture_Release(ent->pic);
9071+        ent->pic = NULL;
9072+    }
9073+
9074+    vlc_mutex_lock(&pc->lock);
9075+
9076+    // If we have a full pool then extract the LRU and free it
9077+    // Free done outside mutex
9078+    if (pc->ent_pool.n >= pc->max_n)
9079+        xs = ent_extract_tail(&pc->ent_pool);
9080+
9081+    ent_add_head(&pc->ent_pool, ent);
9082+
9083+    vlc_mutex_unlock(&pc->lock);
9084+
9085+    ent_free(xs);
9086+}
9087+
9088+// * This could be made more efficient, but this is easy
9089+static void pool_recycle_list(vzc_pool_ctl_t * const pc, ent_list_hdr_t * const elh)
9090+{
9091+    pool_ent_t * ent;
9092+    while ((ent = ent_extract_tail(elh)) != NULL) {
9093+        pool_recycle(pc, ent);
9094+    }
9095+}
9096+
9097+static pool_ent_t * pool_best_fit(vzc_pool_ctl_t * const pc, size_t req_size)
9098+{
9099+    pool_ent_t * best = NULL;
9100+
9101+    vlc_mutex_lock(&pc->lock);
9102+
9103+    {
9104+        pool_ent_t * ent = pc->ent_pool.ents;
9105+
9106+        // Simple scan
9107+        while (ent != NULL) {
9108+            if (ent->size >= req_size && ent->size <= req_size * 2 + POOL_ENT_ALLOC_BLOCK &&
9109+                    (best == NULL || best->size > ent->size))
9110+                best = ent;
9111+            ent = ent->next;
9112+        }
9113+
9114+        // extract best from chain if we've found it
9115+        ent_extract(&pc->ent_pool, best);
9116+    }
9117+
9118+    vlc_mutex_unlock(&pc->lock);
9119+
9120+    if (best == NULL)
9121+        best = pool_ent_alloc_new(req_size);
9122+
9123+    if ((best->seq = ++pc->seq) == 0)
9124+        best->seq = ++pc->seq;  // Never allow to be zero
9125+
9126+    atomic_store(&best->ref_count, 1);
9127+    return best;
9128+}
9129+
9130+
9131+const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[] = { VLC_CODEC_RGBA, VLC_CODEC_BGRA, VLC_CODEC_ARGB, 0 };
9132+
9133+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH)
9134+{
9135+    const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
9136+    *pW = ent->width;
9137+    *pH = ent->height;
9138+}
9139+
9140+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt)
9141+{
9142+    const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
9143+    MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
9144+
9145+    es_fmt->type = MMAL_ES_TYPE_VIDEO;
9146+    es_fmt->encoding = ent->enc_type;
9147+    es_fmt->encoding_variant = 0;
9148+
9149+    v_fmt->width = ent->width;
9150+    v_fmt->height = ent->height;
9151+    v_fmt->crop.x = 0;
9152+    v_fmt->crop.y = 0;
9153+    v_fmt->crop.width = ent->width;
9154+    v_fmt->crop.height = ent->height;
9155+
9156+    return true;
9157+}
9158+
9159+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
9160+                                uint32_t * const pWidth, uint32_t * const pHeight)
9161+{
9162+    const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
9163+    *pWidth = ent->width;
9164+    *pHeight = ent->height;
9165+}
9166+
9167+
9168+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf)
9169+{
9170+    vzc_subbuf_ent_t * sb = buf->user_data;
9171+    return &sb->dreg;
9172+}
9173+
9174+static inline int rescale_x(int x, int mul, int div)
9175+{
9176+    return div == 0 ? x * mul : (x * mul + div/2) / div;
9177+}
9178+
9179+static void rescale_rect(MMAL_RECT_T * const d, const MMAL_RECT_T * const s, const MMAL_RECT_T * mul_rect, const MMAL_RECT_T * div_rect)
9180+{
9181+    d->x      = rescale_x(s->x - div_rect->x, mul_rect->width,  div_rect->width)  + mul_rect->x;
9182+    d->y      = rescale_x(s->y - div_rect->y, mul_rect->height, div_rect->height) + mul_rect->y;
9183+    d->width  = rescale_x(s->width,           mul_rect->width,  div_rect->width);
9184+    d->height = rescale_x(s->height,          mul_rect->height, div_rect->height);
9185+#if TRACE_TRANSFORMS
9186+    fprintf(stderr, "(%d,%d %dx%d) * (%d,%d %dx%d) / (%d,%d %dx%d) -> (%d,%d %dx%d)\n",
9187+            s->x, s->y, s->width, s->height,
9188+            mul_rect->x, mul_rect->y, mul_rect->width, mul_rect->height,
9189+            div_rect->x, div_rect->y, div_rect->width, div_rect->height,
9190+            d->x, d->y, d->width, d->height);
9191+#endif
9192+}
9193+
9194+static MMAL_RECT_T
9195+rect_untransform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t)
9196+{
9197+#if TRACE_TRANSFORMS
9198+    fprintf(stderr, "t=%d, s=%d,%d:%dx%d, c=%d,%d:%dx%d -> ", (int)t,
9199+           s.x,s.y,s.width,s.height,
9200+           c.x,c.y,c.width,c.height);
9201+#endif
9202+    if (is_transform_hflip(t))
9203+        s = rect_hflip(s, c);
9204+    if (is_transform_vflip(t) != 0)
9205+        s = rect_vflip(s, c);
9206+    if (is_transform_transpose(t) != 0)
9207+        s = rect_transpose(s);
9208+#if TRACE_TRANSFORMS
9209+    fprintf(stderr, "s=%d,%d:%dx%d\n",
9210+           s.x,s.y,s.width,s.height);
9211+#endif
9212+    return s;
9213+}
9214+
9215+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect, const MMAL_DISPLAYTRANSFORM_T scale_transform)
9216+{
9217+    vzc_subbuf_ent_t * sb = buf->user_data;
9218+    if (scale_rect == NULL) {
9219+        sb->dreg.dest_rect = sb->orig_dest_rect;
9220+        sb->dreg.transform = MMAL_DISPLAY_ROT0;
9221+    }
9222+    else
9223+    {
9224+        // The scale rect has been transposed if we have a transposing
9225+        // transform - untranspose so we are the same way up as the source
9226+        const MMAL_RECT_T c = (scale_transform & 4) == 0 ? *scale_rect : rect_transpose(*scale_rect);
9227+        rescale_rect(&sb->dreg.dest_rect, &sb->orig_dest_rect,
9228+                     &c, &sb->pic_rect);
9229+        sb->dreg.dest_rect = rect_untransform(sb->dreg.dest_rect, c, scale_transform);
9230+        sb->dreg.transform = scale_transform;
9231+    }
9232+}
9233+
9234+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf)
9235+{
9236+    vzc_subbuf_ent_t * sb = buf->user_data;
9237+    return sb->ent->seq;
9238+}
9239+
9240+
9241+// The intent with the ents_cur & ents_last stuff is to remember the buffers
9242+// we used on the last frame and reuse them on the current one if they are the
9243+// same.  Unfortunately detection of "is_first" is only a heuristic (there are
9244+// no rules governing the order in which things are blended) so we must deal
9245+// (fairly) gracefully with it never (or always) being set.
9246+
9247+// dst_fmt gives the number space in which the destination pixels are specified
9248+
9249+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc,
9250+                                                picture_t * const pic,
9251+                                                const MMAL_RECT_T dst_pic_rect,
9252+                                                const int x_offset, const int y_offset,
9253+                                                const unsigned int alpha,
9254+                                                const bool is_first)
9255+{
9256+    MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(pc->buf_pool->queue);
9257+    vzc_subbuf_ent_t * sb;
9258+
9259+    if (buf == NULL)
9260+        return NULL;
9261+
9262+    if ((sb = calloc(1, sizeof(*sb))) == NULL)
9263+        goto fail1;
9264+
9265+    // If first or we've had a lot of stuff move everything to the last list
9266+    // (we could deal more gracefully with the "too many" case but it shouldn't
9267+    // really happen)
9268+    if (is_first || pc->ents_cur.n >= CTX_BUFS_MAX) {
9269+        pool_recycle_list(pc, &pc->ents_prev);
9270+        ent_list_move(&pc->ents_prev, &pc->ents_cur);
9271+    }
9272+
9273+    sb->dreg.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
9274+    sb->dreg.hdr.size = sizeof(sb->dreg);
9275+    buf->user_data = sb;
9276+
9277+    {
9278+        // ?? Round start offset as well as length
9279+        const video_format_t *const fmt = &pic->format;
9280+
9281+        const unsigned int bpp = (fmt->i_bits_per_pixel + 7) >> 3;
9282+        const unsigned int xl = (fmt->i_x_offset & ~15);
9283+        const unsigned int xr = (fmt->i_x_offset + fmt->i_visible_width + 15) & ~15;
9284+        const size_t dst_stride = (xr - xl) * bpp;
9285+        const size_t dst_lines = ((fmt->i_visible_height + 15) & ~15);
9286+        const size_t dst_size = dst_stride * dst_lines;
9287+
9288+        pool_ent_t * ent = ent_list_extract_pic_ent(&pc->ents_prev, pic);
9289+        bool needs_copy = false;
9290+
9291+        // If we didn't find ent in last then look in cur in case is_first
9292+        // isn't working
9293+        if (ent == NULL)
9294+            ent = ent_list_extract_pic_ent(&pc->ents_cur, pic);
9295+
9296+//        printf("ent_found: %p\n", ent);
9297
9298-int mmal_picture_lock(picture_t *picture)
9299+        if (ent == NULL)
9300+        {
9301+            // Need a new ent
9302+            needs_copy = true;
9303+
9304+            if ((ent = pool_best_fit(pc, dst_size)) == NULL)
9305+                goto fail2;
9306+            if ((ent->enc_type = vlc_to_mmal_video_fourcc(&pic->format)) == 0)
9307+                goto fail2;
9308+
9309+            ent->pic = picture_Hold(pic);
9310+        }
9311+
9312+        ent_add_head(&pc->ents_cur, ent);
9313+
9314+        sb->ent = pool_ent_ref(ent);
9315+        hw_mmal_vzc_pool_ref(pc);
9316+
9317+        // Copy data
9318+        buf->next = NULL;
9319+        buf->cmd = 0;
9320+        buf->data = (uint8_t *)(ent->vc_hdl);
9321+        buf->alloc_size = buf->length = dst_size;
9322+        buf->offset = 0;
9323+        buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
9324+        buf->pts = buf->dts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
9325+        buf->type->video = (MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T){
9326+            .planes = 1,
9327+            .pitch = { dst_stride }
9328+        };
9329+
9330+        // Remember offsets
9331+        sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT |
9332+            MMAL_DISPLAY_SET_DEST_RECT |
9333+            MMAL_DISPLAY_SET_FULLSCREEN |
9334+            MMAL_DISPLAY_SET_TRANSFORM |
9335+            MMAL_DISPLAY_SET_ALPHA;
9336+
9337+        sb->dreg.fullscreen = 0;
9338+
9339+        // Will be set later - zero now to avoid any confusion
9340+        sb->dreg.transform = MMAL_DISPLAY_ROT0;
9341+        sb->dreg.dest_rect = (MMAL_RECT_T){0, 0, 0, 0};
9342+
9343+        sb->dreg.alpha = (uint32_t)(alpha & 0xff) | MMAL_DISPLAY_ALPHA_FLAGS_MIX;
9344+
9345+//        printf("+++ bpp:%d, vis:%dx%d wxh:%dx%d, d:%dx%d\n", bpp, fmt->i_visible_width, fmt->i_visible_height, fmt->i_width, fmt->i_height, dst_stride, dst_lines);
9346+
9347+        sb->dreg.src_rect = (MMAL_RECT_T){
9348+            .x      = (fmt->i_x_offset - xl),
9349+            .y      = 0,
9350+            .width  = fmt->i_visible_width,
9351+            .height = fmt->i_visible_height
9352+        };
9353+
9354+        sb->pic_rect = dst_pic_rect;
9355+
9356+        sb->orig_dest_rect = (MMAL_RECT_T){
9357+            .x      = x_offset,
9358+            .y      = y_offset,
9359+            .width  = fmt->i_visible_width,
9360+            .height = fmt->i_visible_height
9361+        };
9362+
9363+        if (needs_copy)
9364+        {
9365+            ent->width = dst_stride / bpp;
9366+            ent->height = dst_lines;
9367+
9368+            // 2D copy
9369+            {
9370+                uint8_t *d = ent->buf;
9371+                const uint8_t *s = pic->p[0].p_pixels + xl * bpp + fmt->i_y_offset * pic->p[0].i_pitch;
9372+
9373+                mem_copy_2d(d, dst_stride, s, pic->p[0].i_pitch, fmt->i_visible_height, dst_stride);
9374+
9375+                // And make sure it is actually in memory
9376+                if (pc->vcsm_init_type != VCSM_INIT_CMA) {  // ** CMA is currently always uncached
9377+                    flush_range(ent->buf, dst_stride * fmt->i_visible_height);
9378+                }
9379+            }
9380+        }
9381+    }
9382+
9383+    return buf;
9384+
9385+fail2:
9386+    free(sb);
9387+fail1:
9388+    mmal_buffer_header_release(buf);
9389+    return NULL;
9390+}
9391+
9392+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc)
9393+{
9394+    pool_recycle_list(pc, &pc->ents_prev);
9395+    pool_recycle_list(pc, &pc->ents_cur);
9396+}
9397+
9398+static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc)
9399+{
9400+
9401+//    printf("<<< %s\n", __func__);
9402+
9403+    hw_mmal_vzc_pool_flush(pc);
9404+
9405+    ent_free_list(&pc->ent_pool);
9406+
9407+    if (pc->buf_pool != NULL)
9408+        mmal_pool_destroy(pc->buf_pool);
9409+
9410+    vlc_mutex_destroy(&pc->lock);
9411+
9412+    cma_vcsm_exit(pc->vcsm_init_type);
9413+
9414+//    memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash
9415+    free (pc);
9416+
9417+    //    printf(">>> %s\n", __func__);
9418+}
9419+
9420+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc)
9421+{
9422+    int n;
9423+
9424+    if (pc == NULL)
9425+        return;
9426+
9427+    n = atomic_fetch_sub(&pc->ref_count, 1) - 1;
9428+
9429+    if (n != 0)
9430+        return;
9431+
9432+    hw_mmal_vzc_pool_delete(pc);
9433+}
9434+
9435+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc)
9436+{
9437+    atomic_fetch_add(&pc->ref_count, 1);
9438+}
9439+
9440+static MMAL_BOOL_T vcz_pool_release_cb(MMAL_POOL_T * buf_pool, MMAL_BUFFER_HEADER_T *buf, void *userdata)
9441+{
9442+    vzc_pool_ctl_t * const pc = userdata;
9443+    vzc_subbuf_ent_t * const sb = buf->user_data;
9444+
9445+    VLC_UNUSED(buf_pool);
9446+
9447+//    printf("<<< %s\n", __func__);
9448+
9449+    if (sb != NULL) {
9450+        buf->user_data = NULL;
9451+        pool_recycle(pc, sb->ent);
9452+        hw_mmal_vzc_pool_release(pc);
9453+        free(sb);
9454+    }
9455+
9456+//    printf(">>> %s\n", __func__);
9457+
9458+    return MMAL_TRUE;
9459+}
9460+
9461+vzc_pool_ctl_t * hw_mmal_vzc_pool_new()
9462+{
9463+    vzc_pool_ctl_t * const pc = calloc(1, sizeof(*pc));
9464+
9465+    if (pc == NULL)
9466+        return NULL;
9467+
9468+    if ((pc->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
9469+    {
9470+        free(pc);
9471+        return NULL;
9472+    }
9473+
9474+    pc->max_n = 8;
9475+    vlc_mutex_init(&pc->lock);  // Must init before potential destruction
9476+
9477+    if ((pc->buf_pool = mmal_pool_create(64, 0)) == NULL)
9478+    {
9479+        hw_mmal_vzc_pool_delete(pc);
9480+        return NULL;
9481+    }
9482+
9483+    atomic_store(&pc->ref_count, 1);
9484+
9485+    mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc);
9486+
9487+    return pc;
9488+}
9489+
9490+//----------------------------------------------------------------------------
9491+
9492+
9493+static const uint8_t shift_00[] = {0,0,0,0};
9494+static const uint8_t shift_01[] = {0,1,1,1};
9495+
9496+int cma_pic_set_data(picture_t * const pic,
9497+                            const MMAL_ES_FORMAT_T * const mm_esfmt,
9498+                            const MMAL_BUFFER_HEADER_T * const buf)
9499 {
9500-    picture_sys_t *pic_sys = picture->p_sys;
9501-    MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
9502+    const MMAL_VIDEO_FORMAT_T * const mm_fmt = &mm_esfmt->es->video;
9503+    const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = (buf == NULL) ? NULL : &buf->type->video;
9504+    cma_buf_t *const cb = cma_buf_pic_get(pic);
9505+    unsigned int planes = 1;
9506+
9507+    uint8_t * const data = cma_buf_addr(cb);
9508+    if (data == NULL) {
9509+        return VLC_ENOMEM;
9510+    }
9511+
9512+    const uint8_t * ws = shift_00;
9513+    const uint8_t * hs = shift_00;
9514+    int pb = 1;
9515+
9516+    switch (mm_esfmt->encoding)
9517+    {
9518+        case MMAL_ENCODING_ARGB:
9519+        case MMAL_ENCODING_ABGR:
9520+        case MMAL_ENCODING_RGBA:
9521+        case MMAL_ENCODING_BGRA:
9522+        case MMAL_ENCODING_RGB32:
9523+        case MMAL_ENCODING_BGR32:
9524+            pb = 4;
9525+            break;
9526+        case MMAL_ENCODING_RGB16:
9527+            pb = 2;
9528+            break;
9529
9530-    int offset = 0;
9531-    picture->p[0].p_pixels = buffer->data;
9532-    for (int i = 1; i < picture->i_planes; i++) {
9533-        offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines;
9534-        picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset;
9535+        case MMAL_ENCODING_I420:
9536+            ws = shift_01;
9537+            hs = shift_01;
9538+            planes = 3;
9539+            break;
9540+
9541+        case MMAL_ENCODING_YUVUV128:
9542+            hs = shift_01;
9543+            planes = 2;
9544+            break;
9545+
9546+        default:
9547+//            msg_Err(p_filter, "%s: Unexpected format", __func__);
9548+            return VLC_EGENERIC;
9549     }
9550
9551-    pic_sys->displayed = false;
9552+    // Fix up SAR if unset
9553+    if (pic->format.i_sar_den == 0 || pic->format.i_sar_num == 0) {
9554+        pic->format.i_sar_den = mm_fmt->par.den;
9555+        pic->format.i_sar_num = mm_fmt->par.num;
9556+    }
9557
9558+    pic->i_planes = planes;
9559+    unsigned int offset = 0;
9560+    for (unsigned int i = 0; i != planes; ++i) {
9561+        pic->p[i] = (plane_t){
9562+            .p_pixels = data + (buf_vid != NULL ? buf_vid->offset[i] : offset),
9563+            .i_lines = mm_fmt->height >> hs[i],
9564+            .i_pitch = buf_vid != NULL ? buf_vid->pitch[i] : mm_fmt->width * pb,
9565+            .i_pixel_pitch = pb,
9566+            .i_visible_lines = mm_fmt->crop.height >> hs[i],
9567+            .i_visible_pitch = mm_fmt->crop.width >> ws[i]
9568+        };
9569+        offset += pic->p[i].i_pitch * pic->p[i].i_lines;
9570+    }
9571     return VLC_SUCCESS;
9572 }
9573+
9574+int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic)
9575+{
9576+    if (!is_cma_buf_pic_chroma(pic->format.i_chroma))
9577+        return VLC_EGENERIC;
9578+    if (pic->context != NULL)
9579+        return VLC_EBADVAR;
9580+
9581+    pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
9582+
9583+    if (ctx == NULL)
9584+        return VLC_ENOMEM;
9585+
9586+    ctx->cmn.copy = hw_mmal_pic_ctx_copy;
9587+    ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
9588+    ctx->buf_count = 1; // cb takes the place of the 1st buf
9589+    ctx->cb = cb;
9590+
9591+    cma_buf_in_flight(cb);
9592+
9593+    pic->context = &ctx->cmn;
9594+    return VLC_SUCCESS;
9595+}
9596+
9597+cma_buf_t * cma_buf_pic_get(picture_t * const pic)
9598+{
9599+    pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9600+    return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx  == NULL ? 0 : ctx->cb;
9601+}
9602+
9603+
9604+//----------------------------------------------------------------------------
9605+
9606+/* Returns the type of the Pi being used
9607+*/
9608+bool rpi_is_model_pi4(void) {
9609+    return bcm_host_is_model_pi4();
9610+}
9611+
9612+// Preferred mode - none->cma on Pi4 otherwise legacy
9613+static volatile vcsm_init_type_t last_vcsm_type = VCSM_INIT_NONE;
9614+
9615+vcsm_init_type_t cma_vcsm_type(void)
9616+{
9617+    return last_vcsm_type;
9618+}
9619+
9620+vcsm_init_type_t cma_vcsm_init(void)
9621+{
9622+    vcsm_init_type_t rv = VCSM_INIT_NONE;
9623+    // We don't bother locking - taking a copy here should be good enough
9624+    vcsm_init_type_t try_type = last_vcsm_type;
9625+
9626+    if (try_type == VCSM_INIT_NONE) {
9627+        if (bcm_host_is_fkms_active())
9628+            try_type = VCSM_INIT_CMA;
9629+        else
9630+            try_type = VCSM_INIT_LEGACY;
9631+    }
9632+
9633+    if (try_type == VCSM_INIT_CMA) {
9634+        if (vcsm_init_ex(1, -1) == 0)
9635+            rv = VCSM_INIT_CMA;
9636+        else if (vcsm_init_ex(0, -1) == 0)
9637+            rv = VCSM_INIT_LEGACY;
9638+    }
9639+    else
9640+    {
9641+        if (vcsm_init_ex(0, -1) == 0)
9642+            rv = VCSM_INIT_LEGACY;
9643+        else if (vcsm_init_ex(1, -1) == 0)
9644+            rv = VCSM_INIT_CMA;
9645+    }
9646+
9647+    // Just in case this affects vcsm init do after that
9648+    if (rv != VCSM_INIT_NONE)
9649+        bcm_host_init();
9650+
9651+    last_vcsm_type = rv;
9652+    return rv;
9653+}
9654+
9655+void cma_vcsm_exit(const vcsm_init_type_t init_mode)
9656+{
9657+    if (init_mode != VCSM_INIT_NONE)
9658+    {
9659+        vcsm_exit();
9660+        bcm_host_deinit();  // Does nothing but add in case it ever does
9661+    }
9662+}
9663+
9664+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode)
9665+{
9666+    switch (init_mode)
9667+    {
9668+        case VCSM_INIT_CMA:
9669+            return "CMA";
9670+        case VCSM_INIT_LEGACY:
9671+            return "Legacy";
9672+        case VCSM_INIT_NONE:
9673+            return "none";
9674+        default:
9675+            break;
9676+    }
9677+    return "???";
9678+}
9679+
9680+
9681--- a/modules/hw/mmal/mmal_picture.h
9682+++ b/modules/hw/mmal/mmal_picture.h
9683@@ -24,19 +24,298 @@
9684 #ifndef VLC_MMAL_MMAL_PICTURE_H_
9685 #define VLC_MMAL_MMAL_PICTURE_H_
9686
9687+#include <stdatomic.h>
9688+
9689 #include <vlc_common.h>
9690 #include <interface/mmal/mmal.h>
9691
9692+#include "mmal_cma.h"
9693+
9694 /* Think twice before changing this. Incorrect values cause havoc. */
9695 #define NUM_ACTUAL_OPAQUE_BUFFERS 30
9696
9697-struct picture_sys_t {
9698-    vlc_object_t *owner;
9699+#ifndef VLC_TICK_INVALID
9700+#define VLC_TICK_INVALID VLC_TS_INVALID
9701+#define VLC_VER_3 1
9702+#else
9703+#define VLC_VER_3 0
9704+#endif
9705+
9706+typedef struct mmal_port_pool_ref_s
9707+{
9708+    atomic_uint refs;
9709+    MMAL_POOL_T * pool;
9710+    MMAL_PORT_T * port;
9711+} hw_mmal_port_pool_ref_t;
9712+
9713+typedef struct pic_ctx_subpic_s {
9714+    picture_t * subpic;
9715+    int x, y;
9716+    int alpha;
9717+} pic_ctx_subpic_t;
9718+
9719+
9720+#define CTX_BUFS_MAX 4
9721+typedef struct pic_ctx_mmal_s {
9722+    picture_context_t cmn;  // PARENT: Common els at start
9723+
9724+    cma_buf_t * cb;
9725+
9726+    unsigned int buf_count;
9727+    MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX];
9728+
9729+} pic_ctx_mmal_t;
9730+
9731+const char * str_fourcc(char * const buf, const unsigned int fcc);
9732+
9733+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc);
9734+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs);
9735+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc);
9736+// Returns true if fmt_changed
9737+// frame_rate ignored for compare, but is set if something else is updated
9738+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic);
9739+
9740+// Copy pic contents into an existing buffer
9741+int hw_mmal_copy_pic_to_buf(void * const buf_data, uint32_t * const pLength,
9742+                            const MMAL_ES_FORMAT_T * const fmt, const picture_t * const pic);
9743+
9744+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
9745+   const unsigned int headers, const uint32_t payload_size);
9746+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb);
9747+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf);
9748+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr);
9749+static inline void hw_mmal_port_pool_ref_acquire(hw_mmal_port_pool_ref_t * const ppr)
9750+{
9751+    atomic_fetch_add(&ppr->refs, 1);
9752+}
9753+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
9754+                                    hw_mmal_port_pool_ref_t ** pppr,
9755+                                    MMAL_PORT_T * const port,
9756+                                    const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback);
9757+
9758+static inline int hw_mmal_pic_has_sub_bufs(picture_t * const pic)
9759+{
9760+    pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9761+    return ctx->buf_count > 1;
9762+}
9763+
9764+static inline void hw_mmal_pic_sub_buf_add(picture_t * const pic, MMAL_BUFFER_HEADER_T * const sub)
9765+{
9766+    pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9767+
9768+    if (ctx->buf_count >= CTX_BUFS_MAX) {
9769+        mmal_buffer_header_release(sub);
9770+        return;
9771+    }
9772+
9773+    ctx->bufs[ctx->buf_count++] = sub;
9774+}
9775+
9776+static inline MMAL_BUFFER_HEADER_T * hw_mmal_pic_sub_buf_get(picture_t * const pic, const unsigned int n)
9777+{
9778+    pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9779+
9780+    return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1];
9781+}
9782+
9783+static inline bool hw_mmal_chroma_is_mmal(const vlc_fourcc_t chroma)
9784+{
9785+    return
9786+        chroma == VLC_CODEC_MMAL_OPAQUE ||
9787+        chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
9788+        chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
9789+        chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
9790+        chroma == VLC_CODEC_MMAL_ZC_I420 ||
9791+        chroma == VLC_CODEC_MMAL_ZC_RGB32;
9792+}
9793+
9794+static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic)
9795+{
9796+    return hw_mmal_chroma_is_mmal(pic->format.i_chroma);
9797+}
9798+
9799+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn);
9800+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn);
9801+picture_context_t * hw_mmal_gen_context(
9802+    MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr);
9803+
9804+int hw_mmal_get_gpu_mem(void);
9805+
9806+
9807+static inline MMAL_STATUS_T port_parameter_set_uint32(MMAL_PORT_T * port, uint32_t id, uint32_t val)
9808+{
9809+    const MMAL_PARAMETER_UINT32_T param = {
9810+        .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_UINT32_T)},
9811+        .value = val
9812+    };
9813+    return mmal_port_parameter_set(port, &param.hdr);
9814+}
9815+
9816+static inline MMAL_STATUS_T port_parameter_set_bool(MMAL_PORT_T * const port, const uint32_t id, const bool val)
9817+{
9818+    const MMAL_PARAMETER_BOOLEAN_T param = {
9819+        .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_BOOLEAN_T)},
9820+        .enable = val
9821+    };
9822+    return mmal_port_parameter_set(port, &param.hdr);
9823+}
9824+
9825+static inline MMAL_STATUS_T port_send_replicated(MMAL_PORT_T * const port, MMAL_POOL_T * const rep_pool,
9826+                                          MMAL_BUFFER_HEADER_T * const src_buf,
9827+                                          const uint64_t seq)
9828+{
9829+    MMAL_STATUS_T err;
9830+    MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
9831+
9832+    if (rep_buf == NULL)
9833+        return MMAL_ENOSPC;
9834+
9835+    if ((err = mmal_buffer_header_replicate(rep_buf, src_buf)) != MMAL_SUCCESS)
9836+        return err;
9837+
9838+    rep_buf->pts = seq;
9839+
9840+    if ((err = mmal_port_send_buffer(port, rep_buf)) != MMAL_SUCCESS)
9841+    {
9842+        mmal_buffer_header_release(rep_buf);
9843+        return err;
9844+    }
9845+
9846+    return MMAL_SUCCESS;
9847+}
9848+
9849+
9850+static inline void pic_to_buf_copy_props(MMAL_BUFFER_HEADER_T * const buf, const picture_t * const pic)
9851+{
9852+    if (!pic->b_progressive)
9853+    {
9854+        buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9855+        buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9856+    }
9857+    else
9858+    {
9859+        buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9860+        buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9861+    }
9862+    if (pic->b_top_field_first)
9863+    {
9864+        buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9865+        buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9866+    }
9867+    else
9868+    {
9869+        buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9870+        buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9871+    }
9872+    buf->pts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
9873+    buf->dts = buf->pts;
9874+}
9875+
9876+static inline void buf_to_pic_copy_props(picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf)
9877+{
9878+    // Contrary to docn the interlace & tff flags turn up in the header flags rather than the
9879+    // video specific flags (which appear to be currently unused).
9880+    pic->b_progressive = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED) == 0;
9881+    pic->b_top_field_first = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST) != 0;
9882+
9883+    pic->date = buf->pts != MMAL_TIME_UNKNOWN ? buf->pts :
9884+        buf->dts != MMAL_TIME_UNKNOWN ? buf->dts :
9885+            VLC_TICK_INVALID;
9886+}
9887+
9888+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic,
9889+                                              MMAL_POOL_T * const rep_pool,
9890+                                              MMAL_PORT_T * const port,
9891+                                              cma_buf_pool_t * const cbp);
9892+
9893+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool);
9894+
9895+struct vzc_pool_ctl_s;
9896+typedef struct vzc_pool_ctl_s vzc_pool_ctl_t;
9897+
9898+// At the moment we cope with any mono-planar RGBA thing
9899+// We could cope with many other things but they currently don't occur
9900+extern const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[];
9901+static inline bool hw_mmal_vzc_subpic_fmt_valid(const video_frame_format_t * const vf_vlc)
9902+{
9903+    const vlc_fourcc_t vfcc_src = vf_vlc->i_chroma;
9904+    for (const vlc_fourcc_t * p = hw_mmal_vzc_subpicture_chromas; *p != 0; ++p)
9905+        if (*p == vfcc_src)
9906+            return true;
9907+
9908+    return false;
9909+}
9910+
9911+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt);
9912+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf);
9913+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect, const MMAL_DISPLAYTRANSFORM_T scale_transform);
9914+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH);
9915+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf);
9916+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic,
9917+                                                const MMAL_RECT_T dst_pic_rect,
9918+                                                const int x_offset, const int y_offset,
9919+                                                const unsigned int alpha, const bool is_first);
9920+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
9921+                                uint32_t * const pWidth, uint32_t * const pHeight);
9922+
9923+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc);
9924+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc);
9925+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc);
9926+vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void);
9927+
9928+
9929+static inline MMAL_RECT_T vis_mmal_rect(const video_format_t * const fmt)
9930+{
9931+    return (MMAL_RECT_T){
9932+        .x      = fmt->i_x_offset,
9933+        .y      = fmt->i_y_offset,
9934+        .width  = fmt->i_visible_width,
9935+        .height = fmt->i_visible_height
9936+    };
9937+}
9938+
9939+int cma_pic_set_data(picture_t * const pic,
9940+                    const MMAL_ES_FORMAT_T * const mm_esfmt,
9941+                    const MMAL_BUFFER_HEADER_T * const buf);
9942+
9943+// Attaches cma buf to pic
9944+// Marks in_flight if not all_in_flight anyway
9945+int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic);
9946+// Returns a pointer to the cma_buf attached to the pic
9947+// Just a pointer - doesn't add a ref
9948+cma_buf_t * cma_buf_pic_get(picture_t * const pic);
9949+
9950+static inline bool is_cma_buf_pic_chroma(const uint32_t chroma)
9951+{
9952+    return chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
9953+        chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
9954+        chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
9955+        chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
9956+        chroma == VLC_CODEC_MMAL_ZC_I420;
9957+}
9958+
9959+
9960+int rpi_get_model_type(void);
9961+bool rpi_is_model_pi4(void);
9962+bool rpi_is_fkms_active(void);
9963+
9964+typedef enum vcsm_init_type_e {
9965+    VCSM_INIT_NONE = 0,
9966+    VCSM_INIT_LEGACY,
9967+    VCSM_INIT_CMA
9968+} vcsm_init_type_t;
9969+
9970+vcsm_init_type_t cma_vcsm_init(void);
9971+void cma_vcsm_exit(const vcsm_init_type_t init_mode);
9972+vcsm_init_type_t cma_vcsm_type(void);
9973+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode);
9974+
9975
9976-    MMAL_BUFFER_HEADER_T *buffer;
9977-    bool displayed;
9978-};
9979+#define VOUT_DISPLAY_CHANGE_MMAL_BASE 1024
9980+#define VOUT_DISPLAY_CHANGE_MMAL_HIDE (VOUT_DISPLAY_CHANGE_MMAL_BASE + 0)
9981
9982-int mmal_picture_lock(picture_t *picture);
9983+#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize"
9984+#define MMAL_COMPONENT_ISP_RESIZER     "vc.ril.isp"
9985+#define MMAL_COMPONENT_HVS             "vc.ril.hvs"
9986
9987 #endif
9988--- /dev/null
9989+++ b/modules/hw/mmal/rpi_prof.h
9990@@ -0,0 +1,110 @@
9991+#ifndef RPI_PROFILE_H
9992+#define RPI_PROFILE_H
9993+
9994+#include <stdint.h>
9995+#include <inttypes.h>
9996+
9997+#ifndef RPI_PROFILE
9998+#define RPI_PROFILE 0
9999+#endif
10000+
10001+#if RPI_PROFILE
10002+
10003+#include "v7_pmu.h"
10004+
10005+#ifdef RPI_PROC_ALLOC
10006+#define X volatile
10007+#define Z =0
10008+#else
10009+#define X extern volatile
10010+#define Z
10011+#endif
10012+
10013+X uint64_t av_rpi_prof0_cycles Z;
10014+X unsigned int av_rpi_prof0_cnt Z;
10015+#define RPI_prof0_MAX_DURATION 100000
10016+
10017+X uint64_t av_rpi_prof1_cycles Z;
10018+X unsigned int av_rpi_prof1_cnt Z;
10019+#define RPI_prof1_MAX_DURATION 100000
10020+
10021+X uint64_t av_rpi_prof2_cycles Z;
10022+X unsigned int av_rpi_prof2_cnt Z;
10023+#define RPI_prof2_MAX_DURATION 10000
10024+
10025+X uint64_t av_rpi_prof_n_cycles[128];
10026+X unsigned int av_rpi_prof_n_cnt[128];
10027+#define RPI_prof_n_MAX_DURATION 10000
10028+
10029+
10030+#undef X
10031+#undef Z
10032+
10033+#define PROFILE_INIT()\
10034+do {\
10035+    enable_pmu();\
10036+    enable_ccnt();\
10037+} while (0)
10038+
10039+#define PROFILE_START()\
10040+do {\
10041+    volatile uint32_t perf_1 = read_ccnt();\
10042+    volatile uint32_t perf_2
10043+
10044+
10045+#define PROFILE_ACC(x)\
10046+    perf_2 = read_ccnt();\
10047+    {\
10048+        const uint32_t duration = perf_2 - perf_1;\
10049+        if (duration < RPI_##x##_MAX_DURATION)\
10050+        {\
10051+            av_rpi_##x##_cycles += duration;\
10052+            av_rpi_##x##_cnt += 1;\
10053+        }\
10054+    }\
10055+} while(0)
10056+
10057+
10058+#define PROFILE_ACC_N(n)\
10059+    if ((n) >= 0) {\
10060+        perf_2 = read_ccnt();\
10061+        {\
10062+            const uint32_t duration = perf_2 - perf_1;\
10063+            if (duration < RPI_prof_n_MAX_DURATION)\
10064+            {\
10065+                av_rpi_prof_n_cycles[n] += duration;\
10066+                av_rpi_prof_n_cnt[n] += 1;\
10067+            }\
10068+        }\
10069+    }\
10070+} while(0)
10071+
10072+#define PROFILE_PRINTF(x)\
10073+    printf("%-20s cycles=%14" PRIu64 ";  cnt=%8u;  avg=%5" PRIu64 "\n", #x, av_rpi_##x##_cycles, av_rpi_##x##_cnt,\
10074+        av_rpi_##x##_cnt == 0 ? (uint64_t)0 : av_rpi_##x##_cycles / (uint64_t)av_rpi_##x##_cnt)
10075+
10076+#define PROFILE_PRINTF_N(n)\
10077+    printf("prof[%d] cycles=%14" PRIu64 ";  cnt=%8u;  avg=%5" PRIu64 "\n", (n), av_rpi_prof_n_cycles[n], av_rpi_prof_n_cnt[n],\
10078+        av_rpi_prof_n_cnt[n] == 0 ? (uint64_t)0 : av_rpi_prof_n_cycles[n] / (uint64_t)av_rpi_prof_n_cnt[n])
10079+
10080+#define PROFILE_CLEAR_N(n) \
10081+do {\
10082+    av_rpi_prof_n_cycles[n] = 0;\
10083+    av_rpi_prof_n_cnt[n] = 0;\
10084+} while(0)
10085+
10086+#else
10087+
10088+// No profile
10089+#define PROFILE_INIT()
10090+#define PROFILE_START()
10091+#define PROFILE_ACC(x)
10092+#define PROFILE_ACC_N(x)
10093+#define PROFILE_PRINTF(x)
10094+#define PROFILE_PRINTF_N(x)
10095+#define PROFILE_CLEAR_N(n)
10096+
10097+#endif
10098+
10099+#endif
10100+
10101--- /dev/null
10102+++ b/modules/hw/mmal/subpic.c
10103@@ -0,0 +1,257 @@
10104+/*****************************************************************************
10105+ * mmal.c: MMAL-based decoder plugin for Raspberry Pi
10106+ *****************************************************************************
10107+ * Authors: jc@kynesim.co.uk
10108+ *
10109+ * This program is free software; you can redistribute it and/or modify it
10110+ * under the terms of the GNU Lesser General Public License as published by
10111+ * the Free Software Foundation; either version 2.1 of the License, or
10112+ * (at your option) any later version.
10113+ *
10114+ * This program is distributed in the hope that it will be useful,
10115+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10116+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10117+ * GNU Lesser General Public License for more details.
10118+ *
10119+ * You should have received a copy of the GNU Lesser General Public License
10120+ * along with this program; if not, write to the Free Software Foundation,
10121+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
10122+ *****************************************************************************/
10123+
10124+#ifdef HAVE_CONFIG_H
10125+#include "config.h"
10126+#endif
10127+
10128+#include <stdatomic.h>
10129+
10130+#include <vlc_common.h>
10131+#include <vlc_plugin.h>
10132+#include <vlc_codec.h>
10133+#include <vlc_filter.h>
10134+#include <vlc_threads.h>
10135+
10136+#include <bcm_host.h>
10137+#include <interface/mmal/mmal.h>
10138+#include <interface/mmal/util/mmal_util.h>
10139+#include <interface/mmal/util/mmal_default_components.h>
10140+
10141+#include "mmal_picture.h"
10142+#include "subpic.h"
10143+
10144+
10145+#define TRACE_ALL 0
10146+
10147+static inline bool cmp_rect(const MMAL_RECT_T * const a, const MMAL_RECT_T * const b)
10148+{
10149+    return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height;
10150+}
10151+
10152+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const sub)
10153+{
10154+    VLC_UNUSED(p_filter);
10155+    if (sub->port != NULL && sub->port->is_enabled)
10156+        mmal_port_disable(sub->port);
10157+    sub->seq = 0;
10158+}
10159+
10160+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe)
10161+{
10162+    hw_mmal_subpic_flush(p_filter, spe);
10163+
10164+    if (spe->pool != NULL)
10165+        mmal_pool_destroy(spe->pool);
10166+
10167+    // Zap to avoid any accidental reuse
10168+    *spe = (subpic_reg_stash_t){NULL};
10169+}
10170+
10171+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port,
10172+                                  const int display_id, const unsigned int layer)
10173+{
10174+    MMAL_STATUS_T err;
10175+
10176+    // Start by zapping all to zero
10177+    *spe = (subpic_reg_stash_t){NULL};
10178+
10179+    if ((err = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
10180+    {
10181+        msg_Err(p_filter, "Failed to set sub port zero copy");
10182+        return err;
10183+    }
10184+
10185+    if ((spe->pool = mmal_pool_create(30, 0)) == NULL)
10186+    {
10187+        msg_Err(p_filter, "Failed to create sub pool");
10188+        return MMAL_ENOMEM;
10189+    }
10190+
10191+    port->userdata = (void *)p_filter;
10192+    spe->port = port;
10193+    spe->display_id = display_id;
10194+    spe->layer = layer;
10195+
10196+    return MMAL_SUCCESS;
10197+}
10198+
10199+static void conv_subpic_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
10200+{
10201+#if TRACE_ALL
10202+    msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, user=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
10203+            __func__, buf->cmd, buf->user_data, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
10204+#else
10205+    VLC_UNUSED(port);
10206+#endif
10207+
10208+    mmal_buffer_header_release(buf);  // Will extract & release pic in pool callback
10209+}
10210+
10211+static int
10212+subpic_send_empty(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, const uint64_t pts)
10213+{
10214+    MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(spe->pool->queue);
10215+    MMAL_STATUS_T err;
10216+
10217+    if (buf == NULL) {
10218+        msg_Err(p_filter, "Buffer get for subpic failed");
10219+        return -1;
10220+    }
10221+#if TRACE_ALL
10222+    msg_Dbg(p_filter, "Remove pic for sub %d", spe->seq);
10223+#endif
10224+    buf->cmd = 0;
10225+    buf->data = NULL;
10226+    buf->alloc_size = 0;
10227+    buf->offset = 0;
10228+    buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
10229+    buf->pts = pts;
10230+    buf->dts = MMAL_TIME_UNKNOWN;
10231+    buf->user_data = NULL;
10232+
10233+    if ((err = mmal_port_send_buffer(spe->port, buf)) != MMAL_SUCCESS)
10234+    {
10235+        msg_Err(p_filter, "Send buffer to subput failed");
10236+        mmal_buffer_header_release(buf);
10237+        return -1;
10238+    }
10239+    return 0;
10240+}
10241+
10242+// < 0 Error
10243+//   0 Done & stop
10244+//   1 Done & continue
10245+
10246+int hw_mmal_subpic_update(vlc_object_t * const p_filter,
10247+    MMAL_BUFFER_HEADER_T * const sub_buf,
10248+    subpic_reg_stash_t * const spe,
10249+    const video_format_t * const fmt,
10250+    const MMAL_RECT_T * const scale_out,
10251+    const MMAL_DISPLAYTRANSFORM_T transform_out,
10252+    const uint64_t pts)
10253+{
10254+    MMAL_STATUS_T err;
10255+
10256+    if (sub_buf == NULL)
10257+    {
10258+        if (spe->port->is_enabled && spe->seq != 0)
10259+        {
10260+            subpic_send_empty(p_filter, spe, pts);
10261+            spe->seq = 0;
10262+        }
10263+    }
10264+    else
10265+    {
10266+        const unsigned int seq = hw_mmal_vzc_buf_seq(sub_buf);
10267+        bool needs_update = (spe->seq != seq);
10268+
10269+        hw_mmal_vzc_buf_scale_dest_rect(sub_buf, scale_out, transform_out);
10270+
10271+        if (hw_mmal_vzc_buf_set_format(sub_buf, spe->port->format))
10272+        {
10273+            MMAL_DISPLAYREGION_T * const dreg = hw_mmal_vzc_buf_region(sub_buf);
10274+            MMAL_VIDEO_FORMAT_T *const v_fmt = &spe->port->format->es->video;
10275+
10276+            v_fmt->frame_rate.den = fmt->i_frame_rate_base;
10277+            v_fmt->frame_rate.num = fmt->i_frame_rate;
10278+            v_fmt->par.den        = fmt->i_sar_den;
10279+            v_fmt->par.num        = fmt->i_sar_num;
10280+            v_fmt->color_space = MMAL_COLOR_SPACE_UNKNOWN;
10281+
10282+            if (needs_update || dreg->alpha != spe->alpha || !cmp_rect(&dreg->dest_rect, &spe->dest_rect)) {
10283+
10284+                spe->alpha = dreg->alpha;
10285+                spe->dest_rect = dreg->dest_rect;
10286+                needs_update = true;
10287+
10288+                if (spe->display_id >= 0)
10289+                {
10290+                    dreg->display_num = spe->display_id;
10291+                    dreg->set |= MMAL_DISPLAY_SET_NUM;
10292+                }
10293+                dreg->layer = spe->layer;
10294+                dreg->set |= MMAL_DISPLAY_SET_LAYER;
10295+
10296+#if TRACE_ALL
10297+                msg_Dbg(p_filter, "%s: Update region: Set=%x, dest=%dx%d @ (%d,%d), src=%dx%d @ (%d,%d), layer=%d, alpha=%#x",
10298+                        __func__, dreg->set,
10299+                        dreg->dest_rect.width, dreg->dest_rect.height, dreg->dest_rect.x, dreg->dest_rect.y,
10300+                        dreg->src_rect.width, dreg->src_rect.height, dreg->src_rect.x, dreg->src_rect.y,
10301+                        dreg->layer, dreg->alpha);
10302+#endif
10303+
10304+                // If now completely offscreen just flush this & return
10305+                // We only do -ve as (a) that is easy and (b) it seems to be
10306+                // something that can confuse mmal
10307+                if (dreg->dest_rect.y + dreg->dest_rect.height <= 0 ||
10308+                    dreg->dest_rect.x + dreg->dest_rect.width <= 0)
10309+                {
10310+                    if (spe->port->is_enabled)
10311+                        subpic_send_empty(p_filter, spe, pts);
10312+                    spe->seq = seq;
10313+                    return 1;
10314+                }
10315+
10316+                if ((err = mmal_port_parameter_set(spe->port, &dreg->hdr)) != MMAL_SUCCESS)
10317+                {
10318+                    msg_Err(p_filter, "Set display region on subput failed");
10319+                    return -1;
10320+                }
10321+
10322+                if ((err = mmal_port_format_commit(spe->port)) != MMAL_SUCCESS)
10323+                {
10324+                    msg_Dbg(p_filter, "%s: Subpic commit fail: %d", __func__, err);
10325+                    return -1;
10326+                }
10327+            }
10328+        }
10329+
10330+        if (!spe->port->is_enabled)
10331+        {
10332+            spe->port->buffer_num = 30;
10333+            spe->port->buffer_size = spe->port->buffer_size_recommended;  // Not used but shuts up the error checking
10334+
10335+            if ((err = mmal_port_enable(spe->port, conv_subpic_cb)) != MMAL_SUCCESS)
10336+            {
10337+                msg_Dbg(p_filter, "%s: Subpic enable fail: %d", __func__, err);
10338+                return -1;
10339+            }
10340+        }
10341+
10342+        if (needs_update)
10343+        {
10344+#if TRACE_ALL
10345+            msg_Dbg(p_filter, "Update pic for sub %d", spe->seq);
10346+#endif
10347+            if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS)
10348+            {
10349+                msg_Err(p_filter, "Send buffer to subput failed");
10350+                return -1;
10351+            }
10352+
10353+            spe->seq = seq;
10354+        }
10355+    }
10356+    return 1;
10357+}
10358+
10359+
10360+
10361--- /dev/null
10362+++ b/modules/hw/mmal/subpic.h
10363@@ -0,0 +1,33 @@
10364+#ifndef VLC_HW_MMAL_SUBPIC_H_
10365+#define VLC_HW_MMAL_SUBPIC_H_
10366+
10367+typedef struct subpic_reg_stash_s
10368+{
10369+    MMAL_PORT_T * port;
10370+    MMAL_POOL_T * pool;
10371+    int display_id;  // -1 => do not set
10372+    unsigned int layer;
10373+    // Shadow  vars so we can tell if stuff has changed
10374+    MMAL_RECT_T dest_rect;
10375+    unsigned int alpha;
10376+    unsigned int seq;
10377+} subpic_reg_stash_t;
10378+
10379+int hw_mmal_subpic_update(vlc_object_t * const p_filter,
10380+                          MMAL_BUFFER_HEADER_T * const sub_buf,
10381+                          subpic_reg_stash_t * const spe,
10382+                          const video_format_t * const fmt,
10383+                          const MMAL_RECT_T * const scale_out,
10384+                          const MMAL_DISPLAYTRANSFORM_T transform_out,
10385+                          const uint64_t pts);
10386+
10387+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
10388+
10389+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
10390+
10391+// If display id is -1 it will be unset
10392+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port,
10393+                                  const int display_id, const unsigned int layer);
10394+
10395+#endif
10396+
10397--- /dev/null
10398+++ b/modules/hw/mmal/transform_ops.h
10399@@ -0,0 +1,99 @@
10400+#ifndef VLC_MMAL_TRANSFORM_OPS_H
10401+#define VLC_MMAL_TRANSFORM_OPS_H
10402+
10403+#include <vlc_common.h>
10404+#include <vlc_picture.h>
10405+#include <interface/mmal/mmal.h>
10406+
10407+
10408+// These are enums with the same order so simply coerce
10409+static inline MMAL_DISPLAYTRANSFORM_T vlc_to_mmal_transform(const video_orientation_t orientation){
10410+    return (MMAL_DISPLAYTRANSFORM_T)orientation;
10411+}
10412+
10413+// MMAL headers comment these (getting 2 a bit wrong) but do not give
10414+// defines
10415+#define XFORM_H_SHIFT 0  // Hflip
10416+#define XFORM_V_SHIFT 1  // Vflip
10417+#define XFORM_T_SHIFT 2  // Transpose
10418+#define XFORM_H_BIT   (1 << XFORM_H_SHIFT)
10419+#define XFORM_V_BIT   (1 << XFORM_V_SHIFT)
10420+#define XFORM_T_BIT   (1 << XFORM_T_SHIFT)
10421+
10422+static inline bool
10423+is_transform_transpose(const MMAL_DISPLAYTRANSFORM_T t)
10424+{
10425+    return ((unsigned int)t & XFORM_T_BIT) != 0;
10426+}
10427+
10428+static inline bool
10429+is_transform_hflip(const MMAL_DISPLAYTRANSFORM_T t)
10430+{
10431+    return ((unsigned int)t & XFORM_H_BIT) != 0;
10432+}
10433+
10434+static inline bool
10435+is_transform_vflip(const MMAL_DISPLAYTRANSFORM_T t)
10436+{
10437+    return ((unsigned int)t & XFORM_V_BIT) != 0;
10438+}
10439+
10440+static inline MMAL_DISPLAYTRANSFORM_T
10441+swap_transform_hv(const MMAL_DISPLAYTRANSFORM_T x)
10442+{
10443+    return (((x >> XFORM_H_SHIFT) & 1) << XFORM_V_SHIFT) |
10444+           (((x >> XFORM_V_SHIFT) & 1) << XFORM_H_SHIFT) |
10445+           (x & XFORM_T_BIT);
10446+}
10447+
10448+static inline MMAL_DISPLAYTRANSFORM_T
10449+transform_inverse(const MMAL_DISPLAYTRANSFORM_T x)
10450+{
10451+    return is_transform_transpose(x) ? swap_transform_hv(x) : x;
10452+}
10453+
10454+// Transform generated by A then B
10455+// All ops are self inverse so can simply be XORed on their own
10456+// H & V flips after a transpose need to be swapped
10457+static inline MMAL_DISPLAYTRANSFORM_T
10458+combine_transform(const MMAL_DISPLAYTRANSFORM_T a, const MMAL_DISPLAYTRANSFORM_T b)
10459+{
10460+    return a ^ (is_transform_transpose(a) ? swap_transform_hv(b) : b);
10461+}
10462+
10463+static inline MMAL_RECT_T
10464+rect_transpose(const MMAL_RECT_T s)
10465+{
10466+    return (MMAL_RECT_T){
10467+        .x      = s.y,
10468+        .y      = s.x,
10469+        .width  = s.height,
10470+        .height = s.width
10471+    };
10472+}
10473+
10474+// hflip s in c
10475+static inline MMAL_RECT_T rect_hflip(const MMAL_RECT_T s, const MMAL_RECT_T c)
10476+{
10477+    return (MMAL_RECT_T){
10478+        .x = c.x + (c.x + c.width) - (s.x + s.width),
10479+        .y = s.y,
10480+        .width = s.width,
10481+        .height = s.height
10482+    };
10483+}
10484+
10485+// vflip s in c
10486+static inline MMAL_RECT_T rect_vflip(const MMAL_RECT_T s, const MMAL_RECT_T c)
10487+{
10488+    return (MMAL_RECT_T){
10489+        .x = s.x,
10490+        .y = (c.y + c.height) - (s.y - c.y) - s.height,
10491+        .width = s.width,
10492+        .height = s.height
10493+    };
10494+}
10495+
10496+
10497+#endif
10498+
10499--- /dev/null
10500+++ b/modules/hw/mmal/v7_pmu.S
10501@@ -0,0 +1,263 @@
10502+/*------------------------------------------------------------
10503+Performance Monitor Block
10504+------------------------------------------------------------*/
10505+    .arm  @ Make sure we are in ARM mode.
10506+    .text
10507+    .align 2
10508+    .global getPMN @ export this function for the linker
10509+
10510+/*  Returns the number of progammable counters uint32_t getPMN(void) */
10511+
10512+getPMN:
10513+  MRC     p15, 0, r0, c9, c12, 0 /* Read PMNC Register	*/
10514+  MOV     r0, r0, LSR #11        /* Shift N field down to bit 0	*/
10515+  AND     r0, r0, #0x1F          /* Mask to leave just the 5 N bits	*/
10516+  BX      lr
10517+
10518+
10519+
10520+    .global pmn_config @ export this function for the linker
10521+  /* Sets the event for a programmable counter to record	*/
10522+  /* void pmn_config(unsigned counter, uint32_t event)	*/
10523+  /* counter = r0 = Which counter to program  (e.g. 0 for PMN0, 1 for PMN1 */
10524+  /* event   = r1 = The event code	*/
10525+pmn_config:
10526+  AND     r0, r0, #0x1F          /* Mask to leave only bits 4:0	*/
10527+  MCR     p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register	*/
10528+  MCR     p15, 0, r1, c9, c13, 1 /* Write EVTSELx Register	*/
10529+  BX      lr
10530+
10531+
10532+
10533+    .global ccnt_divider @ export this function for the linker
10534+  /* Enables/disables the divider (1/64) on CCNT	*/
10535+  /* void ccnt_divider(int divider)	*/
10536+  /* divider = r0 = If 0 disable divider, else enable dvider	*/
10537+ccnt_divider:
10538+  MRC     p15, 0, r1, c9, c12, 0  /* Read PMNC	*/
10539+
10540+  CMP     r0, #0x0                /* IF (r0 == 0)	*/
10541+  BICEQ   r1, r1, #0x08           /* THEN: Clear the D bit (disables the */
10542+  ORRNE   r1, r1, #0x08           /* ELSE: Set the D bit (enables the di */
10543+
10544+  MCR     p15, 0, r1, c9, c12, 0  /* Write PMNC	*/
10545+  BX      lr
10546+
10547+
10548+  /* ---------------------------------------------------------------	*/
10549+  /* Enable/Disable	*/
10550+  /* ---------------------------------------------------------------	*/
10551+
10552+    .global enable_pmu @ export this function for the linker
10553+  /* Global PMU enable	*/
10554+  /* void enable_pmu(void)	*/
10555+enable_pmu:
10556+  MRC     p15, 0, r0, c9, c12, 0  /* Read PMNC	*/
10557+  ORR     r0, r0, #0x01           /* Set E bit	*/
10558+  MCR     p15, 0, r0, c9, c12, 0  /* Write PMNC	*/
10559+  BX      lr
10560+
10561+
10562+
10563+    .global disable_pmu @ export this function for the linker
10564+  /* Global PMU disable	*/
10565+  /* void disable_pmu(void)	*/
10566+disable_pmu:
10567+  MRC     p15, 0, r0, c9, c12, 0  /* Read PMNC	*/
10568+  BIC     r0, r0, #0x01           /* Clear E bit	*/
10569+  MCR     p15, 0, r0, c9, c12, 0  /* Write PMNC	*/
10570+  BX      lr
10571+
10572+
10573+
10574+    .global enable_ccnt @ export this function for the linker
10575+  /* Enable the CCNT	*/
10576+  /* void enable_ccnt(void)	*/
10577+enable_ccnt:
10578+  MOV     r0, #0x80000000         /* Set C bit	*/
10579+  MCR     p15, 0, r0, c9, c12, 1  /* Write CNTENS Register	*/
10580+  BX      lr
10581+
10582+
10583+
10584+    .global disable_ccnt @ export this function for the linker
10585+  /* Disable the CCNT	*/
10586+  /* void disable_ccnt(void)	*/
10587+disable_ccnt:
10588+  MOV     r0, #0x80000000         /* Clear C bit	*/
10589+  MCR     p15, 0, r0, c9, c12, 2  /* Write CNTENC Register	*/
10590+  BX      lr
10591+
10592+
10593+
10594+    .global enable_pmn @ export this function for the linker
10595+  /* Enable PMN{n}	*/
10596+  /* void enable_pmn(uint32_t counter)	*/
10597+  /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10598+enable_pmn: */
10599+  MOV     r1, #0x1                /* Use arg (r0) to set which counter t */
10600+  MOV     r1, r1, LSL r0
10601+
10602+  MCR     p15, 0, r1, c9, c12, 1  /* Write CNTENS Register	*/
10603+  BX      lr
10604+
10605+
10606+
10607+    .global disable_pmn @ export this function for the linker
10608+  /* Enable PMN{n}	*/
10609+  /* void disable_pmn(uint32_t counter)	*/
10610+  /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10611+disable_pmn: */
10612+  MOV     r1, #0x1                /* Use arg (r0) to set which counter t */
10613+  MOV     r1, r1, LSL r0
10614+
10615+  MCR     p15, 0, r1, c9, c12, 1  /* Write CNTENS Register	*/
10616+  BX      lr
10617+
10618+
10619+
10620+    .global enable_pmu_user_access @ export this function for the linker
10621+  /* Enables User mode access to the PMU (must be called in a priviledge */
10622+  /* void enable_pmu_user_access(void)	*/
10623+enable_pmu_user_access:
10624+  MRC     p15, 0, r0, c9, c14, 0  /* Read PMUSERENR Register	*/
10625+  ORR     r0, r0, #0x01           /* Set EN bit (bit 0)	*/
10626+  MCR     p15, 0, r0, c9, c14, 0  /* Write PMUSERENR Register	*/
10627+  BX      lr
10628+
10629+
10630+
10631+    .global disable_pmu_user_access @ export this function for the linke
10632+  /* Disables User mode access to the PMU (must be called in a priviledg */
10633+  /* void disable_pmu_user_access(void)	*/
10634+disable_pmu_user_access:
10635+  MRC     p15, 0, r0, c9, c14, 0  /* Read PMUSERENR Register	*/
10636+  BIC     r0, r0, #0x01           /* Clear EN bit (bit 0)	*/
10637+  MCR     p15, 0, r0, c9, c14, 0  /* Write PMUSERENR Register	*/
10638+  BX      lr
10639+
10640+
10641+  /* ---------------------------------------------------------------	*/
10642+  /* Counter read registers	*/
10643+  /* ---------------------------------------------------------------	*/
10644+
10645+    .global read_ccnt @ export this function for the linker
10646+  /* Returns the value of CCNT	*/
10647+  /* uint32_t read_ccnt(void)	*/
10648+read_ccnt:
10649+  MRC     p15, 0, r0, c9, c13, 0 /* Read CCNT Register	*/
10650+  BX      lr
10651+
10652+
10653+    .global read_pmn @ export this function for the linker
10654+  /* Returns the value of PMN{n}	*/
10655+  /* uint32_t read_pmn(uint32_t counter)	*/
10656+  /* counter = r0 =  The counter to read (e.g. 0 for PMN0, 1 for PMN1)	*
10657+read_pmn: */
10658+  AND     r0, r0, #0x1F          /* Mask to leave only bits 4:0	*/
10659+  MCR     p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register	*/
10660+  MRC     p15, 0, r0, c9, c13, 2 /* Read current PMNx Register	*/
10661+  BX      lr
10662+
10663+
10664+  /* ---------------------------------------------------------------	*/
10665+  /* Software Increment	*/
10666+  /* ---------------------------------------------------------------	*/
10667+
10668+    .global pmu_software_increment @ export this function for the linker
10669+	/* Writes to software increment register	*/
10670+	/* void pmu_software_increment(uint32_t counter)	*/
10671+	/* counter = r0 =  The counter to increment (e.g. 0 for PMN0, 1 for PMN
10672+pmu_software_increment: */
10673+  MOV     r1, #0x01
10674+  MOV			r1, r1, LSL r0
10675+  MCR     p15, 0, r1, c9, c12, 4 /* Write SWINCR Register	*/
10676+  BX      lr
10677+
10678+  /* ---------------------------------------------------------------	*/
10679+  /* Overflow & Interrupt Generation	*/
10680+  /* ---------------------------------------------------------------	*/
10681+
10682+    .global read_flags @ export this function for the linker
10683+  /* Returns the value of the overflow flags	*/
10684+  /* uint32_t read_flags(void)	*/
10685+read_flags:
10686+  MRC     p15, 0, r0, c9, c12, 3 /* Read FLAG Register	*/
10687+  BX      lr
10688+
10689+
10690+    .global write_flags @ export this function for the linker
10691+  /* Writes the overflow flags	*/
10692+  /* void write_flags(uint32_t flags)	*/
10693+write_flags:
10694+  MCR     p15, 0, r0, c9, c12, 3 /* Write FLAG Register	*/
10695+  BX      lr
10696+
10697+
10698+    .global enable_ccnt_irq @ export this function for the linker
10699+  /* Enables interrupt generation on overflow of the CCNT	*/
10700+  /* void enable_ccnt_irq(void)	*/
10701+enable_ccnt_irq:
10702+  MOV     r0, #0x80000000
10703+  MCR     p15, 0, r0, c9, c14, 1  /* Write INTENS Register	*/
10704+  BX      lr
10705+
10706+    .global disable_ccnt_irq @ export this function for the linker
10707+  /* Disables interrupt generation on overflow of the CCNT	*/
10708+  /* void disable_ccnt_irq(void)	*/
10709+disable_ccnt_irq:
10710+  MOV     r0, #0x80000000
10711+  MCR     p15, 0, r0, c9, c14, 2   /* Write INTENC Register	*/
10712+  BX      lr
10713+
10714+
10715+    .global enable_pmn_irq @ export this function for the linker
10716+  /* Enables interrupt generation on overflow of PMN{x}	*/
10717+  /* void enable_pmn_irq(uint32_t counter)	*/
10718+  /* counter = r0 =  The counter to enable the interrupt for (e.g. 0 for
10719+enable_pmn_irq: */
10720+  MOV     r1, #0x1                 /* Use arg (r0) to set which counter */
10721+  MOV     r0, r1, LSL r0
10722+  MCR     p15, 0, r0, c9, c14, 1   /* Write INTENS Register	*/
10723+  BX      lr
10724+
10725+    .global disable_pmn_irq @ export this function for the linker
10726+  /* Disables interrupt generation on overflow of PMN{x}	*/
10727+  /* void disable_pmn_irq(uint32_t counter)	*/
10728+  /* counter = r0 =  The counter to disable the interrupt for (e.g. 0 fo
10729+disable_pmn_irq: */
10730+  MOV     r1, #0x1                /* Use arg (r0) to set which counter t */
10731+  MOV     r0, r1, LSL r0
10732+  MCR     p15, 0, r0, c9, c14, 2  /* Write INTENC Register	*/
10733+  BX      lr
10734+
10735+  /* ---------------------------------------------------------------	*/
10736+  /* Reset Functions	*/
10737+  /* ---------------------------------------------------------------	*/
10738+
10739+    .global reset_pmn @ export this function for the linker
10740+  /* Resets the programmable counters	*/
10741+  /* void reset_pmn(void)	*/
10742+reset_pmn:
10743+  MRC     p15, 0, r0, c9, c12, 0  /* Read PMNC	*/
10744+  ORR     r0, r0, #0x02           /* Set P bit (Event Counter Reset)	*/
10745+  MCR     p15, 0, r0, c9, c12, 0  /* Write PMNC	*/
10746+  BX      lr
10747+
10748+
10749+	.global reset_ccnt @ export this function for the linker
10750+  /* Resets the CCNT	*/
10751+  /* void reset_ccnt(void)	*/
10752+reset_ccnt:
10753+  MRC     p15, 0, r0, c9, c12, 0  /* Read PMNC	*/
10754+  ORR     r0, r0, #0x04           /* Set C bit (Event Counter Reset)	*/
10755+  MCR     p15, 0, r0, c9, c12, 0  /* Write PMNC	*/
10756+  BX      lr
10757+
10758+
10759+    .end @end of code, this line is optional.
10760+/* ------------------------------------------------------------	*/
10761+/* End of v7_pmu.s	*/
10762+/* ------------------------------------------------------------	*/
10763+
10764+
10765--- /dev/null
10766+++ b/modules/hw/mmal/v7_pmu.h
10767@@ -0,0 +1,113 @@
10768+// ------------------------------------------------------------
10769+// PMU for Cortex-A/R (v7-A/R)
10770+// ------------------------------------------------------------
10771+
10772+#ifndef _V7_PMU_H
10773+#define _V7_PMU_H
10774+
10775+// Returns the number of progammable counters
10776+unsigned int getPMN(void);
10777+
10778+// Sets the event for a programmable counter to record
10779+// counter = r0 = Which counter to program  (e.g. 0 for PMN0, 1 for PMN1)
10780+// event   = r1 = The event code (from appropiate TRM or ARM Architecture Reference Manual)
10781+void pmn_config(unsigned int counter, unsigned int event);
10782+
10783+// Enables/disables the divider (1/64) on CCNT
10784+// divider = r0 = If 0 disable divider, else enable dvider
10785+void ccnt_divider(int divider);
10786+
10787+//
10788+// Enables and disables
10789+//
10790+
10791+// Global PMU enable
10792+// On ARM11 this enables the PMU, and the counters start immediately
10793+// On Cortex this enables the PMU, there are individual enables for the counters
10794+void enable_pmu(void);
10795+
10796+// Global PMU disable
10797+// On Cortex, this overrides the enable state of the individual counters
10798+void disable_pmu(void);
10799+
10800+// Enable the CCNT
10801+void enable_ccnt(void);
10802+
10803+// Disable the CCNT
10804+void disable_ccnt(void);
10805+
10806+// Enable PMN{n}
10807+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10808+void enable_pmn(unsigned int counter);
10809+
10810+// Enable PMN{n}
10811+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10812+void disable_pmn(unsigned int counter);
10813+
10814+//
10815+// Read counter values
10816+//
10817+
10818+// Returns the value of CCNT
10819+unsigned int read_ccnt(void);
10820+
10821+// Returns the value of PMN{n}
10822+// counter = The counter to read (e.g. 0 for PMN0, 1 for PMN1)
10823+unsigned int read_pmn(unsigned int counter);
10824+
10825+//
10826+// Overflow and interrupts
10827+//
10828+
10829+// Returns the value of the overflow flags
10830+unsigned int read_flags(void);
10831+
10832+// Writes the overflow flags
10833+void write_flags(unsigned int flags);
10834+
10835+// Enables interrupt generation on overflow of the CCNT
10836+void enable_ccnt_irq(void);
10837+
10838+// Disables interrupt generation on overflow of the CCNT
10839+void disable_ccnt_irq(void);
10840+
10841+// Enables interrupt generation on overflow of PMN{x}
10842+// counter = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
10843+void enable_pmn_irq(unsigned int counter);
10844+
10845+// Disables interrupt generation on overflow of PMN{x}
10846+// counter = r0 =  The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
10847+void disable_pmn_irq(unsigned int counter);
10848+
10849+//
10850+// Counter reset functions
10851+//
10852+
10853+// Resets the programmable counters
10854+void reset_pmn(void);
10855+
10856+// Resets the CCNT
10857+void reset_ccnt(void);
10858+
10859+//
10860+// Software Increment
10861+
10862+// Writes to software increment register
10863+// counter = The counter to increment (e.g. 0 for PMN0, 1 for PMN1)
10864+void pmu_software_increment(unsigned int counter);
10865+
10866+//
10867+// User mode access
10868+//
10869+
10870+// Enables User mode access to the PMU (must be called in a priviledged mode)
10871+void enable_pmu_user_access(void);
10872+
10873+// Disables User mode access to the PMU (must be called in a priviledged mode)
10874+void disable_pmu_user_access(void);
10875+
10876+#endif
10877+// ------------------------------------------------------------
10878+// End of v7_pmu.h
10879+// ------------------------------------------------------------
10880+
10881--- a/modules/hw/mmal/vout.c
10882+++ b/modules/hw/mmal/vout.c
10883@@ -27,21 +27,28 @@
10884 #endif
10885
10886 #include <math.h>
10887+#include <stdatomic.h>
10888
10889 #include <vlc_common.h>
10890-#include <vlc_atomic.h>
10891 #include <vlc_plugin.h>
10892 #include <vlc_threads.h>
10893 #include <vlc_vout_display.h>
10894+#include <vlc_modules.h>
10895
10896-#include "mmal_picture.h"
10897-
10898+#pragma GCC diagnostic push
10899+#pragma GCC diagnostic ignored "-Wbad-function-cast"
10900 #include <bcm_host.h>
10901+#pragma GCC diagnostic pop
10902 #include <interface/mmal/mmal.h>
10903 #include <interface/mmal/util/mmal_util.h>
10904 #include <interface/mmal/util/mmal_default_components.h>
10905 #include <interface/vmcs_host/vc_tvservice.h>
10906-#include <interface/vmcs_host/vc_dispmanx.h>
10907+
10908+#include "mmal_picture.h"
10909+#include "subpic.h"
10910+#include "transform_ops.h"
10911+
10912+#define TRACE_ALL 0
10913
10914 #define MAX_BUFFERS_IN_TRANSIT 1
10915 #define VC_TV_MAX_MODE_IDS 127
10916@@ -50,10 +57,28 @@
10917 #define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.")
10918 #define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.")
10919
10920-#define MMAL_BLANK_BACKGROUND_NAME "mmal-blank-background"
10921-#define MMAL_BLANK_BACKGROUND_TEXT N_("Blank screen below video.")
10922-#define MMAL_BLANK_BACKGROUND_LONGTEXT N_("Render blank screen below video. " \
10923-        "Increases VideoCore load.")
10924+#define MMAL_DISPLAY_NAME "mmal-display"
10925+#define MMAL_DISPLAY_TEXT N_("Output device for Rpi fullscreen.")
10926+#define MMAL_DISPLAY_LONGTEXT N_("Output device for Rpi fullscreen. " \
10927+"Valid values are HDMI-1,HDMI-2.  By default if qt-fullscreen-screennumber " \
10928+"is specified (or set by Fullscreen Output Device in Preferences) " \
10929+"HDMI-<qt-fullscreen-screennumber+1> will be used, otherwise HDMI-1.")
10930+
10931+#define MMAL_VOUT_TRANSFORM_NAME "mmal-vout-transform"
10932+#define MMAL_VOUT_TRANSFORM_TEXT N_("Video transform for Rpi fullscreen.")
10933+#define MMAL_VOUT_TRANSFORM_LONGTEXT N_("Video transform for Rpi fullscreen."\
10934+"Transforms availible: auto, 0, 90, 180, 270, hflip, vflip, transpose, antitranspose")
10935+
10936+#define MMAL_VOUT_WINDOW_NAME "mmal-vout-window"
10937+#define MMAL_VOUT_WINDOW_TEXT N_("Display window for Rpi fullscreen")
10938+#define MMAL_VOUT_WINDOW_LONGTEXT N_("Display window for Rpi fullscreen."\
10939+"fullscreen|<width>x<height>+<x>+<y>")
10940+
10941+#define MMAL_VOUT_TRANSPARENT_NAME "mmal-vout-transparent"
10942+#define MMAL_VOUT_TRANSPARENT_TEXT N_("Enable layers beneeth the vodeo layer.")
10943+#define MMAL_VOUT_TRANSPARENT_LONGTEXT N_("Enable layers beneath the video layer."\
10944+" By default these are disabled."\
10945+" Having the lower layers enabled can impact video performance")
10946
10947 #define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate"
10948 #define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.")
10949@@ -68,332 +93,628 @@
10950 #define PHASE_OFFSET_TARGET ((double)0.25)
10951 #define PHASE_CHECK_INTERVAL 100
10952
10953-static int Open(vlc_object_t *);
10954-static void Close(vlc_object_t *);
10955-
10956-vlc_module_begin()
10957-    set_shortname(N_("MMAL vout"))
10958-    set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
10959-    set_capability("vout display", 90)
10960-    add_shortcut("mmal_vout")
10961-    add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
10962-    add_bool(MMAL_BLANK_BACKGROUND_NAME, true, MMAL_BLANK_BACKGROUND_TEXT,
10963-                    MMAL_BLANK_BACKGROUND_LONGTEXT, true);
10964-    add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
10965-                    MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
10966-    add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
10967-                    MMAL_NATIVE_INTERLACE_LONGTEXT, false)
10968-    set_callbacks(Open, Close)
10969-vlc_module_end()
10970+#define SUBS_MAX 4
10971
10972-struct dmx_region_t {
10973-    struct dmx_region_t *next;
10974-    picture_t *picture;
10975-    VC_RECT_T bmp_rect;
10976-    VC_RECT_T src_rect;
10977-    VC_RECT_T dst_rect;
10978-    VC_DISPMANX_ALPHA_T alpha;
10979-    DISPMANX_ELEMENT_HANDLE_T element;
10980-    DISPMANX_RESOURCE_HANDLE_T resource;
10981-    int32_t pos_x;
10982-    int32_t pos_y;
10983-};
10984+typedef struct vout_subpic_s {
10985+    MMAL_COMPONENT_T *component;
10986+    subpic_reg_stash_t sub;
10987+} vout_subpic_t;
10988
10989 struct vout_display_sys_t {
10990-    vlc_cond_t buffer_cond;
10991-    vlc_mutex_t buffer_mutex;
10992     vlc_mutex_t manage_mutex;
10993
10994-    plane_t planes[3]; /* Depending on video format up to 3 planes are used */
10995-    picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */
10996-    picture_pool_t *picture_pool;
10997-
10998+    vcsm_init_type_t init_type;
10999     MMAL_COMPONENT_T *component;
11000     MMAL_PORT_T *input;
11001     MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/
11002-    struct dmx_region_t *dmx_region;
11003     int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */
11004
11005-    uint32_t buffer_size; /* size of actual mmal buffers */
11006     int buffers_in_transit; /* number of buffers currently pushed to mmal component */
11007     unsigned num_buffers; /* number of buffers allocated at mmal port */
11008
11009-    DISPMANX_DISPLAY_HANDLE_T dmx_handle;
11010-    DISPMANX_ELEMENT_HANDLE_T bkg_element;
11011-    DISPMANX_RESOURCE_HANDLE_T bkg_resource;
11012-    unsigned display_width;
11013-    unsigned display_height;
11014+    int display_id;
11015+    MMAL_RECT_T win_rect;       // Window rect after transform(s)
11016+    MMAL_RECT_T display_rect;   // Actual shape of display (x, y always 0)
11017+    MMAL_RECT_T req_win;        // User requested window (w=0 => fullscreen)
11018+
11019+    MMAL_RECT_T spu_rect;       // Output rectangle in cfg coords (for subpic placement)
11020+    MMAL_RECT_T dest_rect;      // Output rectangle in display coords
11021+    MMAL_DISPLAYTRANSFORM_T dest_transform;      // Dest window coord transform
11022+    MMAL_DISPLAYTRANSFORM_T display_transform;  // "Native" display transform
11023+    MMAL_DISPLAYTRANSFORM_T video_transform;     // Combined config+native transform
11024
11025-    int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
11026-    int i_frame_rate;
11027+    unsigned int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
11028+    unsigned int i_frame_rate;
11029
11030     int next_phase_check; /* lowpass for phase check frequency */
11031     int phase_offset; /* currently applied offset to presentation time in ns */
11032     int layer; /* the dispman layer (z-index) used for video rendering */
11033+    bool transparent;           // Do not disable layers beneath ours
11034
11035     bool need_configure_display; /* indicates a required display reconfigure to main thread */
11036     bool adjust_refresh_rate;
11037     bool native_interlaced;
11038     bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */
11039     bool b_progressive;
11040-    bool opaque; /* indicated use of opaque picture format (zerocopy) */
11041-};
11042+    bool force_config;
11043
11044-static const vlc_fourcc_t subpicture_chromas[] = {
11045-    VLC_CODEC_RGBA,
11046-    0
11047-};
11048+    vout_subpic_t subs[SUBS_MAX];
11049+    // Stash for subpics derived from the passed subpicture rather than
11050+    // included with the main pic
11051+    MMAL_BUFFER_HEADER_T * subpic_bufs[SUBS_MAX];
11052+
11053+    picture_pool_t * pic_pool;
11054+
11055+    struct vout_isp_conf_s {
11056+        MMAL_COMPONENT_T *component;
11057+        MMAL_PORT_T * input;
11058+        MMAL_PORT_T * output;
11059+        MMAL_QUEUE_T * out_q;
11060+        MMAL_POOL_T * in_pool;
11061+        MMAL_POOL_T * out_pool;
11062+        bool pending;
11063+    } isp;
11064
11065-/* Utility functions */
11066-static inline uint32_t align(uint32_t x, uint32_t y);
11067-static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
11068-                const video_format_t *fmt);
11069+    MMAL_POOL_T * copy_pool;
11070+    MMAL_BUFFER_HEADER_T * copy_buf;
11071
11072-/* VLC vout display callbacks */
11073-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count);
11074-static void vd_prepare(vout_display_t *vd, picture_t *picture,
11075-                subpicture_t *subpicture);
11076-static void vd_display(vout_display_t *vd, picture_t *picture,
11077-                subpicture_t *subpicture);
11078-static int vd_control(vout_display_t *vd, int query, va_list args);
11079-static void vd_manage(vout_display_t *vd);
11080-
11081-/* MMAL callbacks */
11082-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
11083-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
11084+    // Subpic blend if we have to do it here
11085+    vzc_pool_ctl_t * vzc;
11086+};
11087
11088-/* TV service */
11089-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height);
11090-static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
11091-                uint32_t param2);
11092-static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
11093-static int set_latency_target(vout_display_t *vd, bool enable);
11094
11095-/* DispManX */
11096-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture);
11097-static void close_dmx(vout_display_t *vd);
11098-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
11099-                DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region);
11100-static void dmx_region_update(struct dmx_region_t *dmx_region,
11101-                DISPMANX_UPDATE_HANDLE_T update, picture_t *picture);
11102-static void dmx_region_delete(struct dmx_region_t *dmx_region,
11103-                DISPMANX_UPDATE_HANDLE_T update);
11104-static void show_background(vout_display_t *vd, bool enable);
11105-static void maintain_phase_sync(vout_display_t *vd);
11106+// ISP setup
11107
11108-static int Open(vlc_object_t *object)
11109+static inline bool want_isp(const vout_display_t * const vd)
11110 {
11111-    vout_display_t *vd = (vout_display_t *)object;
11112-    vout_display_sys_t *sys;
11113-    uint32_t buffer_pitch, buffer_height;
11114-    vout_display_place_t place;
11115-    MMAL_DISPLAYREGION_T display_region;
11116-    MMAL_STATUS_T status;
11117-    int ret = VLC_SUCCESS;
11118-    unsigned i;
11119+    return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10);
11120+}
11121
11122-    if (vout_display_IsWindowed(vd))
11123-        return VLC_EGENERIC;
11124+static inline bool want_copy(const vout_display_t * const vd)
11125+{
11126+    return (vd->fmt.i_chroma == VLC_CODEC_I420 || vd->fmt.i_chroma == VLC_CODEC_I420_10L);
11127+}
11128
11129-    sys = calloc(1, sizeof(struct vout_display_sys_t));
11130-    if (!sys)
11131-        return VLC_ENOMEM;
11132-    vd->sys = sys;
11133+static inline vlc_fourcc_t req_chroma(const vout_display_t * const vd)
11134+{
11135+    return !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma) && !want_copy(vd) ?
11136+        VLC_CODEC_I420 :
11137+        vd->fmt.i_chroma;
11138+}
11139
11140-    sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
11141-    bcm_host_init();
11142+static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc)
11143+{
11144+    switch (fcc){
11145+    case VLC_CODEC_MMAL_OPAQUE:
11146+        return MMAL_ENCODING_OPAQUE;
11147+    case VLC_CODEC_MMAL_ZC_SAND8:
11148+        return MMAL_ENCODING_YUVUV128;
11149+    case VLC_CODEC_MMAL_ZC_SAND10:
11150+        return MMAL_ENCODING_YUVUV64_10;
11151+    case VLC_CODEC_MMAL_ZC_SAND30:
11152+        return MMAL_ENCODING_YUV10_COL;
11153+    case VLC_CODEC_MMAL_ZC_I420:
11154+    case VLC_CODEC_I420:
11155+        return MMAL_ENCODING_I420;
11156+    default:
11157+        break;
11158+    }
11159+    return MMAL_ENCODING_I420;
11160+}
11161
11162-    sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE;
11163+static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate)
11164+{
11165+    const unsigned int w = is_intermediate ? vd->fmt.i_visible_width  : vd->fmt.i_width ;
11166+    const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height;
11167+    MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
11168
11169-    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
11170-    if (status != MMAL_SUCCESS) {
11171-        msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
11172-                        MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
11173-        ret = VLC_EGENERIC;
11174-        goto out;
11175+    es_fmt->type = MMAL_ES_TYPE_VIDEO;
11176+    es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
11177+    es_fmt->encoding_variant = 0;
11178+
11179+    v_fmt->width  = (w + 31) & ~31;
11180+    v_fmt->height = (h + 15) & ~15;
11181+    v_fmt->crop.x = 0;
11182+    v_fmt->crop.y = 0;
11183+    v_fmt->crop.width = w;
11184+    v_fmt->crop.height = h;
11185+    if (vd->fmt.i_sar_num == 0 || vd->fmt.i_sar_den == 0) {
11186+        v_fmt->par.num        = 1;
11187+        v_fmt->par.den        = 1;
11188+    } else {
11189+        v_fmt->par.num        = vd->fmt.i_sar_num;
11190+        v_fmt->par.den        = vd->fmt.i_sar_den;
11191     }
11192+    v_fmt->frame_rate.num = vd->fmt.i_frame_rate;
11193+    v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base;
11194+    v_fmt->color_space    = vlc_to_mmal_color_space(vd->fmt.space);
11195
11196-    sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
11197-    status = mmal_port_enable(sys->component->control, control_port_cb);
11198-    if (status != MMAL_SUCCESS) {
11199-        msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
11200-                        sys->component->control->name, status, mmal_status_to_string(status));
11201-        ret = VLC_EGENERIC;
11202-        goto out;
11203+    msg_Dbg(vd, "WxH: %dx%d, Crop: %dx%d", v_fmt->width, v_fmt->height, v_fmt->crop.width, v_fmt->crop.height);
11204+}
11205+
11206+static MMAL_RECT_T
11207+display_src_rect(const vout_display_t * const vd, const video_format_t * const src)
11208+{
11209+    const bool wants_isp = want_isp(vd);
11210+
11211+    // Scale source derived cropping to actual picture shape
11212+    return (MMAL_RECT_T){
11213+        .x = wants_isp ? 0 : src->i_x_offset * vd->fmt.i_width / src->i_width,
11214+        .y = wants_isp ? 0 : src->i_y_offset * vd->fmt.i_height / src->i_height,
11215+        .width  = src->i_visible_width  * vd->fmt.i_width / src->i_width,
11216+        .height = src->i_visible_height * vd->fmt.i_height / src->i_height
11217+    };
11218+}
11219+
11220+static void isp_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
11221+{
11222+#if TRACE_ALL
11223+    vout_display_t * const vd = (vout_display_t *)port->userdata;
11224+    pic_ctx_mmal_t * ctx = buf->user_data;
11225+    msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf,
11226+            buf->flags, (long long)buf->pts);
11227+#else
11228+    VLC_UNUSED(port);
11229+#endif
11230+
11231+    mmal_buffer_header_release(buf);
11232+
11233+#if TRACE_ALL
11234+    msg_Dbg(vd, ">>> %s", __func__);
11235+#endif
11236+}
11237+
11238+static void isp_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
11239+{
11240+    vout_display_t *vd = (vout_display_t *)port->userdata;
11241+    MMAL_STATUS_T status;
11242+
11243+    if (buffer->cmd == MMAL_EVENT_ERROR) {
11244+        status = *(uint32_t *)buffer->data;
11245+        msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
11246     }
11247
11248-    sys->input = sys->component->input[0];
11249-    sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
11250+    mmal_buffer_header_release(buffer);
11251+}
11252
11253-    if (sys->opaque) {
11254-        sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
11255-        sys->i_planes = 1;
11256-        sys->buffer_size = sys->input->buffer_size_recommended;
11257-    } else {
11258-        sys->input->format->encoding = MMAL_ENCODING_I420;
11259-        vd->fmt.i_chroma = VLC_CODEC_I420;
11260-        buffer_pitch = align(vd->fmt.i_width, 32);
11261-        buffer_height = align(vd->fmt.i_height, 16);
11262-        sys->i_planes = 3;
11263-        sys->buffer_size = 3 * buffer_pitch * buffer_height / 2;
11264-    }
11265-
11266-    sys->input->format->es->video.width = vd->fmt.i_width;
11267-    sys->input->format->es->video.height = vd->fmt.i_height;
11268-    sys->input->format->es->video.crop.x = 0;
11269-    sys->input->format->es->video.crop.y = 0;
11270-    sys->input->format->es->video.crop.width = vd->fmt.i_width;
11271-    sys->input->format->es->video.crop.height = vd->fmt.i_height;
11272-    sys->input->format->es->video.par.num = vd->source.i_sar_num;
11273-    sys->input->format->es->video.par.den = vd->source.i_sar_den;
11274+static void isp_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
11275+{
11276+    if (buf->cmd == 0 && buf->length != 0)
11277+    {
11278+        // The filter structure etc. should always exist if we have contents
11279+        // but might not on later flushes as we shut down
11280+        vout_display_t * const vd = (vout_display_t *)port->userdata;
11281+        struct vout_isp_conf_s *const isp = &vd->sys->isp;
11282
11283-    status = mmal_port_format_commit(sys->input);
11284-    if (status != MMAL_SUCCESS) {
11285-        msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
11286-                        sys->input->name, status, mmal_status_to_string(status));
11287-        ret = VLC_EGENERIC;
11288-        goto out;
11289+#if TRACE_ALL
11290+        msg_Dbg(vd, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts);
11291+#endif
11292+        mmal_queue_put(isp->out_q, buf);
11293+#if TRACE_ALL
11294+        msg_Dbg(vd, ">>> %s: out Q len=%d", __func__, mmal_queue_length(isp->out_q));
11295+#endif
11296     }
11297-    sys->input->buffer_size = sys->input->buffer_size_recommended;
11298+    else
11299+    {
11300+        mmal_buffer_header_reset(buf);
11301+        mmal_buffer_header_release(buf);
11302+    }
11303+}
11304
11305-    vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
11306-    display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
11307-    display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
11308-    display_region.fullscreen = MMAL_FALSE;
11309-    display_region.src_rect.x = vd->fmt.i_x_offset;
11310-    display_region.src_rect.y = vd->fmt.i_y_offset;
11311-    display_region.src_rect.width = vd->fmt.i_visible_width;
11312-    display_region.src_rect.height = vd->fmt.i_visible_height;
11313-    display_region.dest_rect.x = place.x;
11314-    display_region.dest_rect.y = place.y;
11315-    display_region.dest_rect.width = place.width;
11316-    display_region.dest_rect.height = place.height;
11317-    display_region.layer = sys->layer;
11318-    display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
11319-            MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
11320-    status = mmal_port_parameter_set(sys->input, &display_region.hdr);
11321-    if (status != MMAL_SUCCESS) {
11322-        msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
11323-                        status, mmal_status_to_string(status));
11324-        ret = VLC_EGENERIC;
11325-        goto out;
11326+static void isp_empty_out_q(struct vout_isp_conf_s * const isp)
11327+{
11328+    MMAL_BUFFER_HEADER_T * buf;
11329+    // We can be called as part of error recovery so allow for missing Q
11330+    if (isp->out_q == NULL)
11331+        return;
11332+
11333+    while ((buf = mmal_queue_get(isp->out_q)) != NULL)
11334+        mmal_buffer_header_release(buf);
11335+}
11336+
11337+static void isp_flush(struct vout_isp_conf_s * const isp)
11338+{
11339+    if (!isp->input->is_enabled)
11340+        mmal_port_disable(isp->input);
11341+
11342+    if (isp->output->is_enabled)
11343+        mmal_port_disable(isp->output);
11344+
11345+    isp_empty_out_q(isp);
11346+    isp->pending = false;
11347+}
11348+
11349+static MMAL_STATUS_T isp_prepare(vout_display_t * const vd, struct vout_isp_conf_s * const isp)
11350+{
11351+    MMAL_STATUS_T err;
11352+    MMAL_BUFFER_HEADER_T * buf;
11353+
11354+    if (!isp->output->is_enabled) {
11355+        if ((err = mmal_port_enable(isp->output, isp_output_cb)) != MMAL_SUCCESS)
11356+        {
11357+            msg_Err(vd, "ISP output port enable failed");
11358+            return err;
11359+        }
11360     }
11361
11362-    for (i = 0; i < sys->i_planes; ++i) {
11363-        sys->planes[i].i_lines = buffer_height;
11364-        sys->planes[i].i_pitch = buffer_pitch;
11365-        sys->planes[i].i_visible_lines = vd->fmt.i_visible_height;
11366-        sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width;
11367+    while ((buf = mmal_queue_get(isp->out_pool->queue)) != NULL) {
11368+        if ((err = mmal_port_send_buffer(isp->output, buf)) != MMAL_SUCCESS)
11369+        {
11370+            msg_Err(vd, "ISP output port stuff failed");
11371+            return err;
11372+        }
11373+    }
11374
11375-        if (i > 0) {
11376-            sys->planes[i].i_lines /= 2;
11377-            sys->planes[i].i_pitch /= 2;
11378-            sys->planes[i].i_visible_lines /= 2;
11379-            sys->planes[i].i_visible_pitch /= 2;
11380+    if (!isp->input->is_enabled) {
11381+        if ((err = mmal_port_enable(isp->input, isp_input_cb)) != MMAL_SUCCESS)
11382+        {
11383+            msg_Err(vd, "ISP input port enable failed");
11384+            return err;
11385         }
11386     }
11387+    return MMAL_SUCCESS;
11388+}
11389
11390-    vlc_mutex_init(&sys->buffer_mutex);
11391-    vlc_cond_init(&sys->buffer_cond);
11392-    vlc_mutex_init(&sys->manage_mutex);
11393+static void isp_close(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
11394+{
11395+    struct vout_isp_conf_s * const isp = &vd_sys->isp;
11396+    VLC_UNUSED(vd);
11397
11398-    vd->pool = vd_pool;
11399-    vd->prepare = vd_prepare;
11400-    vd->display = vd_display;
11401-    vd->control = vd_control;
11402-    vd->manage = vd_manage;
11403+    if (isp->component == NULL)
11404+        return;
11405
11406-    vc_tv_register_callback(tvservice_cb, vd);
11407+    isp_flush(isp);
11408
11409-    if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) {
11410-        vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height);
11411-    } else {
11412-        sys->display_width = vd->cfg->display.width;
11413-        sys->display_height = vd->cfg->display.height;
11414+    if (isp->component->control->is_enabled)
11415+        mmal_port_disable(isp->component->control);
11416+
11417+    if (isp->out_q != NULL) {
11418+        // 1st junk anything lying around
11419+        isp_empty_out_q(isp);
11420+
11421+        mmal_queue_destroy(isp->out_q);
11422+        isp->out_q = NULL;
11423     }
11424
11425-    sys->dmx_handle = vc_dispmanx_display_open(0);
11426-    vd->info.subpicture_chromas = subpicture_chromas;
11427+    if (isp->out_pool != NULL) {
11428+        mmal_port_pool_destroy(isp->output, isp->out_pool);
11429+        isp->out_pool = NULL;
11430+    }
11431
11432-    vout_display_DeleteWindow(vd, NULL);
11433+    isp->input = NULL;
11434+    isp->output = NULL;
11435
11436-out:
11437-    if (ret != VLC_SUCCESS)
11438-        Close(object);
11439+    mmal_component_release(isp->component);
11440+    isp->component = NULL;
11441
11442-    return ret;
11443+    return;
11444 }
11445
11446-static void Close(vlc_object_t *object)
11447+// Restuff into output rather than return to pool is we can
11448+static MMAL_BOOL_T isp_out_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata)
11449 {
11450-    vout_display_t *vd = (vout_display_t *)object;
11451-    vout_display_sys_t *sys = vd->sys;
11452-    char response[20]; /* answer is hvs_update_fields=%1d */
11453-    unsigned i;
11454+    struct vout_isp_conf_s * const isp = userdata;
11455+    VLC_UNUSED(pool);
11456+    if (isp->output->is_enabled) {
11457+        mmal_buffer_header_reset(buffer);
11458+        if (mmal_port_send_buffer(isp->output, buffer) == MMAL_SUCCESS)
11459+            return MMAL_FALSE;
11460+    }
11461+    return MMAL_TRUE;
11462+}
11463
11464-    vc_tv_unregister_callback_full(tvservice_cb, vd);
11465+static MMAL_STATUS_T isp_setup(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
11466+{
11467+    struct vout_isp_conf_s * const isp = &vd_sys->isp;
11468+    MMAL_STATUS_T err;
11469
11470-    if (sys->dmx_handle)
11471-        close_dmx(vd);
11472+    if ((err = mmal_component_create(MMAL_COMPONENT_ISP_RESIZER, &isp->component)) != MMAL_SUCCESS) {
11473+        msg_Err(vd, "Cannot create ISP component");
11474+        return err;
11475+    }
11476+    isp->input = isp->component->input[0];
11477+    isp->output = isp->component->output[0];
11478
11479-    if (sys->component && sys->component->control->is_enabled)
11480-        mmal_port_disable(sys->component->control);
11481+    isp->component->control->userdata = (void *)vd;
11482+    if ((err = mmal_port_enable(isp->component->control, isp_control_port_cb)) != MMAL_SUCCESS) {
11483+        msg_Err(vd, "Failed to enable ISP control port");
11484+        goto fail;
11485+    }
11486
11487-    if (sys->input && sys->input->is_enabled)
11488-        mmal_port_disable(sys->input);
11489+    isp->input->userdata = (void *)vd;
11490+    display_set_format(vd, isp->input->format, false);
11491
11492-    if (sys->component && sys->component->is_enabled)
11493-        mmal_component_disable(sys->component);
11494+    if ((err = port_parameter_set_bool(isp->input, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
11495+        goto fail;
11496
11497-    if (sys->pool)
11498-        mmal_port_pool_destroy(sys->input, sys->pool);
11499+    if ((err = mmal_port_format_commit(isp->input)) != MMAL_SUCCESS) {
11500+        msg_Err(vd, "Failed to set ISP input format");
11501+        goto fail;
11502+    }
11503
11504-    if (sys->component)
11505-        mmal_component_release(sys->component);
11506+    isp->input->buffer_size = isp->input->buffer_size_recommended;
11507+    isp->input->buffer_num = 30;
11508
11509-    if (sys->picture_pool)
11510-        picture_pool_Release(sys->picture_pool);
11511-    else
11512-        for (i = 0; i < sys->num_buffers; ++i)
11513-            if (sys->pictures[i]) {
11514-                mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer);
11515-                picture_Release(sys->pictures[i]);
11516-            }
11517+    if ((isp->in_pool = mmal_pool_create(isp->input->buffer_num, 0)) == NULL)
11518+    {
11519+        msg_Err(vd, "Failed to create input pool");
11520+        goto fail;
11521+    }
11522
11523-    vlc_mutex_destroy(&sys->buffer_mutex);
11524-    vlc_cond_destroy(&sys->buffer_cond);
11525-    vlc_mutex_destroy(&sys->manage_mutex);
11526+    if ((isp->out_q = mmal_queue_create()) == NULL)
11527+    {
11528+        err = MMAL_ENOMEM;
11529+        goto fail;
11530+    }
11531
11532-    if (sys->native_interlaced) {
11533-        if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
11534-                response[18] != '0')
11535-            msg_Warn(vd, "Could not reset hvs field mode");
11536+    display_set_format(vd, isp->output->format, true);
11537+
11538+    if ((err = port_parameter_set_bool(isp->output, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
11539+        goto fail;
11540+
11541+    if ((err = mmal_port_format_commit(isp->output)) != MMAL_SUCCESS) {
11542+        msg_Err(vd, "Failed to set ISP input format");
11543+        goto fail;
11544     }
11545
11546-    free(sys->pictures);
11547-    free(sys);
11548+    isp->output->buffer_size = isp->output->buffer_size_recommended;
11549+    isp->output->buffer_num = 2;
11550+    isp->output->userdata = (void *)vd;
11551+
11552+    if ((isp->out_pool = mmal_port_pool_create(isp->output, isp->output->buffer_num, isp->output->buffer_size)) == NULL)
11553+    {
11554+        msg_Err(vd, "Failed to make ISP port pool");
11555+        goto fail;
11556+    }
11557+
11558+    mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp);
11559+
11560+    if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS)
11561+        goto fail;
11562+
11563+    return MMAL_SUCCESS;
11564
11565-    bcm_host_deinit();
11566+fail:
11567+    isp_close(vd, vd_sys);
11568+    return err;
11569 }
11570
11571-static inline uint32_t align(uint32_t x, uint32_t y) {
11572-    uint32_t mod = x % y;
11573-    if (mod == 0)
11574-        return x;
11575+static MMAL_STATUS_T isp_check(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
11576+{
11577+    struct vout_isp_conf_s *const isp = &vd_sys->isp;
11578+    const bool has_isp = (isp->component != NULL);
11579+    const bool wants_isp = want_isp(vd);
11580+
11581+    if (has_isp == wants_isp)
11582+    {
11583+        // All OK - do nothing
11584+    }
11585+    else if (has_isp)
11586+    {
11587+        // ISP active but we don't want it
11588+        isp_flush(isp);
11589+
11590+        // Check we have everything back and then kill it
11591+        if (mmal_queue_length(isp->out_pool->queue) == isp->output->buffer_num)
11592+            isp_close(vd, vd_sys);
11593+    }
11594     else
11595-        return x + y - mod;
11596+    {
11597+        // ISP closed but we want it
11598+        return isp_setup(vd, vd_sys);
11599+    }
11600+
11601+    return MMAL_SUCCESS;
11602+}
11603+
11604+/* TV service */
11605+static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
11606+                uint32_t param2);
11607+static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
11608+static int set_latency_target(vout_display_t *vd, bool enable);
11609+
11610+// Mmal
11611+static void maintain_phase_sync(vout_display_t *vd);
11612+
11613+
11614+
11615+static void vd_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
11616+{
11617+#if TRACE_ALL
11618+    vout_display_t * const vd = (vout_display_t *)port->userdata;
11619+    pic_ctx_mmal_t * ctx = buf->user_data;
11620+    msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf,
11621+            buf->flags, (long long)buf->pts);
11622+#else
11623+    VLC_UNUSED(port);
11624+#endif
11625+
11626+    mmal_buffer_header_release(buf);
11627+
11628+#if TRACE_ALL
11629+    msg_Dbg(vd, ">>> %s", __func__);
11630+#endif
11631+}
11632+
11633+static int query_resolution(vout_display_t *vd, const int display_id, unsigned *width, unsigned *height)
11634+{
11635+    TV_DISPLAY_STATE_T display_state = {0};
11636+    int ret = 0;
11637+
11638+    if (vc_tv_get_display_state_id(display_id, &display_state) == 0) {
11639+        msg_Dbg(vd, "State=%#x", display_state.state);
11640+        if (display_state.state & 0xFF) {
11641+            msg_Dbg(vd, "HDMI: %dx%d", display_state.display.hdmi.width, display_state.display.hdmi.height);
11642+            *width = display_state.display.hdmi.width;
11643+            *height = display_state.display.hdmi.height;
11644+        } else if (display_state.state & 0xFF00) {
11645+            msg_Dbg(vd, "SDTV: %dx%d", display_state.display.sdtv.width, display_state.display.sdtv.height);
11646+            *width = display_state.display.sdtv.width;
11647+            *height = display_state.display.sdtv.height;
11648+        } else {
11649+            msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
11650+            ret = -1;
11651+        }
11652+    } else {
11653+        msg_Warn(vd, "Failed to query display resolution");
11654+        ret = -1;
11655+    }
11656+
11657+    return ret;
11658+}
11659+
11660+static inline MMAL_RECT_T
11661+place_to_mmal_rect(const vout_display_place_t place)
11662+{
11663+    return (MMAL_RECT_T){
11664+        .x      = place.x,
11665+        .y      = place.y,
11666+        .width  = place.width,
11667+        .height = place.height
11668+    };
11669+}
11670+
11671+static MMAL_RECT_T
11672+place_out(const vout_display_cfg_t * cfg,
11673+          const video_format_t * fmt,
11674+          const MMAL_RECT_T r)
11675+{
11676+    video_format_t tfmt;
11677+    vout_display_cfg_t tcfg;
11678+    vout_display_place_t place;
11679+
11680+    // Fix SAR if unknown
11681+    if (fmt->i_sar_den == 0 || fmt->i_sar_num == 0) {
11682+        tfmt = *fmt;
11683+        tfmt.i_sar_den = 1;
11684+        tfmt.i_sar_num = 1;
11685+        fmt = &tfmt;
11686+    }
11687+
11688+    // Override what VLC thinks might be going on with display size
11689+    // if we know better
11690+    if (r.width != 0 && r.height != 0)
11691+    {
11692+        tcfg = *cfg;
11693+        tcfg.display.width = r.width;
11694+        tcfg.display.height = r.height;
11695+        cfg = &tcfg;
11696+    }
11697+
11698+    vout_display_PlacePicture(&place, fmt, cfg, false);
11699+
11700+    place.x += r.x;
11701+    place.y += r.y;
11702+
11703+    return place_to_mmal_rect(place);
11704+}
11705+
11706+static MMAL_RECT_T
11707+rect_transform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t)
11708+{
11709+    if (is_transform_transpose(t))
11710+        s = rect_transpose(s);
11711+    if (is_transform_hflip(t))
11712+        s = rect_hflip(s, c);
11713+    if (is_transform_vflip(t) != 0)
11714+        s = rect_vflip(s, c);
11715+    return s;
11716+}
11717+
11718+static void
11719+place_dest_rect(vout_display_t * const vd,
11720+          const vout_display_cfg_t * const cfg,
11721+          const video_format_t * fmt)
11722+{
11723+    vout_display_sys_t * const sys = vd->sys;
11724+    sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect),
11725+                                    sys->display_rect, sys->dest_transform);
11726+}
11727+
11728+static void
11729+place_spu_rect(vout_display_t * const vd,
11730+          const vout_display_cfg_t * const cfg,
11731+          const video_format_t * fmt)
11732+{
11733+    vout_display_sys_t * const sys = vd->sys;
11734+    static const MMAL_RECT_T r0 = {0};
11735+
11736+    sys->spu_rect = place_out(cfg, fmt, r0);
11737+    sys->spu_rect.x = 0;
11738+    sys->spu_rect.y = 0;
11739+
11740+    // Copy place override logic for spu pos from video_output.c
11741+    // This info doesn't appear to reside anywhere natively
11742+
11743+    if (fmt->i_width * fmt->i_height >= (unsigned int)(sys->spu_rect.width * sys->spu_rect.height)) {
11744+        sys->spu_rect.width  = fmt->i_visible_width;
11745+        sys->spu_rect.height = fmt->i_visible_height;
11746+    }
11747+
11748+    if (ORIENT_IS_SWAP(fmt->orientation))
11749+        sys->spu_rect = rect_transpose(sys->spu_rect);
11750+}
11751+
11752+static void
11753+place_rects(vout_display_t * const vd,
11754+          const vout_display_cfg_t * const cfg,
11755+          const video_format_t * fmt)
11756+{
11757+    place_dest_rect(vd, cfg, fmt);
11758+    place_spu_rect(vd, cfg, fmt);
11759+}
11760+
11761+static int
11762+set_input_region(vout_display_t * const vd, const video_format_t * const fmt)
11763+{
11764+    const vout_display_sys_t * const sys = vd->sys;
11765+    MMAL_DISPLAYREGION_T display_region = {
11766+        .hdr = {
11767+            .id = MMAL_PARAMETER_DISPLAYREGION,
11768+            .size = sizeof(MMAL_DISPLAYREGION_T)
11769+        },
11770+        .display_num = sys->display_id,
11771+        .fullscreen = MMAL_FALSE,
11772+        .transform = sys->video_transform,
11773+        .dest_rect = sys->dest_rect,
11774+        .src_rect = display_src_rect(vd, fmt),
11775+        .noaspect = MMAL_TRUE,
11776+        .mode = MMAL_DISPLAY_MODE_FILL,
11777+        .layer = sys->layer,
11778+        .alpha = 0xff | (sys->transparent ? 0 : (1 << 29)),
11779+        .set =
11780+            MMAL_DISPLAY_SET_NUM |
11781+            MMAL_DISPLAY_SET_FULLSCREEN |
11782+            MMAL_DISPLAY_SET_TRANSFORM |
11783+            MMAL_DISPLAY_SET_DEST_RECT |
11784+            MMAL_DISPLAY_SET_SRC_RECT |
11785+            MMAL_DISPLAY_SET_NOASPECT |
11786+            MMAL_DISPLAY_SET_MODE |
11787+            MMAL_DISPLAY_SET_LAYER |
11788+            MMAL_DISPLAY_SET_ALPHA
11789+    };
11790+    MMAL_STATUS_T status = mmal_port_parameter_set(sys->input, &display_region.hdr);
11791+    if (status != MMAL_SUCCESS) {
11792+        msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
11793+                        status, mmal_status_to_string(status));
11794+        return -EINVAL;
11795+    }
11796+    return 0;
11797 }
11798
11799 static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
11800                 const video_format_t *fmt)
11801 {
11802-    vout_display_sys_t *sys = vd->sys;
11803-    vout_display_place_t place;
11804-    MMAL_DISPLAYREGION_T display_region;
11805+    vout_display_sys_t * const sys = vd->sys;
11806     MMAL_STATUS_T status;
11807
11808     if (!cfg && !fmt)
11809+    {
11810+        msg_Err(vd, "%s: Missing cfg & fmt", __func__);
11811         return -EINVAL;
11812+    }
11813+
11814+    isp_check(vd, sys);
11815
11816     if (fmt) {
11817         sys->input->format->es->video.par.num = fmt->i_sar_num;
11818@@ -412,30 +733,14 @@ static int configure_display(vout_displa
11819     if (!cfg)
11820         cfg = vd->cfg;
11821
11822-    vout_display_PlacePicture(&place, fmt, cfg, false);
11823+    sys->video_transform = combine_transform(
11824+        vlc_to_mmal_transform(fmt->orientation), sys->display_transform);
11825
11826-    display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
11827-    display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
11828-    display_region.fullscreen = MMAL_FALSE;
11829-    display_region.src_rect.x = fmt->i_x_offset;
11830-    display_region.src_rect.y = fmt->i_y_offset;
11831-    display_region.src_rect.width = fmt->i_visible_width;
11832-    display_region.src_rect.height = fmt->i_visible_height;
11833-    display_region.dest_rect.x = place.x;
11834-    display_region.dest_rect.y = place.y;
11835-    display_region.dest_rect.width = place.width;
11836-    display_region.dest_rect.height = place.height;
11837-    display_region.layer = sys->layer;
11838-    display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
11839-            MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
11840-    status = mmal_port_parameter_set(sys->input, &display_region.hdr);
11841-    if (status != MMAL_SUCCESS) {
11842-        msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
11843-                        status, mmal_status_to_string(status));
11844+    place_rects(vd, cfg, fmt);
11845+
11846+    if (set_input_region(vd, fmt) != 0)
11847         return -EINVAL;
11848-    }
11849
11850-    show_background(vd, var_InheritBool(vd, MMAL_BLANK_BACKGROUND_NAME));
11851     sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME);
11852     sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED);
11853     if (sys->adjust_refresh_rate) {
11854@@ -446,204 +751,217 @@ static int configure_display(vout_displa
11855     return 0;
11856 }
11857
11858+static void kill_pool(vout_display_sys_t * const sys)
11859+{
11860+    if (sys->pic_pool != NULL) {
11861+        picture_pool_Release(sys->pic_pool);
11862+        sys->pic_pool = NULL;
11863+    }
11864+}
11865+
11866+// Actual picture pool for MMAL opaques is just a set of trivial containers
11867 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
11868 {
11869-    vout_display_sys_t *sys = vd->sys;
11870-    picture_resource_t picture_res;
11871-    picture_pool_configuration_t picture_pool_cfg;
11872-    video_format_t fmt = vd->fmt;
11873-    MMAL_STATUS_T status;
11874-    unsigned i;
11875+    vout_display_sys_t * const sys = vd->sys;
11876
11877-    if (sys->picture_pool) {
11878-        if (sys->num_buffers < count)
11879-            msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures",
11880-                            count, sys->num_buffers);
11881+    msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__,
11882+            vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height);
11883
11884-        goto out;
11885+    if (sys->pic_pool == NULL) {
11886+        sys->pic_pool = picture_pool_NewFromFormat(&vd->fmt, count);
11887     }
11888+    return sys->pic_pool;
11889+}
11890
11891-    if (sys->opaque) {
11892-        if (count <= NUM_ACTUAL_OPAQUE_BUFFERS)
11893-            count = NUM_ACTUAL_OPAQUE_BUFFERS;
11894+static inline bool
11895+check_shape(vout_display_t * const vd, const picture_t * const p_pic)
11896+{
11897+    if (vd->fmt.i_width == p_pic->format.i_width &&
11898+        vd->fmt.i_height == p_pic->format.i_height)
11899+        return true;
11900+    return false;
11901+}
11902
11903-        MMAL_PARAMETER_BOOLEAN_T zero_copy = {
11904-            { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
11905-            1
11906-        };
11907+static void vd_display(vout_display_t *vd, picture_t *p_pic,
11908+                subpicture_t *subpicture)
11909+{
11910+    vout_display_sys_t * const sys = vd->sys;
11911+    MMAL_STATUS_T err;
11912
11913-        status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
11914-        if (status != MMAL_SUCCESS) {
11915-           msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
11916-                    sys->input->name, status, mmal_status_to_string(status));
11917-           goto out;
11918-        }
11919+#if TRACE_ALL
11920+    {
11921+        char dbuf0[5];
11922+        msg_Dbg(vd, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %dx%d@%d,%d", __func__,
11923+                str_fourcc(dbuf0, p_pic->format.i_chroma), p_pic->format.i_width, p_pic->format.i_height,
11924+                p_pic->format.i_x_offset, p_pic->format.i_y_offset,
11925+                p_pic->format.i_visible_width, p_pic->format.i_visible_height,
11926+                p_pic->format.i_sar_num, p_pic->format.i_sar_den,
11927+                sys->dest_rect.width, sys->dest_rect.height, sys->dest_rect.x, sys->dest_rect.y);
11928     }
11929-
11930-    if (count < sys->input->buffer_num_recommended)
11931-        count = sys->input->buffer_num_recommended;
11932-
11933-#ifndef NDEBUG
11934-    msg_Dbg(vd, "Creating picture pool with %u pictures", count);
11935 #endif
11936
11937-    sys->input->buffer_num = count;
11938-    status = mmal_port_enable(sys->input, input_port_cb);
11939-    if (status != MMAL_SUCCESS) {
11940-        msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
11941-                        sys->input->name, status, mmal_status_to_string(status));
11942-        goto out;
11943+    // If we had subpics then we have attached them to the main pic in prepare
11944+    // so all we have to do here is delete the refs
11945+    if (subpicture != NULL) {
11946+        subpicture_Delete(subpicture);
11947     }
11948
11949-    status = mmal_component_enable(sys->component);
11950-    if (status != MMAL_SUCCESS) {
11951-        msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
11952-                        sys->component->name, status, mmal_status_to_string(status));
11953-        goto out;
11954+    if (!check_shape(vd, p_pic))
11955+    {
11956+        msg_Err(vd, "Pic/fmt shape mismatch");
11957+        goto fail;
11958+    }
11959+
11960+    if (!sys->input->is_enabled &&
11961+        (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS)
11962+    {
11963+        msg_Err(vd, "Input port enable failed");
11964+        goto fail;
11965+    }
11966+    // Stuff into input
11967+    // We assume the BH is already set up with values reflecting pic date etc.
11968+    if (sys->copy_buf != NULL) {
11969+        MMAL_BUFFER_HEADER_T *const buf = sys->copy_buf;
11970+        sys->copy_buf = NULL;
11971+#if TRACE_ALL
11972+        msg_Dbg(vd, "--- %s: Copy stuff", __func__);
11973+#endif
11974+        if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
11975+        {
11976+            mmal_buffer_header_release(buf);
11977+            msg_Err(vd, "Send copy buffer to render input failed");
11978+            goto fail;
11979+        }
11980     }
11981-
11982-    sys->num_buffers = count;
11983-    sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers,
11984-            sys->input->buffer_size);
11985-    if (!sys->pool) {
11986-        msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
11987-                        count, sys->input->buffer_size);
11988-        goto out;
11989+    else if (sys->isp.pending) {
11990+        MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q);
11991+        sys->isp.pending = false;
11992+#if TRACE_ALL
11993+        msg_Dbg(vd, "--- %s: ISP stuff", __func__);
11994+#endif
11995+        if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
11996+        {
11997+            mmal_buffer_header_release(buf);
11998+            msg_Err(vd, "Send ISP buffer to render input failed");
11999+            goto fail;
12000+        }
12001     }
12002-
12003-    memset(&picture_res, 0, sizeof(picture_resource_t));
12004-    sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *));
12005-    for (i = 0; i < sys->num_buffers; ++i) {
12006-        picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
12007-        picture_res.p_sys->owner = (vlc_object_t *)vd;
12008-        picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue);
12009-
12010-        sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res);
12011-        if (!sys->pictures[i]) {
12012-            msg_Err(vd, "Failed to create picture");
12013-            free(picture_res.p_sys);
12014-            goto out;
12015+    else
12016+    {
12017+        MMAL_BUFFER_HEADER_T *const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->pool);
12018+        if (pic_buf == NULL)
12019+        {
12020+            msg_Err(vd, "Replicated buffer get fail");
12021+            goto fail;
12022         }
12023
12024-        sys->pictures[i]->i_planes = sys->i_planes;
12025-        memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t));
12026-    }
12027
12028-    memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
12029-    picture_pool_cfg.picture_count = sys->num_buffers;
12030-    picture_pool_cfg.picture = sys->pictures;
12031-    picture_pool_cfg.lock = mmal_picture_lock;
12032+        // If dimensions have chnaged then fix that
12033+        if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
12034+        {
12035+            msg_Dbg(vd, "Reset port format");
12036+
12037+            // HVS can deal with on-line dimension changes
12038+            if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
12039+                msg_Warn(vd, "Input format commit failed");
12040+        }
12041
12042-    sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
12043-    if (!sys->picture_pool) {
12044-        msg_Err(vd, "Failed to create picture pool");
12045-        goto out;
12046+        if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
12047+        {
12048+            mmal_buffer_header_release(pic_buf);
12049+            msg_Err(vd, "Send buffer to input failed");
12050+            goto fail;
12051+        }
12052     }
12053
12054-out:
12055-    return sys->picture_pool;
12056-}
12057-
12058-static void vd_prepare(vout_display_t *vd, picture_t *picture,
12059-                subpicture_t *subpicture)
12060-{
12061-    vout_display_sys_t *sys = vd->sys;
12062-    picture_sys_t *pic_sys = picture->p_sys;
12063-
12064-    if (!sys->adjust_refresh_rate || pic_sys->displayed)
12065-        return;
12066-
12067-    /* Apply the required phase_offset to the picture, so that vd_display()
12068-     * will be called at the corrected time from the core */
12069-    picture->date += sys->phase_offset;
12070-}
12071-
12072-static void vd_display(vout_display_t *vd, picture_t *picture,
12073-                subpicture_t *subpicture)
12074-{
12075-    vout_display_sys_t *sys = vd->sys;
12076-    picture_sys_t *pic_sys = picture->p_sys;
12077-    MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
12078-    MMAL_STATUS_T status;
12079-
12080-    if (picture->format.i_frame_rate != sys->i_frame_rate ||
12081-        picture->format.i_frame_rate_base != sys->i_frame_rate_base ||
12082-        picture->b_progressive != sys->b_progressive ||
12083-        picture->b_top_field_first != sys->b_top_field_first) {
12084-        sys->b_top_field_first = picture->b_top_field_first;
12085-        sys->b_progressive = picture->b_progressive;
12086-        sys->i_frame_rate = picture->format.i_frame_rate;
12087-        sys->i_frame_rate_base = picture->format.i_frame_rate_base;
12088-        configure_display(vd, NULL, &picture->format);
12089-    }
12090-
12091-    if (!pic_sys->displayed || !sys->opaque) {
12092-        buffer->cmd = 0;
12093-        buffer->length = sys->input->buffer_size;
12094-        buffer->user_data = picture;
12095-
12096-        status = mmal_port_send_buffer(sys->input, buffer);
12097-        if (status == MMAL_SUCCESS)
12098-            atomic_fetch_add(&sys->buffers_in_transit, 1);
12099-
12100-        if (status != MMAL_SUCCESS) {
12101-            msg_Err(vd, "Failed to send buffer to input port. Frame dropped");
12102-            picture_Release(picture);
12103+    {
12104+        unsigned int sub_no = 0;
12105+        MMAL_BUFFER_HEADER_T **psub_bufs2 = sys->subpic_bufs;
12106+        const bool is_mmal_pic = hw_mmal_pic_is_mmal(p_pic);
12107+
12108+        for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
12109+            int rv;
12110+            MMAL_BUFFER_HEADER_T * const sub_buf = !is_mmal_pic ? NULL :
12111+                hw_mmal_pic_sub_buf_get(p_pic, sub_no);
12112+
12113+            if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd),
12114+                                            sub_buf != NULL ? sub_buf : *psub_bufs2++,
12115+                                            &sys->subs[sub_no].sub,
12116+                                            &p_pic->format,
12117+                                            &sys->dest_rect,
12118+                                            sys->display_transform,
12119+                                            p_pic->date)) == 0)
12120+                break;
12121+            else if (rv < 0)
12122+                goto fail;
12123         }
12124-
12125-        pic_sys->displayed = true;
12126-    } else {
12127-        picture_Release(picture);
12128     }
12129
12130-    display_subpicture(vd, subpicture);
12131+fail:
12132+    for (unsigned int i = 0; i != SUBS_MAX && sys->subpic_bufs[i] != NULL; ++i) {
12133+        mmal_buffer_header_release(sys->subpic_bufs[i]);
12134+        sys->subpic_bufs[i] = NULL;
12135+    }
12136
12137-    if (subpicture)
12138-        subpicture_Delete(subpicture);
12139+    picture_Release(p_pic);
12140
12141     if (sys->next_phase_check == 0 && sys->adjust_refresh_rate)
12142         maintain_phase_sync(vd);
12143     sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL;
12144-
12145-    if (sys->opaque) {
12146-        vlc_mutex_lock(&sys->buffer_mutex);
12147-        while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT)
12148-            vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex);
12149-        vlc_mutex_unlock(&sys->buffer_mutex);
12150-    }
12151 }
12152
12153 static int vd_control(vout_display_t *vd, int query, va_list args)
12154 {
12155-    vout_display_sys_t *sys = vd->sys;
12156-    vout_display_cfg_t cfg;
12157-    const vout_display_cfg_t *tmp_cfg;
12158+    vout_display_sys_t * const sys = vd->sys;
12159     int ret = VLC_EGENERIC;
12160+    VLC_UNUSED(args);
12161
12162     switch (query) {
12163-        case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
12164-            tmp_cfg = va_arg(args, const vout_display_cfg_t *);
12165-            if (tmp_cfg->display.width == sys->display_width &&
12166-                            tmp_cfg->display.height == sys->display_height) {
12167-                cfg = *vd->cfg;
12168-                cfg.display.width = sys->display_width;
12169-                cfg.display.height = sys->display_height;
12170-                if (configure_display(vd, &cfg, NULL) >= 0)
12171-                    ret = VLC_SUCCESS;
12172-            }
12173-            break;
12174-
12175         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
12176         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
12177-            if (configure_display(vd, NULL, &vd->source) >= 0)
12178+            if (configure_display(vd, vd->cfg, &vd->source) >= 0)
12179                 ret = VLC_SUCCESS;
12180             break;
12181
12182-        case VOUT_DISPLAY_RESET_PICTURES:
12183-            vlc_assert_unreachable();
12184         case VOUT_DISPLAY_CHANGE_ZOOM:
12185-            msg_Warn(vd, "Unsupported control query %d", query);
12186+        case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
12187+        case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
12188+        {
12189+            const vout_display_cfg_t * const cfg = va_arg(args, const vout_display_cfg_t *);
12190+
12191+            if (configure_display(vd, cfg, &vd->source) >= 0)
12192+                ret = VLC_SUCCESS;
12193+            break;
12194+        }
12195+
12196+        case VOUT_DISPLAY_RESET_PICTURES:
12197+            msg_Warn(vd, "Reset Pictures");
12198+            kill_pool(sys);
12199+            vd->fmt = vd->source; // Take (nearly) whatever source wants to give us
12200+            vd->fmt.i_chroma = req_chroma(vd);  // Adjust chroma to something we can actaully deal with
12201+            ret = VLC_SUCCESS;
12202+            break;
12203+
12204+        case VOUT_DISPLAY_CHANGE_MMAL_HIDE:
12205+        {
12206+            MMAL_STATUS_T err;
12207+            unsigned int i;
12208+
12209+            msg_Dbg(vd, "Hide display");
12210+
12211+            for (i = 0; i != SUBS_MAX; ++i)
12212+                hw_mmal_subpic_flush(VLC_OBJECT(vd), &sys->subs[i].sub);
12213+
12214+            if (sys->input->is_enabled &&
12215+                (err = mmal_port_disable(sys->input)) != MMAL_SUCCESS)
12216+            {
12217+                msg_Err(vd, "Unable to disable port: err=%d", err);
12218+                break;
12219+            }
12220+            sys->force_config = true;
12221+            ret = VLC_SUCCESS;
12222             break;
12223+        }
12224
12225         default:
12226             msg_Warn(vd, "Unknown control query %d", query);
12227@@ -653,79 +971,207 @@ static int vd_control(vout_display_t *vd
12228     return ret;
12229 }
12230
12231+static void set_display_windows(vout_display_t *const vd, vout_display_sys_t *const sys)
12232+{
12233+    unsigned int width, height;
12234+    if (query_resolution(vd, sys->display_id, &width, &height) < 0) {
12235+        width = vd->cfg->display.width;
12236+        height = vd->cfg->display.height;
12237+    }
12238+    sys->display_rect = (MMAL_RECT_T){0, 0, width, height};
12239+
12240+    sys->win_rect = (sys->req_win.width != 0) ?
12241+            sys->req_win :
12242+         is_transform_transpose(sys->display_transform) ?
12243+            rect_transpose(sys->display_rect) : sys->display_rect;
12244+}
12245+
12246 static void vd_manage(vout_display_t *vd)
12247 {
12248-    vout_display_sys_t *sys = vd->sys;
12249-    unsigned width, height;
12250+    vout_display_sys_t *const sys = vd->sys;
12251
12252     vlc_mutex_lock(&sys->manage_mutex);
12253
12254     if (sys->need_configure_display) {
12255-        close_dmx(vd);
12256-        sys->dmx_handle = vc_dispmanx_display_open(0);
12257-
12258-        if (query_resolution(vd, &width, &height) >= 0) {
12259-            sys->display_width = width;
12260-            sys->display_height = height;
12261-            vout_display_SendEventDisplaySize(vd, width, height);
12262-        }
12263-
12264         sys->need_configure_display = false;
12265+        set_display_windows(vd, sys);
12266     }
12267
12268     vlc_mutex_unlock(&sys->manage_mutex);
12269 }
12270
12271-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
12272+
12273+static int attach_subpics(vout_display_t * const vd, vout_display_sys_t * const sys,
12274+                          subpicture_t * const subpicture)
12275 {
12276-    vout_display_t *vd = (vout_display_t *)port->userdata;
12277-    MMAL_STATUS_T status;
12278+    unsigned int n = 0;
12279
12280-    if (buffer->cmd == MMAL_EVENT_ERROR) {
12281-        status = *(uint32_t *)buffer->data;
12282-        msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
12283+    if (sys->vzc == NULL) {
12284+        if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
12285+        {
12286+            msg_Err(vd, "Failed to allocate VZC");
12287+            return VLC_ENOMEM;
12288+        }
12289     }
12290
12291-    mmal_buffer_header_release(buffer);
12292+    // Attempt to import the subpics
12293+    for (subpicture_t * spic = subpicture; spic != NULL; spic = spic->p_next)
12294+    {
12295+        for (subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) {
12296+            picture_t *const src = sreg->p_picture;
12297+
12298+#if TRACE_ALL
12299+            char dbuf0[5];
12300+            msg_Dbg(vd, "  [%p:%p] Pos=%d,%d max=%dx%d, src=%dx%d/%dx%d o:%d, spu=%d,%d:%dx%d, vd->fmt=%dx%d/%dx%d, vd->source=%dx%d/%dx%d, cfg=%dx%d, zoom=%d/%d, Alpha=%d, Fmt=%s", src, src->p[0].p_pixels,
12301+                    sreg->i_x, sreg->i_y,
12302+                    sreg->i_max_width, sreg->i_max_height,
12303+                    src->format.i_visible_width, src->format.i_visible_height,
12304+                    src->format.i_width, src->format.i_height,
12305+                    src->format.orientation,
12306+                    sys->spu_rect.x, sys->spu_rect.y, sys->spu_rect.width, sys->spu_rect.height,
12307+                    vd->fmt.i_visible_width, vd->fmt.i_visible_height,
12308+                    vd->fmt.i_width, vd->fmt.i_height,
12309+                    vd->source.i_visible_width, vd->source.i_visible_height,
12310+                    vd->source.i_width, vd->source.i_height,
12311+                    vd->cfg->display.width, vd->cfg->display.height,
12312+                    vd->cfg->zoom.num, vd->cfg->zoom.den,
12313+                    sreg->i_alpha,
12314+                    str_fourcc(dbuf0, src->format.i_chroma));
12315+#endif
12316+
12317+            // At this point I think the subtitles are being placed in the
12318+            // coord space of the placed rectangle in the cfg display space
12319+            if ((sys->subpic_bufs[n] = hw_mmal_vzc_buf_from_pic(sys->vzc,
12320+                src,
12321+                (MMAL_RECT_T){.width = sys->spu_rect.width, .height=sys->spu_rect.height},
12322+                sreg->i_x, sreg->i_y,
12323+                sreg->i_alpha,
12324+                n == 0)) == NULL)
12325+            {
12326+                msg_Err(vd, "Failed to allocate vzc buffer for subpic");
12327+                return VLC_ENOMEM;
12328+            }
12329+
12330+            if (++n == SUBS_MAX)
12331+                return VLC_SUCCESS;
12332+        }
12333+    }
12334+    return VLC_SUCCESS;
12335 }
12336
12337-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
12338+
12339+static void vd_prepare(vout_display_t *vd, picture_t *p_pic,
12340+#if VLC_VER_3
12341+                       subpicture_t *subpicture
12342+#else
12343+                       subpicture_t *subpicture, vlc_tick_t date
12344+#endif
12345+                       )
12346 {
12347-    vout_display_t *vd = (vout_display_t *)port->userdata;
12348+    MMAL_STATUS_T err;
12349+    vout_display_sys_t * const sys = vd->sys;
12350+
12351+    vd_manage(vd);
12352+
12353+    if (!check_shape(vd, p_pic))
12354+        return;
12355+
12356+    if (sys->force_config ||
12357+        p_pic->format.i_frame_rate != sys->i_frame_rate ||
12358+        p_pic->format.i_frame_rate_base != sys->i_frame_rate_base ||
12359+        p_pic->b_progressive != sys->b_progressive ||
12360+        p_pic->b_top_field_first != sys->b_top_field_first)
12361+    {
12362+        sys->force_config = false;
12363+        sys->b_top_field_first = p_pic->b_top_field_first;
12364+        sys->b_progressive = p_pic->b_progressive;
12365+        sys->i_frame_rate = p_pic->format.i_frame_rate;
12366+        sys->i_frame_rate_base = p_pic->format.i_frame_rate_base;
12367+        configure_display(vd, NULL, &vd->source);
12368+    }
12369+
12370+    // Subpics can either turn up attached to the main pic or in the
12371+    // subpic list here  - if they turn up here then process into temp
12372+    // buffers
12373+    if (subpicture != NULL) {
12374+        attach_subpics(vd, sys, subpicture);
12375+    }
12376+
12377+    // *****
12378+    if (want_copy(vd)) {
12379+        if (sys->copy_buf != NULL) {
12380+            msg_Err(vd, "Copy buf not NULL");
12381+            mmal_buffer_header_release(sys->copy_buf);
12382+            sys->copy_buf = NULL;
12383+        }
12384+
12385+        MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->copy_pool->queue);
12386+        // Copy 2d
12387+        hw_mmal_copy_pic_to_buf(buf->data, &buf->length, sys->input->format, p_pic);
12388+        buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
12389+
12390+        sys->copy_buf = buf;
12391+    }
12392+
12393+    if (isp_check(vd, sys) != MMAL_SUCCESS) {
12394+        return;
12395+    }
12396+
12397+    if (want_isp(vd))
12398+    {
12399+        struct vout_isp_conf_s * const isp = &sys->isp;
12400+        MMAL_BUFFER_HEADER_T * buf;
12401+
12402+        // This should be empty - make it so if it isn't
12403+        isp_empty_out_q(isp);
12404+        isp->pending = false;
12405+
12406+        // Stuff output
12407+        if (isp_prepare(vd, isp) != MMAL_SUCCESS)
12408+            return;
12409+
12410+        if ((buf = hw_mmal_pic_buf_replicated(p_pic, isp->in_pool)) == NULL)
12411+        {
12412+            msg_Err(vd, "Pic has no attached buffer");
12413+            return;
12414+        }
12415+
12416+        if ((err = mmal_port_send_buffer(isp->input, buf)) != MMAL_SUCCESS)
12417+        {
12418+            msg_Err(vd, "Send buffer to input failed");
12419+            mmal_buffer_header_release(buf);
12420+            return;
12421+        }
12422+
12423+        isp->pending = true;
12424+    }
12425+
12426+#if 0
12427+    VLC_UNUSED(date);
12428     vout_display_sys_t *sys = vd->sys;
12429-    picture_t *picture = (picture_t *)buffer->user_data;
12430+    picture_sys_t *pic_sys = picture->p_sys;
12431
12432-    if (picture)
12433-        picture_Release(picture);
12434+    if (!sys->adjust_refresh_rate || pic_sys->displayed)
12435+        return;
12436
12437-    vlc_mutex_lock(&sys->buffer_mutex);
12438-    atomic_fetch_sub(&sys->buffers_in_transit, 1);
12439-    vlc_cond_signal(&sys->buffer_cond);
12440-    vlc_mutex_unlock(&sys->buffer_mutex);
12441+    /* Apply the required phase_offset to the picture, so that vd_display()
12442+     * will be called at the corrected time from the core */
12443+    picture->date += sys->phase_offset;
12444+#endif
12445 }
12446
12447-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
12448+
12449+static void vd_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
12450 {
12451-    TV_DISPLAY_STATE_T display_state;
12452-    int ret = 0;
12453+    vout_display_t *vd = (vout_display_t *)port->userdata;
12454+    MMAL_STATUS_T status;
12455
12456-    if (vc_tv_get_display_state(&display_state) == 0) {
12457-        if (display_state.state & 0xFF) {
12458-            *width = display_state.display.hdmi.width;
12459-            *height = display_state.display.hdmi.height;
12460-        } else if (display_state.state & 0xFF00) {
12461-            *width = display_state.display.sdtv.width;
12462-            *height = display_state.display.sdtv.height;
12463-        } else {
12464-            msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
12465-            ret = -1;
12466-        }
12467-    } else {
12468-        msg_Warn(vd, "Failed to query display resolution");
12469-        ret = -1;
12470+    if (buffer->cmd == MMAL_EVENT_ERROR) {
12471+        status = *(uint32_t *)buffer->data;
12472+        msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
12473     }
12474
12475-    return ret;
12476+    mmal_buffer_header_release(buffer);
12477 }
12478
12479 static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
12480@@ -780,9 +1226,9 @@ static void adjust_refresh_rate(vout_dis
12481     double best_score, score;
12482     int i;
12483
12484-    vc_tv_get_display_state(&display_state);
12485+    vc_tv_get_display_state_id(sys->display_id, &display_state);
12486     if(display_state.display.hdmi.mode != HDMI_MODE_OFF) {
12487-        num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group,
12488+        num_modes = vc_tv_hdmi_get_supported_modes_new_id(sys->display_id, display_state.display.hdmi.group,
12489                         supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL);
12490
12491         for (i = 0; i < num_modes; ++i) {
12492@@ -810,7 +1256,7 @@ static void adjust_refresh_rate(vout_dis
12493         if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) {
12494             msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32,
12495                             supported_modes[best_id].frame_rate);
12496-            vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI,
12497+            vc_tv_hdmi_power_on_explicit_new_id(sys->display_id, HDMI_MODE_HDMI,
12498                             supported_modes[best_id].group,
12499                             supported_modes[best_id].code);
12500         }
12501@@ -828,148 +1274,12 @@ static void adjust_refresh_rate(vout_dis
12502     }
12503 }
12504
12505-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture)
12506-{
12507-    vout_display_sys_t *sys = vd->sys;
12508-    struct dmx_region_t **dmx_region = &sys->dmx_region;
12509-    struct dmx_region_t *unused_dmx_region;
12510-    DISPMANX_UPDATE_HANDLE_T update = 0;
12511-    picture_t *picture;
12512-    video_format_t *fmt;
12513-    struct dmx_region_t *dmx_region_next;
12514-
12515-    if(subpicture) {
12516-        subpicture_region_t *region = subpicture->p_region;
12517-        while(region) {
12518-            picture = region->p_picture;
12519-            fmt = &region->fmt;
12520-
12521-            if(!*dmx_region) {
12522-                if(!update)
12523-                    update = vc_dispmanx_update_start(10);
12524-                *dmx_region = dmx_region_new(vd, update, region);
12525-            } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) ||
12526-                    ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) ||
12527-                    ((*dmx_region)->pos_x != region->i_x) ||
12528-                    ((*dmx_region)->pos_y != region->i_y) ||
12529-                    ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) {
12530-                dmx_region_next = (*dmx_region)->next;
12531-                if(!update)
12532-                    update = vc_dispmanx_update_start(10);
12533-                dmx_region_delete(*dmx_region, update);
12534-                *dmx_region = dmx_region_new(vd, update, region);
12535-                (*dmx_region)->next = dmx_region_next;
12536-            } else if((*dmx_region)->picture != picture) {
12537-                if(!update)
12538-                    update = vc_dispmanx_update_start(10);
12539-                dmx_region_update(*dmx_region, update, picture);
12540-            }
12541-
12542-            dmx_region = &(*dmx_region)->next;
12543-            region = region->p_next;
12544-        }
12545-    }
12546-
12547-    /* Remove remaining regions */
12548-    unused_dmx_region = *dmx_region;
12549-    while(unused_dmx_region) {
12550-        dmx_region_next = unused_dmx_region->next;
12551-        if(!update)
12552-            update = vc_dispmanx_update_start(10);
12553-        dmx_region_delete(unused_dmx_region, update);
12554-        unused_dmx_region = dmx_region_next;
12555-    }
12556-    *dmx_region = NULL;
12557-
12558-    if(update)
12559-        vc_dispmanx_update_submit_sync(update);
12560-}
12561-
12562-static void close_dmx(vout_display_t *vd)
12563-{
12564-    vout_display_sys_t *sys = vd->sys;
12565-    DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10);
12566-    struct dmx_region_t *dmx_region = sys->dmx_region;
12567-    struct dmx_region_t *dmx_region_next;
12568-
12569-    while(dmx_region) {
12570-        dmx_region_next = dmx_region->next;
12571-        dmx_region_delete(dmx_region, update);
12572-        dmx_region = dmx_region_next;
12573-    }
12574-
12575-    vc_dispmanx_update_submit_sync(update);
12576-    sys->dmx_region = NULL;
12577-
12578-    show_background(vd, false);
12579-
12580-    vc_dispmanx_display_close(sys->dmx_handle);
12581-    sys->dmx_handle = DISPMANX_NO_HANDLE;
12582-}
12583-
12584-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
12585-                DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region)
12586-{
12587-    vout_display_sys_t *sys = vd->sys;
12588-    video_format_t *fmt = &region->fmt;
12589-    struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t));
12590-    uint32_t image_handle;
12591-
12592-    dmx_region->pos_x = region->i_x;
12593-    dmx_region->pos_y = region->i_y;
12594-
12595-    vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width,
12596-                    fmt->i_visible_height);
12597-    vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16,
12598-                    fmt->i_visible_height << 16);
12599-    vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y,
12600-                    fmt->i_visible_width, fmt->i_visible_height);
12601-
12602-    dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32,
12603-                    dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16),
12604-                    dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16),
12605-                    &image_handle);
12606-    vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
12607-                    region->p_picture->p[0].i_pitch,
12608-                    region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect);
12609-
12610-    dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX;
12611-    dmx_region->alpha.opacity = region->i_alpha;
12612-    dmx_region->alpha.mask = DISPMANX_NO_HANDLE;
12613-    dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle,
12614-                    sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource,
12615-                    &dmx_region->src_rect, DISPMANX_PROTECTION_NONE,
12616-                    &dmx_region->alpha, NULL, VC_IMAGE_ROT0);
12617-
12618-    dmx_region->next = NULL;
12619-    dmx_region->picture = region->p_picture;
12620-
12621-    return dmx_region;
12622-}
12623-
12624-static void dmx_region_update(struct dmx_region_t *dmx_region,
12625-                DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
12626-{
12627-    vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
12628-                    picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect);
12629-    vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource);
12630-    dmx_region->picture = picture;
12631-}
12632-
12633-static void dmx_region_delete(struct dmx_region_t *dmx_region,
12634-                DISPMANX_UPDATE_HANDLE_T update)
12635-{
12636-    vc_dispmanx_element_remove(update, dmx_region->element);
12637-    vc_dispmanx_resource_delete(dmx_region->resource);
12638-    free(dmx_region);
12639-}
12640-
12641 static void maintain_phase_sync(vout_display_t *vd)
12642 {
12643     MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = {
12644         .hdr = { MMAL_PARAMETER_VIDEO_RENDER_STATS, sizeof(render_stats) },
12645     };
12646-    int32_t frame_duration = 1000000 /
12647+    int32_t frame_duration = CLOCK_FREQ /
12648         ((double)vd->sys->i_frame_rate /
12649         vd->sys->i_frame_rate_base);
12650     vout_display_sys_t *sys = vd->sys;
12651@@ -1012,32 +1322,436 @@ static void maintain_phase_sync(vout_dis
12652     }
12653 }
12654
12655-static void show_background(vout_display_t *vd, bool enable)
12656+static void CloseMmalVout(vlc_object_t *object)
12657 {
12658-    vout_display_sys_t *sys = vd->sys;
12659-    uint32_t image_ptr, color = 0xFF000000;
12660-    VC_RECT_T dst_rect, src_rect;
12661-    DISPMANX_UPDATE_HANDLE_T update;
12662-
12663-    if (enable && !sys->bkg_element) {
12664-        sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1,
12665-                        &image_ptr);
12666-        vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1);
12667-        vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32,
12668-                        sizeof(color), &color, &dst_rect);
12669-        vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16);
12670-        vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0);
12671-        update = vc_dispmanx_update_start(0);
12672-        sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle,
12673-                        sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect,
12674-                        DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0);
12675-        vc_dispmanx_update_submit_sync(update);
12676-    } else if (!enable && sys->bkg_element) {
12677-        update = vc_dispmanx_update_start(0);
12678-        vc_dispmanx_element_remove(update, sys->bkg_element);
12679-        vc_dispmanx_resource_delete(sys->bkg_resource);
12680-        vc_dispmanx_update_submit_sync(update);
12681-        sys->bkg_element = DISPMANX_NO_HANDLE;
12682-        sys->bkg_resource = DISPMANX_NO_HANDLE;
12683+    vout_display_t * const vd = (vout_display_t *)object;
12684+    vout_display_sys_t * const sys = vd->sys;
12685+    char response[20]; /* answer is hvs_update_fields=%1d */
12686+
12687+#if TRACE_ALL
12688+    msg_Dbg(vd, "<<< %s", __func__);
12689+#endif
12690+
12691+    kill_pool(sys);
12692+
12693+    vc_tv_unregister_callback_full(tvservice_cb, vd);
12694+
12695+    // Shouldn't be anything here - but just in case
12696+    for (unsigned int i = 0; i != SUBS_MAX; ++i)
12697+        if (sys->subpic_bufs[i] != NULL)
12698+            mmal_buffer_header_release(sys->subpic_bufs[i]);
12699+
12700+    for (unsigned int i = 0; i != SUBS_MAX; ++i) {
12701+        vout_subpic_t * const sub = sys->subs + i;
12702+        if (sub->component != NULL) {
12703+            hw_mmal_subpic_close(VLC_OBJECT(vd), &sub->sub);
12704+            if (sub->component->control->is_enabled)
12705+                mmal_port_disable(sub->component->control);
12706+            if (sub->component->is_enabled)
12707+                mmal_component_disable(sub->component);
12708+            mmal_component_release(sub->component);
12709+            sub->component = NULL;
12710+        }
12711     }
12712+
12713+    if (sys->input && sys->input->is_enabled)
12714+        mmal_port_disable(sys->input);
12715+
12716+    if (sys->component && sys->component->control->is_enabled)
12717+        mmal_port_disable(sys->component->control);
12718+
12719+    if (sys->copy_buf != NULL)
12720+        mmal_buffer_header_release(sys->copy_buf);
12721+
12722+    if (sys->input != NULL && sys->copy_pool != NULL)
12723+        mmal_port_pool_destroy(sys->input, sys->copy_pool);
12724+
12725+    if (sys->component && sys->component->is_enabled)
12726+        mmal_component_disable(sys->component);
12727+
12728+    if (sys->pool)
12729+        mmal_pool_destroy(sys->pool);
12730+
12731+    if (sys->component)
12732+        mmal_component_release(sys->component);
12733+
12734+    isp_close(vd, sys);
12735+
12736+    hw_mmal_vzc_pool_release(sys->vzc);
12737+
12738+    vlc_mutex_destroy(&sys->manage_mutex);
12739+
12740+    if (sys->native_interlaced) {
12741+        if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
12742+                response[18] != '0')
12743+            msg_Warn(vd, "Could not reset hvs field mode");
12744+    }
12745+
12746+    cma_vcsm_exit(sys->init_type);;
12747+
12748+    free(sys);
12749+
12750+#if TRACE_ALL
12751+    msg_Dbg(vd, ">>> %s", __func__);
12752+#endif
12753+}
12754+
12755+
12756+static const struct {
12757+    const char * name;
12758+    int num;
12759+} display_name_to_num[] = {
12760+    {"auto",    -1},
12761+    {"hdmi-1",  DISPMANX_ID_HDMI0},
12762+    {"hdmi-2",  DISPMANX_ID_HDMI1},
12763+    {NULL,      -2}
12764+};
12765+
12766+static const struct {
12767+    const char * name;
12768+    int transform_num;
12769+} transform_name_to_num[] = {
12770+    {"auto",    -1},
12771+    {"0",       MMAL_DISPLAY_ROT0},
12772+    {"hflip",   MMAL_DISPLAY_MIRROR_ROT0},
12773+    {"vflip",   MMAL_DISPLAY_MIRROR_ROT180},
12774+    {"180",     MMAL_DISPLAY_ROT180},
12775+    {"transpose", MMAL_DISPLAY_MIRROR_ROT90},
12776+    {"270",     MMAL_DISPLAY_ROT270},
12777+    {"90",      MMAL_DISPLAY_ROT90},
12778+    {"antitranspose", MMAL_DISPLAY_MIRROR_ROT270},
12779+    {NULL,      -2}
12780+};
12781+
12782+static int find_display_num(const char * const name)
12783+{
12784+    unsigned int i;
12785+    for (i = 0; display_name_to_num[i].name != NULL && strcasecmp(display_name_to_num[i].name, name) != 0; ++i)
12786+        /* Loop */;
12787+    return display_name_to_num[i].num;
12788+}
12789+
12790+static int find_transform_num(const char * const name)
12791+{
12792+    unsigned int i;
12793+    for (i = 0; transform_name_to_num[i].name != NULL && strcasecmp(transform_name_to_num[i].name, name) != 0; ++i)
12794+        /* Loop */;
12795+    return transform_name_to_num[i].transform_num;
12796+}
12797+
12798+#if HAVE_X11_XLIB_H
12799+#include <X11/Xlib.h>
12800+#include <X11/extensions/Xrandr.h>
12801+static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd)
12802+{
12803+    Display * const x = XOpenDisplay(NULL);
12804+    Rotation cur_rot = 0;
12805+    MMAL_DISPLAYTRANSFORM_T trans;
12806+
12807+    if (x == NULL)
12808+        return MMAL_DISPLAY_ROT0;
12809+
12810+    XRRRotations(x, 0, &cur_rot);
12811+    XCloseDisplay(x);
12812+
12813+    // Convert to MMAL
12814+    // xrandr seems to rotate the other way to mmal
12815+
12816+    switch (cur_rot)
12817+    {
12818+        case 0:
12819+        case RR_Rotate_0:
12820+            trans = MMAL_DISPLAY_ROT0;
12821+            break;
12822+        case RR_Rotate_90:
12823+            trans = MMAL_DISPLAY_ROT270;
12824+            break;
12825+        case RR_Rotate_180:
12826+            trans = MMAL_DISPLAY_ROT180;
12827+            break;
12828+        case RR_Rotate_270:
12829+            trans = MMAL_DISPLAY_ROT90;
12830+            break;
12831+        case RR_Reflect_X:
12832+            trans = MMAL_DISPLAY_MIRROR_ROT0;
12833+            break;
12834+        case RR_Reflect_Y:
12835+            trans = MMAL_DISPLAY_MIRROR_ROT180;
12836+            break;
12837+        default:
12838+            msg_Info(vd, "Unexpected X rotation value: %#x", cur_rot);
12839+            trans = MMAL_DISPLAY_ROT0;
12840+            break;
12841+    }
12842+
12843+    return trans;
12844+}
12845+#else
12846+static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd)
12847+{
12848+    VLC_UNUSED(vd);
12849+    return MMAL_DISPLAY_ROT0;
12850+}
12851+#endif
12852+
12853+static MMAL_RECT_T str_to_rect(const char * s)
12854+{
12855+    MMAL_RECT_T rect = {0};
12856+    rect.width = strtoul(s, (char**)&s, 0);
12857+    if (*s == '\0')
12858+        return rect;
12859+    if (*s++ != 'x')
12860+        goto fail;
12861+    rect.height = strtoul(s, (char**)&s, 0);
12862+    if (*s == '\0')
12863+        return rect;
12864+    if (*s++ != '+')
12865+        goto fail;
12866+    rect.x = strtoul(s, (char**)&s, 0);
12867+    if (*s == '\0')
12868+        return rect;
12869+    if (*s++ != '+')
12870+        goto fail;
12871+    rect.y = strtoul(s, (char**)&s, 0);
12872+    if (*s != '\0')
12873+        goto fail;
12874+    return rect;
12875+
12876+fail:
12877+    return (MMAL_RECT_T){0,0,0,0};
12878+}
12879+
12880+static int OpenMmalVout(vlc_object_t *object)
12881+{
12882+    vout_display_t *vd = (vout_display_t *)object;
12883+    vout_display_sys_t *sys;
12884+    MMAL_STATUS_T status;
12885+    int ret = VLC_EGENERIC;
12886+    // At the moment all copy is via I420
12887+    const bool needs_copy = !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma);
12888+    const MMAL_FOURCC_T enc_in = needs_copy ? MMAL_ENCODING_I420 :
12889+        vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
12890+
12891+#if TRACE_ALL
12892+    msg_Dbg(vd, "<<< %s: o:%d", __func__, (int)vd->fmt.orientation);
12893+#endif
12894+
12895+    get_xrandr_rotation(vd);
12896+
12897+    sys = calloc(1, sizeof(struct vout_display_sys_t));
12898+    if (!sys)
12899+        return VLC_ENOMEM;
12900+    vd->sys = sys;
12901+
12902+    vlc_mutex_init(&sys->manage_mutex);
12903+
12904+    if ((sys->init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
12905+    {
12906+        msg_Err(vd, "VCSM init fail");
12907+        goto fail;
12908+    }
12909+
12910+    vc_tv_register_callback(tvservice_cb, vd);
12911+
12912+    sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
12913+    sys->transparent = var_InheritBool(vd, MMAL_VOUT_TRANSPARENT_NAME);
12914+
12915+    {
12916+        const char *display_name = var_InheritString(vd, MMAL_DISPLAY_NAME);
12917+        int qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber" );
12918+        int display_id = find_display_num(display_name);
12919+//        sys->display_id = display_id < 0 ? vc_tv_get_default_display_id() : display_id;
12920+        sys->display_id = display_id >= 0 ? display_id :
12921+            qt_num == 1 ? DISPMANX_ID_HDMI1 : DISPMANX_ID_HDMI;
12922+        if (display_id < -1)
12923+            msg_Warn(vd, "Unknown display device: '%s'", display_name);
12924+        else
12925+            msg_Dbg(vd, "Display device: %s, qt=%d id=%d display=%d", display_name,
12926+                    qt_num, display_id, sys->display_id);
12927+    }
12928+
12929+    {
12930+        const char *window_str = var_InheritString(vd, MMAL_VOUT_WINDOW_NAME);
12931+        sys->req_win = str_to_rect(window_str);
12932+        if (sys->req_win.width != 0)
12933+            msg_Dbg(vd, "Window: %dx%d @ %d,%d",
12934+                    sys->req_win.width, sys->req_win.height,
12935+                    sys->req_win.x, sys->req_win.y);
12936+    }
12937+
12938+    {
12939+        const char *transform_name = var_InheritString(vd, MMAL_VOUT_TRANSFORM_NAME);
12940+        int transform_num = find_transform_num(transform_name);
12941+        sys->display_transform = transform_num < 0 ?
12942+            get_xrandr_rotation(vd) :
12943+            (MMAL_DISPLAYTRANSFORM_T)transform_num;
12944+
12945+        if (transform_num < -1)
12946+            msg_Warn(vd, "Unknown vout transform: '%s'", transform_name);
12947+        else
12948+            msg_Dbg(vd, "Display transform: %s, mmal_display_transform=%d",
12949+                    transform_name, (int)sys->display_transform);
12950+
12951+        sys->video_transform = combine_transform(
12952+            vlc_to_mmal_transform(vd->fmt.orientation), sys->display_transform);
12953+        sys->dest_transform = transform_inverse(sys->display_transform);
12954+    }
12955+
12956+    status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
12957+    if (status != MMAL_SUCCESS) {
12958+        msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
12959+                        MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
12960+        goto fail;
12961+    }
12962+
12963+    sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
12964+    status = mmal_port_enable(sys->component->control, vd_control_port_cb);
12965+    if (status != MMAL_SUCCESS) {
12966+        msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
12967+                        sys->component->control->name, status, mmal_status_to_string(status));
12968+        goto fail;
12969+    }
12970+
12971+    sys->input = sys->component->input[0];
12972+    sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
12973+
12974+    sys->input->format->encoding = enc_in;
12975+    sys->input->format->encoding_variant = 0;
12976+    sys->i_planes = 1;
12977+
12978+    display_set_format(vd, sys->input->format, want_isp(vd));
12979+
12980+    status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
12981+    if (status != MMAL_SUCCESS) {
12982+       msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
12983+                sys->input->name, status, mmal_status_to_string(status));
12984+       goto fail;
12985+    }
12986+
12987+    status = mmal_port_format_commit(sys->input);
12988+    if (status != MMAL_SUCCESS) {
12989+        msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
12990+                        sys->input->name, status, mmal_status_to_string(status));
12991+        goto fail;
12992+    }
12993+
12994+    sys->input->buffer_size = sys->input->buffer_size_recommended;
12995+
12996+    if (!needs_copy) {
12997+        sys->input->buffer_num = 30;
12998+    }
12999+    else {
13000+        sys->input->buffer_num = 2;
13001+        if ((sys->copy_pool = mmal_port_pool_create(sys->input, 2, sys->input->buffer_size)) == NULL)
13002+        {
13003+            msg_Err(vd, "Cannot create copy pool");
13004+            goto fail;
13005+        }
13006+    }
13007+
13008+    set_display_windows(vd, sys);
13009+
13010+    configure_display(vd, vd->cfg, &vd->source);
13011+
13012+    status = mmal_port_enable(sys->input, vd_input_port_cb);
13013+    if (status != MMAL_SUCCESS) {
13014+        msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
13015+                sys->input->name, status, mmal_status_to_string(status));
13016+        goto fail;
13017+    }
13018+
13019+    status = mmal_component_enable(sys->component);
13020+    if (status != MMAL_SUCCESS) {
13021+        msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
13022+                sys->component->name, status, mmal_status_to_string(status));
13023+        goto fail;
13024+    }
13025+
13026+    if ((sys->pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
13027+    {
13028+        msg_Err(vd, "Failed to create input pool");
13029+        goto fail;
13030+    }
13031+
13032+    for (unsigned int i = 0; i != SUBS_MAX; ++i) {
13033+        vout_subpic_t * const sub = sys->subs + i;
13034+        if ((status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sub->component)) != MMAL_SUCCESS)
13035+        {
13036+            msg_Dbg(vd, "Failed to create subpic component %d", i);
13037+            goto fail;
13038+        }
13039+        sub->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
13040+        if ((status = mmal_port_enable(sub->component->control, vd_control_port_cb)) != MMAL_SUCCESS) {
13041+            msg_Err(vd, "Failed to enable control port %s on sub %d (status=%"PRIx32" %s)",
13042+                            sys->component->control->name, i, status, mmal_status_to_string(status));
13043+            goto fail;
13044+        }
13045+        if ((status = hw_mmal_subpic_open(VLC_OBJECT(vd), &sub->sub, sub->component->input[0],
13046+                                          sys->display_id, sys->layer + i + 1)) != MMAL_SUCCESS) {
13047+            msg_Dbg(vd, "Failed to open subpic %d", i);
13048+            goto fail;
13049+        }
13050+        if ((status = mmal_component_enable(sub->component)) != MMAL_SUCCESS)
13051+        {
13052+            msg_Dbg(vd, "Failed to enable subpic component %d", i);
13053+            goto fail;
13054+        }
13055+    }
13056+
13057+    // If we can't deal with it directly ask for I420
13058+    vd->fmt.i_chroma = req_chroma(vd);
13059+
13060+    vd->info = (vout_display_info_t){
13061+        .is_slow = false,
13062+        .has_double_click = false,
13063+        .needs_hide_mouse = false,
13064+        .has_pictures_invalid = true,
13065+        .subpicture_chromas = hw_mmal_vzc_subpicture_chromas
13066+    };
13067+
13068+    vd->pool = vd_pool;
13069+    vd->prepare = vd_prepare;
13070+    vd->display = vd_display;
13071+    vd->control = vd_control;
13072+
13073+
13074+    msg_Dbg(vd, ">>> %s: ok", __func__);
13075+    return VLC_SUCCESS;
13076+
13077+fail:
13078+    CloseMmalVout(object);
13079+
13080+    msg_Dbg(vd, ">>> %s: rv=%d", __func__, ret);
13081+
13082+    return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
13083 }
13084+
13085+vlc_module_begin()
13086+
13087+    add_submodule()
13088+
13089+    set_shortname(N_("MMAL vout"))
13090+    set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
13091+    set_capability("vout display", 16)  // 1 point better than ASCII art
13092+    add_shortcut("mmal_vout")
13093+    set_category( CAT_VIDEO )
13094+    set_subcategory( SUBCAT_VIDEO_VOUT )
13095+
13096+    add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
13097+    add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
13098+                    MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
13099+    add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
13100+                    MMAL_NATIVE_INTERLACE_LONGTEXT, false)
13101+    add_string(MMAL_DISPLAY_NAME, "auto", MMAL_DISPLAY_TEXT,
13102+                    MMAL_DISPLAY_LONGTEXT, false)
13103+    add_string(MMAL_VOUT_TRANSFORM_NAME, "auto", MMAL_VOUT_TRANSFORM_TEXT,
13104+                    MMAL_VOUT_TRANSFORM_LONGTEXT, false)
13105+    add_string(MMAL_VOUT_WINDOW_NAME, "fullscreen", MMAL_VOUT_WINDOW_TEXT,
13106+                    MMAL_VOUT_WINDOW_LONGTEXT, false)
13107+    add_bool(MMAL_VOUT_TRANSPARENT_NAME, false, MMAL_VOUT_TRANSPARENT_TEXT,
13108+                    MMAL_VOUT_TRANSPARENT_LONGTEXT, false)
13109+    set_callbacks(OpenMmalVout, CloseMmalVout)
13110+
13111+vlc_module_end()
13112+
13113+
13114--- /dev/null
13115+++ b/modules/hw/mmal/xsplitter.c
13116@@ -0,0 +1,584 @@
13117+#ifdef HAVE_CONFIG_H
13118+#include "config.h"
13119+#endif
13120+
13121+#include <stdatomic.h>
13122+
13123+#include <vlc_common.h>
13124+#include <vlc_plugin.h>
13125+#include <vlc_threads.h>
13126+#include <vlc_vout_display.h>
13127+#include <vlc_modules.h>
13128+
13129+#include <bcm_host.h>
13130+#include <interface/mmal/mmal.h>
13131+#include <interface/mmal/util/mmal_util.h>
13132+#include <interface/mmal/util/mmal_default_components.h>
13133+
13134+#include "mmal_picture.h"
13135+
13136+#define TRACE_ALL 0
13137+
13138+typedef struct display_desc_s
13139+{
13140+    vout_display_t * vout;
13141+    unsigned int max_pels;
13142+} display_desc_t;
13143+
13144+typedef struct mmal_x11_sys_s
13145+{
13146+    bool use_mmal;
13147+    display_desc_t * cur_desc;
13148+    display_desc_t mmal_desc;
13149+    display_desc_t x_desc;
13150+    uint32_t changed;
13151+    vlc_fourcc_t subpicture_chromas[16];
13152+} mmal_x11_sys_t;
13153+
13154+#define MAX_GL_PELS (1920*1080)
13155+#define MAX_MMAL_PELS (4096*4096)  // Should never be hit
13156+
13157+#if 0
13158+// Gen prog for the following table
13159+// Not done inline in case we end up pulling in FP libs we don't want
13160+#include <math.h>
13161+#include <stdio.h>
13162+
13163+int main(int argc, char *argv[])
13164+{
13165+    unsigned int i;
13166+    for (i = 0; i != 64; ++i)
13167+    {
13168+        printf(" [%2u]=%5u,", i, (unsigned int)(0.5 + (1/sqrt((i + 5)/4.0) * 65536.0)));
13169+        if (i % 4 == 3)
13170+            printf("\n");
13171+    }
13172+}
13173+#endif
13174+
13175+static const uint16_t sqrt_tab[64] = {
13176+    [ 0]=58617, [ 1]=53510, [ 2]=49541, [ 3]=46341,
13177+    [ 4]=43691, [ 5]=41449, [ 6]=39520, [ 7]=37837,
13178+    [ 8]=36353, [ 9]=35030, [10]=33843, [11]=32768,
13179+    [12]=31790, [13]=30894, [14]=30070, [15]=29309,
13180+    [16]=28602, [17]=27945, [18]=27330, [19]=26755,
13181+    [20]=26214, [21]=25705, [22]=25225, [23]=24770,
13182+    [24]=24339, [25]=23930, [26]=23541, [27]=23170,
13183+    [28]=22817, [29]=22479, [30]=22155, [31]=21845,
13184+    [32]=21548, [33]=21263, [34]=20988, [35]=20724,
13185+    [36]=20470, [37]=20225, [38]=19988, [39]=19760,
13186+    [40]=19539, [41]=19326, [42]=19119, [43]=18919,
13187+    [44]=18725, [45]=18536, [46]=18354, [47]=18176,
13188+    [48]=18004, [49]=17837, [50]=17674, [51]=17515,
13189+    [52]=17361, [53]=17211, [54]=17064, [55]=16921,
13190+    [56]=16782, [57]=16646, [58]=16514, [59]=16384,
13191+    [60]=16257, [61]=16134, [62]=16013, [63]=15895
13192+};
13193+#define SQRT_MAX (sizeof(sqrt_tab)/sizeof(sqrt_tab[0]) - 1)
13194+
13195+static bool cpy_fmt_limit_size(const display_desc_t * const dd,
13196+                           video_format_t * const dst,
13197+                           const video_format_t * const src)
13198+{
13199+    const unsigned int src_pel = src->i_visible_width * src->i_visible_height;
13200+
13201+    *dst = *src;
13202+
13203+    if (src_pel <= dd->max_pels)
13204+        return false;
13205+
13206+    // scaling factor sqrt(max_pel/cur_pel)
13207+    // sqrt done by lookup & 16 bit fixed-point maths - not exactly accurate but
13208+    // easily good enough & avoids floating point (which may be slow)
13209+    // src_pel > max_pel so n >= 0
13210+    // Rounding should be such that exact sqrts work and everything else rounds
13211+    // down
13212+    unsigned int n = ((src_pel * 4 - 1) / dd->max_pels) - 4;
13213+    unsigned int scale = sqrt_tab[n >= SQRT_MAX ? SQRT_MAX : n];
13214+
13215+    // Rescale width - rounding up to 16
13216+    unsigned int width = ((src->i_visible_width * scale + (16 << 16) - 1) >> 16) & ~15;
13217+    // Rescale height based on new width
13218+    unsigned int height = (src->i_visible_height * width + src->i_visible_width/2) / src->i_visible_width;
13219+
13220+//    fprintf(stderr, "%dx%d -> %dx%d\n", src->i_visible_width, src->i_visible_height, width, height);
13221+
13222+    dst->i_width          = width;
13223+    dst->i_visible_width  = width;
13224+    dst->i_height         = height;
13225+    dst->i_visible_height = height;
13226+    return true;
13227+}
13228+
13229+static void unload_display_module(vout_display_t * const x_vout)
13230+{
13231+    if (x_vout != NULL) {
13232+       if (x_vout->module != NULL) {
13233+            module_unneed(x_vout, x_vout->module);
13234+        }
13235+        vlc_object_release(x_vout);
13236+    }
13237+}
13238+
13239+static void CloseMmalX11(vlc_object_t *object)
13240+{
13241+    vout_display_t * const vd = (vout_display_t *)object;
13242+    mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13243+
13244+    msg_Dbg(vd, "<<< %s", __func__);
13245+
13246+    if (sys == NULL)
13247+        return;
13248+
13249+    unload_display_module(sys->x_desc.vout);
13250+
13251+    unload_display_module(sys->mmal_desc.vout);
13252+
13253+    free(sys);
13254+
13255+    msg_Dbg(vd, ">>> %s", __func__);
13256+}
13257+
13258+static void mmal_x11_event(vout_display_t * x_vd, int cmd, va_list args)
13259+{
13260+    vout_display_t * const vd = x_vd->owner.sys;
13261+#if TRACE_ALL
13262+    msg_Dbg(vd, "<<< %s (cmd=%d)", __func__, cmd);
13263+#endif
13264+
13265+    // Do not fall into the display assert if Invalid not supported
13266+    if (cmd == VOUT_DISPLAY_EVENT_PICTURES_INVALID &&
13267+            !vd->info.has_pictures_invalid)
13268+        return;
13269+
13270+    vd->owner.event(vd, cmd, args);
13271+}
13272+
13273+static vout_window_t * mmal_x11_window_new(vout_display_t * x_vd, unsigned type)
13274+{
13275+    vout_display_t * const vd = x_vd->owner.sys;
13276+#if TRACE_ALL
13277+    msg_Dbg(vd, "<<< %s (type=%d)", __func__, type);
13278+#endif
13279+    return vd->owner.window_new(vd, type);
13280+}
13281+
13282+static void mmal_x11_window_del(vout_display_t * x_vd, vout_window_t * win)
13283+{
13284+    vout_display_t * const vd = x_vd->owner.sys;
13285+#if TRACE_ALL
13286+    msg_Dbg(vd, "<<< %s", __func__);
13287+#endif
13288+    vd->owner.window_del(vd, win);
13289+}
13290+
13291+
13292+static int load_display_module(vout_display_t * const vd,
13293+                                display_desc_t * const dd,
13294+                                const char * const cap,
13295+                                const char * const module_name)
13296+{
13297+    vout_display_t * const x_vout = vlc_object_create(vd, sizeof(*x_vout));
13298+
13299+    dd->vout = NULL;
13300+    if (!x_vout)
13301+        return -1;
13302+
13303+    x_vout->owner.sys = vd;
13304+    x_vout->owner.event = mmal_x11_event;
13305+    x_vout->owner.window_new = mmal_x11_window_new;
13306+    x_vout->owner.window_del = mmal_x11_window_del;
13307+
13308+    x_vout->cfg    = vd->cfg;
13309+    x_vout->info   = vd->info;
13310+    cpy_fmt_limit_size(dd, &x_vout->source, &vd->source);
13311+    cpy_fmt_limit_size(dd, &x_vout->fmt,    &vd->fmt);
13312+
13313+    if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL)
13314+    {
13315+        msg_Err(vd, "Failed to open Xsplitter:%s module", module_name);
13316+        goto fail;
13317+    }
13318+
13319+    msg_Dbg(vd, "R/G/B: %08x/%08x/%08x", x_vout->fmt.i_rmask, x_vout->fmt.i_gmask, x_vout->fmt.i_bmask);
13320+
13321+    dd->vout = x_vout;
13322+    return 0;
13323+
13324+fail:
13325+    vlc_object_release(x_vout);
13326+    return -1;
13327+}
13328+
13329+
13330+/* Return a pointer over the current picture_pool_t* (mandatory).
13331+ *
13332+ * For performance reasons, it is best to provide at least count
13333+ * pictures but it is not mandatory.
13334+ * You can return NULL when you cannot/do not want to allocate
13335+ * pictures.
13336+ * The vout display module keeps the ownership of the pool and can
13337+ * destroy it only when closing or on invalid pictures control.
13338+ */
13339+static picture_pool_t * mmal_x11_pool(vout_display_t * vd, unsigned count)
13340+{
13341+    mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13342+    vout_display_t * const x_vd = sys->cur_desc->vout;
13343+#if TRACE_ALL
13344+    char buf0[5];
13345+    msg_Dbg(vd, "<<< %s (count=%d) %s:%dx%d->%s:%dx%d", __func__, count,
13346+            str_fourcc(buf0, vd->fmt.i_chroma),
13347+            vd->fmt.i_width, vd->fmt.i_height,
13348+            str_fourcc(buf0, x_vd->fmt.i_chroma),
13349+            x_vd->fmt.i_width, x_vd->fmt.i_height);
13350+#endif
13351+    picture_pool_t * pool = x_vd->pool(x_vd, count);
13352+#if TRACE_ALL
13353+    msg_Dbg(vd, ">>> %s: %p", __func__, pool);
13354+#endif
13355+    return pool;
13356+}
13357+
13358+/* Prepare a picture and an optional subpicture for display (optional).
13359+ *
13360+ * It is called before the next pf_display call to provide as much
13361+ * time as possible to prepare the given picture and the subpicture
13362+ * for display.
13363+ * You are guaranted that pf_display will always be called and using
13364+ * the exact same picture_t and subpicture_t.
13365+ * You cannot change the pixel content of the picture_t or of the
13366+ * subpicture_t.
13367+ */
13368+static void mmal_x11_prepare(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
13369+{
13370+    mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13371+    vout_display_t * const x_vd = sys->cur_desc->vout;
13372+#if TRACE_ALL
13373+    msg_Dbg(vd, "<<< %s", __func__);
13374+#endif
13375+    if (x_vd->prepare)
13376+        x_vd->prepare(x_vd, pic, sub);
13377+}
13378+
13379+/* Display a picture and an optional subpicture (mandatory).
13380+ *
13381+ * The picture and the optional subpicture must be displayed as soon as
13382+ * possible.
13383+ * You cannot change the pixel content of the picture_t or of the
13384+ * subpicture_t.
13385+ *
13386+ * This function gives away the ownership of the picture and of the
13387+ * subpicture, so you must release them as soon as possible.
13388+ */
13389+static void mmal_x11_display(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
13390+{
13391+    mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13392+    vout_display_t * const x_vd = sys->cur_desc->vout;
13393+
13394+#if TRACE_ALL
13395+    const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic);
13396+    msg_Dbg(vd, "<<< %s: fmt: %dx%d/%dx%d, pic:%dx%d, pts=%lld, mmal=%d/%d", __func__, vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height, pic->format.i_width, pic->format.i_height, (long long)pic->date,
13397+            is_mmal_pic, sys->use_mmal);
13398+#endif
13399+
13400+    if (x_vd->fmt.i_chroma != pic->format.i_chroma ||
13401+        x_vd->fmt.i_width  != pic->format.i_width ||
13402+        x_vd->fmt.i_height != pic->format.i_height)
13403+    {
13404+        msg_Dbg(vd, "%s: Picture dropped", __func__);
13405+        picture_Release(pic);
13406+        if (sub != NULL)
13407+            subpicture_Delete(sub);
13408+        return;
13409+    }
13410+
13411+    x_vd->display(x_vd, pic, sub);
13412+}
13413+
13414+
13415+static int vout_display_Control(const display_desc_t * const dd, int query, ...)
13416+{
13417+    va_list args;
13418+    int result;
13419+
13420+    va_start(args, query);
13421+    result = dd->vout->control(dd->vout, query, args);
13422+    va_end(args);
13423+
13424+    return result;
13425+}
13426+
13427+static bool want_mmal_vout(vout_display_t * const vd, const mmal_x11_sys_t * const sys)
13428+{
13429+    return sys->mmal_desc.vout != NULL &&
13430+        (sys->x_desc.vout == NULL || var_InheritBool(vd, "fullscreen"));
13431+}
13432+
13433+static inline int
13434+up_rv(const int a, const int b)
13435+{
13436+    return a != 0 ? a : b;
13437+}
13438+
13439+static int
13440+reset_pictures(vout_display_t * const vd, const display_desc_t * const desc)
13441+{
13442+    int rv = 0;
13443+    VLC_UNUSED(vd);
13444+    if (desc->vout)
13445+    {
13446+        // If the display doesn't have has_pictures_invalid then it doesn't
13447+        // expect RESET_PICTURES
13448+        if (desc->vout->info.has_pictures_invalid)
13449+            vout_display_Control(desc, VOUT_DISPLAY_RESET_PICTURES);
13450+    }
13451+    return rv;
13452+}
13453+
13454+static int
13455+replay_controls(vout_display_t * const vd, const display_desc_t * const desc, const int32_t changed)
13456+{
13457+    if ((changed & (1 << VOUT_DISPLAY_CHANGE_DISPLAY_FILLED)) != 0)
13458+        vout_display_Control(desc, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, vd->cfg);
13459+    if ((changed & (1 << VOUT_DISPLAY_CHANGE_ZOOM)) != 0)
13460+        vout_display_Control(desc, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg);
13461+    if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) |
13462+                    (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0)
13463+        cpy_fmt_limit_size(desc, &desc->vout->source, &vd->source);
13464+    if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT)) != 0)
13465+        vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT);
13466+    if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP)) != 0)
13467+        vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_CROP);
13468+    if ((changed & (1 << VOUT_DISPLAY_CHANGE_VIEWPOINT)) != 0)
13469+        vout_display_Control(desc, VOUT_DISPLAY_CHANGE_VIEWPOINT, vd->cfg);
13470+    return 0;
13471+}
13472+
13473+/* Control on the module (mandatory) */
13474+static int mmal_x11_control(vout_display_t * vd, int ctl, va_list va)
13475+{
13476+    mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13477+    display_desc_t *x_desc = sys->cur_desc;
13478+    int rv;
13479+#if TRACE_ALL
13480+    msg_Dbg(vd, "<<< %s[%d] (ctl=%d)", __func__, sys->use_mmal, ctl);
13481+#endif
13482+    // Remember what we've told this vd - unwanted ctls ignored on replay
13483+    if (ctl >= 0 && ctl <= 31)
13484+        sys->changed |= (1 << ctl);
13485+
13486+    switch (ctl) {
13487+        case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
13488+        {
13489+            const vout_display_cfg_t * const cfg = va_arg(va, const vout_display_cfg_t *);
13490+            const bool want_mmal = want_mmal_vout(vd, sys);
13491+            const bool swap_vout = (sys->use_mmal != want_mmal);
13492+            display_desc_t * const new_desc = want_mmal ? &sys->mmal_desc : &sys->x_desc;
13493+
13494+            msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d",
13495+                    cfg->display.width, cfg->display.height, sys->mmal_desc.vout, want_mmal,
13496+                    var_InheritBool(vd, "fullscreen"));
13497+
13498+            // Repeat any control calls that we sent to the previous vd
13499+            if (swap_vout && sys->changed != 0) {
13500+                const uint32_t changed = sys->changed;
13501+                sys->changed = 0;
13502+                replay_controls(vd, new_desc, changed);
13503+            }
13504+
13505+            if (swap_vout) {
13506+                if (sys->use_mmal) {
13507+                    vout_display_Control(x_desc, VOUT_DISPLAY_CHANGE_MMAL_HIDE);
13508+                }
13509+                vout_display_SendEventPicturesInvalid(vd);
13510+            }
13511+
13512+            rv = vout_display_Control(new_desc, ctl, cfg);
13513+            if (rv == VLC_SUCCESS) {
13514+                vd->fmt       = new_desc->vout->fmt;
13515+                sys->cur_desc = new_desc;
13516+                sys->use_mmal = want_mmal;
13517+            }
13518+
13519+
13520+            break;
13521+        }
13522+
13523+        case VOUT_DISPLAY_RESET_PICTURES:
13524+            {
13525+                char dbuf0[5], dbuf1[5], dbuf2[5];
13526+                msg_Dbg(vd, "<<< %s: Pic reset: fmt: %s,%dx%d<-%s,%dx%d, source: %s,%dx%d/%dx%d", __func__,
13527+                        str_fourcc(dbuf0, vd->fmt.i_chroma), vd->fmt.i_width, vd->fmt.i_height,
13528+                        str_fourcc(dbuf1, x_desc->vout->fmt.i_chroma), x_desc->vout->fmt.i_width, x_desc->vout->fmt.i_height,
13529+                        str_fourcc(dbuf2, vd->source.i_chroma), vd->source.i_width, vd->source.i_height, x_desc->vout->source.i_width,
13530+                        x_desc->vout->source.i_height);
13531+            }
13532+            rv = reset_pictures(vd, &sys->x_desc);
13533+            rv = up_rv(rv, reset_pictures(vd, &sys->mmal_desc));
13534+
13535+            vd->fmt = x_desc->vout->fmt;
13536+            break;
13537+
13538+        case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
13539+        case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
13540+            cpy_fmt_limit_size(x_desc, &x_desc->vout->source, &vd->source);
13541+
13542+            /* FALLTHRU */
13543+        default:
13544+            rv = x_desc->vout->control(x_desc->vout, ctl, va);
13545+//            vd->fmt  = x_vd->fmt;
13546+            break;
13547+    }
13548+#if TRACE_ALL
13549+    msg_Dbg(vd, ">>> %s (rv=%d)", __func__, rv);
13550+#endif
13551+    return rv;
13552+}
13553+
13554+#define DO_MANAGE 0
13555+
13556+#if DO_MANAGE
13557+/* Manage pending event (optional) */
13558+static void mmal_x11_manage(vout_display_t * vd)
13559+{
13560+    mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13561+    vout_display_t * const x_vd = sys->cur_desc->vout;
13562+#if TRACE_ALL
13563+    msg_Dbg(vd, "<<< %s", __func__);
13564+#endif
13565+    x_vd->manage(x_vd);
13566+}
13567+#endif
13568+
13569+static int OpenMmalX11(vlc_object_t *object)
13570+{
13571+    vout_display_t * const vd = (vout_display_t *)object;
13572+    mmal_x11_sys_t * const sys = calloc(1, sizeof(*sys));
13573+    int ret = VLC_SUCCESS;
13574+
13575+    if (sys == NULL) {
13576+        return VLC_EGENERIC;
13577+    }
13578+    vd->sys = (vout_display_sys_t *)sys;
13579+
13580+    vd->info = (vout_display_info_t){
13581+        .is_slow = false,
13582+        .has_double_click = false,
13583+        .needs_hide_mouse = false,
13584+        .has_pictures_invalid = true,
13585+        .subpicture_chromas = NULL
13586+    };
13587+
13588+    {
13589+        char dbuf0[5];
13590+        msg_Dbg(vd, ">>> %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
13591+                str_fourcc(dbuf0, vd->fmt.i_chroma),
13592+                vd->fmt.i_width,         vd->fmt.i_height,
13593+                vd->fmt.i_x_offset,      vd->fmt.i_y_offset,
13594+                vd->fmt.i_visible_width, vd->fmt.i_visible_height,
13595+                vd->fmt.i_sar_num,       vd->fmt.i_sar_den);
13596+    }
13597+
13598+    sys->x_desc.max_pels = MAX_GL_PELS;
13599+    sys->mmal_desc.max_pels = MAX_MMAL_PELS;
13600+
13601+    if (load_display_module(vd, &sys->x_desc, "vout display", "opengles2") == 0)
13602+    {
13603+        msg_Dbg(vd, "Opengles2 output found");
13604+    }
13605+    else
13606+    {
13607+        sys->x_desc.max_pels = MAX_MMAL_PELS;
13608+        if (load_display_module(vd, &sys->x_desc, "vout display", "xcb_x11") == 0)
13609+            msg_Dbg(vd, "X11 XCB output found");
13610+    }
13611+
13612+    if ((load_display_module(vd, &sys->mmal_desc, "vout display", "mmal_vout")) == 0)
13613+        msg_Dbg(vd, "MMAL output found");
13614+
13615+    if (sys->mmal_desc.vout == NULL && sys->x_desc.vout == NULL) {
13616+        char dbuf0[5], dbuf1[5];
13617+        msg_Info(vd, "No valid output found for vout (%s/%s)", str_fourcc(dbuf0, vd->fmt.i_chroma), str_fourcc(dbuf1, vd->source.i_chroma));
13618+        goto fail;
13619+    }
13620+
13621+    vd->pool = mmal_x11_pool;
13622+    vd->prepare = mmal_x11_prepare;
13623+    vd->display = mmal_x11_display;
13624+    vd->control = mmal_x11_control;
13625+#if DO_MANAGE
13626+    vd->manage = mmal_x11_manage;
13627+#endif
13628+
13629+    if (want_mmal_vout(vd, sys)) {
13630+        sys->cur_desc = &sys->mmal_desc;
13631+        sys->use_mmal = true;
13632+    }
13633+    else {
13634+        sys->cur_desc = &sys->x_desc;
13635+        sys->use_mmal = false;
13636+    }
13637+
13638+    if (sys->mmal_desc.vout == NULL || sys->x_desc.vout == NULL) {
13639+        vd->info = sys->cur_desc->vout->info;
13640+        vd->info.has_pictures_invalid = true;  // Should make this unwanted
13641+    }
13642+    else {
13643+        // We have both - construct a combination
13644+        vd->info = (vout_display_info_t){
13645+            .is_slow              = false,
13646+            .has_double_click     = sys->mmal_desc.vout->info.has_double_click || sys->x_desc.vout->info.has_double_click,
13647+            .needs_hide_mouse     = sys->mmal_desc.vout->info.needs_hide_mouse || sys->x_desc.vout->info.needs_hide_mouse,
13648+            .has_pictures_invalid = true,
13649+        };
13650+        // Construct intersection of subpicture chromas
13651+        // sys calloced so no need to add the terminating zero
13652+        if (sys->mmal_desc.vout->info.subpicture_chromas != NULL && sys->x_desc.vout->info.subpicture_chromas != NULL) {
13653+            unsigned int n = 0;
13654+            // N^2 - fix if we ever care
13655+            for (const vlc_fourcc_t * p1 = sys->mmal_desc.vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) {
13656+                for (const vlc_fourcc_t * p2 = sys->x_desc.vout->info.subpicture_chromas; *p2 != 0; ++p2) {
13657+                    if (*p1 == *p2) {
13658+                        sys->subpicture_chromas[n++] = *p1;
13659+                        break;
13660+                    }
13661+                }
13662+            }
13663+            if (n != 0)
13664+                vd->info.subpicture_chromas = sys->subpicture_chromas;
13665+        }
13666+    }
13667+    vd->fmt  = sys->cur_desc->vout->fmt;
13668+
13669+#if TRACE_ALL
13670+    {
13671+        char dbuf0[5];
13672+        msg_Dbg(vd, ">>> %s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
13673+                module_get_name(sys->cur_desc->vout->module, false),
13674+                str_fourcc(dbuf0, vd->fmt.i_chroma),
13675+                vd->fmt.i_width,         vd->fmt.i_height,
13676+                vd->fmt.i_x_offset,      vd->fmt.i_y_offset,
13677+                vd->fmt.i_visible_width, vd->fmt.i_visible_height,
13678+                vd->fmt.i_sar_num,       vd->fmt.i_sar_den);
13679+    }
13680+#endif
13681+    return VLC_SUCCESS;
13682+
13683+fail:
13684+    CloseMmalX11(VLC_OBJECT(vd));
13685+    return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
13686+}
13687+
13688+
13689+
13690+
13691+vlc_module_begin()
13692+    set_shortname(N_("MMAL x11 splitter"))
13693+    set_description(N_("MMAL x11 splitter for Raspberry Pi"))
13694+    set_capability("vout display", 300)  // Between GLES & GL
13695+    add_shortcut("mmal_x11")
13696+    set_category( CAT_VIDEO )
13697+    set_subcategory( SUBCAT_VIDEO_VOUT )
13698+    set_callbacks(OpenMmalX11, CloseMmalX11)
13699+vlc_module_end()
13700+
13701--- a/modules/video_output/opengl/egl.c
13702+++ b/modules/video_output/opengl/egl.c
13703@@ -43,6 +43,8 @@
13704 # include "../android/utils.h"
13705 #endif
13706
13707+#define REQUIRE_DMA_BUF_IMPORT 1
13708+
13709 typedef struct vlc_gl_sys_t
13710 {
13711     EGLDisplay display;
13712@@ -355,6 +357,14 @@ static int Open (vlc_object_t *obj, cons
13713         goto error;
13714     }
13715
13716+#if REQUIRE_DMA_BUF_IMPORT
13717+    if (!CheckToken(ext, "EGL_EXT_image_dma_buf_import"))
13718+    {
13719+        msg_Dbg(obj, "No dma_buf_import - fall back to X");
13720+        goto error;
13721+    }
13722+#endif
13723+
13724     const EGLint conf_attr[] = {
13725         EGL_RED_SIZE, 5,
13726         EGL_GREEN_SIZE, 5,
13727--- a/src/input/decoder.c
13728+++ b/src/input/decoder.c
13729@@ -1995,6 +1995,7 @@ void input_DecoderDelete( decoder_t *p_d
13730     vlc_mutex_lock( &p_owner->lock );
13731     p_owner->b_waiting = false;
13732     vlc_cond_signal( &p_owner->wait_request );
13733+    vlc_mutex_unlock( &p_owner->lock );
13734
13735     /* If the video output is paused or slow, or if the picture pool size was
13736      * under-estimated (e.g. greedy video filter, buggy decoder...), the
13737@@ -2005,7 +2006,6 @@ void input_DecoderDelete( decoder_t *p_d
13738      * worker threads (if any) and the decoder thread to terminate. */
13739     if( p_owner->p_vout != NULL )
13740         vout_Cancel( p_owner->p_vout, true );
13741-    vlc_mutex_unlock( &p_owner->lock );
13742
13743     vlc_join( p_owner->thread, NULL );
13744
13745--- a/src/misc/fourcc.c
13746+++ b/src/misc/fourcc.c
13747@@ -755,8 +755,13 @@ static const struct
13748     { { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422,
13749         VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT },
13750                                                FAKE_FMT() },
13751-    { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE,
13752-        VLC_CODEC_D3D9_OPAQUE,    VLC_CODEC_D3D11_OPAQUE },
13753+    { { VLC_CODEC_ANDROID_OPAQUE },            FAKE_FMT() },
13754+    { { VLC_CODEC_MMAL_OPAQUE, VLC_CODEC_MMAL_ZC_SAND30   },
13755+                                               FAKE_FMT() },
13756+    { { VLC_CODEC_MMAL_ZC_I420,   VLC_CODEC_MMAL_ZC_SAND8,
13757+        VLC_CODEC_MMAL_ZC_SAND10, VLC_CODEC_MMAL_ZC_RGB32 },
13758+                                               FAKE_FMT() },
13759+    { { VLC_CODEC_D3D9_OPAQUE,    VLC_CODEC_D3D11_OPAQUE },
13760                                                FAKE_FMT() },
13761     { { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B },
13762                                                FAKE_FMT() },
13763--- a/src/misc/picture.c
13764+++ b/src/misc/picture.c
13765@@ -365,10 +365,30 @@ void picture_CopyProperties( picture_t *
13766     p_dst->b_top_field_first = p_src->b_top_field_first;
13767 }
13768
13769+static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma)
13770+{
13771+    return i_chroma == VLC_CODEC_MMAL_OPAQUE ||
13772+        i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
13773+        i_chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
13774+        i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
13775+        i_chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
13776+        i_chroma == VLC_CODEC_MMAL_ZC_SAND8;
13777+}
13778+
13779 void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src )
13780 {
13781-    for( int i = 0; i < p_src->i_planes ; i++ )
13782-        plane_CopyPixels( p_dst->p+i, p_src->p+i );
13783+    if( is_zc_chroma(p_src->format.i_chroma) )
13784+    {
13785+        assert(p_dst->i_planes == 0);
13786+        p_dst->i_planes = p_src->i_planes;
13787+        for( int i = 0; i < p_src->i_planes; i++ )
13788+            p_dst->p[i] = p_src->p[i];
13789+    }
13790+    else
13791+    {
13792+        for( int i = 0; i < p_src->i_planes; i++ )
13793+            plane_CopyPixels( p_dst->p+i, p_src->p+i );
13794+    }
13795
13796     assert( p_dst->context == NULL );
13797
13798--- a/src/video_output/video_output.c
13799+++ b/src/video_output/video_output.c
13800@@ -964,6 +964,17 @@ static picture_t *ConvertRGB32AndBlend(v
13801     return NULL;
13802 }
13803
13804+
13805+static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma)
13806+{
13807+    return i_chroma == VLC_CODEC_MMAL_OPAQUE ||
13808+        i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
13809+        i_chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
13810+        i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
13811+        i_chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
13812+        i_chroma == VLC_CODEC_MMAL_ZC_SAND8;
13813+}
13814+
13815 static int ThreadDisplayRenderPicture(vout_thread_t *vout, bool is_forced)
13816 {
13817     vout_thread_sys_t *sys = vout->p;
13818@@ -1098,7 +1109,7 @@ static int ThreadDisplayRenderPicture(vo
13819     }
13820
13821     assert(vout_IsDisplayFiltered(vd) == !sys->display.use_dr);
13822-    if (sys->display.use_dr && !is_direct) {
13823+    if (sys->display.use_dr && !is_direct && !is_zc_chroma(todisplay->format.i_chroma)) {
13824         picture_t *direct = NULL;
13825         if (likely(vout->p->display_pool != NULL))
13826             direct = picture_pool_Get(vout->p->display_pool);
13827