blob: 6038d0ee39cdba4554578ce7855e4e088272adb4 [file] [log] [blame]
Andrew Geissler87f5cff2022-09-30 13:13:31 -05001Upstream-status: Pending
2
3--- a/configure.ac
4+++ b/configure.ac
5@@ -3478,6 +3478,9 @@ dnl
6 AC_ARG_ENABLE(mmal,
7 AS_HELP_STRING([--enable-mmal],
8 [Multi-Media Abstraction Layer (MMAL) hardware plugin (default enable)]))
9+AC_ARG_ENABLE(mmal_avcodec,
10+ AS_HELP_STRING([--enable-mmal-avcodec],
11+ [Use MMAL enabled avcodec libs (default disable)]))
12 if test "${enable_mmal}" != "no"; then
13 VLC_SAVE_FLAGS
14 LDFLAGS="${LDFLAGS} -L/opt/vc/lib -lvchostif"
15@@ -3488,7 +3491,7 @@ if test "${enable_mmal}" != "no"; then
16 VLC_ADD_PLUGIN([mmal])
17 VLC_ADD_LDFLAGS([mmal],[ -L/opt/vc/lib ])
18 VLC_ADD_CFLAGS([mmal],[ -isystem /opt/vc/include -isystem /opt/vc/include/interface/vcos/pthreads -isystem /opt/vc/include/interface/vmcs_host/linux ])
19- VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif ]) ], [
20+ VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif -lvchiq_arm -lvcsm ]) ], [
21 AS_IF([test "${enable_mmal}" = "yes"],
22 [ AC_MSG_ERROR([Cannot find bcm library...]) ],
23 [ AC_MSG_WARN([Cannot find bcm library...]) ])
24@@ -3500,6 +3503,7 @@ if test "${enable_mmal}" != "no"; then
25 VLC_RESTORE_FLAGS
26 fi
27 AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"])
28+AM_CONDITIONAL([HAVE_MMAL_AVCODEC], [test "${enable_mmal_avcodec}" = "yes"])
29
30 dnl
31 dnl evas plugin
32--- a/include/vlc_fourcc.h
33+++ b/include/vlc_fourcc.h
34@@ -365,6 +365,11 @@
35
36 /* Broadcom MMAL opaque buffer type */
37 #define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L')
38+#define VLC_CODEC_MMAL_ZC_SAND8 VLC_FOURCC('Z','S','D','8')
39+#define VLC_CODEC_MMAL_ZC_SAND10 VLC_FOURCC('Z','S','D','0')
40+#define VLC_CODEC_MMAL_ZC_SAND30 VLC_FOURCC('Z','S','D','3')
41+#define VLC_CODEC_MMAL_ZC_I420 VLC_FOURCC('Z','4','2','0')
42+#define VLC_CODEC_MMAL_ZC_RGB32 VLC_FOURCC('Z','R','G','B')
43
44 /* DXVA2 opaque video surface for use with D3D9 */
45 #define VLC_CODEC_D3D9_OPAQUE VLC_FOURCC('D','X','A','9') /* 4:2:0 8 bpc */
46--- a/modules/hw/mmal/Makefile.am
47+++ b/modules/hw/mmal/Makefile.am
48@@ -1,23 +1,57 @@
49 include $(top_srcdir)/modules/common.am
50 mmaldir = $(pluginsdir)/mmal
51
52-AM_CFLAGS += $(CFLAGS_mmal)
53-AM_LDFLAGS += -rpath '$(mmaldir)' $(LDFLAGS_mmal)
54+AM_CFLAGS += -pthread $(CFLAGS_mmal)
55+AM_LDFLAGS += -pthread -rpath '$(mmaldir)' $(LDFLAGS_mmal)
56
57-libmmal_vout_plugin_la_SOURCES = vout.c mmal_picture.c mmal_picture.h
58+libmmal_vout_plugin_la_SOURCES = vout.c mmal_cma.c mmal_picture.c subpic.c\
59+ mmal_cma.h mmal_picture.h subpic.h transform_ops.h\
60+ mmal_piccpy_neon.S
61 libmmal_vout_plugin_la_CFLAGS = $(AM_CFLAGS)
62-libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm
63+libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm -lX11 -lXrandr
64 libmmal_vout_plugin_la_LIBADD = $(LIBS_mmal)
65 mmal_LTLIBRARIES = libmmal_vout_plugin.la
66
67-libmmal_codec_plugin_la_SOURCES = codec.c
68+libmmal_codec_plugin_la_SOURCES = codec.c mmal_cma.c mmal_picture.c subpic.c\
69+ mmal_cma.h mmal_picture.h subpic.h transform_ops.h\
70+ blend_rgba_neon.S mmal_piccpy_neon.S
71 libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS)
72 libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
73 libmmal_codec_plugin_la_LIBADD = $(LIBS_mmal)
74 mmal_LTLIBRARIES += libmmal_codec_plugin.la
75
76-libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c
77+libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c mmal_cma.c\
78+ mmal_cma.h mmal_picture.h transform_ops.h\
79+ mmal_piccpy_neon.S
80 libmmal_deinterlace_plugin_la_CFLAGS = $(AM_CFLAGS)
81 libmmal_deinterlace_plugin_la_LDFLAGS = $(AM_LDFLAGS)
82 libmmal_deinterlace_plugin_la_LIBADD = $(LIBS_mmal)
83 mmal_LTLIBRARIES += libmmal_deinterlace_plugin.la
84+
85+libmmal_xsplitter_plugin_la_SOURCES = xsplitter.c mmal_picture.c mmal_cma.c\
86+ mmal_cma.h mmal_picture.h transform_ops.h\
87+ mmal_piccpy_neon.S
88+libmmal_xsplitter_plugin_la_CFLAGS = $(AM_CFLAGS)
89+libmmal_xsplitter_plugin_la_LDFLAGS = $(AM_LDFLAGS)
90+libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal)
91+mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la
92+
93+libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c\
94+ mmal_cma.h mmal_picture.h transform_ops.h\
95+ mmal_piccpy_neon.S
96+libmmal_converter_plugin_la_CFLAGS = $(AM_CFLAGS)
97+libmmal_converter_plugin_la_LDFLAGS = $(AM_LDFLAGS)
98+libmmal_converter_plugin_la_LIBADD = $(LIBS_mmal)
99+mmal_LTLIBRARIES += libmmal_converter_plugin.la
100+
101+if HAVE_MMAL_AVCODEC
102+libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_cma.c mmal_picture.c\
103+ mmal_cma.h mmal_picture.h transform_ops.h\
104+ mmal_piccpy_neon.S
105+libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS)
106+libmmal_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
107+libmmal_avcodec_plugin_la_LIBADD = $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(LIBS_mmal)
108+mmal_LTLIBRARIES += libmmal_avcodec_plugin.la
109+endif
110+
111+
112--- /dev/null
113+++ b/modules/hw/mmal/blend_rgba_neon.S
114@@ -0,0 +1,197 @@
115+ .syntax unified
116+ .arm
117+// .thumb
118+ .text
119+ .align 16
120+ .arch armv7-a
121+ .fpu neon-vfpv4
122+
123+@ blend_rgbx_rgba_neon
124+
125+@ Implements /255 as ((x * 257) + 0x8000) >> 16
126+@ This generates something in the range [(x+126)/255, (x+127)/255] which is good enough
127+
128+@ There is advantage to aligning src and/or dest - dest gives a bit more due to being used twice
129+
130+
131+
132+@ [r0] RGBx dest loaded into d20-d23
133+@ [r1] RGBA src merge loaded into d16-d19
134+@ r2 plane alpha
135+@ r3 count (pixels)
136+
137+.macro blend_main sR, sG, sB, sA, dR, dG, dB, dA
138+
139+ push { r4, lr }
140+
141+ vdup.u8 d7, r2
142+
143+ subs r3, #8
144+ vmov.u8 d6, #0xff
145+
146+ blt 2f
147+
148+ @ If < 16 bytes to move then don't bother trying to align
149+ @ (a) This means the the align doesn't need to worry about r3 underflow
150+ @ (b) The overhead would be greater than any gain
151+ cmp r3, #8
152+ mov r4, r3
153+ ble 1f
154+
155+ @ Align r1 on a 32 byte boundary
156+ neg r3, r0
157+ ubfx r3, r3, #2, #3
158+
159+ cmp r3, #0
160+ blne 10f
161+
162+ sub r3, r4, r3
163+
164+1:
165+ vld4.8 {d16, d17, d18, d19}, [r1]
166+
167+1:
168+ vmull.u8 q15, \sA, d7
169+
170+ vld4.8 {d20, d21, d22, d23}, [r0]
171+
172+ vsra.u16 q15, q15, #8
173+ subs r3, #8
174+ vrshrn.u16 d31, q15, #8
175+ vsub.u8 d30, d6, d31
176+
177+ vmull.u8 q12, \sR, d31
178+ vmull.u8 q13, \sG, d31
179+ vmull.u8 q14, \sB, d31
180+ addge r1, #32
181+
182+ vmlal.u8 q12, \dR, d30
183+ vmlal.u8 q13, \dG, d30
184+ vmlal.u8 q14, \dB, d30
185+ vld4.8 {d16, d17, d18, d19}, [r1]
186+
187+ vsra.u16 q12, q12, #8 @ * 257/256
188+ vsra.u16 q13, q13, #8
189+ vsra.u16 q14, q14, #8
190+
191+ vrshrn.u16 \dR, q12, #8
192+ vrshrn.u16 \dG, q13, #8
193+ vrshrn.u16 \dB, q14, #8
194+ vmov.u8 \dA, #0xff
195+
196+ vst4.8 {d20, d21, d22, d23}, [r0]!
197+ bge 1b
198+ add r1, #32
199+
200+2:
201+ cmp r3, #-8
202+ blgt 10f
203+
204+ pop { r4, pc }
205+
206+
207+// Partial version
208+// Align @ start & deal with tail
209+10:
210+ lsls r2, r3, #30 @ b2 -> C, b1 -> N
211+ mov r2, r0
212+ bcc 1f
213+ vld4.8 {d16[0], d17[0], d18[0], d19[0]}, [r1]!
214+ vld4.8 {d20[0], d21[0], d22[0], d23[0]}, [r2]!
215+ vld4.8 {d16[1], d17[1], d18[1], d19[1]}, [r1]!
216+ vld4.8 {d20[1], d21[1], d22[1], d23[1]}, [r2]!
217+ vld4.8 {d16[2], d17[2], d18[2], d19[2]}, [r1]!
218+ vld4.8 {d20[2], d21[2], d22[2], d23[2]}, [r2]!
219+ vld4.8 {d16[3], d17[3], d18[3], d19[3]}, [r1]!
220+ vld4.8 {d20[3], d21[3], d22[3], d23[3]}, [r2]!
221+1:
222+ bpl 1f
223+ vld4.8 {d16[4], d17[4], d18[4], d19[4]}, [r1]!
224+ vld4.8 {d20[4], d21[4], d22[4], d23[4]}, [r2]!
225+ vld4.8 {d16[5], d17[5], d18[5], d19[5]}, [r1]!
226+ vld4.8 {d20[5], d21[5], d22[5], d23[5]}, [r2]!
227+1:
228+ tst r3, #1
229+ beq 1f
230+ vld4.8 {d16[6], d17[6], d18[6], d19[6]}, [r1]!
231+ vld4.8 {d20[6], d21[6], d22[6], d23[6]}, [r2]!
232+1:
233+ @ Set conditions for later
234+ lsls r2, r3, #30 @ b2 -> C, b1 -> N
235+
236+ vmull.u8 q15, \sA, d7
237+ vsra.u16 q15, q15, #8
238+ vrshrn.u16 d31, q15, #8
239+ vsub.u8 d30, d6, d31
240+
241+ vmull.u8 q12, \sR, d31
242+ vmull.u8 q13, \sG, d31
243+ vmull.u8 q14, \sB, d31
244+
245+ vmlal.u8 q12, \dR, d30
246+ vmlal.u8 q13, \dG, d30
247+ vmlal.u8 q14, \dB, d30
248+
249+ vsra.u16 q12, q12, #8
250+ vsra.u16 q13, q13, #8
251+ vsra.u16 q14, q14, #8
252+
253+ vrshrn.u16 \dR, q12, #8
254+ vrshrn.u16 \dG, q13, #8
255+ vrshrn.u16 \dB, q14, #8
256+ vmov.u8 \dA, #0xff
257+
258+ bcc 1f
259+ vst4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]!
260+ vst4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]!
261+ vst4.8 {d20[2], d21[2], d22[2], d23[2]}, [r0]!
262+ vst4.8 {d20[3], d21[3], d22[3], d23[3]}, [r0]!
263+1:
264+ bpl 1f
265+ vst4.8 {d20[4], d21[4], d22[4], d23[4]}, [r0]!
266+ vst4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]!
267+1:
268+ tst r3, #1
269+ bxeq lr
270+ vst4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]!
271+
272+ bx lr
273+
274+.endm
275+
276+
277+@ [r0] RGBx dest (Byte order: R, G, B, x)
278+@ [r1] RGBA src merge (Byte order: R, G, B, A)
279+@ r2 plane alpha
280+@ r3 count (pixels)
281+
282+@ Whilst specified as RGBx+RGBA the only important part is the position of
283+@ alpha, the other components are all treated the same
284+
285+@ [r0] RGBx dest (Byte order: R, G, B, x)
286+@ [r1] RGBA src merge (Byte order: R, G, B, A) - same as above
287+@ r2 plane alpha
288+@ r3 count (pixels)
289+ .align 16
290+ .global blend_rgbx_rgba_neon
291+#ifdef __ELF__
292+ .type blend_rgbx_rgba_neon, %function
293+#endif
294+blend_rgbx_rgba_neon:
295+ blend_main d16, d17, d18, d19, d20, d21, d22, d23
296+
297+
298+@ [r0] RGBx dest (Byte order: R, G, B, x)
299+@ [r1] RGBA src merge (Byte order: B, G, R, A) - B / R swapped
300+@ r2 plane alpha
301+@ r3 count (pixels)
302+ .align 16
303+ .global blend_bgrx_rgba_neon
304+#ifdef __ELF__
305+ .type blend_bgrx_rgba_neon, %function
306+#endif
307+blend_bgrx_rgba_neon:
308+ blend_main d18, d17, d16, d19, d20, d21, d22, d23
309+
310+
311+
312--- /dev/null
313+++ b/modules/hw/mmal/blend_rgba_neon.h
314@@ -0,0 +1,17 @@
315+#ifndef HW_MMAL_BLEND_RGBA_NEON_H
316+#define HW_MMAL_BLEND_RGBA_NEON_H
317+
318+#ifdef __cplusplus
319+extern "C" {
320+#endif
321+
322+typedef void blend_neon_fn(void * dest, const void * src, int alpha, unsigned int n);
323+extern blend_neon_fn blend_rgbx_rgba_neon;
324+extern blend_neon_fn blend_bgrx_rgba_neon;
325+
326+#ifdef __cplusplus
327+}
328+#endif
329+
330+#endif
331+
332--- /dev/null
333+++ b/modules/hw/mmal/blend_test.c
334@@ -0,0 +1,180 @@
335+#include <stdio.h>
336+#include <stdint.h>
337+#include <memory.h>
338+
339+#include "blend_rgba_neon.h"
340+
341+#define RPI_PROFILE 1
342+#define RPI_PROC_ALLOC 1
343+#include "rpi_prof.h"
344+
345+static inline unsigned div255(unsigned v)
346+{
347+ // This models what we we do in the asm for / 255
348+ // It generates something in the range [(i+126)/255, (i+127)/255] which is good enough
349+ return ((v * 257) + 0x8000) >> 16;
350+}
351+
352+static inline unsigned int a_merge(unsigned int dst, unsigned src, unsigned f)
353+{
354+ return div255((255 - f) * (dst) + src * f);
355+}
356+
357+
358+static void merge_line(void * dest, const void * src, int alpha, unsigned int n)
359+{
360+ unsigned int i;
361+ const uint8_t * s_data = src;
362+ uint8_t * d_data = dest;
363+
364+ for (i = 0; i != n; ++i) {
365+ const uint32_t s_pel = ((const uint32_t *)s_data)[i];
366+ const uint32_t d_pel = ((const uint32_t *)d_data)[i];
367+ const unsigned int a = div255(alpha * (s_pel >> 24));
368+ ((uint32_t *)d_data)[i] = 0xff000000 |
369+ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 16) & 0xff, a) << 16) |
370+ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) |
371+ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 0) & 0xff, a) << 0 );
372+ }
373+}
374+
375+
376+// Merge RGBA with BGRA
377+static void merge_line2(void * dest, const void * src, int alpha, unsigned int n)
378+{
379+ unsigned int i;
380+ const uint8_t * s_data = src;
381+ uint8_t * d_data = dest;
382+
383+ for (i = 0; i != n; ++i) {
384+ const uint32_t s_pel = ((const uint32_t *)s_data)[i];
385+ const uint32_t d_pel = ((const uint32_t *)d_data)[i];
386+ const unsigned int a = div255(alpha * (s_pel >> 24));
387+ ((uint32_t *)d_data)[i] = 0xff000000 |
388+ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 16) & 0xff, a) << 0 ) |
389+ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) |
390+ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 0) & 0xff, a) << 16);
391+ }
392+}
393+
394+#define BUF_SIZE 256
395+#define BUF_SLACK 16
396+#define BUF_ALIGN 64
397+#define BUF_ALLOC (BUF_SIZE + 2*BUF_SLACK + BUF_ALIGN)
398+
399+static void test_line(const uint32_t * const dx, const unsigned int d_off,
400+ const uint32_t * const sx, const unsigned int s_off,
401+ const unsigned int alpha, const unsigned int len, const int prof_no)
402+{
403+ uint32_t d0_buf[BUF_ALLOC];
404+ uint32_t d1_buf[BUF_ALLOC];
405+ const uint32_t * const s0 = sx + s_off;
406+
407+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
408+ uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
409+ unsigned int i;
410+
411+ memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4);
412+ memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4);
413+
414+ merge_line(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
415+
416+ PROFILE_START();
417+ blend_rgbx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
418+ PROFILE_ACC_N(prof_no);
419+
420+ for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) {
421+ if (d0[i] != d1[i]) {
422+ printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len);
423+ }
424+ }
425+}
426+
427+static void test_line2(const uint32_t * const dx, const unsigned int d_off,
428+ const uint32_t * const sx, const unsigned int s_off,
429+ const unsigned int alpha, const unsigned int len, const int prof_no)
430+{
431+ uint32_t d0_buf[BUF_ALLOC];
432+ uint32_t d1_buf[BUF_ALLOC];
433+ const uint32_t * const s0 = sx + s_off;
434+
435+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
436+ uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
437+ unsigned int i;
438+
439+ memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4);
440+ memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4);
441+
442+ merge_line2(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
443+
444+ PROFILE_START();
445+ blend_bgrx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
446+ PROFILE_ACC_N(prof_no);
447+
448+ for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) {
449+ if (d0[i] != d1[i]) {
450+ printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len);
451+ }
452+ }
453+}
454+
455+
456+
457+int main(int argc, char *argv[])
458+{
459+ unsigned int i, j;
460+ uint32_t d0_buf[BUF_ALLOC];
461+ uint32_t s0_buf[BUF_ALLOC];
462+
463+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + 63) & ~63) + 0;
464+ uint32_t * const s0 = (uint32_t *)(((uintptr_t)s0_buf + 63) & ~63) + 0;
465+
466+ PROFILE_INIT();
467+
468+ for (i = 0; i != 255*255; ++i) {
469+ unsigned int a = div255(i);
470+ unsigned int b = (i + 127)/255;
471+ unsigned int c = (i + 126)/255;
472+ if (a != b && a != c)
473+ printf("%d/255: %d != %d/%d\n", i, a, b, c);
474+ }
475+
476+ for (i = 0; i != BUF_ALLOC; ++i) {
477+ d0_buf[i] = 0xff00 | i;
478+ s0_buf[i] = (i << 24) | 0x40ffc0;
479+ }
480+
481+ for (i = 0; i != 256; ++i) {
482+ test_line(d0, 0, s0, 0, i, 256, -1);
483+ }
484+ for (i = 0; i != 256; ++i) {
485+ test_line(d0, 0, s0, 0, 128, i, -1);
486+ }
487+
488+ for (j = 0; j != 16; ++j) {
489+ for (i = 0; i != 256; ++i) {
490+ test_line(d0, j & 3, s0, j >> 2, i, 256, j);
491+ }
492+ PROFILE_PRINTF_N(j);
493+ PROFILE_CLEAR_N(j);
494+ }
495+ printf("Done 1\n");
496+
497+ for (i = 0; i != 256; ++i) {
498+ test_line2(d0, 0, s0, 0, i, 256, -1);
499+ }
500+ for (i = 0; i != 256; ++i) {
501+ test_line2(d0, 0, s0, 0, 128, i, -1);
502+ }
503+
504+ for (j = 0; j != 16; ++j) {
505+ for (i = 0; i != 256; ++i) {
506+ test_line2(d0, j & 3, s0, j >> 2, i, 256, j);
507+ }
508+ PROFILE_PRINTF_N(j);
509+ }
510+ printf("Done 2\n");
511+
512+ return 0;
513+}
514+
515--- a/modules/hw/mmal/codec.c
516+++ b/modules/hw/mmal/codec.c
517@@ -26,267 +26,443 @@
518 #include "config.h"
519 #endif
520
521+#include <stdatomic.h>
522+
523 #include <vlc_common.h>
524-#include <vlc_atomic.h>
525 #include <vlc_plugin.h>
526 #include <vlc_codec.h>
527+#include <vlc_filter.h>
528 #include <vlc_threads.h>
529
530-#include <bcm_host.h>
531 #include <interface/mmal/mmal.h>
532 #include <interface/mmal/util/mmal_util.h>
533 #include <interface/mmal/util/mmal_default_components.h>
534
535+#include <interface/vcsm/user-vcsm.h>
536+
537+#include "mmal_cma.h"
538 #include "mmal_picture.h"
539
540+#include "subpic.h"
541+#include "blend_rgba_neon.h"
542+
543+#define TRACE_ALL 0
544+
545+#define OPT_TO_FROM_ZC 0
546+
547 /*
548 * This seems to be a bit high, but reducing it causes instabilities
549 */
550 #define NUM_EXTRA_BUFFERS 5
551+//#define NUM_EXTRA_BUFFERS 10
552 #define NUM_DECODER_BUFFER_HEADERS 30
553
554-#define MIN_NUM_BUFFERS_IN_TRANSIT 2
555+#define CONVERTER_BUFFERS 4 // Buffers on the output of the converter
556+
557+#define MMAL_SLICE_HEIGHT 16
558+#define MMAL_ALIGN_W 32
559+#define MMAL_ALIGN_H 16
560
561 #define MMAL_OPAQUE_NAME "mmal-opaque"
562 #define MMAL_OPAQUE_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.")
563 #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.")
564
565-static int OpenDecoder(decoder_t *dec);
566-static void CloseDecoder(decoder_t *dec);
567-
568-vlc_module_begin()
569- set_shortname(N_("MMAL decoder"))
570- set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
571- set_capability("video decoder", 90)
572- add_shortcut("mmal_decoder")
573- add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
574- set_callbacks(OpenDecoder, CloseDecoder)
575-vlc_module_end()
576+#define MMAL_RESIZE_NAME "mmal-resize"
577+#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.")
578+#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.")
579+
580+#define MMAL_ISP_NAME "mmal-isp"
581+#define MMAL_ISP_TEXT N_("Use mmal isp rather than hvs.")
582+#define MMAL_ISP_LONGTEXT N_("Use mmal isp rather than hvs. This may be faster but has no blend.")
583
584-struct decoder_sys_t {
585- bool opaque;
586+typedef struct decoder_sys_t
587+{
588 MMAL_COMPONENT_T *component;
589 MMAL_PORT_T *input;
590 MMAL_POOL_T *input_pool;
591 MMAL_PORT_T *output;
592- MMAL_POOL_T *output_pool; /* only used for non-opaque mode */
593+ hw_mmal_port_pool_ref_t *ppr;
594 MMAL_ES_FORMAT_T *output_format;
595- vlc_sem_t sem;
596
597+ MMAL_STATUS_T err_stream;
598 bool b_top_field_first;
599 bool b_progressive;
600
601+ bool b_flushed;
602+
603+ vcsm_init_type_t vcsm_init_type;
604+
605+ // Lock to avoid pic update & allocate happenening simultainiously
606+ // * We should be able to arrange life s.t. this isn't needed
607+ // but while we are confused apply belt & braces
608+ vlc_mutex_t pic_lock;
609+
610 /* statistics */
611- int output_in_transit;
612- int input_in_transit;
613 atomic_bool started;
614-};
615+} decoder_sys_t;
616
617-/* Utilities */
618-static int change_output_format(decoder_t *dec);
619-static int send_output_buffer(decoder_t *dec);
620-static void fill_output_port(decoder_t *dec);
621-
622-/* VLC decoder callback */
623-static int decode(decoder_t *dec, block_t *block);
624-static void flush_decoder(decoder_t *dec);
625-
626-/* MMAL callbacks */
627-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
628-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
629-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
630
631-static int OpenDecoder(decoder_t *dec)
632-{
633- int ret = VLC_SUCCESS;
634- decoder_sys_t *sys;
635- MMAL_PARAMETER_UINT32_T extra_buffers;
636- MMAL_STATUS_T status;
637+typedef struct supported_mmal_enc_s {
638+ struct {
639+ MMAL_PARAMETER_HEADER_T header;
640+ MMAL_FOURCC_T encodings[64];
641+ } supported;
642+ int n;
643+} supported_mmal_enc_t;
644+
645+#define SUPPORTED_MMAL_ENC_INIT \
646+{ \
647+ {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, \
648+ -1 \
649+}
650
651- if (dec->fmt_in.i_codec != VLC_CODEC_MPGV &&
652- dec->fmt_in.i_codec != VLC_CODEC_H264)
653- return VLC_EGENERIC;
654+static supported_mmal_enc_t supported_decode_in_enc = SUPPORTED_MMAL_ENC_INIT;
655
656- sys = calloc(1, sizeof(decoder_sys_t));
657- if (!sys) {
658- ret = VLC_ENOMEM;
659- goto out;
660+static bool is_enc_supported(supported_mmal_enc_t * const support, const MMAL_FOURCC_T fcc)
661+{
662+ int i;
663+
664+ if (fcc == 0)
665+ return false;
666+ if (support->n == -1)
667+ return true; // Unknown - say OK
668+ for (i = 0; i < support->n; ++i) {
669+ if (support->supported.encodings[i] == fcc)
670+ return true;
671 }
672- dec->p_sys = sys;
673+ return false;
674+}
675
676- sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME);
677- bcm_host_init();
678+static bool set_and_test_enc_supported(supported_mmal_enc_t * const support, MMAL_PORT_T * port, const MMAL_FOURCC_T fcc)
679+{
680+ if (support->n >= 0)
681+ /* already done */;
682+ else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&support->supported) != MMAL_SUCCESS)
683+ support->n = 0;
684+ else
685+ support->n = (support->supported.header.size - sizeof(support->supported.header)) /
686+ sizeof(support->supported.encodings[0]);
687
688- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
689- if (status != MMAL_SUCCESS) {
690- msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
691- MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
692- ret = VLC_EGENERIC;
693- goto out;
694- }
695+ return is_enc_supported(support, fcc);
696+}
697
698- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
699- status = mmal_port_enable(sys->component->control, control_port_cb);
700- if (status != MMAL_SUCCESS) {
701- msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
702- sys->component->control->name, status, mmal_status_to_string(status));
703- ret = VLC_EGENERIC;
704- goto out;
705+static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc)
706+{
707+ switch (fcc){
708+ case VLC_CODEC_MJPG:
709+ return MMAL_ENCODING_MJPEG;
710+ case VLC_CODEC_MP1V:
711+ return MMAL_ENCODING_MP1V;
712+ case VLC_CODEC_MPGV:
713+ case VLC_CODEC_MP2V:
714+ return MMAL_ENCODING_MP2V;
715+ case VLC_CODEC_H263:
716+ return MMAL_ENCODING_H263;
717+ case VLC_CODEC_MP4V:
718+ return MMAL_ENCODING_MP4V;
719+ case VLC_CODEC_H264:
720+ return MMAL_ENCODING_H264;
721+ case VLC_CODEC_VP6:
722+ return MMAL_ENCODING_VP6;
723+ case VLC_CODEC_VP8:
724+ return MMAL_ENCODING_VP8;
725+ case VLC_CODEC_WMV1:
726+ return MMAL_ENCODING_WMV1;
727+ case VLC_CODEC_WMV2:
728+ return MMAL_ENCODING_WMV2;
729+ case VLC_CODEC_WMV3:
730+ return MMAL_ENCODING_WMV3;
731+ case VLC_CODEC_VC1:
732+ return MMAL_ENCODING_WVC1;
733+ case VLC_CODEC_THEORA:
734+ return MMAL_ENCODING_THEORA;
735+ default:
736+ break;
737 }
738+ return 0;
739+}
740
741- sys->input = sys->component->input[0];
742- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
743- if (dec->fmt_in.i_codec == VLC_CODEC_MPGV)
744- sys->input->format->encoding = MMAL_ENCODING_MP2V;
745- else
746- sys->input->format->encoding = MMAL_ENCODING_H264;
747+static MMAL_FOURCC_T pic_to_slice_mmal_fourcc(const MMAL_FOURCC_T fcc)
748+{
749+ switch (fcc){
750+ case MMAL_ENCODING_I420:
751+ return MMAL_ENCODING_I420_SLICE;
752+ case MMAL_ENCODING_I422:
753+ return MMAL_ENCODING_I422_SLICE;
754+ case MMAL_ENCODING_ARGB:
755+ return MMAL_ENCODING_ARGB_SLICE;
756+ case MMAL_ENCODING_RGBA:
757+ return MMAL_ENCODING_RGBA_SLICE;
758+ case MMAL_ENCODING_ABGR:
759+ return MMAL_ENCODING_ABGR_SLICE;
760+ case MMAL_ENCODING_BGRA:
761+ return MMAL_ENCODING_BGRA_SLICE;
762+ case MMAL_ENCODING_RGB16:
763+ return MMAL_ENCODING_RGB16_SLICE;
764+ case MMAL_ENCODING_RGB24:
765+ return MMAL_ENCODING_RGB24_SLICE;
766+ case MMAL_ENCODING_RGB32:
767+ return MMAL_ENCODING_RGB32_SLICE;
768+ case MMAL_ENCODING_BGR16:
769+ return MMAL_ENCODING_BGR16_SLICE;
770+ case MMAL_ENCODING_BGR24:
771+ return MMAL_ENCODING_BGR24_SLICE;
772+ case MMAL_ENCODING_BGR32:
773+ return MMAL_ENCODING_BGR32_SLICE;
774+ default:
775+ break;
776+ }
777+ return 0;
778+}
779
780- if (dec->fmt_in.i_codec == VLC_CODEC_H264) {
781- if (dec->fmt_in.i_extra > 0) {
782- status = mmal_format_extradata_alloc(sys->input->format,
783- dec->fmt_in.i_extra);
784- if (status == MMAL_SUCCESS) {
785- memcpy(sys->input->format->extradata, dec->fmt_in.p_extra,
786- dec->fmt_in.i_extra);
787- sys->input->format->extradata_size = dec->fmt_in.i_extra;
788- } else {
789- msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)",
790- sys->input->name, status, mmal_status_to_string(status));
791- }
792+#define DEBUG_SQUARES 0
793+#if DEBUG_SQUARES
794+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)
795+{
796+ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x;
797+ unsigned int i;
798+ for (i = 0; i != h; ++i) {
799+ unsigned int j;
800+ for (j = 0; j != w; ++j) {
801+ p[j] = val;
802 }
803+ p += pic_stride;
804 }
805+}
806+#endif
807
808- status = mmal_port_format_commit(sys->input);
809- if (status != MMAL_SUCCESS) {
810- msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
811- sys->input->name, status, mmal_status_to_string(status));
812- ret = VLC_EGENERIC;
813- goto out;
814+#if 0
815+static inline void draw_line(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int len, int inc)
816+{
817+ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x;
818+ while (len-- != 0) {
819+ *p = ~0U;
820+ p += inc;
821 }
822- sys->input->buffer_size = sys->input->buffer_size_recommended;
823- sys->input->buffer_num = sys->input->buffer_num_recommended;
824+}
825
826- status = mmal_port_enable(sys->input, input_port_cb);
827- if (status != MMAL_SUCCESS) {
828- msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
829- sys->input->name, status, mmal_status_to_string(status));
830- ret = VLC_EGENERIC;
831- goto out;
832- }
833
834- sys->output = sys->component->output[0];
835- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
836+static void draw_corners(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
837+{
838+ const unsigned int len = 20;
839+ draw_line(pic_buf, pic_stride, x, y, len, 1);
840+ draw_line(pic_buf, pic_stride, x, y, len, pic_stride);
841+ draw_line(pic_buf, pic_stride, x + w - 1, y, len, -1);
842+ draw_line(pic_buf, pic_stride, x + w - 1, y, len, pic_stride);
843+ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -1);
844+ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -(int)pic_stride);
845+ draw_line(pic_buf, pic_stride, x, y + h - 1, len, 1);
846+ draw_line(pic_buf, pic_stride, x, y + h - 1, len, -(int)pic_stride);
847+}
848+#endif
849
850- if (sys->opaque) {
851- extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
852- extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
853- extra_buffers.value = NUM_EXTRA_BUFFERS;
854- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
855- if (status != MMAL_SUCCESS) {
856- msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
857- status, mmal_status_to_string(status));
858- ret = VLC_EGENERIC;
859- goto out;
860- }
861+static MMAL_RATIONAL_T
862+rationalize_sar(unsigned int num, unsigned int den)
863+{
864+ static const unsigned int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 0};
865+ const unsigned int * p = primes;
866
867- msg_Dbg(dec, "Activate zero-copy for output port");
868- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
869- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
870- 1
871- };
872+ // If either num or den is 0 then return a well formed "unknown"
873+ if (num == 0 || den == 0) {
874+ return (MMAL_RATIONAL_T){.num = 0, .den = 0};
875+ }
876
877- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
878- if (status != MMAL_SUCCESS) {
879- msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
880- sys->output->name, status, mmal_status_to_string(status));
881- goto out;
882+ while (*p != 0 && num >= *p && den >= *p) {
883+ if (num % *p != 0 || den % *p != 0)
884+ ++p;
885+ else {
886+ num /= *p;
887+ den /= *p;
888 }
889 }
890+ return (MMAL_RATIONAL_T){.num = num, .den = den};
891+}
892
893- status = mmal_port_enable(sys->output, output_port_cb);
894- if (status != MMAL_SUCCESS) {
895- msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)",
896- sys->output->name, status, mmal_status_to_string(status));
897- ret = VLC_EGENERIC;
898- goto out;
899- }
900+// Buffer either attached to pic or released
901+static picture_t * alloc_opaque_pic(decoder_t * const dec, MMAL_BUFFER_HEADER_T * const buf)
902+{
903+ decoder_sys_t *const dec_sys = dec->p_sys;
904
905- status = mmal_component_enable(sys->component);
906- if (status != MMAL_SUCCESS) {
907- msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
908- sys->component->name, status, mmal_status_to_string(status));
909- ret = VLC_EGENERIC;
910- goto out;
911+ vlc_mutex_lock(&dec_sys->pic_lock);
912+ picture_t * const pic = decoder_NewPicture(dec);
913+ vlc_mutex_unlock(&dec_sys->pic_lock);
914+
915+ if (pic == NULL)
916+ goto fail1;
917+
918+ if (buf->length == 0) {
919+ msg_Err(dec, "%s: Empty buffer", __func__);
920+ goto fail2;
921 }
922
923- sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
924+ if ((pic->context = hw_mmal_gen_context(buf, dec_sys->ppr)) == NULL)
925+ goto fail2;
926
927- if (sys->opaque) {
928- dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
929- dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
930- } else {
931- dec->fmt_out.i_codec = VLC_CODEC_I420;
932- dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
933+ buf_to_pic_copy_props(pic, buf);
934+
935+#if TRACE_ALL
936+ msg_Dbg(dec, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
937+#endif
938+
939+ return pic;
940+
941+fail2:
942+ picture_Release(pic);
943+fail1:
944+ // Recycle rather than release to avoid buffer starvation if NewPic fails
945+ hw_mmal_port_pool_ref_recycle(dec_sys->ppr, buf);
946+ return NULL;
947+}
948+
949+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
950+{
951+ decoder_t *dec = (decoder_t *)port->userdata;
952+ MMAL_STATUS_T status;
953+
954+#if TRACE_ALL
955+ msg_Dbg(dec, "<<< %s: cmd=%d, data=%p", __func__, buffer->cmd, buffer->data);
956+#endif
957+
958+ if (buffer->cmd == MMAL_EVENT_ERROR) {
959+ status = *(uint32_t *)buffer->data;
960+ dec->p_sys->err_stream = status;
961+ msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
962+ mmal_status_to_string(status));
963 }
964
965- dec->pf_decode = decode;
966- dec->pf_flush = flush_decoder;
967+ mmal_buffer_header_release(buffer);
968+}
969
970- vlc_sem_init(&sys->sem, 0);
971+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
972+{
973+ block_t * const block = (block_t *)buffer->user_data;
974
975-out:
976- if (ret != VLC_SUCCESS)
977- CloseDecoder(dec);
978+ (void)port; // Unused
979
980- return ret;
981+#if TRACE_ALL
982+ msg_Dbg((decoder_t *)port->userdata, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
983+ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
984+#endif
985+
986+ mmal_buffer_header_reset(buffer);
987+ mmal_buffer_header_release(buffer);
988+
989+ if (block != NULL)
990+ block_Release(block);
991 }
992
993-static void CloseDecoder(decoder_t *dec)
994+static void decoder_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
995 {
996- decoder_sys_t *sys = dec->p_sys;
997- MMAL_BUFFER_HEADER_T *buffer;
998+ decoder_t * const dec = (decoder_t *)port->userdata;
999
1000- if (!sys)
1001+ if (buffer->cmd == 0 && buffer->length != 0)
1002+ {
1003+#if TRACE_ALL
1004+ msg_Dbg(dec, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
1005+ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
1006+#endif
1007+
1008+ picture_t *pic = alloc_opaque_pic(dec, buffer);
1009+#if TRACE_ALL
1010+ msg_Dbg(dec, "flags=%#x, video flags=%#x", buffer->flags, buffer->type->video.flags);
1011+#endif
1012+ if (pic == NULL)
1013+ msg_Err(dec, "Failed to allocate new picture");
1014+ else
1015+ decoder_QueueVideo(dec, pic);
1016+ // Buffer released or attached to pic - do not release again
1017 return;
1018+ }
1019
1020- if (sys->component && sys->component->control->is_enabled)
1021- mmal_port_disable(sys->component->control);
1022+ if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED)
1023+ {
1024+ decoder_sys_t * const sys = dec->p_sys;
1025+ MMAL_EVENT_FORMAT_CHANGED_T * const fmt = mmal_event_format_changed_get(buffer);
1026+ MMAL_ES_FORMAT_T * const format = mmal_format_alloc();
1027
1028- if (sys->input && sys->input->is_enabled)
1029- mmal_port_disable(sys->input);
1030+ if (format == NULL)
1031+ msg_Err(dec, "Failed to allocate new format");
1032+ else
1033+ {
1034+ mmal_format_full_copy(format, fmt->format);
1035+ format->encoding = MMAL_ENCODING_OPAQUE;
1036
1037- if (sys->output && sys->output->is_enabled)
1038- mmal_port_disable(sys->output);
1039+ // If no PAR in the stream - see if we've got one from the demux
1040+ if (format->es->video.par.den <= 0 || format->es->video.par.num <= 0) {
1041+ unsigned int n = dec->fmt_in.video.i_sar_num;
1042+ unsigned int d = dec->fmt_in.video.i_sar_den;
1043+
1044+ if (n == 0 || d == 0) {
1045+ // Guesswork required
1046+ const unsigned int w = format->es->video.width;
1047+ const unsigned int h = format->es->video.height;
1048+ if ((w == 704 || w == 720) && (h == 480 || h == 576)) {
1049+ // Very likely SD 4:3
1050+ n = w * 3;
1051+ d = h * 4;
1052+ }
1053+ else
1054+ {
1055+ // Otherwise guess SAR 1:1
1056+ n = 1;
1057+ d = 1;
1058+ }
1059+ }
1060
1061- if (sys->component && sys->component->is_enabled)
1062- mmal_component_disable(sys->component);
1063+ format->es->video.par = rationalize_sar(n, d);
1064+ }
1065
1066- if (sys->input_pool)
1067- mmal_pool_destroy(sys->input_pool);
1068+ if (sys->output_format != NULL)
1069+ mmal_format_free(sys->output_format);
1070
1071- if (sys->output_format)
1072- mmal_format_free(sys->output_format);
1073+ sys->output_format = format;
1074+ }
1075+ }
1076+ else if (buffer->cmd != 0) {
1077+ char buf0[5];
1078+ msg_Warn(dec, "Unexpected output cb event: %s", str_fourcc(buf0, buffer->cmd));
1079+ }
1080
1081- if (sys->output_pool)
1082- mmal_pool_destroy(sys->output_pool);
1083+ // If we get here then we were flushing (cmd == 0 && len == 0) or
1084+ // that was an EVENT - in either case we want to release the buffer
1085+ // back to its pool rather than recycle it.
1086+ mmal_buffer_header_reset(buffer);
1087+ buffer->user_data = NULL;
1088+ mmal_buffer_header_release(buffer);
1089+}
1090
1091- if (sys->component)
1092- mmal_component_release(sys->component);
1093
1094- vlc_sem_destroy(&sys->sem);
1095- free(sys);
1096
1097- bcm_host_deinit();
1098+static void fill_output_port(decoder_t *dec)
1099+{
1100+ decoder_sys_t *sys = dec->p_sys;
1101+
1102+ if (decoder_UpdateVideoFormat(dec) != 0)
1103+ {
1104+ // If we have a new format don't bother stuffing the buffer
1105+ // We should get a reset RSN
1106+#if TRACE_ALL
1107+ msg_Dbg(dec, "%s: Updated", __func__);
1108+#endif
1109+
1110+ return;
1111+ }
1112+
1113+ hw_mmal_port_pool_ref_fill(sys->ppr);
1114+ return;
1115 }
1116
1117 static int change_output_format(decoder_t *dec)
1118 {
1119 MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type;
1120- decoder_sys_t *sys = dec->p_sys;
1121+ decoder_sys_t * const sys = dec->p_sys;
1122 MMAL_STATUS_T status;
1123- int pool_size;
1124 int ret = 0;
1125
1126+#if TRACE_ALL
1127+ msg_Dbg(dec, "%s: <<<", __func__);
1128+#endif
1129+
1130 if (atomic_load(&sys->started)) {
1131 mmal_format_full_copy(sys->output->format, sys->output_format);
1132 status = mmal_port_format_commit(sys->output);
1133@@ -300,7 +476,9 @@ static int change_output_format(decoder_
1134 }
1135
1136 port_reset:
1137+#if TRACE_ALL
1138 msg_Dbg(dec, "%s: Do full port reset", __func__);
1139+#endif
1140 status = mmal_port_disable(sys->output);
1141 if (status != MMAL_SUCCESS) {
1142 msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)",
1143@@ -310,6 +488,7 @@ port_reset:
1144 }
1145
1146 mmal_format_full_copy(sys->output->format, sys->output_format);
1147+
1148 status = mmal_port_format_commit(sys->output);
1149 if (status != MMAL_SUCCESS) {
1150 msg_Err(dec, "Failed to commit output format (status=%"PRIx32" %s)",
1151@@ -318,18 +497,10 @@ port_reset:
1152 goto out;
1153 }
1154
1155- if (sys->opaque) {
1156- sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
1157- pool_size = NUM_DECODER_BUFFER_HEADERS;
1158- } else {
1159- sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended,
1160- MIN_NUM_BUFFERS_IN_TRANSIT);
1161- pool_size = sys->output->buffer_num;
1162- }
1163-
1164+ sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
1165 sys->output->buffer_size = sys->output->buffer_size_recommended;
1166
1167- status = mmal_port_enable(sys->output, output_port_cb);
1168+ status = mmal_port_enable(sys->output, decoder_output_cb);
1169 if (status != MMAL_SUCCESS) {
1170 msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)",
1171 status, mmal_status_to_string(status));
1172@@ -338,25 +509,14 @@ port_reset:
1173 }
1174
1175 if (!atomic_load(&sys->started)) {
1176- if (!sys->opaque) {
1177- sys->output_pool = mmal_port_pool_create(sys->output, pool_size, 0);
1178- msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num);
1179- }
1180-
1181 atomic_store(&sys->started, true);
1182
1183 /* we need one picture from vout for each buffer header on the output
1184 * port */
1185- dec->i_extra_picture_buffers = pool_size;
1186-
1187- /* remove what VLC core reserves as it is part of the pool_size
1188- * already */
1189- if (dec->fmt_in.i_codec == VLC_CODEC_H264)
1190- dec->i_extra_picture_buffers -= 19;
1191- else
1192- dec->i_extra_picture_buffers -= 3;
1193-
1194+ dec->i_extra_picture_buffers = 10;
1195+#if TRACE_ALL
1196 msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers);
1197+#endif
1198 }
1199
1200 apply_fmt:
1201@@ -366,8 +526,8 @@ apply_fmt:
1202 dec->fmt_out.video.i_y_offset = sys->output->format->es->video.crop.y;
1203 dec->fmt_out.video.i_visible_width = sys->output->format->es->video.crop.width;
1204 dec->fmt_out.video.i_visible_height = sys->output->format->es->video.crop.height;
1205- dec->fmt_out.video.i_sar_num = sys->output->format->es->video.par.num;
1206- dec->fmt_out.video.i_sar_den = sys->output->format->es->video.par.den;
1207+ dec->fmt_out.video.i_sar_num = sys->output_format->es->video.par.num; // SAR can be killed by commit
1208+ dec->fmt_out.video.i_sar_den = sys->output_format->es->video.par.den;
1209 dec->fmt_out.video.i_frame_rate = sys->output->format->es->video.frame_rate.num;
1210 dec->fmt_out.video.i_frame_rate_base = sys->output->format->es->video.frame_rate.den;
1211
1212@@ -382,12 +542,19 @@ apply_fmt:
1213 sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive);
1214 sys->b_top_field_first = sys->b_progressive ? true :
1215 (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst);
1216+#if TRACE_ALL
1217 msg_Dbg(dec, "Detected %s%s video (%d)",
1218 sys->b_progressive ? "progressive" : "interlaced",
1219 sys->b_progressive ? "" : (sys->b_top_field_first ? " tff" : " bff"),
1220 interlace_type.eMode);
1221+#endif
1222 }
1223
1224+ // Tell the rest of the world we have changed format
1225+ vlc_mutex_lock(&sys->pic_lock);
1226+ ret = decoder_UpdateVideoFormat(dec);
1227+ vlc_mutex_unlock(&sys->pic_lock);
1228+
1229 out:
1230 mmal_format_free(sys->output_format);
1231 sys->output_format = NULL;
1232@@ -395,144 +562,85 @@ out:
1233 return ret;
1234 }
1235
1236-static int send_output_buffer(decoder_t *dec)
1237+static MMAL_STATUS_T
1238+set_extradata_and_commit(decoder_t * const dec, decoder_sys_t * const sys)
1239 {
1240- decoder_sys_t *sys = dec->p_sys;
1241- MMAL_BUFFER_HEADER_T *buffer;
1242- picture_sys_t *p_sys;
1243- picture_t *picture = NULL;
1244 MMAL_STATUS_T status;
1245- unsigned buffer_size = 0;
1246- int ret = 0;
1247
1248- if (!sys->output->is_enabled)
1249- return VLC_EGENERIC;
1250-
1251- /* If local output pool is allocated, use it - this is only the case for
1252- * non-opaque modes */
1253- if (sys->output_pool) {
1254- buffer = mmal_queue_get(sys->output_pool->queue);
1255- if (!buffer) {
1256- msg_Warn(dec, "Failed to get new buffer");
1257- return VLC_EGENERIC;
1258- }
1259- }
1260-
1261- if (!decoder_UpdateVideoFormat(dec))
1262- picture = decoder_NewPicture(dec);
1263- if (!picture) {
1264- msg_Warn(dec, "Failed to get new picture");
1265- ret = -1;
1266- goto err;
1267- }
1268-
1269- p_sys = picture->p_sys;
1270- for (int i = 0; i < picture->i_planes; i++)
1271- buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch;
1272-
1273- if (sys->output_pool) {
1274- mmal_buffer_header_reset(buffer);
1275- buffer->alloc_size = sys->output->buffer_size;
1276- if (buffer_size < sys->output->buffer_size) {
1277- msg_Err(dec, "Retrieved picture with too small data block (%d < %d)",
1278- buffer_size, sys->output->buffer_size);
1279- ret = VLC_EGENERIC;
1280- goto err;
1281- }
1282-
1283- if (!sys->opaque)
1284- buffer->data = picture->p[0].p_pixels;
1285- } else {
1286- buffer = p_sys->buffer;
1287- if (!buffer) {
1288- msg_Warn(dec, "Picture has no buffer attached");
1289- picture_Release(picture);
1290- return VLC_EGENERIC;
1291- }
1292- buffer->data = p_sys->buffer->data;
1293- }
1294- buffer->user_data = picture;
1295- buffer->cmd = 0;
1296-
1297- status = mmal_port_send_buffer(sys->output, buffer);
1298+ status = mmal_port_format_commit(sys->input);
1299 if (status != MMAL_SUCCESS) {
1300- msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)",
1301- status, mmal_status_to_string(status));
1302- ret = -1;
1303- goto err;
1304- }
1305- atomic_fetch_add(&sys->output_in_transit, 1);
1306-
1307- return ret;
1308-
1309-err:
1310- if (picture)
1311- picture_Release(picture);
1312- if (sys->output_pool && buffer) {
1313- buffer->data = NULL;
1314- mmal_buffer_header_release(buffer);
1315+ msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
1316+ sys->input->name, status, mmal_status_to_string(status));
1317 }
1318- return ret;
1319+ return status;
1320 }
1321
1322-static void fill_output_port(decoder_t *dec)
1323+static MMAL_STATUS_T decoder_send_extradata(decoder_t * const dec, decoder_sys_t *const sys)
1324 {
1325- decoder_sys_t *sys = dec->p_sys;
1326-
1327- unsigned max_buffers_in_transit = 0;
1328- int buffers_available = 0;
1329- int buffers_to_send = 0;
1330- int i;
1331+ if (dec->fmt_in.i_codec == VLC_CODEC_H264 &&
1332+ dec->fmt_in.i_extra > 0)
1333+ {
1334+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->input_pool->queue);
1335+ MMAL_STATUS_T status;
1336+
1337+ mmal_buffer_header_reset(buf);
1338+ buf->cmd = 0;
1339+ buf->user_data = NULL;
1340+ buf->alloc_size = sys->input->buffer_size;
1341+ buf->length = dec->fmt_in.i_extra;
1342+ buf->data = dec->fmt_in.p_extra;
1343+ buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG;
1344
1345- if (sys->output_pool) {
1346- max_buffers_in_transit = __MAX(sys->output_pool->headers_num,
1347- MIN_NUM_BUFFERS_IN_TRANSIT);
1348- buffers_available = mmal_queue_length(sys->output_pool->queue);
1349- } else {
1350- max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS;
1351- buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit);
1352+ status = mmal_port_send_buffer(sys->input, buf);
1353+ if (status != MMAL_SUCCESS) {
1354+ msg_Err(dec, "Failed to send extradata buffer to input port (status=%"PRIx32" %s)",
1355+ status, mmal_status_to_string(status));
1356+ return status;
1357+ }
1358 }
1359- buffers_to_send = max_buffers_in_transit - atomic_load(&sys->output_in_transit);
1360
1361- if (buffers_to_send > buffers_available)
1362- buffers_to_send = buffers_available;
1363-
1364-#ifndef NDEBUG
1365- msg_Dbg(dec, "Send %d buffers to output port (available: %d, "
1366- "in_transit: %d, buffer_num: %d)",
1367- buffers_to_send, buffers_available,
1368- atomic_load(&sys->output_in_transit),
1369- sys->output->buffer_num);
1370-#endif
1371- for (i = 0; i < buffers_to_send; ++i)
1372- if (send_output_buffer(dec) < 0)
1373- break;
1374+ return MMAL_SUCCESS;
1375 }
1376
1377 static void flush_decoder(decoder_t *dec)
1378 {
1379- decoder_sys_t *sys = dec->p_sys;
1380- MMAL_BUFFER_HEADER_T *buffer;
1381- MMAL_STATUS_T status;
1382+ decoder_sys_t *const sys = dec->p_sys;
1383
1384- msg_Dbg(dec, "Flushing decoder ports...");
1385- mmal_port_flush(sys->output);
1386- mmal_port_flush(sys->input);
1387-
1388- while (atomic_load(&sys->output_in_transit) ||
1389- atomic_load(&sys->input_in_transit))
1390- vlc_sem_wait(&sys->sem);
1391+#if TRACE_ALL
1392+ msg_Dbg(dec, "%s: <<<", __func__);
1393+#endif
1394+
1395+ if (!sys->b_flushed) {
1396+ mmal_port_disable(sys->input);
1397+ mmal_port_disable(sys->output);
1398+ // We can leave the input disabled, but we want the output enabled
1399+ // in order to sink any buffers returning from other modules
1400+ mmal_port_enable(sys->output, decoder_output_cb);
1401+ sys->b_flushed = true;
1402+ }
1403+#if TRACE_ALL
1404+ msg_Dbg(dec, "%s: >>>", __func__);
1405+#endif
1406 }
1407
1408 static int decode(decoder_t *dec, block_t *block)
1409 {
1410 decoder_sys_t *sys = dec->p_sys;
1411 MMAL_BUFFER_HEADER_T *buffer;
1412- bool need_flush = false;
1413 uint32_t len;
1414- uint32_t flags = 0;
1415+ uint32_t flags = MMAL_BUFFER_HEADER_FLAG_FRAME_START;
1416 MMAL_STATUS_T status;
1417
1418+#if TRACE_ALL
1419+ msg_Dbg(dec, "<<< %s: %lld/%lld", __func__, block == NULL ? -1LL : block->i_dts, block == NULL ? -1LL : block->i_pts);
1420+#endif
1421+
1422+ if (sys->err_stream != MMAL_SUCCESS) {
1423+ msg_Err(dec, "MMAL error reported by ctrl");
1424+ flush_decoder(dec);
1425+ return VLCDEC_ECRITICAL; /// I think they are all fatal
1426+ }
1427+
1428 /*
1429 * Configure output port if necessary
1430 */
1431@@ -541,18 +649,50 @@ static int decode(decoder_t *dec, block_
1432 msg_Err(dec, "Failed to change output port format");
1433 }
1434
1435- if (!block)
1436- goto out;
1437+ if (block == NULL)
1438+ return VLCDEC_SUCCESS;
1439
1440 /*
1441 * Check whether full flush is required
1442 */
1443- if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
1444+ if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
1445+#if TRACE_ALL
1446+ msg_Dbg(dec, "%s: >>> Discontinuity", __func__);
1447+#endif
1448 flush_decoder(dec);
1449+ }
1450+
1451+ if (block->i_buffer == 0)
1452+ {
1453 block_Release(block);
1454 return VLCDEC_SUCCESS;
1455 }
1456
1457+ // Reenable stuff if the last thing we did was flush
1458+ if (!sys->output->is_enabled &&
1459+ (status = mmal_port_enable(sys->output, decoder_output_cb)) != MMAL_SUCCESS)
1460+ {
1461+ msg_Err(dec, "Output port enable failed");
1462+ goto fail;
1463+ }
1464+
1465+ if (!sys->input->is_enabled)
1466+ {
1467+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
1468+ goto fail;
1469+
1470+ if ((status = mmal_port_enable(sys->input, input_port_cb)) != MMAL_SUCCESS)
1471+ {
1472+ msg_Err(dec, "Input port enable failed");
1473+ goto fail;
1474+ }
1475+
1476+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
1477+ goto fail;
1478+ }
1479+
1480+ // *** We cannot get a picture to put the result in 'till we have
1481+ // reported the size & the output stages have been set up
1482 if (atomic_load(&sys->started))
1483 fill_output_port(dec);
1484
1485@@ -563,18 +703,21 @@ static int decode(decoder_t *dec, block_
1486 if (block->i_flags & BLOCK_FLAG_CORRUPTED)
1487 flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED;
1488
1489- while (block && block->i_buffer > 0) {
1490- buffer = mmal_queue_timedwait(sys->input_pool->queue, 100);
1491+ while (block != NULL)
1492+ {
1493+ buffer = mmal_queue_wait(sys->input_pool->queue);
1494 if (!buffer) {
1495 msg_Err(dec, "Failed to retrieve buffer header for input data");
1496- need_flush = true;
1497- break;
1498+ goto fail;
1499 }
1500+
1501 mmal_buffer_header_reset(buffer);
1502 buffer->cmd = 0;
1503- buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts;
1504+ buffer->pts = block->i_pts != VLC_TICK_INVALID ? block->i_pts :
1505+ block->i_dts != VLC_TICK_INVALID ? block->i_dts : MMAL_TIME_UNKNOWN;
1506 buffer->dts = block->i_dts;
1507 buffer->alloc_size = sys->input->buffer_size;
1508+ buffer->user_data = NULL;
1509
1510 len = block->i_buffer;
1511 if (len > buffer->alloc_size)
1512@@ -585,94 +728,1808 @@ static int decode(decoder_t *dec, block_
1513 block->i_buffer -= len;
1514 buffer->length = len;
1515 if (block->i_buffer == 0) {
1516+ flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
1517+ if (block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE) {
1518+ msg_Dbg(dec, "EOS sent");
1519+ flags |= MMAL_BUFFER_HEADER_FLAG_EOS;
1520+ }
1521 buffer->user_data = block;
1522 block = NULL;
1523 }
1524 buffer->flags = flags;
1525
1526+#if TRACE_ALL
1527+ msg_Dbg(dec, "%s: -- Send buffer: cmd=%d, data=%p, size=%d, len=%d, offset=%d, flags=%#x, pts=%lld, dts=%lld", __func__,\
1528+ buffer->cmd, buffer->data, buffer->alloc_size, buffer->length, buffer->offset,
1529+ buffer->flags, (long long)buffer->pts, (long long)buffer->dts);
1530+#endif
1531 status = mmal_port_send_buffer(sys->input, buffer);
1532 if (status != MMAL_SUCCESS) {
1533 msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)",
1534 status, mmal_status_to_string(status));
1535- break;
1536+ goto fail;
1537 }
1538- atomic_fetch_add(&sys->input_in_transit, 1);
1539+
1540+ // Reset flushed flag once we have sent a buf
1541+ sys->b_flushed = false;
1542+ flags &= ~MMAL_BUFFER_HEADER_FLAG_FRAME_START;
1543 }
1544+ return VLCDEC_SUCCESS;
1545
1546-out:
1547- if (need_flush)
1548- flush_decoder(dec);
1549+fail:
1550+ flush_decoder(dec);
1551+ return VLCDEC_ECRITICAL;
1552
1553- return VLCDEC_SUCCESS;
1554 }
1555
1556-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1557+
1558+static void CloseDecoder(decoder_t *dec)
1559 {
1560- decoder_t *dec = (decoder_t *)port->userdata;
1561+ decoder_sys_t *sys = dec->p_sys;
1562+
1563+#if TRACE_ALL
1564+ msg_Dbg(dec, "%s: <<<", __func__);
1565+#endif
1566+
1567+ if (!sys)
1568+ return;
1569+
1570+ if (sys->component != NULL) {
1571+ if (sys->input->is_enabled)
1572+ mmal_port_disable(sys->input);
1573+
1574+ if (sys->output->is_enabled)
1575+ mmal_port_disable(sys->output);
1576+
1577+ if (sys->component->control->is_enabled)
1578+ mmal_port_disable(sys->component->control);
1579+
1580+ if (sys->component->is_enabled)
1581+ mmal_component_disable(sys->component);
1582+
1583+ mmal_component_release(sys->component);
1584+ }
1585+
1586+ if (sys->input_pool != NULL)
1587+ mmal_pool_destroy(sys->input_pool);
1588+
1589+ if (sys->output_format != NULL)
1590+ mmal_format_free(sys->output_format);
1591+
1592+ hw_mmal_port_pool_ref_release(sys->ppr, false);
1593+
1594+ cma_vcsm_exit(sys->vcsm_init_type);
1595+
1596+ vlc_mutex_destroy(&sys->pic_lock);
1597+ free(sys);
1598+}
1599+
1600+static int OpenDecoder(decoder_t *dec)
1601+{
1602+ int ret = VLC_EGENERIC;
1603+ decoder_sys_t *sys;
1604 MMAL_STATUS_T status;
1605+ const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec);
1606+
1607+#if TRACE_ALL || 1
1608+ {
1609+ char buf1[5], buf2[5], buf2a[5];
1610+ char buf3[5], buf4[5];
1611+ MMAL_RATIONAL_T r = rationalize_sar(dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den);
1612+
1613+ msg_Dbg(dec, "%s: <<< (%s/%s)[%s] %dx%d %d/%d=%d/%d o:%#x -> (%s/%s) %dx%d %d/%d o:%#x", __func__,
1614+ str_fourcc(buf1, dec->fmt_in.i_codec),
1615+ str_fourcc(buf2, dec->fmt_in.video.i_chroma),
1616+ str_fourcc(buf2a, in_fcc),
1617+ dec->fmt_in.video.i_width, dec->fmt_in.video.i_height,
1618+ dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den,
1619+ r.num, r.den,
1620+ (int)dec->fmt_in.video.orientation,
1621+ str_fourcc(buf3, dec->fmt_out.i_codec),
1622+ str_fourcc(buf4, dec->fmt_out.video.i_chroma),
1623+ dec->fmt_out.video.i_width, dec->fmt_out.video.i_height,
1624+ dec->fmt_out.video.i_sar_num, dec->fmt_out.video.i_sar_den,
1625+ (int)dec->fmt_out.video.orientation);
1626+ }
1627+#endif
1628+
1629+ if (!is_enc_supported(&supported_decode_in_enc, in_fcc))
1630+ return VLC_EGENERIC;
1631+
1632+ sys = calloc(1, sizeof(decoder_sys_t));
1633+ if (!sys) {
1634+ ret = VLC_ENOMEM;
1635+ goto fail;
1636+ }
1637+ dec->p_sys = sys;
1638+ vlc_mutex_init(&sys->pic_lock);
1639+
1640+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
1641+ msg_Err(dec, "VCSM init failed");
1642+ goto fail;
1643+ }
1644+ msg_Info(dec, "VCSM init succeeded: %s", cma_vcsm_init_str(sys->vcsm_init_type));
1645+
1646+ sys->err_stream = MMAL_SUCCESS;
1647+
1648+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
1649+ if (status != MMAL_SUCCESS) {
1650+ msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
1651+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
1652+ goto fail;
1653+ }
1654+
1655+ sys->input = sys->component->input[0];
1656+ sys->output = sys->component->output[0];
1657+
1658+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
1659+ sys->input->format->encoding = in_fcc;
1660+
1661+ if (!set_and_test_enc_supported(&supported_decode_in_enc, sys->input, in_fcc)) {
1662+#if TRACE_ALL
1663+ char cbuf[5];
1664+ msg_Dbg(dec, "Format not supported: %s", str_fourcc(cbuf, in_fcc));
1665+#endif
1666+ goto fail;
1667+ }
1668+
1669+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
1670+ status = mmal_port_enable(sys->component->control, control_port_cb);
1671+ if (status != MMAL_SUCCESS) {
1672+ msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
1673+ sys->component->control->name, status, mmal_status_to_string(status));
1674+ goto fail;
1675+ }
1676+
1677+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
1678+ goto fail;
1679+
1680+ sys->input->buffer_size = sys->input->buffer_size_recommended;
1681+ sys->input->buffer_num = sys->input->buffer_num_recommended;
1682+
1683+ status = mmal_port_enable(sys->input, input_port_cb);
1684+ if (status != MMAL_SUCCESS) {
1685+ msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
1686+ sys->input->name, status, mmal_status_to_string(status));
1687+ goto fail;
1688+ }
1689+
1690+ // Set vanishingly unlikely shape (or at least crop)
1691+ // to ensure that we get a resolution changed event
1692+ // Small wxh are rejected (128x128 is rejected) so pick a
1693+ // plausible size.
1694+ // Crop doesn't seem to be checked for being constrained by wxh
1695+ // so we could place it outside the pic to be sure that it is
1696+ // never matched but stick with something legal in case it is ever
1697+ // actually checked
1698+ sys->output->format->es->video.height = 256;
1699+ sys->output->format->es->video.width = 256;
1700+ sys->output->format->es->video.crop.height = 4;
1701+ sys->output->format->es->video.crop.width = 2;
1702+ sys->output->format->es->video.crop.x = 66;
1703+ sys->output->format->es->video.crop.y = 88;
1704+
1705+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(dec), &sys->ppr,
1706+ sys->output, NUM_EXTRA_BUFFERS, decoder_output_cb)) != MMAL_SUCCESS)
1707+ goto fail;
1708+
1709+ status = mmal_component_enable(sys->component);
1710+ if (status != MMAL_SUCCESS) {
1711+ msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
1712+ sys->component->name, status, mmal_status_to_string(status));
1713+ goto fail;
1714+ }
1715+
1716+ if ((sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
1717+ {
1718+ msg_Err(dec, "Failed to create input pool");
1719+ goto fail;
1720+ }
1721+
1722+ sys->b_flushed = true;
1723+
1724+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
1725+ goto fail;
1726+
1727+ // Given no better ideas at this point copy input format to output
1728+ // This also copies container stuff (such as orientation) that we do not
1729+ // decode from the ES but may be important to display
1730+ video_format_Copy(&dec->fmt_out.video, &dec->fmt_in.video);
1731+ dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
1732+ dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
1733+
1734+
1735+ dec->pf_decode = decode;
1736+ dec->pf_flush = flush_decoder;
1737+
1738+#if TRACE_ALL
1739+ msg_Dbg(dec, ">>> %s: ok", __func__);
1740+#endif
1741+ return 0;
1742+
1743+fail:
1744+ CloseDecoder(dec);
1745+#if TRACE_ALL
1746+msg_Dbg(dec, ">>> %s: FAIL: ret=%d", __func__, ret);
1747+#endif
1748+ return ret;
1749+}
1750+
1751+// ----------------------------
1752+
1753+#define CONV_MAX_LATENCY 1 // In frames
1754+
1755+typedef struct pic_fifo_s {
1756+ picture_t * head;
1757+ picture_t * tail;
1758+} pic_fifo_t;
1759+
1760+static inline picture_t * pic_fifo_get(pic_fifo_t * const pf)
1761+{
1762+ picture_t * const pic = pf->head;;
1763+ if (pic != NULL) {
1764+ pf->head = pic->p_next;
1765+ pic->p_next = NULL;
1766+ }
1767+ return pic;
1768+}
1769+
1770+static inline picture_t * pic_fifo_get_all(pic_fifo_t * const pf)
1771+{
1772+ picture_t * const pic = pf->head;;
1773+ pf->head = NULL;
1774+ return pic;
1775+}
1776+
1777+static inline void pic_fifo_release_all(pic_fifo_t * const pf)
1778+{
1779+ picture_t * pic;
1780+ while ((pic = pic_fifo_get(pf)) != NULL) {
1781+ picture_Release(pic);
1782+ }
1783+}
1784+
1785+static inline void pic_fifo_init(pic_fifo_t * const pf)
1786+{
1787+ pf->head = NULL;
1788+ pf->tail = NULL; // Not strictly needed
1789+}
1790+
1791+static inline void pic_fifo_put(pic_fifo_t * const pf, picture_t * pic)
1792+{
1793+ pic->p_next = NULL;
1794+ if (pf->head == NULL)
1795+ pf->head = pic;
1796+ else
1797+ pf->tail->p_next = pic;
1798+ pf->tail = pic;
1799+}
1800+
1801+#define SUBS_MAX 3
1802+
1803+typedef enum filter_resizer_e {
1804+ FILTER_RESIZER_RESIZER,
1805+ FILTER_RESIZER_ISP,
1806+ FILTER_RESIZER_HVS
1807+} filter_resizer_t;
1808+
1809+typedef struct conv_frame_stash_s
1810+{
1811+ mtime_t pts;
1812+ MMAL_BUFFER_HEADER_T * sub_bufs[SUBS_MAX];
1813+} conv_frame_stash_t;
1814+
1815+typedef struct filter_sys_t {
1816+ filter_resizer_t resizer_type;
1817+ MMAL_COMPONENT_T *component;
1818+ MMAL_PORT_T *input;
1819+ MMAL_PORT_T *output;
1820+ MMAL_POOL_T *out_pool; // Free output buffers
1821+ MMAL_POOL_T *in_pool; // Input pool to get BH for replication
1822+
1823+ cma_buf_pool_t * cma_in_pool;
1824+ cma_buf_pool_t * cma_out_pool;
1825+
1826+ subpic_reg_stash_t subs[SUBS_MAX];
1827+
1828+ pic_fifo_t ret_pics;
1829+
1830+ unsigned int pic_n;
1831+ vlc_sem_t sem;
1832+ vlc_mutex_t lock;
1833+
1834+ MMAL_STATUS_T err_stream;
1835+
1836+ bool needs_copy_in;
1837+ bool is_cma;
1838+ bool is_sliced;
1839+ bool out_fmt_set;
1840+ const char * component_name;
1841+ MMAL_PORT_BH_CB_T in_port_cb_fn;
1842+ MMAL_PORT_BH_CB_T out_port_cb_fn;
1843+
1844+ uint64_t frame_seq;
1845+ conv_frame_stash_t stash[16];
1846+
1847+ // Slice specific tracking stuff
1848+ struct {
1849+ pic_fifo_t pics;
1850+ unsigned int line; // Lines filled
1851+ } slice;
1852+
1853+ vcsm_init_type_t vcsm_init_type;
1854+} filter_sys_t;
1855+
1856+
1857+static MMAL_STATUS_T pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic)
1858+{
1859+ unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3;
1860+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
1861+
1862+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
1863+ es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format);
1864+ es_fmt->encoding_variant = 0;
1865+
1866+ // Fill in crop etc.
1867+ hw_mmal_vlc_fmt_to_mmal_fmt(es_fmt, &pic->format);
1868+ // Override width / height with strides if appropriate
1869+ if (bpp != 0) {
1870+ v_fmt->width = pic->p[0].i_pitch / bpp;
1871+ v_fmt->height = pic->p[0].i_lines;
1872+ }
1873+ return MMAL_SUCCESS;
1874+}
1875+
1876+
1877+static MMAL_STATUS_T conv_enable_in(filter_t * const p_filter, filter_sys_t * const sys)
1878+{
1879+ MMAL_STATUS_T err = MMAL_SUCCESS;
1880+
1881+ if (!sys->input->is_enabled &&
1882+ (err = mmal_port_enable(sys->input, sys->in_port_cb_fn)) != MMAL_SUCCESS)
1883+ {
1884+ msg_Err(p_filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
1885+ sys->input->name, err, mmal_status_to_string(err));
1886+ }
1887+ return err;
1888+}
1889+
1890+static MMAL_STATUS_T conv_enable_out(filter_t * const p_filter, filter_sys_t * const sys)
1891+{
1892+ MMAL_STATUS_T err = MMAL_SUCCESS;
1893+
1894+ if (sys->is_cma)
1895+ {
1896+ if (sys->cma_out_pool == NULL &&
1897+ (sys->cma_out_pool = cma_buf_pool_new(CONVERTER_BUFFERS, CONVERTER_BUFFERS, true, "mmal_resizer")) == NULL)
1898+ {
1899+ msg_Err(p_filter, "Failed to alloc cma buf pool");
1900+ return MMAL_ENOMEM;
1901+ }
1902+ }
1903+ else
1904+ {
1905+ cma_buf_pool_deletez(&sys->cma_out_pool);
1906+ }
1907+
1908+ if (!sys->output->is_enabled &&
1909+ (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS)
1910+ {
1911+ msg_Err(p_filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
1912+ sys->output->name, err, mmal_status_to_string(err));
1913+ }
1914+ return err;
1915+}
1916+
1917+static void conv_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1918+{
1919+ filter_t * const p_filter = (filter_t *)port->userdata;
1920+
1921+#if TRACE_ALL
1922+ msg_Dbg(p_filter, "%s: <<< cmd=%d, data=%p, pic=%p", __func__, buffer->cmd, buffer->data, buffer->user_data);
1923+#endif
1924
1925 if (buffer->cmd == MMAL_EVENT_ERROR) {
1926- status = *(uint32_t *)buffer->data;
1927- msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
1928+ MMAL_STATUS_T status = *(uint32_t *)buffer->data;
1929+
1930+ p_filter->p_sys->err_stream = status;
1931+
1932+ msg_Err(p_filter, "MMAL error %"PRIx32" \"%s\"", status,
1933 mmal_status_to_string(status));
1934 }
1935
1936 mmal_buffer_header_release(buffer);
1937 }
1938
1939-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1940+static void conv_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
1941 {
1942- block_t *block = (block_t *)buffer->user_data;
1943- decoder_t *dec = (decoder_t *)port->userdata;
1944- decoder_sys_t *sys = dec->p_sys;
1945- buffer->user_data = NULL;
1946+#if TRACE_ALL
1947+ picture_context_t * ctx = buf->user_data;
1948+// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
1949+
1950+ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, ctx=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
1951+ __func__, buf->cmd, ctx, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
1952+#else
1953+ VLC_UNUSED(port);
1954+#endif
1955+
1956+ mmal_buffer_header_release(buf);
1957+
1958+#if TRACE_ALL
1959+ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
1960+#endif
1961+}
1962+
1963+static void conv_out_q_pic(filter_sys_t * const sys, picture_t * const pic)
1964+{
1965+ pic->p_next = NULL;
1966+
1967+ vlc_mutex_lock(&sys->lock);
1968+ pic_fifo_put(&sys->ret_pics, pic);
1969+ vlc_mutex_unlock(&sys->lock);
1970
1971- mmal_buffer_header_release(buffer);
1972- if (block)
1973- block_Release(block);
1974- atomic_fetch_sub(&sys->input_in_transit, 1);
1975 vlc_sem_post(&sys->sem);
1976 }
1977
1978-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1979+static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
1980 {
1981- decoder_t *dec = (decoder_t *)port->userdata;
1982- decoder_sys_t *sys = dec->p_sys;
1983- picture_t *picture;
1984- MMAL_EVENT_FORMAT_CHANGED_T *fmt;
1985- MMAL_ES_FORMAT_T *format;
1986-
1987- if (buffer->cmd == 0) {
1988- picture = (picture_t *)buffer->user_data;
1989- if (buffer->length > 0) {
1990- picture->date = buffer->pts;
1991- picture->b_progressive = sys->b_progressive;
1992- picture->b_top_field_first = sys->b_top_field_first;
1993- decoder_QueueVideo(dec, picture);
1994- } else {
1995- picture_Release(picture);
1996- if (sys->output_pool) {
1997- buffer->user_data = NULL;
1998- buffer->alloc_size = 0;
1999- buffer->data = NULL;
2000- mmal_buffer_header_release(buffer);
2001- }
2002- }
2003- atomic_fetch_sub(&sys->output_in_transit, 1);
2004- vlc_sem_post(&sys->sem);
2005- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
2006- fmt = mmal_event_format_changed_get(buffer);
2007+ filter_t * const p_filter = (filter_t *)port->userdata;
2008+ filter_sys_t * const sys = p_filter->p_sys;
2009
2010- format = mmal_format_alloc();
2011- mmal_format_full_copy(format, fmt->format);
2012+#if TRACE_ALL
2013+ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld/%lld", __func__,
2014+ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size,
2015+ (long long)buf->pts, (long long)sys->stash[(unsigned int)(buf->pts & 0xf)].pts);
2016+#endif
2017+ if (buf->cmd == 0) {
2018+ picture_t * const pic = (picture_t *)buf->user_data;
2019
2020- if (sys->opaque)
2021- format->encoding = MMAL_ENCODING_OPAQUE;
2022+ if (pic == NULL) {
2023+ msg_Err(p_filter, "%s: Buffer has no attached picture", __func__);
2024+ }
2025+ else if (buf->data == NULL || buf->length == 0)
2026+ {
2027+#if TRACE_ALL
2028+ msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
2029+#endif
2030+ }
2031+ else
2032+ {
2033+ buf_to_pic_copy_props(pic, buf);
2034+
2035+ // Set pic data pointers from buf aux info now it has it
2036+ if (sys->is_cma) {
2037+ if (cma_pic_set_data(pic, sys->output->format, buf) != VLC_SUCCESS)
2038+ msg_Err(p_filter, "Failed to set data");
2039+ }
2040+
2041+// 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);
2042+#if DEBUG_SQUARES
2043+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, 32, 32, 0xffff0000);
2044+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 32, 0, 32, 32, 0xff00ff00);
2045+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 64, 0, 32, 32, 0xff0000ff);
2046+#endif
2047+
2048+ buf->user_data = NULL; // Responsability for this pic no longer with buffer
2049+ conv_out_q_pic(sys, pic);
2050+ }
2051+ }
2052+
2053+ mmal_buffer_header_release(buf);
2054+}
2055+
2056+
2057+static void slice_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
2058+{
2059+ filter_t * const p_filter = (filter_t *)port->userdata;
2060+ filter_sys_t * const sys = p_filter->p_sys;
2061+
2062+#if TRACE_ALL
2063+ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld", __func__,
2064+ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, (long long)buf->pts);
2065+#endif
2066+
2067+ if (buf->cmd != 0)
2068+ {
2069+ mmal_buffer_header_release(buf);
2070+ return;
2071+ }
2072+
2073+ if (buf->data == NULL || buf->length == 0)
2074+ {
2075+#if TRACE_ALL
2076+ msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
2077+#endif
2078+ }
2079+ else
2080+ {
2081+ // Got slice
2082+ picture_t *pic = sys->slice.pics.head;
2083+ const unsigned int scale_lines = sys->output->format->es->video.height; // Expected lines of callback
2084+
2085+ if (pic == NULL) {
2086+ msg_Err(p_filter, "No output picture");
2087+ goto fail;
2088+ }
2089+
2090+ // Copy lines
2091+ // * single plane only - fix for I420
2092+ {
2093+ const unsigned int scale_n = __MIN(scale_lines - sys->slice.line, MMAL_SLICE_HEIGHT);
2094+ const unsigned int pic_lines = pic->p[0].i_lines;
2095+ const unsigned int copy_n = sys->slice.line + scale_n <= pic_lines ? scale_n :
2096+ sys->slice.line >= pic_lines ? 0 :
2097+ pic_lines - sys->slice.line;
2098+
2099+ const unsigned int src_stride = buf->type->video.pitch[0];
2100+ const unsigned int dst_stride = pic->p[0].i_pitch;
2101+ uint8_t *dst = pic->p[0].p_pixels + sys->slice.line * dst_stride;
2102+ const uint8_t *src = buf->data + buf->type->video.offset[0];
2103+
2104+ if (src_stride == dst_stride) {
2105+ if (copy_n != 0)
2106+ memcpy(dst, src, src_stride * copy_n);
2107+ }
2108+ else {
2109+ unsigned int i;
2110+ for (i = 0; i != copy_n; ++i) {
2111+ memcpy(dst, src, __MIN(dst_stride, src_stride));
2112+ dst += dst_stride;
2113+ src += src_stride;
2114+ }
2115+ }
2116+ sys->slice.line += scale_n;
2117+ }
2118+
2119+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) {
2120+
2121+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) {
2122+ // Stuff doesn't add up...
2123+ msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags);
2124+ goto fail;
2125+ }
2126+ else {
2127+ sys->slice.line = 0;
2128+
2129+ vlc_mutex_lock(&sys->lock);
2130+ pic_fifo_get(&sys->slice.pics); // Remove head from Q
2131+ vlc_mutex_unlock(&sys->lock);
2132+
2133+ buf_to_pic_copy_props(pic, buf);
2134+ conv_out_q_pic(sys, pic);
2135+ }
2136+ }
2137+ }
2138+
2139+ // Put back
2140+ buf->user_data = NULL; // Zap here to make sure we can't reuse later
2141+ mmal_buffer_header_reset(buf);
2142+
2143+ if (mmal_port_send_buffer(sys->output, buf) != MMAL_SUCCESS) {
2144+ mmal_buffer_header_release(buf);
2145+ }
2146+ return;
2147+
2148+fail:
2149+ sys->err_stream = MMAL_EIO;
2150+ vlc_sem_post(&sys->sem); // If we were waiting then break us out - the flush should fix sem values
2151+}
2152+
2153+
2154+static void conv_flush(filter_t * p_filter)
2155+{
2156+ filter_sys_t * const sys = p_filter->p_sys;
2157+ unsigned int i;
2158+
2159+#if TRACE_ALL
2160+ msg_Dbg(p_filter, "<<< %s", __func__);
2161+#endif
2162+
2163+ if (sys->resizer_type == FILTER_RESIZER_HVS)
2164+ {
2165+ for (i = 0; i != SUBS_MAX; ++i) {
2166+ hw_mmal_subpic_flush(VLC_OBJECT(p_filter), sys->subs + i);
2167+ }
2168+ }
2169+
2170+ if (sys->input != NULL && sys->input->is_enabled)
2171+ mmal_port_disable(sys->input);
2172+
2173+ if (sys->output != NULL && sys->output->is_enabled)
2174+ mmal_port_disable(sys->output);
2175+
2176+// cma_buf_pool_deletez(&sys->cma_out_pool);
2177+
2178+ // Free up anything we may have already lying around
2179+ // Don't need lock as the above disables should have prevented anything
2180+ // happening in the background
2181+
2182+ for (i = 0; i != 16; ++i) {
2183+ conv_frame_stash_t *const stash = sys->stash + i;
2184+ unsigned int sub_no;
2185+
2186+ stash->pts = MMAL_TIME_UNKNOWN;
2187+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2188+ if (stash->sub_bufs[sub_no] != NULL) {
2189+ mmal_buffer_header_release(stash->sub_bufs[sub_no]);
2190+ stash->sub_bufs[sub_no] = NULL;
2191+ }
2192+ }
2193+ }
2194+
2195+ pic_fifo_release_all(&sys->slice.pics);
2196+ pic_fifo_release_all(&sys->ret_pics);
2197+
2198+ // Reset sem values - easiest & most reliable way is to just kill & re-init
2199+ vlc_sem_destroy(&sys->sem);
2200+ vlc_sem_init(&sys->sem, 0);
2201+ sys->pic_n = 0;
2202+
2203+ // Reset error status
2204+ sys->err_stream = MMAL_SUCCESS;
2205+
2206+#if TRACE_ALL
2207+ msg_Dbg(p_filter, ">>> %s", __func__);
2208+#endif
2209+}
2210+
2211+static void conv_stash_fixup(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const p_pic)
2212+{
2213+ conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf);
2214+ unsigned int sub_no;
2215+ VLC_UNUSED(p_filter);
2216+
2217+ p_pic->date = stash->pts;
2218+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2219+ if (stash->sub_bufs[sub_no] != NULL) {
2220+ // **** Do stashed blend
2221+ // **** Aaargh, bother... need to rescale subs too
2222+
2223+ mmal_buffer_header_release(stash->sub_bufs[sub_no]);
2224+ stash->sub_bufs[sub_no] = NULL;
2225+ }
2226+ }
2227+}
2228+
2229+// Output buffers may contain a pic ref on error or flush
2230+// Free it
2231+static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
2232+{
2233+ VLC_UNUSED(userdata);
2234+
2235+ picture_t * const pic = header->user_data;
2236+ header->user_data = NULL;
2237+
2238+ if (pic != NULL)
2239+ picture_Release(pic);
2240+
2241+ return MMAL_FALSE;
2242+}
2243+
2244+static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const pic)
2245+{
2246+ MMAL_STATUS_T status;
2247+
2248+ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
2249+ sys->output->format->type = MMAL_ES_TYPE_VIDEO;
2250+ sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
2251+ sys->output->format->encoding_variant = 0;
2252+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->output->format, &p_filter->fmt_out.video);
2253+
2254+ if (pic != NULL)
2255+ {
2256+ // Override default format width/height if we have a pic we need to match
2257+ if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS)
2258+ {
2259+ char cbuf[5];
2260+ 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);
2261+ return status;
2262+ }
2263+
2264+ MMAL_VIDEO_FORMAT_T *fmt = &sys->output->format->es->video;
2265+ msg_Dbg(p_filter, "%s: %dx%d [(0,0) %dx%d]", __func__, fmt->width, fmt->height, fmt->crop.width, fmt->crop.height);
2266+ }
2267+
2268+ if (sys->is_sliced) {
2269+ // Override height for slice
2270+ sys->output->format->es->video.height = MMAL_SLICE_HEIGHT;
2271+ }
2272+
2273+ mmal_log_dump_format(sys->output->format);
2274+
2275+ status = mmal_port_format_commit(sys->output);
2276+ if (status != MMAL_SUCCESS) {
2277+ msg_Err(p_filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
2278+ sys->output->name, status, mmal_status_to_string(status));
2279+ return status;
2280+ }
2281+
2282+ sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended);
2283+ sys->output->buffer_size = sys->output->buffer_size_recommended;
2284+
2285+ if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS)
2286+ return status;
2287+
2288+ return MMAL_SUCCESS;
2289+}
2290+
2291+
2292+static picture_t *conv_get_out_pics(filter_sys_t * const sys)
2293+{
2294+ picture_t * ret_pics;
2295+
2296+ vlc_sem_wait(&sys->sem);
2297+
2298+ // Return a single pending buffer
2299+ vlc_mutex_lock(&sys->lock);
2300+ ret_pics = pic_fifo_get(&sys->ret_pics);
2301+ vlc_mutex_unlock(&sys->lock);
2302+
2303+ return ret_pics;
2304+}
2305+
2306+static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic)
2307+{
2308+ filter_sys_t * const sys = p_filter->p_sys;
2309+ picture_t * ret_pics = NULL;
2310+ MMAL_STATUS_T err;
2311+ const uint64_t frame_seq = ++sys->frame_seq;
2312+ conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf);
2313+ MMAL_BUFFER_HEADER_T * out_buf = NULL;
2314+
2315+#if TRACE_ALL
2316+ {
2317+ char dbuf0[5], dbuf1[5];
2318+ 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__,
2319+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
2320+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
2321+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
2322+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
2323+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
2324+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
2325+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
2326+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
2327+ }
2328+#endif
2329+
2330+ if (sys->err_stream != MMAL_SUCCESS) {
2331+ goto stream_fail;
2332+ }
2333+
2334+ // Check pic fmt corresponds to what we have set up
2335+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
2336+ {
2337+ msg_Dbg(p_filter, "Reset input port format");
2338+
2339+ // HVS can take new formats without disable, others need it
2340+ if (sys->resizer_type != FILTER_RESIZER_HVS) {
2341+ // Extract any pending pic
2342+ if (sys->pic_n >= 2) {
2343+ ret_pics = conv_get_out_pics(sys);
2344+ // If pic_n == 1 then we return without trying to get stuff
2345+ sys->pic_n = 1;
2346+ }
2347+ if (sys->input->is_enabled) {
2348+ if ((err = mmal_port_disable(sys->input)) != MMAL_SUCCESS)
2349+ msg_Warn(p_filter, "Format update disable failed: %s", mmal_status_to_string(err));
2350+ }
2351+ }
2352+
2353+// mmal_log_dump_port(sys->input);
2354+ if ((err = mmal_port_format_commit(sys->input)) != MMAL_SUCCESS)
2355+ msg_Warn(p_filter, "Format update commit failed: %s", mmal_status_to_string(err));
2356+
2357+ // (Re)enable if required will be done later
2358+ }
2359+
2360+ if (p_pic->context == NULL) {
2361+ // Can't have stashed subpics if not one of our pics
2362+ if (!sys->needs_copy_in)
2363+ msg_Dbg(p_filter, "%s: No context", __func__);
2364+ }
2365+ else if (sys->resizer_type == FILTER_RESIZER_HVS)
2366+ {
2367+ unsigned int sub_no = 0;
2368+
2369+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2370+ int rv;
2371+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter),
2372+ hw_mmal_pic_sub_buf_get(p_pic, sub_no),
2373+ sys->subs + sub_no,
2374+ &p_pic->format,
2375+ &sys->output->format->es->video.crop,
2376+ MMAL_DISPLAY_ROT0,
2377+ frame_seq)) == 0)
2378+ break;
2379+ else if (rv < 0)
2380+ goto fail;
2381+ }
2382+ }
2383+ else
2384+ {
2385+ unsigned int sub_no = 0;
2386+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2387+ if ((stash->sub_bufs[sub_no] = hw_mmal_pic_sub_buf_get(p_pic, sub_no)) != NULL) {
2388+ mmal_buffer_header_acquire(stash->sub_bufs[sub_no]);
2389+ }
2390+ }
2391+ }
2392+
2393+ if (!sys->out_fmt_set) {
2394+ sys->out_fmt_set = true;
2395+
2396+ if (sys->is_sliced) {
2397+ // If zc then we will do stride conversion when we copy to arm side
2398+ // so no need to worry about actual pic dimensions here
2399+ if ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS)
2400+ goto fail;
2401+
2402+ sys->out_pool = mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size);
2403+ }
2404+ else {
2405+ picture_t *pic = filter_NewPicture(p_filter);
2406+ err = conv_set_output(p_filter, sys, pic);
2407+ picture_Release(pic);
2408+ if (err != MMAL_SUCCESS)
2409+ goto fail;
2410+
2411+ sys->out_pool = mmal_pool_create(sys->output->buffer_num, 0);
2412+ }
2413+
2414+ if (sys->out_pool == NULL) {
2415+ msg_Err(p_filter, "Failed to create output pool");
2416+ goto fail;
2417+ }
2418+ }
2419+
2420+ // Reenable stuff if the last thing we did was flush
2421+ if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS ||
2422+ (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
2423+ goto fail;
2424+
2425+ // We attach pic to buf before stuffing the output port
2426+ // We could attach the pic on output for cma, but it is a lot easier to keep
2427+ // the code common.
2428+ {
2429+ picture_t * const out_pic = filter_NewPicture(p_filter);
2430+
2431+ if (out_pic == NULL)
2432+ {
2433+ msg_Err(p_filter, "Failed to alloc required filter output pic");
2434+ goto fail;
2435+ }
2436+
2437+ out_pic->format.i_sar_den = p_filter->fmt_out.video.i_sar_den;
2438+ out_pic->format.i_sar_num = p_filter->fmt_out.video.i_sar_num;
2439+
2440+ if (sys->is_sliced) {
2441+ vlc_mutex_lock(&sys->lock);
2442+ pic_fifo_put(&sys->slice.pics, out_pic);
2443+ vlc_mutex_unlock(&sys->lock);
2444+
2445+ // Poke any returned pic buffers into output
2446+ // In general this should only happen immediately after enable
2447+ while ((out_buf = mmal_queue_get(sys->out_pool->queue)) != NULL)
2448+ mmal_port_send_buffer(sys->output, out_buf);
2449+ }
2450+ else
2451+ {
2452+ // 1 in - 1 out
2453+ if ((out_buf = mmal_queue_wait(sys->out_pool->queue)) == NULL)
2454+ {
2455+ msg_Err(p_filter, "Failed to get output buffer");
2456+ picture_Release(out_pic);
2457+ goto fail;
2458+ }
2459+ mmal_buffer_header_reset(out_buf);
2460+
2461+ // Attach out_pic to the buffer & ensure it is freed when the buffer is released
2462+ // On a good send callback the pic will be extracted to avoid this
2463+ out_buf->user_data = out_pic;
2464+ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, NULL);
2465+
2466+#if 0
2467+ {
2468+ char dbuf0[5];
2469+ msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d",
2470+ str_fourcc(dbuf0, out_pic->format.i_chroma),
2471+ out_pic->format.i_width, out_pic->format.i_height,
2472+ out_pic->format.i_x_offset, out_pic->format.i_y_offset,
2473+ out_pic->format.i_visible_width, out_pic->format.i_visible_height,
2474+ out_pic->format.i_sar_num, out_pic->format.i_sar_den);
2475+ }
2476+#endif
2477+
2478+ if (sys->is_cma) {
2479+ int rv;
2480+
2481+ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size);
2482+ if (cb == NULL) {
2483+ char dbuf0[5];
2484+ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d",
2485+ str_fourcc(dbuf0, out_pic->format.i_chroma),
2486+ sys->output->buffer_size);
2487+ goto fail;
2488+ }
2489+ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable
2490+ out_buf->data = (uint8_t *)vc_h;
2491+ out_buf->alloc_size = sys->output->buffer_size;
2492+
2493+ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS)
2494+ {
2495+ char dbuf0[5];
2496+ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d",
2497+ str_fourcc(dbuf0, out_pic->format.i_chroma),
2498+ rv);
2499+ cma_buf_unref(cb);
2500+ goto fail;
2501+ }
2502+ }
2503+ else {
2504+ out_buf->data = out_pic->p[0].p_pixels;
2505+ out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines;
2506+ //**** stride ????
2507+ }
2508+
2509+#if TRACE_ALL
2510+ msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld",
2511+ p_pic, out_buf->data, out_buf->user_data, out_buf->flags,
2512+ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts);
2513+#endif
2514+
2515+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
2516+ {
2517+ msg_Err(p_filter, "Send buffer to output failed");
2518+ goto fail;
2519+ }
2520+ out_buf = NULL;
2521+ }
2522+ }
2523+
2524+
2525+ // Stuff into input
2526+ // We assume the BH is already set up with values reflecting pic date etc.
2527+ stash->pts = p_pic->date;
2528+ {
2529+ MMAL_BUFFER_HEADER_T *const pic_buf = sys->needs_copy_in ?
2530+ hw_mmal_pic_buf_copied(p_pic, sys->in_pool, sys->input, sys->cma_in_pool) :
2531+ hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
2532+
2533+ // Whether or not we extracted the pic_buf we are done with the picture
2534+ picture_Release(p_pic);
2535+ p_pic = NULL;
2536+
2537+ if (pic_buf == NULL) {
2538+ msg_Err(p_filter, "Pic has no attached buffer");
2539+ goto fail;
2540+ }
2541+
2542+ pic_buf->pts = frame_seq;
2543+
2544+#if TRACE_ALL
2545+ msg_Dbg(p_filter, "In buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d/%d, pts=%lld",
2546+ p_pic, pic_buf->data, pic_buf->user_data, pic_buf->flags,
2547+ pic_buf->length, pic_buf->alloc_size, sys->input->buffer_size, (long long)pic_buf->pts);
2548+#endif
2549+
2550+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
2551+ {
2552+ msg_Err(p_filter, "Send buffer to input failed");
2553+ mmal_buffer_header_release(pic_buf);
2554+ goto fail;
2555+ }
2556+ }
2557+
2558+ // We have a 1 pic latency for everything except the 1st pic which we
2559+ // wait for.
2560+ // This means we get a single static pic out
2561+ if (sys->pic_n++ == 1) {
2562+#if TRACE_ALL
2563+ msg_Dbg(p_filter, ">>> %s: Pic1=%p", __func__, ret_pics);
2564+#endif
2565+ return ret_pics;
2566+ }
2567+
2568+ ret_pics = conv_get_out_pics(sys);
2569+
2570+ if (sys->err_stream != MMAL_SUCCESS)
2571+ goto stream_fail;
2572+
2573+ conv_stash_fixup(p_filter, sys, ret_pics);
2574+
2575+#if TRACE_ALL
2576+ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
2577+#endif
2578+
2579+ return ret_pics;
2580+
2581+stream_fail:
2582+ msg_Err(p_filter, "MMAL error reported by callback");
2583+fail:
2584+#if TRACE_ALL
2585+ msg_Err(p_filter, ">>> %s: FAIL", __func__);
2586+#endif
2587+ if (ret_pics != NULL)
2588+ picture_Release(ret_pics);
2589+ if (out_buf != NULL)
2590+ mmal_buffer_header_release(out_buf);
2591+ if (p_pic != NULL)
2592+ picture_Release(p_pic);
2593+ conv_flush(p_filter);
2594+ return NULL;
2595+}
2596+
2597+static void CloseConverter(vlc_object_t * obj)
2598+{
2599+ filter_t * const p_filter = (filter_t *)obj;
2600+ filter_sys_t * const sys = p_filter->p_sys;
2601+ unsigned int i;
2602+
2603+#if TRACE_ALL
2604+ msg_Dbg(obj, "<<< %s", __func__);
2605+#endif
2606+
2607+ if (sys == NULL)
2608+ return;
2609+
2610+ // Disables input & output ports
2611+ conv_flush(p_filter);
2612+
2613+ cma_buf_pool_deletez(&sys->cma_in_pool);
2614+ cma_buf_pool_deletez(&sys->cma_out_pool);
2615+
2616+ if (sys->component && sys->component->control->is_enabled)
2617+ mmal_port_disable(sys->component->control);
2618+
2619+ if (sys->component && sys->component->is_enabled)
2620+ mmal_component_disable(sys->component);
2621+
2622+ if (sys->resizer_type == FILTER_RESIZER_HVS)
2623+ {
2624+ for (i = 0; i != SUBS_MAX; ++i) {
2625+ hw_mmal_subpic_close(VLC_OBJECT(p_filter), sys->subs + i);
2626+ }
2627+ }
2628+
2629+ if (sys->out_pool)
2630+ {
2631+ if (sys->is_sliced)
2632+ mmal_port_pool_destroy(sys->output, sys->out_pool);
2633+ else
2634+ mmal_pool_destroy(sys->out_pool);
2635+ }
2636+
2637+ if (sys->in_pool != NULL)
2638+ mmal_pool_destroy(sys->in_pool);
2639+
2640+ if (sys->component)
2641+ mmal_component_release(sys->component);
2642+
2643+ cma_vcsm_exit(sys->vcsm_init_type);
2644+
2645+ vlc_sem_destroy(&sys->sem);
2646+ vlc_mutex_destroy(&sys->lock);
2647+
2648+ p_filter->p_sys = NULL;
2649+ free(sys);
2650+}
2651+
2652+
2653+static inline MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt)
2654+{
2655+ if (hw_mmal_chroma_is_mmal(fmt->i_chroma))
2656+ return vlc_to_mmal_video_fourcc(fmt);
2657+
2658+ if (fmt->i_chroma == VLC_CODEC_I420 ||
2659+ fmt->i_chroma == VLC_CODEC_I420_10L)
2660+ return MMAL_ENCODING_I420;
2661+
2662+ return 0;
2663+}
2664+
2665+static inline MMAL_FOURCC_T filter_enc_out(const video_format_t * const fmt)
2666+{
2667+ const MMAL_FOURCC_T mmes = vlc_to_mmal_video_fourcc(fmt);
2668+ // Can only copy out single plane stuff currently - this could be fixed!
2669+ return hw_mmal_chroma_is_mmal(fmt->i_chroma) || mmes != MMAL_ENCODING_I420 ? mmes : 0;
2670+}
2671+
2672+
2673+static int OpenConverter(vlc_object_t * obj)
2674+{
2675+ filter_t * const p_filter = (filter_t *)obj;
2676+ int ret = VLC_EGENERIC;
2677+ filter_sys_t *sys;
2678+ MMAL_STATUS_T status;
2679+ MMAL_FOURCC_T enc_out = filter_enc_out(&p_filter->fmt_out.video);
2680+ const MMAL_FOURCC_T enc_in = filter_enc_in(&p_filter->fmt_in.video);
2681+ bool use_resizer;
2682+ bool use_isp;
2683+ int gpu_mem;
2684+
2685+ // At least in principle we should deal with any mmal format as input
2686+ if (enc_in == 0 || enc_out == 0)
2687+ return VLC_EGENERIC;
2688+
2689+ // Can't transform
2690+ if (p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation)
2691+ return VLC_EGENERIC;
2692+
2693+ use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME);
2694+ use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME);
2695+
2696+retry:
2697+ // ** Make more generic by checking supported encs
2698+ //
2699+ // Must use ISP - HVS can't do this, nor can resizer
2700+ if (enc_in == MMAL_ENCODING_YUVUV64_10) {
2701+ // If resizer selected then just give up
2702+ if (use_resizer)
2703+ return VLC_EGENERIC;
2704+ // otherwise downgrade HVS to ISP
2705+ use_isp = true;
2706+ }
2707+ // HVS can't do I420
2708+ if (enc_out == MMAL_ENCODING_I420) {
2709+ use_isp = true;
2710+ }
2711+ // Only HVS can deal with SAND30
2712+ if (enc_in == MMAL_ENCODING_YUV10_COL) {
2713+ if (use_isp || use_resizer)
2714+ return VLC_EGENERIC;
2715+ }
2716
2717- sys->output_format = format;
2718
2719- mmal_buffer_header_release(buffer);
2720+ if (use_resizer) {
2721+ // use resizer overrides use_isp
2722+ use_isp = false;
2723+ }
2724+
2725+ // Check we have a sliced version of the fourcc if we want the resizer
2726+ if (use_resizer &&
2727+ (enc_out = pic_to_slice_mmal_fourcc(enc_out)) == 0) {
2728+ return VLC_EGENERIC;
2729+ }
2730+
2731+ gpu_mem = hw_mmal_get_gpu_mem();
2732+
2733+ {
2734+ char dbuf0[5], dbuf1[5], dbuf2[5], dbuf3[5];
2735+ 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__,
2736+ use_resizer ? "resize" : use_isp ? "isp" : "hvs",
2737+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), str_fourcc(dbuf2, enc_in),
2738+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
2739+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
2740+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
2741+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
2742+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), str_fourcc(dbuf3, enc_out),
2743+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
2744+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
2745+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
2746+ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
2747+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den,
2748+ gpu_mem);
2749+ }
2750+
2751+ sys = calloc(1, sizeof(filter_sys_t));
2752+ if (!sys) {
2753+ ret = VLC_ENOMEM;
2754+ goto fail;
2755+ }
2756+ p_filter->p_sys = sys;
2757+
2758+ // Init stuff the we destroy unconditionaly in Close first
2759+ vlc_mutex_init(&sys->lock);
2760+ vlc_sem_init(&sys->sem, 0);
2761+ sys->err_stream = MMAL_SUCCESS;
2762+ pic_fifo_init(&sys->ret_pics);
2763+ pic_fifo_init(&sys->slice.pics);
2764+
2765+ sys->needs_copy_in = !hw_mmal_chroma_is_mmal(p_filter->fmt_in.video.i_chroma);
2766+ sys->in_port_cb_fn = conv_input_port_cb;
2767+
2768+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
2769+ msg_Err(p_filter, "VCSM init failed");
2770+ goto fail;
2771+ }
2772+
2773+ if (use_resizer) {
2774+ sys->resizer_type = FILTER_RESIZER_RESIZER;
2775+ sys->is_sliced = true;
2776+ sys->component_name = MMAL_COMPONENT_DEFAULT_RESIZER;
2777+ sys->out_port_cb_fn = slice_output_port_cb;
2778+ }
2779+ else if (use_isp) {
2780+ sys->resizer_type = FILTER_RESIZER_ISP;
2781+ sys->is_sliced = false; // Copy directly into filter picture
2782+ sys->component_name = MMAL_COMPONENT_ISP_RESIZER;
2783+ sys->out_port_cb_fn = conv_output_port_cb;
2784 } else {
2785- mmal_buffer_header_release(buffer);
2786+ sys->resizer_type = FILTER_RESIZER_HVS;
2787+ sys->is_sliced = false; // Copy directly into filter picture
2788+ sys->component_name = MMAL_COMPONENT_HVS;
2789+ sys->out_port_cb_fn = conv_output_port_cb;
2790+ }
2791+ sys->is_cma = is_cma_buf_pic_chroma(p_filter->fmt_out.video.i_chroma);
2792+
2793+ status = mmal_component_create(sys->component_name, &sys->component);
2794+ if (status != MMAL_SUCCESS) {
2795+ if (!use_isp && !use_resizer) {
2796+ msg_Warn(p_filter, "Failed to rcreate HVS resizer - retrying with ISP");
2797+ CloseConverter(obj);
2798+ use_isp = true;
2799+ goto retry;
2800+ }
2801+ msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
2802+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
2803+ goto fail;
2804 }
2805+ sys->output = sys->component->output[0];
2806+ sys->input = sys->component->input[0];
2807+
2808+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
2809+ status = mmal_port_enable(sys->component->control, conv_control_port_cb);
2810+ if (status != MMAL_SUCCESS) {
2811+ msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
2812+ sys->component->control->name, status, mmal_status_to_string(status));
2813+ goto fail;
2814+ }
2815+
2816+ if (sys->needs_copy_in &&
2817+ (sys->cma_in_pool = cma_buf_pool_new(2, 2, true, "conv-copy-in")) == NULL)
2818+ {
2819+ msg_Err(p_filter, "Failed to allocate input CMA pool");
2820+ goto fail;
2821+ }
2822+
2823+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
2824+ sys->input->format->type = MMAL_ES_TYPE_VIDEO;
2825+ sys->input->format->encoding = enc_in;
2826+ sys->input->format->encoding_variant = MMAL_ENCODING_I420;
2827+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &p_filter->fmt_in.video);
2828+ port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, 1);
2829+
2830+ mmal_log_dump_format(sys->input->format);
2831+
2832+ status = mmal_port_format_commit(sys->input);
2833+ if (status != MMAL_SUCCESS) {
2834+ msg_Err(p_filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
2835+ sys->input->name, status, mmal_status_to_string(status));
2836+ goto fail;
2837+ }
2838+ sys->input->buffer_size = sys->input->buffer_size_recommended;
2839+ sys->input->buffer_num = NUM_DECODER_BUFFER_HEADERS;
2840+
2841+ if ((status = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
2842+ goto fail;
2843+
2844+ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced || sys->is_cma);
2845+
2846+ status = mmal_component_enable(sys->component);
2847+ if (status != MMAL_SUCCESS) {
2848+ msg_Err(p_filter, "Failed to enable component %s (status=%"PRIx32" %s)",
2849+ sys->component->name, status, mmal_status_to_string(status));
2850+ goto fail;
2851+ }
2852+
2853+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
2854+ {
2855+ msg_Err(p_filter, "Failed to create input pool");
2856+ goto fail;
2857+ }
2858+
2859+ if (sys->resizer_type == FILTER_RESIZER_HVS)
2860+ {
2861+ unsigned int i;
2862+ for (i = 0; i != SUBS_MAX; ++i) {
2863+ if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], -1, i + 1) != MMAL_SUCCESS)
2864+ {
2865+ msg_Err(p_filter, "Failed to open subpic %d", i);
2866+ goto fail;
2867+ }
2868+ }
2869+ }
2870+
2871+ p_filter->pf_video_filter = conv_filter;
2872+ p_filter->pf_flush = conv_flush;
2873+ // video_drain NIF in filter structure
2874+
2875+#if TRACE_ALL
2876+ msg_Dbg(p_filter, ">>> %s: ok", __func__);
2877+#endif
2878+
2879+ return VLC_SUCCESS;
2880+
2881+fail:
2882+ CloseConverter(obj);
2883+
2884+ if (!use_resizer && status == MMAL_ENOMEM) {
2885+ use_resizer = true;
2886+ msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer");
2887+ goto retry;
2888+ }
2889+
2890+#if TRACE_ALL
2891+ msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret);
2892+#endif
2893+ return ret;
2894+}
2895+
2896+#if OPT_TO_FROM_ZC
2897+//----------------------------------------------------------------------------
2898+//
2899+// Simple copy in to ZC
2900+
2901+typedef struct to_zc_sys_s {
2902+ vcsm_init_type_t vcsm_init_type;
2903+ cma_buf_pool_t * cma_out_pool;
2904+} to_zc_sys_t;
2905+
2906+
2907+static size_t buf_alloc_size(const vlc_fourcc_t i_chroma, const unsigned int width, const unsigned int height)
2908+{
2909+ const unsigned int pels = width * height;
2910+
2911+ switch (i_chroma)
2912+ {
2913+ case VLC_CODEC_MMAL_ZC_RGB32:
2914+ return pels * 4;
2915+ case VLC_CODEC_MMAL_ZC_I420:
2916+ return pels * 3 / 2;
2917+ default:
2918+ break;
2919+ }
2920+ return 0;
2921+}
2922+
2923+
2924+static picture_t *
2925+to_zc_filter(filter_t *p_filter, picture_t *in_pic)
2926+{
2927+ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys;
2928+#if TRACE_ALL
2929+ msg_Dbg(p_filter, "<<< %s", __func__);
2930+#endif
2931+
2932+ assert(p_filter->fmt_out.video.i_chroma == VLC_CODEC_MMAL_ZC_I420);
2933+
2934+ picture_t * const out_pic = filter_NewPicture(p_filter);
2935+ if (out_pic == NULL)
2936+ goto fail0;
2937+
2938+ MMAL_ES_SPECIFIC_FORMAT_T mm_vfmt = {.video={0}};
2939+ MMAL_ES_FORMAT_T mm_esfmt = {
2940+ .encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video),
2941+ .es = &mm_vfmt};
2942+
2943+ hw_mmal_vlc_fmt_to_mmal_fmt(&mm_esfmt, &p_filter->fmt_out.video);
2944+
2945+ const size_t buf_alloc = buf_alloc_size(p_filter->fmt_out.video.i_chroma,
2946+ mm_vfmt.video.width, mm_vfmt.video.height);
2947+ if (buf_alloc == 0)
2948+ goto fail1;
2949+ cma_buf_t *const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, buf_alloc);
2950+ if (cb == NULL)
2951+ goto fail1;
2952+
2953+ if (cma_buf_pic_attach(cb, out_pic) != VLC_SUCCESS)
2954+ goto fail2;
2955+ cma_pic_set_data(out_pic, &mm_esfmt, NULL);
2956+
2957+ hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), NULL, &mm_esfmt, in_pic);
2958+
2959+ // Copy pic properties
2960+ out_pic->date = in_pic->date;
2961+ out_pic->b_force = in_pic->b_force;
2962+ out_pic->b_progressive = in_pic->b_progressive;
2963+ out_pic->b_top_field_first = in_pic->b_top_field_first;
2964+ out_pic->i_nb_fields = in_pic->i_nb_fields;
2965+
2966+ picture_Release(in_pic);
2967+
2968+ return out_pic;
2969+
2970+fail2:
2971+ cma_buf_unref(cb);
2972+fail1:
2973+ picture_Release(out_pic);
2974+fail0:
2975+ picture_Release(in_pic);
2976+ return NULL;
2977+}
2978+
2979+static void to_zc_flush(filter_t * p_filter)
2980+{
2981+ VLC_UNUSED(p_filter);
2982 }
2983+
2984+static void CloseConverterToZc(vlc_object_t * obj)
2985+{
2986+ filter_t * const p_filter = (filter_t *)obj;
2987+ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys;
2988+
2989+ if (sys == NULL)
2990+ return;
2991+
2992+ p_filter->p_sys = NULL;
2993+
2994+ cma_buf_pool_deletez(&sys->cma_out_pool);
2995+ cma_vcsm_exit(sys->vcsm_init_type);
2996+
2997+ free(sys);
2998+}
2999+
3000+static bool to_zc_validate_fmt(const video_format_t * const f_in, const video_format_t * const f_out)
3001+{
3002+ if (!((f_in->i_chroma == VLC_CODEC_I420 || f_in->i_chroma == VLC_CODEC_I420_10L) &&
3003+ f_out->i_chroma == VLC_CODEC_MMAL_ZC_I420))
3004+ {
3005+ return false;
3006+ }
3007+ if (f_in->i_height != f_out->i_height ||
3008+ f_in->i_width != f_out->i_width)
3009+ {
3010+ return false;
3011+ }
3012+
3013+ return true;
3014+}
3015+
3016+static int OpenConverterToZc(vlc_object_t * obj)
3017+{
3018+ int ret = VLC_EGENERIC;
3019+ filter_t * const p_filter = (filter_t *)obj;
3020+
3021+ if (!to_zc_validate_fmt(&p_filter->fmt_in.video, &p_filter->fmt_out.video))
3022+ goto fail;
3023+
3024+ {
3025+ char dbuf0[5], dbuf1[5];
3026+ 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__,
3027+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma),
3028+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
3029+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
3030+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
3031+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
3032+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma),
3033+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
3034+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
3035+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
3036+ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
3037+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
3038+ }
3039+
3040+ to_zc_sys_t * const sys = calloc(1, sizeof(*sys));
3041+ if (!sys) {
3042+ ret = VLC_ENOMEM;
3043+ goto fail;
3044+ }
3045+ p_filter->p_sys = (filter_sys_t *)sys;
3046+
3047+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
3048+ msg_Err(p_filter, "VCSM init failed");
3049+ goto fail;
3050+ }
3051+
3052+ if ((sys->cma_out_pool = cma_buf_pool_new(5, 5, true, "conv-to-zc")) == NULL)
3053+ {
3054+ msg_Err(p_filter, "Failed to allocate input CMA pool");
3055+ goto fail;
3056+ }
3057+
3058+ p_filter->pf_video_filter = to_zc_filter;
3059+ p_filter->pf_flush = to_zc_flush;
3060+ return VLC_SUCCESS;
3061+
3062+fail:
3063+ CloseConverterToZc(obj);
3064+ return ret;
3065+}
3066+
3067+//----------------------------------------------------------------------------
3068+//
3069+// Simple "copy" from ZC
3070+
3071+static void CloseConverterFromZc(vlc_object_t * obj)
3072+{
3073+ VLC_UNUSED(obj);
3074+}
3075+
3076+static int OpenConverterFromZc(vlc_object_t * obj)
3077+{
3078+ return VLC_EGENERIC;
3079+}
3080+#endif
3081+//----------------------------------------------------------------------------
3082+
3083+typedef struct blend_sys_s {
3084+ vzc_pool_ctl_t * vzc;
3085+ const picture_t * last_dst; // Not a ref, just a hint that we have a new pic
3086+ vcsm_init_type_t vcsm_init_type;
3087+} blend_sys_t;
3088+
3089+static void FilterBlendMmal(filter_t *p_filter,
3090+ picture_t *dst, const picture_t * src,
3091+ int x_offset, int y_offset, int alpha)
3092+{
3093+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
3094+#if TRACE_ALL
3095+ 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);
3096+#endif
3097+ // If nothing to do then do nothing
3098+ if (alpha == 0 ||
3099+ src->format.i_visible_height == 0 ||
3100+ src->format.i_visible_width == 0)
3101+ {
3102+ return;
3103+ }
3104+
3105+ if (dst->context == NULL)
3106+ msg_Err(p_filter, "MMAL pic missing context");
3107+ else
3108+ {
3109+ // cast away src const so we can ref it
3110+ MMAL_BUFFER_HEADER_T *buf = hw_mmal_vzc_buf_from_pic(sys->vzc, (picture_t *)src,
3111+ vis_mmal_rect(&dst->format),
3112+ x_offset, y_offset,
3113+ alpha,
3114+ dst != sys->last_dst || !hw_mmal_pic_has_sub_bufs(dst));
3115+ if (buf == NULL) {
3116+ msg_Err(p_filter, "Failed to allocate vzc buffer for subpic");
3117+ return;
3118+ }
3119+
3120+ hw_mmal_pic_sub_buf_add(dst, buf);
3121+
3122+ sys->last_dst = dst;
3123+ }
3124+}
3125+
3126+static void FlushBlendMmal(filter_t * p_filter)
3127+{
3128+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
3129+ sys->last_dst = NULL;
3130+ hw_mmal_vzc_pool_flush(sys->vzc);
3131+}
3132+
3133+static void CloseBlendMmal(vlc_object_t *object)
3134+{
3135+ filter_t * const p_filter = (filter_t *)object;
3136+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
3137+
3138+ if (sys != NULL) {
3139+ p_filter->p_sys = NULL;
3140+
3141+ hw_mmal_vzc_pool_release(sys->vzc);
3142+ cma_vcsm_exit(sys->vcsm_init_type);
3143+ free(sys);
3144+ }
3145+}
3146+
3147+static int OpenBlendMmal(vlc_object_t *object)
3148+{
3149+ filter_t * const p_filter = (filter_t *)object;
3150+ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
3151+
3152+ if (!hw_mmal_chroma_is_mmal(vfcc_dst) ||
3153+ !hw_mmal_vzc_subpic_fmt_valid(&p_filter->fmt_in.video))
3154+ {
3155+ return VLC_EGENERIC;
3156+ }
3157+
3158+ {
3159+ char dbuf0[5], dbuf1[5];
3160+ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %dx%d]->%s,%dx%d [(%d,%d) %dx%d]", __func__,
3161+ "blend",
3162+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
3163+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
3164+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
3165+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
3166+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
3167+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
3168+ }
3169+
3170+ {
3171+ blend_sys_t * const sys = calloc(1, sizeof (*sys));
3172+ if (sys == NULL)
3173+ return VLC_ENOMEM;
3174+
3175+ p_filter->p_sys = (filter_sys_t *)sys;
3176+
3177+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
3178+ msg_Err(p_filter, "VCSM init failed");
3179+ goto fail;
3180+ }
3181+
3182+ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
3183+ goto fail;
3184+ }
3185+
3186+ p_filter->pf_video_blend = FilterBlendMmal;
3187+ p_filter->pf_flush = FlushBlendMmal;
3188+
3189+ return VLC_SUCCESS;
3190+
3191+fail:
3192+ CloseBlendMmal(VLC_OBJECT(p_filter));
3193+ return VLC_ENOMEM;
3194+}
3195+
3196+// ---------------------------------------------------------------------------
3197+
3198+static void FilterBlendNeon(filter_t *p_filter,
3199+ picture_t *dst_pic, const picture_t * src_pic,
3200+ int x_offset, int y_offset, int alpha)
3201+{
3202+ const uint8_t * s_data;
3203+ uint8_t * d_data;
3204+ int width = src_pic->format.i_visible_width;
3205+ int height = src_pic->format.i_visible_height;
3206+ blend_neon_fn *const blend_fn = (blend_neon_fn * )p_filter->p_sys;
3207+
3208+#if TRACE_ALL
3209+ 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);
3210+#endif
3211+
3212+ if (alpha == 0 ||
3213+ src_pic->format.i_visible_height == 0 ||
3214+ src_pic->format.i_visible_width == 0)
3215+ {
3216+ return;
3217+ }
3218+
3219+ x_offset += dst_pic->format.i_x_offset;
3220+ y_offset += dst_pic->format.i_y_offset;
3221+
3222+ // Deal with R/B overrun
3223+ if (x_offset + width >= (int)(dst_pic->format.i_x_offset + dst_pic->format.i_visible_width))
3224+ width = dst_pic->format.i_x_offset + dst_pic->format.i_visible_width - x_offset;
3225+ if (y_offset + height >= (int)(dst_pic->format.i_y_offset + dst_pic->format.i_visible_height))
3226+ height = dst_pic->format.i_y_offset + dst_pic->format.i_visible_height - y_offset;
3227+
3228+ if (width <= 0 || height <= 0) {
3229+ return;
3230+ }
3231+
3232+ // *** L/U overrun
3233+
3234+ s_data = src_pic->p[0].p_pixels +
3235+ src_pic->p[0].i_pixel_pitch * src_pic->format.i_x_offset +
3236+ src_pic->p[0].i_pitch * src_pic->format.i_y_offset;
3237+ d_data = dst_pic->p[0].p_pixels +
3238+ dst_pic->p[0].i_pixel_pitch * x_offset +
3239+ dst_pic->p[0].i_pitch * y_offset;
3240+
3241+
3242+ do {
3243+ blend_fn(d_data, s_data, alpha, width);
3244+ s_data += src_pic->p[0].i_pitch;
3245+ d_data += dst_pic->p[0].i_pitch;
3246+ } while (--height > 0);
3247+}
3248+
3249+static void CloseBlendNeon(vlc_object_t *object)
3250+{
3251+ VLC_UNUSED(object);
3252+}
3253+
3254+static int OpenBlendNeon(vlc_object_t *object)
3255+{
3256+ filter_t * const p_filter = (filter_t *)object;
3257+ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
3258+ MMAL_FOURCC_T mfcc_src = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video);
3259+ MMAL_FOURCC_T mfcc_dst = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
3260+ blend_neon_fn * blend_fn = (blend_neon_fn *)0;
3261+
3262+ // Non-alpha RGB only for dest
3263+ if (vfcc_dst != VLC_CODEC_RGB32)
3264+ return VLC_EGENERIC;
3265+
3266+ // Check we have appropriate blend fn (mmal doesn't have a non-alpha RGB32)
3267+ switch (mfcc_src) {
3268+ case MMAL_ENCODING_RGBA:
3269+ if (mfcc_dst == MMAL_ENCODING_RGBA)
3270+ blend_fn = blend_rgbx_rgba_neon;
3271+ else if (mfcc_dst == MMAL_ENCODING_BGRA)
3272+ blend_fn = blend_bgrx_rgba_neon;
3273+ break;
3274+
3275+ case MMAL_ENCODING_BGRA:
3276+ if (mfcc_dst == MMAL_ENCODING_BGRA)
3277+ blend_fn = blend_rgbx_rgba_neon;
3278+ else if (mfcc_dst == MMAL_ENCODING_RGBA)
3279+ blend_fn = blend_bgrx_rgba_neon;
3280+ break;
3281+
3282+ default:
3283+ break;
3284+ }
3285+
3286+ if (blend_fn == (blend_neon_fn *)0)
3287+ {
3288+ return VLC_EGENERIC;
3289+ }
3290+
3291+ p_filter->p_sys = (void *)blend_fn;
3292+ p_filter->pf_video_blend = FilterBlendNeon;
3293+
3294+ {
3295+ char dbuf0[5], dbuf1[5];
3296+ char dbuf0a[5], dbuf1a[5];
3297+ msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %dx%d]->%s/%s,%dx%d [(%d,%d) %dx%d]", __func__,
3298+ "blend",
3299+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma),
3300+ str_fourcc(dbuf0a, mfcc_src),
3301+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
3302+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
3303+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
3304+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma),
3305+ str_fourcc(dbuf1a, mfcc_dst),
3306+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
3307+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
3308+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
3309+ }
3310+
3311+ return VLC_SUCCESS;
3312+}
3313+
3314+vlc_module_begin()
3315+ set_category( CAT_INPUT )
3316+ set_subcategory( SUBCAT_INPUT_VCODEC )
3317+ set_shortname(N_("MMAL decoder"))
3318+ set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
3319+ set_capability("video decoder", 90)
3320+ add_shortcut("mmal_decoder")
3321+ add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
3322+ set_callbacks(OpenDecoder, CloseDecoder)
3323+
3324+ add_submodule()
3325+ set_category( CAT_VIDEO )
3326+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3327+ set_shortname(N_("MMAL resizer"))
3328+ set_description(N_("MMAL resizing conversion filter"))
3329+ add_shortcut("mmal_converter")
3330+ set_capability( "video converter", 900 )
3331+ add_bool(MMAL_RESIZE_NAME, /* default */ false, MMAL_RESIZE_TEXT, MMAL_RESIZE_LONGTEXT, /* advanced option */ false)
3332+ add_bool(MMAL_ISP_NAME, /* default */ false, MMAL_ISP_TEXT, MMAL_ISP_LONGTEXT, /* advanced option */ false)
3333+ set_callbacks(OpenConverter, CloseConverter)
3334+
3335+#if OPT_TO_FROM_ZC
3336+ add_submodule()
3337+ set_category( CAT_VIDEO )
3338+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3339+ set_shortname(N_("MMAL to ZC"))
3340+ set_description(N_("MMAL conversion to ZC filter"))
3341+ add_shortcut("mmal_to_zc")
3342+ set_capability( "video converter", 901 )
3343+ set_callbacks(OpenConverterToZc, CloseConverterToZc)
3344+
3345+ add_submodule()
3346+ set_category( CAT_VIDEO )
3347+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3348+ set_shortname(N_("MMAL from ZC"))
3349+ set_description(N_("MMAL conversion from ZC filter"))
3350+ add_shortcut("mmal_from_zc")
3351+ set_capability( "video converter", 902 )
3352+ set_callbacks(OpenConverterFromZc, CloseConverterFromZc)
3353+#endif
3354+
3355+ add_submodule()
3356+ set_category( CAT_VIDEO )
3357+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3358+ set_description(N_("Video pictures blending for MMAL"))
3359+ add_shortcut("mmal_blend")
3360+ set_capability("video blending", 120)
3361+ set_callbacks(OpenBlendMmal, CloseBlendMmal)
3362+
3363+ add_submodule()
3364+ set_category( CAT_VIDEO )
3365+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3366+ set_description(N_("Video pictures blending for neon"))
3367+ add_shortcut("neon_blend")
3368+ set_capability("video blending", 110)
3369+ set_callbacks(OpenBlendNeon, CloseBlendNeon)
3370+
3371+vlc_module_end()
3372+
3373+
3374--- /dev/null
3375+++ b/modules/hw/mmal/converter_mmal.c
3376@@ -0,0 +1,479 @@
3377+#ifdef HAVE_CONFIG_H
3378+# include "config.h"
3379+#endif
3380+
3381+#include <unistd.h>
3382+#include <fcntl.h>
3383+#include <sys/ioctl.h>
3384+#include <sys/mman.h>
3385+
3386+#include <interface/vcsm/user-vcsm.h>
3387+
3388+#include <vlc_common.h>
3389+#include <vlc_picture.h>
3390+
3391+#include <libdrm/drm_fourcc.h>
3392+#include <EGL/egl.h>
3393+#include <EGL/eglext.h>
3394+#include <GLES2/gl2.h>
3395+#include <GLES2/gl2ext.h>
3396+
3397+#include "mmal_cma.h"
3398+
3399+#include "../../video_output/opengl/converter.h"
3400+
3401+#include "mmal_picture.h"
3402+
3403+#include <assert.h>
3404+
3405+#define TRACE_ALL 0
3406+
3407+typedef struct mmal_gl_converter_s
3408+{
3409+ EGLint drm_fourcc;
3410+ vcsm_init_type_t vcsm_init_type;
3411+ cma_buf_t * last_cb;
3412+
3413+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
3414+} mmal_gl_converter_t;
3415+
3416+
3417+static EGLint vlc_to_gl_fourcc(const video_format_t * const fmt)
3418+{
3419+ // Converting to mmal selects the right RGB32 varient
3420+ switch(vlc_to_mmal_video_fourcc(fmt))
3421+ {
3422+ case MMAL_ENCODING_I420:
3423+ return MMAL_FOURCC('Y','U','1','2');
3424+ case MMAL_ENCODING_YV12:
3425+ return MMAL_FOURCC('Y','V','1','2');
3426+ case MMAL_ENCODING_I422:
3427+ return MMAL_FOURCC('Y','U','1','6');
3428+// case MMAL_ENCODING_YUVUV128: // Doesn't actually work yet
3429+ case MMAL_ENCODING_NV12:
3430+ return MMAL_FOURCC('N','V','1','2');
3431+ case MMAL_ENCODING_NV21:
3432+ return MMAL_FOURCC('N','V','2','1');
3433+ case MMAL_ENCODING_RGB16:
3434+ return MMAL_FOURCC('R','G','1','6');
3435+ case MMAL_ENCODING_RGB24:
3436+ return MMAL_FOURCC('B','G','2','4');
3437+ case MMAL_ENCODING_BGR24:
3438+ return MMAL_FOURCC('R','G','2','4');
3439+ case MMAL_ENCODING_BGR32:
3440+ case MMAL_ENCODING_BGRA:
3441+ return MMAL_FOURCC('X','R','2','4');
3442+ case MMAL_ENCODING_RGB32:
3443+ case MMAL_ENCODING_RGBA:
3444+ return MMAL_FOURCC('X','B','2','4');
3445+ default:
3446+ break;
3447+ }
3448+ return 0;
3449+}
3450+
3451+typedef struct tex_context_s {
3452+ picture_context_t cmn;
3453+ GLuint texture;
3454+
3455+ PFNGLDELETETEXTURESPROC DeleteTextures; // Copy fn pointer so we don't need tc on delete
3456+} tex_context_t;
3457+
3458+static void tex_context_delete(tex_context_t * const tex)
3459+{
3460+ tex->DeleteTextures(1, &tex->texture);
3461+ free(tex);
3462+}
3463+
3464+static void tex_context_destroy(picture_context_t * pic_ctx)
3465+{
3466+ tex_context_delete((tex_context_t *)pic_ctx);
3467+}
3468+
3469+static picture_context_t * tex_context_copy(picture_context_t * pic_ctx)
3470+{
3471+ return pic_ctx;
3472+}
3473+
3474+static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic, cma_buf_t * const cb)
3475+{
3476+ mmal_gl_converter_t * const sys = tc->priv;
3477+ tex_context_t * tex = (tex_context_t *)cma_buf_context2(cb);
3478+ if (tex != NULL)
3479+ return tex;
3480+
3481+ if ((tex = malloc(sizeof(*tex))) == NULL)
3482+ return NULL;
3483+
3484+ *tex = (tex_context_t){
3485+ .cmn = {
3486+ .destroy = tex_context_destroy,
3487+ .copy = tex_context_copy
3488+ },
3489+ .texture = 0,
3490+ .DeleteTextures = tc->vt->DeleteTextures
3491+ };
3492+
3493+ {
3494+ EGLint attribs[30];
3495+ EGLint * a = attribs;
3496+ const int fd = cma_buf_fd(cb);
3497+ uint8_t * base_addr = cma_buf_addr(cb);
3498+
3499+ if (pic->i_planes >= 4 || pic->i_planes <= 0)
3500+ {
3501+ msg_Err(tc, "%s: Bad planes: %d", __func__, pic->i_planes);
3502+ goto fail;
3503+ }
3504+
3505+ *a++ = EGL_WIDTH;
3506+ *a++ = pic->format.i_visible_width;
3507+ *a++ = EGL_HEIGHT;
3508+ *a++ = pic->format.i_visible_height;
3509+ *a++ = EGL_LINUX_DRM_FOURCC_EXT;
3510+ *a++ = sys->drm_fourcc;
3511+
3512+ if (pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8)
3513+ {
3514+ // Sand is its own very special bunny :-(
3515+ static const EGLint attnames[] = {
3516+ EGL_DMA_BUF_PLANE0_FD_EXT,
3517+ EGL_DMA_BUF_PLANE0_OFFSET_EXT,
3518+ EGL_DMA_BUF_PLANE0_PITCH_EXT,
3519+ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
3520+ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
3521+ EGL_DMA_BUF_PLANE1_FD_EXT,
3522+ EGL_DMA_BUF_PLANE1_OFFSET_EXT,
3523+ EGL_DMA_BUF_PLANE1_PITCH_EXT,
3524+ EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
3525+ EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT
3526+ };
3527+
3528+ const EGLint * n = attnames;
3529+
3530+ for (int i = 0; i < pic->i_planes; ++i)
3531+ {
3532+ const uint64_t mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(pic->p[i].i_pitch >> 7);
3533+
3534+ *a++ = *n++;
3535+ *a++ = fd;
3536+ *a++ = *n++;
3537+ *a++ = pic->p[i].p_pixels - base_addr;
3538+ *a++ = *n++;
3539+ *a++ = pic->format.i_width;
3540+ *a++ = *n++;
3541+ *a++ = (EGLint)(mod >> 32);
3542+ *a++ = *n++;
3543+ *a++ = (EGLint)(mod & 0xffffffff);
3544+ }
3545+ }
3546+ else
3547+ {
3548+ static const EGLint attnames[] = {
3549+ EGL_DMA_BUF_PLANE0_FD_EXT,
3550+ EGL_DMA_BUF_PLANE0_OFFSET_EXT,
3551+ EGL_DMA_BUF_PLANE0_PITCH_EXT,
3552+ EGL_DMA_BUF_PLANE1_FD_EXT,
3553+ EGL_DMA_BUF_PLANE1_OFFSET_EXT,
3554+ EGL_DMA_BUF_PLANE1_PITCH_EXT,
3555+ EGL_DMA_BUF_PLANE2_FD_EXT,
3556+ EGL_DMA_BUF_PLANE2_OFFSET_EXT,
3557+ EGL_DMA_BUF_PLANE2_PITCH_EXT,
3558+ EGL_DMA_BUF_PLANE3_FD_EXT,
3559+ EGL_DMA_BUF_PLANE3_OFFSET_EXT,
3560+ EGL_DMA_BUF_PLANE3_PITCH_EXT
3561+ };
3562+
3563+ const EGLint * n = attnames;
3564+
3565+ for (int i = 0; i < pic->i_planes; ++i)
3566+ {
3567+ *a++ = *n++;
3568+ *a++ = fd;
3569+ *a++ = *n++;
3570+ *a++ = pic->p[i].p_pixels - base_addr;
3571+ *a++ = *n++;
3572+ *a++ = pic->p[i].i_pitch;
3573+ }
3574+ }
3575+
3576+ *a = EGL_NONE;
3577+
3578+ const EGLImage image = tc->gl->egl.createImageKHR(tc->gl, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
3579+ if (!image) {
3580+ msg_Err(tc, "Failed to import fd %d: Err=%#x", fd, tc->vt->GetError());
3581+ goto fail;
3582+ }
3583+
3584+ // ** ?? tc->tex_target
3585+ tc->vt->GenTextures(1, &tex->texture);
3586+ tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture);
3587+ tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3588+ tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3589+ sys->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
3590+
3591+ tc->gl->egl.destroyImageKHR(tc->gl, image);
3592+ }
3593+
3594+ if (cma_buf_add_context2(cb, &tex->cmn) != VLC_SUCCESS)
3595+ {
3596+ msg_Err(tc, "%s: add_context2 failed", __func__);
3597+ goto fail;
3598+ }
3599+ return tex;
3600+
3601+fail:
3602+ tex_context_delete(tex);
3603+ return NULL;
3604+}
3605+
3606+
3607+static int
3608+tc_mmal_update(const opengl_tex_converter_t *tc, GLuint *textures,
3609+ const GLsizei *tex_width, const GLsizei *tex_height,
3610+ picture_t *pic, const size_t *plane_offset)
3611+{
3612+ mmal_gl_converter_t * const sys = tc->priv;
3613+#if TRACE_ALL
3614+ {
3615+ char cbuf[5];
3616+ msg_Dbg(tc, "%s: %s %d*%dx%d : %d*%dx%d", __func__,
3617+ str_fourcc(cbuf, pic->format.i_chroma),
3618+ tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines);
3619+ }
3620+#endif
3621+ VLC_UNUSED(tex_width);
3622+ VLC_UNUSED(tex_height);
3623+ VLC_UNUSED(plane_offset);
3624+
3625+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma))
3626+ {
3627+ char cbuf[5];
3628+ msg_Err(tc, "Pic with unexpected chroma: %s", str_fourcc(cbuf, pic->format.i_chroma));
3629+ return VLC_EGENERIC;
3630+ }
3631+
3632+ cma_buf_t * const cb = cma_buf_pic_get(pic);
3633+ if (cb == NULL)
3634+ {
3635+ msg_Err(tc, "Pic missing cma buf");
3636+ return VLC_EGENERIC;
3637+ }
3638+
3639+ tex_context_t * const tex = get_tex_context(tc, pic, cb);
3640+ if (tex == NULL)
3641+ return VLC_EGENERIC;
3642+
3643+// tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture);
3644+
3645+ cma_buf_unref(sys->last_cb);
3646+ sys->last_cb = cma_buf_ref(cb);
3647+
3648+ textures[0] = tex->texture;
3649+ return VLC_SUCCESS;
3650+}
3651+
3652+static int
3653+tc_mmal_fetch_locations(opengl_tex_converter_t *tc, GLuint program)
3654+{
3655+ tc->uloc.Texture[0] = tc->vt->GetUniformLocation(program, "Texture0");
3656+ return tc->uloc.Texture[0] != -1 ? VLC_SUCCESS : VLC_EGENERIC;
3657+}
3658+
3659+static void
3660+tc_mmal_prepare_shader(const opengl_tex_converter_t *tc,
3661+ const GLsizei *tex_width, const GLsizei *tex_height,
3662+ float alpha)
3663+{
3664+ (void) tex_width; (void) tex_height; (void) alpha;
3665+ VLC_UNUSED(tc);
3666+// tc->vt->Uniform1i(tc->uloc.Texture[0], 0);
3667+}
3668+
3669+static GLuint
3670+tc_fragment_shader_init(opengl_tex_converter_t * const tc, const GLenum tex_target,
3671+ const vlc_fourcc_t chroma, const video_color_space_t yuv_space)
3672+{
3673+ VLC_UNUSED(yuv_space);
3674+
3675+ tc->tex_count = 1;
3676+ tc->tex_target = tex_target;
3677+ tc->texs[0] = (struct opengl_tex_cfg) {
3678+ { 1, 1 }, { 1, 1 }, GL_RGB, chroma, GL_UNSIGNED_SHORT //** ??
3679+ };
3680+
3681+ tc->pf_fetch_locations = tc_mmal_fetch_locations;
3682+ tc->pf_prepare_shader = tc_mmal_prepare_shader;
3683+
3684+
3685+ const char fs[] =
3686+ "#extension GL_OES_EGL_image_external : enable\n"
3687+ "precision mediump float;\n"
3688+ "uniform samplerExternalOES Texture0;\n"
3689+ "varying vec2 TexCoord0;\n"
3690+ "void main() {\n"
3691+ " gl_FragColor = texture2D(Texture0, TexCoord0);\n"
3692+ "}\n";
3693+
3694+
3695+ const char *code = fs;
3696+
3697+ GLuint fragment_shader = tc->vt->CreateShader(GL_FRAGMENT_SHADER);
3698+ tc->vt->ShaderSource(fragment_shader, 1, &code, NULL);
3699+ tc->vt->CompileShader(fragment_shader);
3700+ return fragment_shader;
3701+}
3702+
3703+
3704+static void
3705+CloseGLConverter(vlc_object_t *obj)
3706+{
3707+ opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj;
3708+ mmal_gl_converter_t * const sys = tc->priv;
3709+
3710+ if (sys == NULL)
3711+ return;
3712+
3713+ cma_buf_unref(sys->last_cb);
3714+ cma_vcsm_exit(sys->vcsm_init_type);
3715+ free(sys);
3716+}
3717+
3718+
3719+// Pick a chroma that we can convert to
3720+// Prefer I420 as smallest
3721+static vlc_fourcc_t chroma_in_out(const vlc_fourcc_t chroma_in)
3722+{
3723+ switch (chroma_in)
3724+ {
3725+ case VLC_CODEC_MMAL_OPAQUE:
3726+ case VLC_CODEC_MMAL_ZC_I420:
3727+ case VLC_CODEC_MMAL_ZC_SAND8:
3728+ case VLC_CODEC_MMAL_ZC_SAND10: // ISP only
3729+ return VLC_CODEC_MMAL_ZC_I420;
3730+ case VLC_CODEC_MMAL_ZC_SAND30: // HVS only
3731+ case VLC_CODEC_MMAL_ZC_RGB32:
3732+ return VLC_CODEC_MMAL_ZC_RGB32; // HVS can't generate YUV of any sort
3733+ default:
3734+ break;
3735+ }
3736+ return 0;
3737+}
3738+
3739+
3740+static int
3741+OpenGLConverter(vlc_object_t *obj)
3742+{
3743+ opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj;
3744+ int rv = VLC_EGENERIC;
3745+ const EGLint eglfmt = vlc_to_gl_fourcc(&tc->fmt);
3746+ const vlc_fourcc_t chroma_out = chroma_in_out(tc->fmt.i_chroma);
3747+
3748+ // Do we know what to do with this?
3749+ if (chroma_out == 0)
3750+ return rv;
3751+
3752+ {
3753+ char dbuf0[5], dbuf1[5], dbuf2[5];
3754+ msg_Dbg(tc, "<<< %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__,
3755+ str_fourcc(dbuf0, tc->fmt.i_chroma),
3756+ str_fourcc(dbuf1, eglfmt),
3757+ tc->fmt.i_width, tc->fmt.i_height,
3758+ tc->fmt.i_x_offset, tc->fmt.i_y_offset,
3759+ tc->fmt.i_visible_width, tc->fmt.i_visible_height,
3760+ tc->fmt.i_sar_num, tc->fmt.i_sar_den,
3761+ str_fourcc(dbuf2, chroma_out));
3762+ }
3763+
3764+ if (tc->gl->ext != VLC_GL_EXT_EGL ||
3765+ !tc->gl->egl.createImageKHR || !tc->gl->egl.destroyImageKHR)
3766+ {
3767+ // Missing an important callback
3768+ msg_Dbg(tc, "Missing EGL xxxImageKHR calls");
3769+ return rv;
3770+ }
3771+
3772+ if ((tc->priv = calloc(1, sizeof(mmal_gl_converter_t))) == NULL)
3773+ {
3774+ msg_Err(tc, "priv alloc failure");
3775+ rv = VLC_ENOMEM;
3776+ goto fail;
3777+ }
3778+ mmal_gl_converter_t * const sys = tc->priv;
3779+
3780+ sys->drm_fourcc = eglfmt;
3781+
3782+ if ((sys->vcsm_init_type = cma_vcsm_init()) != VCSM_INIT_CMA) {
3783+ msg_Dbg(tc, "VCSM init failed");
3784+ goto fail;
3785+ }
3786+
3787+ if ((sys->glEGLImageTargetTexture2DOES = vlc_gl_GetProcAddress(tc->gl, "glEGLImageTargetTexture2DOES")) == NULL)
3788+ {
3789+ msg_Err(tc, "Failed to bind GL fns");
3790+ goto fail;
3791+ }
3792+
3793+ if ((tc->fshader = tc_fragment_shader_init(tc, GL_TEXTURE_EXTERNAL_OES,
3794+ eglfmt == 0 ? VLC_CODEC_RGB32 : tc->fmt.i_chroma,
3795+ eglfmt == 0 ? COLOR_SPACE_SRGB : tc->fmt.space)) == 0)
3796+ {
3797+ msg_Err(tc, "Failed to make shader");
3798+ goto fail;
3799+ }
3800+
3801+ if (eglfmt == 0)
3802+ {
3803+ tc->fmt.i_chroma = chroma_out;
3804+ tc->fmt.i_bits_per_pixel = 8;
3805+ if (tc->fmt.i_chroma == VLC_CODEC_MMAL_ZC_RGB32)
3806+ {
3807+ tc->fmt.i_rmask = 0xff0000;
3808+ tc->fmt.i_gmask = 0xff00;
3809+ tc->fmt.i_bmask = 0xff;
3810+ tc->fmt.space = COLOR_SPACE_SRGB;
3811+ }
3812+ else
3813+ {
3814+ tc->fmt.i_rmask = 0;
3815+ tc->fmt.i_gmask = 0;
3816+ tc->fmt.i_bmask = 0;
3817+ tc->fmt.space = COLOR_SPACE_UNDEF;
3818+ }
3819+ sys->drm_fourcc = vlc_to_gl_fourcc(&tc->fmt);
3820+ }
3821+
3822+ tc->handle_texs_gen = true; // We manage the texs
3823+ tc->pf_update = tc_mmal_update;
3824+
3825+#if TRACE_ALL
3826+ {
3827+ char dbuf0[5], dbuf1[5], dbuf2[5];
3828+ msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__,
3829+ str_fourcc(dbuf0, tc->fmt.i_chroma),
3830+ str_fourcc(dbuf1, sys->drm_fourcc),
3831+ tc->fmt.i_width, tc->fmt.i_height,
3832+ tc->fmt.i_x_offset, tc->fmt.i_y_offset,
3833+ tc->fmt.i_visible_width, tc->fmt.i_visible_height,
3834+ tc->fmt.i_sar_num, tc->fmt.i_sar_den,
3835+ str_fourcc(dbuf2, chroma_out));
3836+ }
3837+#endif
3838+
3839+ return VLC_SUCCESS;
3840+
3841+fail:
3842+ CloseGLConverter(obj);
3843+ return rv;
3844+}
3845+
3846+vlc_module_begin ()
3847+ set_description("MMAL OpenGL surface converter")
3848+ set_shortname (N_("MMALGLConverter"))
3849+ set_capability("glconv", 900)
3850+ set_callbacks(OpenGLConverter, CloseGLConverter)
3851+ set_category(CAT_VIDEO)
3852+ set_subcategory(SUBCAT_VIDEO_VOUT)
3853+ add_shortcut("mmal_gl_converter")
3854+vlc_module_end ()
3855+
3856--- a/modules/hw/mmal/deinterlace.c
3857+++ b/modules/hw/mmal/deinterlace.c
3858@@ -26,11 +26,12 @@
3859 #include "config.h"
3860 #endif
3861
3862-#include <vlc_picture_pool.h>
3863+#include <stdatomic.h>
3864+
3865 #include <vlc_common.h>
3866+#include <vlc_picture_pool.h>
3867 #include <vlc_plugin.h>
3868 #include <vlc_filter.h>
3869-#include <vlc_atomic.h>
3870
3871 #include "mmal_picture.h"
3872
3873@@ -39,468 +40,814 @@
3874 #include <interface/mmal/util/mmal_util.h>
3875 #include <interface/mmal/util/mmal_default_components.h>
3876
3877-#define MIN_NUM_BUFFERS_IN_TRANSIT 2
3878+#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu"
3879+#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.")
3880+#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.")
3881
3882-#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu"
3883-#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.")
3884-#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.")
3885+#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv"
3886+#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace")
3887+#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace")
3888
3889-static int Open(filter_t *filter);
3890-static void Close(filter_t *filter);
3891+#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast"
3892+#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace")
3893+#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace")
3894
3895-vlc_module_begin()
3896- set_shortname(N_("MMAL deinterlace"))
3897- set_description(N_("MMAL-based deinterlace filter plugin"))
3898- set_capability("video filter", 0)
3899- set_category(CAT_VIDEO)
3900- set_subcategory(SUBCAT_VIDEO_VFILTER)
3901- set_callbacks(Open, Close)
3902- add_shortcut("deinterlace")
3903- add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT,
3904- MMAL_DEINTERLACE_QPU_LONGTEXT, true);
3905-vlc_module_end()
3906+#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none"
3907+#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace")
3908+#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\
3909+ "This is the default for > SD if < 96M gpu-mem")
3910+
3911+#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate"
3912+#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate")
3913+#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input")
3914+
3915+#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate"
3916+#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate")
3917+#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input")
3918
3919-struct filter_sys_t {
3920+
3921+typedef struct filter_sys_t
3922+{
3923 MMAL_COMPONENT_T *component;
3924 MMAL_PORT_T *input;
3925 MMAL_PORT_T *output;
3926+ MMAL_POOL_T *in_pool;
3927+
3928+ MMAL_QUEUE_T * out_q;
3929+
3930+ // Bind this lot somehow into ppr????
3931+ bool is_cma;
3932+ cma_buf_pool_t * cma_out_pool;
3933+ MMAL_POOL_T * out_pool;
3934+
3935+ hw_mmal_port_pool_ref_t *out_ppr;
3936+
3937+ bool half_rate;
3938+ bool use_qpu;
3939+ bool use_fast;
3940+ bool use_passthrough;
3941+ unsigned int seq_in; // Seq of next frame to submit (1-15) [Init=1]
3942+ unsigned int seq_out; // Seq of last frame received (1-15) [Init=15]
3943
3944- MMAL_QUEUE_T *filtered_pictures;
3945- vlc_sem_t sem;
3946+ vcsm_init_type_t vcsm_init_type;
3947
3948- atomic_bool started;
3949+} filter_sys_t;
3950
3951- /* statistics */
3952- int output_in_transit;
3953- int input_in_transit;
3954-};
3955-
3956-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
3957-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
3958-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
3959-static picture_t *deinterlace(filter_t *filter, picture_t *picture);
3960-static void flush(filter_t *filter);
3961
3962 #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
3963
3964-static int Open(filter_t *filter)
3965+#define TRACE_ALL 0
3966+
3967+
3968+
3969+// Buffer attached to pic on success, is still valid on failure
3970+static picture_t * di_alloc_opaque(filter_t * const p_filter, MMAL_BUFFER_HEADER_T * const buf)
3971 {
3972- int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
3973- (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base /
3974- filter->fmt_in.video.i_frame_rate : 0;
3975- bool use_qpu = var_InheritBool(filter, MMAL_DEINTERLACE_QPU);
3976+ filter_sys_t *const filter_sys = p_filter->p_sys;
3977+ picture_t * const pic = filter_NewPicture(p_filter);
3978
3979- MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
3980- { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
3981- MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
3982- 4,
3983- { 3, frame_duration, 0, use_qpu }
3984- };
3985+ if (pic == NULL)
3986+ goto fail1;
3987
3988- int ret = VLC_SUCCESS;
3989- MMAL_STATUS_T status;
3990- filter_sys_t *sys;
3991+ if (buf->length == 0) {
3992+ msg_Err(p_filter, "%s: Empty buffer", __func__);
3993+ goto fail2;
3994+ }
3995
3996- msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!",
3997- frame_duration, use_qpu ? "used" : "unused");
3998+ if ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL)
3999+ goto fail2;
4000
4001- if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
4002- return VLC_EGENERIC;
4003+ buf_to_pic_copy_props(pic, buf);
4004
4005- if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
4006- return VLC_EGENERIC;
4007+#if TRACE_ALL
4008+ msg_Dbg(p_filter, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
4009+#endif
4010
4011- sys = calloc(1, sizeof(filter_sys_t));
4012- if (!sys)
4013- return VLC_ENOMEM;
4014- filter->p_sys = sys;
4015+ return pic;
4016
4017- bcm_host_init();
4018+fail2:
4019+ picture_Release(pic);
4020+fail1:
4021+// mmal_buffer_header_release(buf);
4022+ return NULL;
4023+}
4024
4025- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
4026- if (status != MMAL_SUCCESS) {
4027- msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
4028- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4029- ret = VLC_EGENERIC;
4030- goto out;
4031- }
4032+static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4033+{
4034+#if TRACE_ALL
4035+ pic_ctx_mmal_t * ctx = buffer->user_data;
4036+// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
4037+
4038+ msg_Dbg((filter_t *)port->userdata, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buffer->cmd, ctx, buffer,
4039+ buffer->flags, (long long)buffer->pts);
4040+#else
4041+ VLC_UNUSED(port);
4042+#endif
4043
4044- status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
4045- if (status != MMAL_SUCCESS) {
4046- msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
4047- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4048- ret = VLC_EGENERIC;
4049- goto out;
4050- }
4051+ mmal_buffer_header_release(buffer);
4052
4053- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4054- status = mmal_port_enable(sys->component->control, control_port_cb);
4055- if (status != MMAL_SUCCESS) {
4056- msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
4057- sys->component->control->name, status, mmal_status_to_string(status));
4058- ret = VLC_EGENERIC;
4059- goto out;
4060+#if TRACE_ALL
4061+ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
4062+#endif
4063+}
4064+
4065+static void di_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
4066+{
4067+ if (buf->cmd == 0 && buf->length != 0)
4068+ {
4069+ // The filter structure etc. should always exist if we have contents
4070+ // but might not on later flushes as we shut down
4071+ filter_t * const p_filter = (filter_t *)port->userdata;
4072+ filter_sys_t * const sys = p_filter->p_sys;
4073+
4074+#if TRACE_ALL
4075+ msg_Dbg(p_filter, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts);
4076+#endif
4077+ mmal_queue_put(sys->out_q, buf);
4078+#if TRACE_ALL
4079+ msg_Dbg(p_filter, ">>> %s: out Q len=%d", __func__, mmal_queue_length(sys->out_q));
4080+#endif
4081+ return;
4082 }
4083
4084- sys->input = sys->component->input[0];
4085- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4086- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
4087- sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
4088- sys->input->format->es->video.width = filter->fmt_in.video.i_width;
4089- sys->input->format->es->video.height = filter->fmt_in.video.i_height;
4090- sys->input->format->es->video.crop.x = 0;
4091- sys->input->format->es->video.crop.y = 0;
4092- sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width;
4093- sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height;
4094- sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num;
4095- sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den;
4096+ mmal_buffer_header_reset(buf); // User data stays intact so release will kill pic
4097+ mmal_buffer_header_release(buf);
4098+}
4099
4100- es_format_Copy(&filter->fmt_out, &filter->fmt_in);
4101- filter->fmt_out.video.i_frame_rate *= 2;
4102
4103- status = mmal_port_format_commit(sys->input);
4104- if (status != MMAL_SUCCESS) {
4105- msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
4106- sys->input->name, status, mmal_status_to_string(status));
4107- ret = VLC_EGENERIC;
4108- goto out;
4109- }
4110- sys->input->buffer_size = sys->input->buffer_size_recommended;
4111- sys->input->buffer_num = sys->input->buffer_num_recommended;
4112
4113- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
4114- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
4115- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
4116- 1
4117- };
4118+static MMAL_STATUS_T fill_output_from_q(filter_t * const p_filter, filter_sys_t * const sys, MMAL_QUEUE_T * const q)
4119+{
4120+ MMAL_BUFFER_HEADER_T * out_buf;
4121
4122- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
4123- if (status != MMAL_SUCCESS) {
4124- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
4125- sys->input->name, status, mmal_status_to_string(status));
4126- goto out;
4127+ while ((out_buf = mmal_queue_get(q)) != NULL)
4128+ {
4129+ MMAL_STATUS_T err;
4130+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
4131+ {
4132+ msg_Err(p_filter, "Send buffer to output failed");
4133+ mmal_queue_put_back(q, out_buf);
4134+ return err;
4135 }
4136 }
4137+ return MMAL_SUCCESS;
4138+}
4139
4140- status = mmal_port_enable(sys->input, input_port_cb);
4141- if (status != MMAL_SUCCESS) {
4142- msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
4143- sys->input->name, status, mmal_status_to_string(status));
4144- ret = VLC_EGENERIC;
4145- goto out;
4146- }
4147+// Output buffers may contain a pic ref on error or flush
4148+// Free it
4149+static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
4150+{
4151+ VLC_UNUSED(userdata);
4152
4153- sys->output = sys->component->output[0];
4154- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4155- mmal_format_full_copy(sys->output->format, sys->input->format);
4156+ cma_buf_t * const cb = header->user_data;
4157+ header->user_data = NULL;
4158+ cma_buf_unref(cb); // Copes fine with NULL
4159
4160- status = mmal_port_format_commit(sys->output);
4161- if (status != MMAL_SUCCESS) {
4162- msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
4163- sys->input->name, status, mmal_status_to_string(status));
4164- ret = VLC_EGENERIC;
4165- goto out;
4166+ return MMAL_FALSE;
4167+}
4168+
4169+static inline unsigned int seq_inc(unsigned int x)
4170+{
4171+ return x + 1 >= 16 ? 1 : x + 1;
4172+}
4173+
4174+static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq)
4175+{
4176+ return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq);
4177+}
4178+
4179+static picture_t *deinterlace(filter_t * p_filter, picture_t * p_pic)
4180+{
4181+ filter_sys_t * const sys = p_filter->p_sys;
4182+ picture_t *ret_pics = NULL;
4183+ MMAL_STATUS_T err;
4184+ MMAL_BUFFER_HEADER_T * out_buf = NULL;
4185+
4186+#if TRACE_ALL
4187+ msg_Dbg(p_filter, "<<< %s", __func__);
4188+#endif
4189+
4190+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
4191+ {
4192+ // ****** Breaks on opaque (at least)
4193+
4194+ if (sys->input->is_enabled)
4195+ mmal_port_disable(sys->input);
4196+#if 0
4197+ if (sys->output->is_enabled)
4198+ mmal_port_disable(sys->output);
4199+
4200+ mmal_format_full_copy(sys->output->format, sys->input->format);
4201+ mmal_port_format_commit(sys->output);
4202+ sys->output->buffer_num = 30;
4203+ sys->output->buffer_size = sys->input->buffer_size_recommended;
4204+ mmal_port_enable(sys->output, di_output_port_cb);
4205+#endif
4206+ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
4207+ msg_Err(p_filter, "Failed to update pic format");
4208+ sys->input->buffer_num = 30;
4209+ sys->input->buffer_size = sys->input->buffer_size_recommended;
4210+ mmal_log_dump_format(sys->input->format);
4211+ }
4212+
4213+ // Reenable stuff if the last thing we did was flush
4214+ // Output should always be enabled
4215+ if (!sys->input->is_enabled &&
4216+ (err = mmal_port_enable(sys->input, di_input_port_cb)) != MMAL_SUCCESS)
4217+ {
4218+ msg_Err(p_filter, "Input port reenable failed");
4219+ goto fail;
4220+ }
4221+
4222+ if (!sys->is_cma)
4223+ {
4224+ // Fill output from anything that has turned up in pool Q
4225+ if (hw_mmal_port_pool_ref_fill(sys->out_ppr) != MMAL_SUCCESS)
4226+ {
4227+ msg_Err(p_filter, "Out port fill fail");
4228+ goto fail;
4229+ }
4230 }
4231+ else
4232+ {
4233+ // We are expecting one in - one out so simply wedge a new bufer
4234+ // into the output port. Flow control will happen on cma alloc.
4235+
4236+ if ((out_buf = mmal_queue_get(sys->out_pool->queue)) == NULL)
4237+ {
4238+ // Should never happen
4239+ msg_Err(p_filter, "Failed to get output buffer");
4240+ goto fail;
4241+ }
4242+ mmal_buffer_header_reset(out_buf);
4243
4244- sys->output->buffer_num = 3;
4245+ // Attach cma_buf to the buffer & ensure it is freed when the buffer is released
4246+ // On a good send callback the pic will be extracted to avoid this
4247+ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, p_filter);
4248+
4249+ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size);
4250+ if ((out_buf->user_data = cb) == NULL) // Check & attach cb to buf
4251+ {
4252+ char dbuf0[5];
4253+ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d",
4254+ str_fourcc(dbuf0, p_pic->format.i_chroma),
4255+ sys->output->buffer_size);
4256+ goto fail;
4257+ }
4258+ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable
4259+ out_buf->data = (uint8_t *)vc_h;
4260+ out_buf->alloc_size = sys->output->buffer_size;
4261+
4262+#if TRACE_ALL
4263+ msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld",
4264+ p_pic, out_buf->data, out_buf->user_data, out_buf->flags,
4265+ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts);
4266+#endif
4267
4268- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
4269- MMAL_PARAMETER_UINT32_T extra_buffers = {
4270- { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) },
4271- 5
4272- };
4273- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
4274- if (status != MMAL_SUCCESS) {
4275- msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
4276- status, mmal_status_to_string(status));
4277- goto out;
4278+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
4279+ {
4280+ msg_Err(p_filter, "Send buffer to output failed");
4281+ goto fail;
4282 }
4283+ out_buf = NULL;
4284+ }
4285
4286- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
4287- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
4288- 1
4289- };
4290+ // Stuff into input
4291+ // We assume the BH is already set up with values reflecting pic date etc.
4292+ {
4293+ MMAL_BUFFER_HEADER_T * const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
4294+
4295+ if (pic_buf == NULL)
4296+ {
4297+ msg_Err(p_filter, "Pic has not attached buffer");
4298+ goto fail;
4299+ }
4300
4301- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
4302- if (status != MMAL_SUCCESS) {
4303- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
4304- sys->output->name, status, mmal_status_to_string(status));
4305- goto out;
4306+ picture_Release(p_pic);
4307+
4308+ // Add a sequence to the flags so we can track what we have actually
4309+ // deinterlaced
4310+ pic_buf->flags = (pic_buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0));
4311+ sys->seq_in = seq_inc(sys->seq_in);
4312+
4313+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
4314+ {
4315+ msg_Err(p_filter, "Send buffer to input failed");
4316+ mmal_buffer_header_release(pic_buf);
4317+ goto fail;
4318 }
4319 }
4320
4321- status = mmal_port_enable(sys->output, output_port_cb);
4322- if (status != MMAL_SUCCESS) {
4323- msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
4324- sys->output->name, status, mmal_status_to_string(status));
4325- ret = VLC_EGENERIC;
4326- goto out;
4327+ // Return anything that is in the out Q
4328+ {
4329+ picture_t ** pp_pic = &ret_pics;
4330+
4331+ // Advanced di has a 3 frame latency, so if the seq delta is greater
4332+ // than that then we are expecting at least two frames of output. Wait
4333+ // for one of those.
4334+ // seq_in is seq of the next frame we are going to submit (1-15, no 0)
4335+ // seq_out is last frame we removed from Q
4336+ // So after 4 frames sent (1st time we want to wait), 0 rx seq_in=5, seq_out=15, delta=5
4337+
4338+ 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)
4339+ {
4340+ const unsigned int seq_out = (out_buf->flags / MMAL_BUFFER_HEADER_FLAG_USER0) & 0xf;
4341+ int rv;
4342+
4343+ picture_t * out_pic;
4344+
4345+ if (sys->is_cma)
4346+ {
4347+ // Alloc pic
4348+ if ((out_pic = filter_NewPicture(p_filter)) == NULL)
4349+ {
4350+ // Can't alloc pic - just stop extraction
4351+ mmal_queue_put_back(sys->out_q, out_buf);
4352+ out_buf = NULL;
4353+ msg_Warn(p_filter, "Failed to alloc new filter output pic");
4354+ break;
4355+ }
4356+
4357+ // Extract cma_buf from buf & attach to pic
4358+ cma_buf_t * const cb = (cma_buf_t *)out_buf->user_data;
4359+ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS)
4360+ {
4361+ char dbuf0[5];
4362+ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d",
4363+ str_fourcc(dbuf0, out_pic->format.i_chroma),
4364+ rv);
4365+ // cb still attached to buffer and will be freed with it
4366+ goto fail;
4367+ }
4368+ out_buf->user_data = NULL;
4369+
4370+ buf_to_pic_copy_props(out_pic, out_buf);
4371+
4372+ // Set pic data pointers from buf aux info now it has it
4373+ if ((rv = cma_pic_set_data(out_pic, sys->output->format, out_buf)) != VLC_SUCCESS)
4374+ {
4375+ char dbuf0[5];
4376+ msg_Err(p_filter, "Failed to set data: fmt=%s, rv=%d",
4377+ str_fourcc(dbuf0, sys->output->format->encoding),
4378+ rv);
4379+ }
4380+
4381+ out_buf->user_data = NULL; // Responsability for this pic no longer with buffer
4382+ mmal_buffer_header_release(out_buf);
4383+ }
4384+ else
4385+ {
4386+ out_pic = di_alloc_opaque(p_filter, out_buf);
4387+
4388+ if (out_pic == NULL) {
4389+ msg_Warn(p_filter, "Failed to alloc new filter output pic");
4390+ mmal_queue_put_back(sys->out_q, out_buf); // Wedge buf back into Q in the hope we can alloc a pic later
4391+ out_buf = NULL;
4392+ break;
4393+ }
4394+ }
4395+ out_buf = NULL; // Now attached to pic or recycled
4396+
4397+#if TRACE_ALL
4398+ 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));
4399+#endif
4400+
4401+ *pp_pic = out_pic;
4402+ pp_pic = &out_pic->p_next;
4403+
4404+ // Ignore 0 seqs
4405+ // Don't think these should actually happen
4406+ if (seq_out != 0)
4407+ sys->seq_out = seq_out;
4408+ }
4409+
4410+ // Crash on lockup
4411+ assert(ret_pics != NULL || seq_delta(sys->seq_in, sys->seq_out) < 5);
4412 }
4413
4414- status = mmal_component_enable(sys->component);
4415- if (status != MMAL_SUCCESS) {
4416- msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
4417- sys->component->name, status, mmal_status_to_string(status));
4418- ret = VLC_EGENERIC;
4419- goto out;
4420+#if TRACE_ALL
4421+ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
4422+#endif
4423+
4424+ return ret_pics;
4425+
4426+fail:
4427+ if (out_buf != NULL)
4428+ mmal_buffer_header_release(out_buf);
4429+ picture_Release(p_pic);
4430+ return NULL;
4431+}
4432+
4433+static void di_flush(filter_t *p_filter)
4434+{
4435+ filter_sys_t * const sys = p_filter->p_sys;
4436+
4437+#if TRACE_ALL
4438+ msg_Dbg(p_filter, "<<< %s", __func__);
4439+#endif
4440+
4441+ if (sys->input != NULL && sys->input->is_enabled)
4442+ mmal_port_disable(sys->input);
4443+
4444+ if (sys->output != NULL && sys->output->is_enabled)
4445+ {
4446+ if (sys->is_cma)
4447+ {
4448+ MMAL_BUFFER_HEADER_T * buf;
4449+ mmal_port_disable(sys->output);
4450+ while ((buf = mmal_queue_get(sys->out_q)) != NULL)
4451+ mmal_buffer_header_release(buf);
4452+ }
4453+ else
4454+ {
4455+ // Wedge anything we've got into the output port as that will free the underlying buffers
4456+ fill_output_from_q(p_filter, sys, sys->out_q);
4457+
4458+ mmal_port_disable(sys->output);
4459+
4460+ // If that dumped anything real into the out_q then have another go
4461+ if (mmal_queue_length(sys->out_q) != 0)
4462+ {
4463+ mmal_port_enable(sys->output, di_output_port_cb);
4464+ fill_output_from_q(p_filter, sys, sys->out_q);
4465+ mmal_port_disable(sys->output);
4466+ // Out q should now be empty & should remain so until the input is reenabled
4467+ }
4468+ }
4469+ mmal_port_enable(sys->output, di_output_port_cb);
4470+
4471+ // Leaving the input disabled is fine - but we want to leave the output enabled
4472+ // so we can retrieve buffers that are still bound to pictures
4473 }
4474
4475- sys->filtered_pictures = mmal_queue_create();
4476+ sys->seq_in = 1;
4477+ sys->seq_out = 15;
4478
4479- filter->pf_video_filter = deinterlace;
4480- filter->pf_flush = flush;
4481+#if TRACE_ALL
4482+ msg_Dbg(p_filter, ">>> %s", __func__);
4483+#endif
4484+}
4485
4486- vlc_sem_init(&sys->sem, 0);
4487
4488-out:
4489- if (ret != VLC_SUCCESS)
4490- Close(filter);
4491+static void pass_flush(filter_t *p_filter)
4492+{
4493+ // Nothing to do
4494+ VLC_UNUSED(p_filter);
4495+}
4496
4497- return ret;
4498+static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic)
4499+{
4500+ VLC_UNUSED(p_filter);
4501+
4502+ p_pic->b_progressive = true;
4503+ return p_pic;
4504 }
4505
4506-static void Close(filter_t *filter)
4507+
4508+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4509 {
4510- filter_sys_t *sys = filter->p_sys;
4511- MMAL_BUFFER_HEADER_T *buffer;
4512+ filter_t *filter = (filter_t *)port->userdata;
4513+ MMAL_STATUS_T status;
4514
4515- if (!sys)
4516+ if (buffer->cmd == MMAL_EVENT_ERROR) {
4517+ status = *(uint32_t *)buffer->data;
4518+ msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
4519+ mmal_status_to_string(status));
4520+ }
4521+
4522+ mmal_buffer_header_reset(buffer);
4523+ mmal_buffer_header_release(buffer);
4524+}
4525+
4526+static void CloseMmalDeinterlace(filter_t *filter)
4527+{
4528+ filter_sys_t * const sys = filter->p_sys;
4529+
4530+#if TRACE_ALL
4531+ msg_Dbg(filter, "<<< %s", __func__);
4532+#endif
4533+
4534+ if (sys == NULL)
4535 return;
4536
4537- if (sys->component && sys->component->control->is_enabled)
4538- mmal_port_disable(sys->component->control);
4539+ if (sys->use_passthrough)
4540+ {
4541+ free(sys);
4542+ return;
4543+ }
4544
4545- if (sys->input && sys->input->is_enabled)
4546- mmal_port_disable(sys->input);
4547+ di_flush(filter);
4548
4549- if (sys->output && sys->output->is_enabled)
4550- mmal_port_disable(sys->output);
4551+ if (sys->component && sys->component->control->is_enabled)
4552+ mmal_port_disable(sys->component->control);
4553
4554 if (sys->component && sys->component->is_enabled)
4555 mmal_component_disable(sys->component);
4556
4557- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
4558- picture_t *pic = (picture_t *)buffer->user_data;
4559- picture_Release(pic);
4560+ if (sys->in_pool != NULL)
4561+ mmal_pool_destroy(sys->in_pool);
4562+
4563+ hw_mmal_port_pool_ref_release(sys->out_ppr, false);
4564+ // Once we exit filter & sys are invalid so mark as such
4565+ if (sys->output != NULL)
4566+ sys->output->userdata = NULL;
4567+
4568+ if (sys->is_cma)
4569+ {
4570+ if (sys->output && sys->output->is_enabled)
4571+ mmal_port_disable(sys->output);
4572+
4573+ cma_buf_pool_deletez(&sys->cma_out_pool);
4574+
4575+ if (sys->out_pool != NULL)
4576+ mmal_pool_destroy(sys->out_pool);
4577 }
4578
4579- if (sys->filtered_pictures)
4580- mmal_queue_destroy(sys->filtered_pictures);
4581+ if (sys->out_q != NULL)
4582+ mmal_queue_destroy(sys->out_q);
4583
4584 if (sys->component)
4585 mmal_component_release(sys->component);
4586
4587- vlc_sem_destroy(&sys->sem);
4588+ cma_vcsm_exit(sys->vcsm_init_type);
4589+
4590 free(sys);
4591+}
4592+
4593
4594- bcm_host_deinit();
4595+static bool is_fmt_valid_in(const vlc_fourcc_t fmt)
4596+{
4597+ return fmt == VLC_CODEC_MMAL_OPAQUE ||
4598+ fmt == VLC_CODEC_MMAL_ZC_I420 ||
4599+ fmt == VLC_CODEC_MMAL_ZC_SAND8;
4600 }
4601
4602-static int send_output_buffer(filter_t *filter)
4603+static int OpenMmalDeinterlace(filter_t *filter)
4604 {
4605- filter_sys_t *sys = filter->p_sys;
4606- MMAL_BUFFER_HEADER_T *buffer;
4607+ int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
4608+ CLOCK_FREQ * filter->fmt_in.video.i_frame_rate_base /
4609+ filter->fmt_in.video.i_frame_rate : 0;
4610+
4611+ int ret = VLC_EGENERIC;
4612 MMAL_STATUS_T status;
4613- picture_t *picture;
4614- int ret = 0;
4615+ filter_sys_t *sys;
4616+
4617+ msg_Dbg(filter, "<<< %s", __func__);
4618+
4619+ if (!is_fmt_valid_in(filter->fmt_in.video.i_chroma) ||
4620+ filter->fmt_out.video.i_chroma != filter->fmt_in.video.i_chroma)
4621+ return VLC_EGENERIC;
4622
4623- if (!sys->output->is_enabled) {
4624- ret = VLC_EGENERIC;
4625- goto out;
4626+ sys = calloc(1, sizeof(filter_sys_t));
4627+ if (!sys)
4628+ return VLC_ENOMEM;
4629+ filter->p_sys = sys;
4630+
4631+ sys->seq_in = 1;
4632+ sys->seq_out = 15;
4633+ sys->is_cma = is_cma_buf_pic_chroma(filter->fmt_out.video.i_chroma);
4634+
4635+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
4636+ msg_Err(filter, "VCSM init failed");
4637+ goto fail;
4638+ }
4639+
4640+ if (rpi_is_model_pi4())
4641+ {
4642+ sys->half_rate = true;
4643+ sys->use_qpu = false;
4644+ sys->use_fast = true;
4645+ }
4646+ else
4647+ {
4648+ sys->half_rate = false;
4649+ sys->use_qpu = true;
4650+ sys->use_fast = false;
4651+ }
4652+ sys->use_passthrough = false;
4653+
4654+ if (filter->fmt_in.video.i_width * filter->fmt_in.video.i_height > 768 * 576)
4655+ {
4656+ // We get stressed if we have to try too hard - so make life easier
4657+ sys->half_rate = true;
4658+ // Also check we actually have enough memory to do this
4659+ // Memory always comes from GPU if Opaque
4660+ // Assume we have plenty of memory if it comes from CMA
4661+ if ((!sys->is_cma || sys->vcsm_init_type == VCSM_INIT_LEGACY) &&
4662+ hw_mmal_get_gpu_mem() < (96 << 20))
4663+ {
4664+ sys->use_passthrough = true;
4665+ msg_Warn(filter, "Deinterlace bypassed due to lack of GPU memory");
4666+ }
4667 }
4668
4669- picture = filter_NewPicture(filter);
4670- if (!picture) {
4671- msg_Warn(filter, "Failed to get new picture");
4672- ret = -1;
4673- goto out;
4674+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU))
4675+ sys->use_qpu = false;
4676+ if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV))
4677+ {
4678+ sys->use_fast = false;
4679+ sys->use_passthrough = false;
4680+ }
4681+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FAST))
4682+ {
4683+ sys->use_fast = true;
4684+ sys->use_passthrough = false;
4685+ }
4686+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NONE))
4687+ sys->use_passthrough = true;
4688+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FULL_RATE))
4689+ sys->half_rate = false;
4690+ if (var_InheritBool(filter, MMAL_DEINTERLACE_HALF_RATE))
4691+ sys->half_rate = true;
4692+
4693+ if (sys->use_passthrough)
4694+ {
4695+ filter->pf_video_filter = pass_deinterlace;
4696+ filter->pf_flush = pass_flush;
4697+ // Don't need VCSM - get rid of it now
4698+ cma_vcsm_exit(sys->vcsm_init_type);
4699+ sys->vcsm_init_type = VCSM_INIT_NONE;
4700+ return 0;
4701+ }
4702+
4703+ {
4704+ char dbuf0[5], dbuf1[5];
4705+ msg_Dbg(filter, "%s: %s,%dx%d [(%d,%d) %d/%d] -> %s,%dx%d [(%d,%d) %dx%d]: %s %s %s", __func__,
4706+ str_fourcc(dbuf0, filter->fmt_in.video.i_chroma),
4707+ filter->fmt_in.video.i_width, filter->fmt_in.video.i_height,
4708+ filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset,
4709+ filter->fmt_in.video.i_visible_width, filter->fmt_in.video.i_visible_height,
4710+ str_fourcc(dbuf1, filter->fmt_out.video.i_chroma),
4711+ filter->fmt_out.video.i_width, filter->fmt_out.video.i_height,
4712+ filter->fmt_out.video.i_x_offset, filter->fmt_out.video.i_y_offset,
4713+ filter->fmt_out.video.i_visible_width, filter->fmt_out.video.i_visible_height,
4714+ sys->use_qpu ? "QPU" : "VPU",
4715+ sys->use_fast ? "FAST" : "ADV",
4716+ sys->use_passthrough ? "PASS" : sys->half_rate ? "HALF" : "FULL");
4717 }
4718- picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate;
4719- picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base;
4720
4721- buffer = picture->p_sys->buffer;
4722- buffer->user_data = picture;
4723- buffer->cmd = 0;
4724+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
4725+ if (status != MMAL_SUCCESS) {
4726+ msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
4727+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4728+ goto fail;
4729+ }
4730
4731- mmal_picture_lock(picture);
4732+ {
4733+ const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
4734+ { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
4735+ sys->use_fast ?
4736+ MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST :
4737+ MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
4738+ 4,
4739+ { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu }
4740+ };
4741
4742- status = mmal_port_send_buffer(sys->output, buffer);
4743+ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
4744+ if (status != MMAL_SUCCESS) {
4745+ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
4746+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4747+ goto fail;
4748+ }
4749+ }
4750+
4751+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4752+ status = mmal_port_enable(sys->component->control, control_port_cb);
4753 if (status != MMAL_SUCCESS) {
4754- msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)",
4755- status, mmal_status_to_string(status));
4756- mmal_buffer_header_release(buffer);
4757- picture_Release(picture);
4758- ret = -1;
4759- } else {
4760- atomic_fetch_add(&sys->output_in_transit, 1);
4761- vlc_sem_post(&sys->sem);
4762+ msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
4763+ sys->component->control->name, status, mmal_status_to_string(status));
4764+ goto fail;
4765 }
4766
4767-out:
4768- return ret;
4769-}
4770+ sys->input = sys->component->input[0];
4771+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4772+ sys->input->format->encoding = vlc_to_mmal_video_fourcc(&filter->fmt_in.video);
4773+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &filter->fmt_in.video);
4774
4775-static void fill_output_port(filter_t *filter)
4776-{
4777- filter_sys_t *sys = filter->p_sys;
4778- /* allow at least 2 buffers in transit */
4779- unsigned max_buffers_in_transit = __MAX(2, MIN_NUM_BUFFERS_IN_TRANSIT);
4780- int buffers_available = sys->output->buffer_num -
4781- atomic_load(&sys->output_in_transit) -
4782- mmal_queue_length(sys->filtered_pictures);
4783- int buffers_to_send = max_buffers_in_transit - sys->output_in_transit;
4784- int i;
4785+ es_format_Copy(&filter->fmt_out, &filter->fmt_in);
4786+ if (!sys->half_rate)
4787+ filter->fmt_out.video.i_frame_rate *= 2;
4788
4789- if (buffers_to_send > buffers_available)
4790- buffers_to_send = buffers_available;
4791+ status = mmal_port_format_commit(sys->input);
4792+ if (status != MMAL_SUCCESS) {
4793+ msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
4794+ sys->input->name, status, mmal_status_to_string(status));
4795+ goto fail;
4796+ }
4797+ sys->input->buffer_size = sys->input->buffer_size_recommended;
4798+ sys->input->buffer_num = 30;
4799+// sys->input->buffer_num = sys->input->buffer_num_recommended;
4800
4801-#ifndef NDEBUG
4802- msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
4803- buffers_to_send, buffers_available, sys->output_in_transit,
4804- sys->output->buffer_num);
4805-#endif
4806- for (i = 0; i < buffers_to_send; ++i) {
4807- if (send_output_buffer(filter) < 0)
4808- break;
4809+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
4810+ {
4811+ msg_Err(filter, "Failed to create input pool");
4812+ goto fail;
4813 }
4814-}
4815
4816-static picture_t *deinterlace(filter_t *filter, picture_t *picture)
4817-{
4818- filter_sys_t *sys = filter->p_sys;
4819- MMAL_BUFFER_HEADER_T *buffer;
4820- picture_t *out_picture = NULL;
4821- picture_t *ret = NULL;
4822- MMAL_STATUS_T status;
4823- unsigned i = 0;
4824+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
4825+ if (status != MMAL_SUCCESS) {
4826+ msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
4827+ sys->input->name, status, mmal_status_to_string(status));
4828+ goto fail;
4829+ }
4830
4831- fill_output_port(filter);
4832+ status = mmal_port_enable(sys->input, di_input_port_cb);
4833+ if (status != MMAL_SUCCESS) {
4834+ msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
4835+ sys->input->name, status, mmal_status_to_string(status));
4836+ goto fail;
4837+ }
4838
4839- buffer = picture->p_sys->buffer;
4840- buffer->user_data = picture;
4841- buffer->pts = picture->date;
4842- buffer->cmd = 0;
4843
4844- if (!picture->p_sys->displayed) {
4845- status = mmal_port_send_buffer(sys->input, buffer);
4846- if (status != MMAL_SUCCESS) {
4847- msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)",
4848- status, mmal_status_to_string(status));
4849- picture_Release(picture);
4850- } else {
4851- picture->p_sys->displayed = true;
4852- atomic_fetch_add(&sys->input_in_transit, 1);
4853- vlc_sem_post(&sys->sem);
4854- }
4855- } else {
4856- picture_Release(picture);
4857- }
4858-
4859- /*
4860- * Send output buffers
4861- */
4862- while(atomic_load(&sys->started) && i < 2) {
4863- if (buffer = mmal_queue_timedwait(sys->filtered_pictures, 2000)) {
4864- i++;
4865- if (!out_picture) {
4866- out_picture = (picture_t *)buffer->user_data;
4867- ret = out_picture;
4868- } else {
4869- out_picture->p_next = (picture_t *)buffer->user_data;
4870- out_picture = out_picture->p_next;
4871- }
4872- out_picture->date = buffer->pts;
4873- } else {
4874- msg_Dbg(filter, "Failed waiting for filtered picture");
4875- break;
4876- }
4877+ if ((sys->out_q = mmal_queue_create()) == NULL)
4878+ {
4879+ msg_Err(filter, "Failed to create out Q");
4880+ goto fail;
4881 }
4882- if (out_picture)
4883- out_picture->p_next = NULL;
4884
4885- return ret;
4886-}
4887-
4888-static void flush(filter_t *filter)
4889-{
4890- filter_sys_t *sys = filter->p_sys;
4891- MMAL_BUFFER_HEADER_T *buffer;
4892+ sys->output = sys->component->output[0];
4893+ mmal_format_full_copy(sys->output->format, sys->input->format);
4894
4895- msg_Dbg(filter, "flush deinterlace filter");
4896+ if (!sys->is_cma)
4897+ {
4898+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS)
4899+ goto fail;
4900+ }
4901+ else
4902+ {
4903+ // CMA stuff
4904+ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4905+
4906+ if ((sys->cma_out_pool = cma_buf_pool_new(8, 8, true, "deinterlace")) == NULL)
4907+ {
4908+ msg_Err(filter, "Failed to alloc cma buf pool");
4909+ goto fail;
4910+ }
4911
4912- msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)",
4913- sys->input_in_transit, sys->output_in_transit);
4914- mmal_port_flush(sys->output);
4915- mmal_port_flush(sys->input);
4916-
4917- msg_Dbg(filter, "flush: wait for all buffers to be returned");
4918- while (atomic_load(&sys->input_in_transit) ||
4919- atomic_load(&sys->output_in_transit))
4920- vlc_sem_wait(&sys->sem);
4921-
4922- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
4923- picture_t *pic = (picture_t *)buffer->user_data;
4924- msg_Dbg(filter, "flush: release already filtered pic %p",
4925- (void *)pic);
4926- picture_Release(pic);
4927- }
4928- atomic_store(&sys->started, false);
4929- msg_Dbg(filter, "flush: done");
4930-}
4931+ // Rate control done by CMA in flight logic, so have "inexhaustable" pool here
4932+ if ((sys->out_pool = mmal_pool_create(30, 0)) == NULL)
4933+ {
4934+ msg_Err(filter, "Failed to alloc out pool");
4935+ goto fail;
4936+ }
4937
4938-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4939-{
4940- filter_t *filter = (filter_t *)port->userdata;
4941- MMAL_STATUS_T status;
4942+ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, true);
4943
4944- if (buffer->cmd == MMAL_EVENT_ERROR) {
4945- status = *(uint32_t *)buffer->data;
4946- msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
4947- mmal_status_to_string(status));
4948- }
4949+ if ((status = mmal_port_format_commit(sys->output)) != MMAL_SUCCESS)
4950+ {
4951+ msg_Err(filter, "Output port format commit failed");
4952+ goto fail;
4953+ }
4954
4955- mmal_buffer_header_release(buffer);
4956-}
4957+ sys->output->buffer_num = 30;
4958+ sys->output->buffer_size = sys->output->buffer_size_recommended;
4959
4960-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4961-{
4962- picture_t *picture = (picture_t *)buffer->user_data;
4963- filter_t *filter = (filter_t *)port->userdata;
4964- filter_sys_t *sys = filter->p_sys;
4965+ // CB just drops all bufs into out_q
4966+ if ((status = mmal_port_enable(sys->output, di_output_port_cb)) != MMAL_SUCCESS)
4967+ {
4968+ msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
4969+ sys->output->name, status, mmal_status_to_string(status));
4970+ goto fail;
4971+ }
4972+ }
4973
4974- if (picture) {
4975- picture_Release(picture);
4976- } else {
4977- msg_Warn(filter, "Got buffer without picture on input port - OOOPS");
4978- mmal_buffer_header_release(buffer);
4979+ status = mmal_component_enable(sys->component);
4980+ if (status != MMAL_SUCCESS) {
4981+ msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
4982+ sys->component->name, status, mmal_status_to_string(status));
4983+ goto fail;
4984 }
4985
4986- atomic_fetch_sub(&sys->input_in_transit, 1);
4987- vlc_sem_post(&sys->sem);
4988+ filter->pf_video_filter = deinterlace;
4989+ filter->pf_flush = di_flush;
4990+ return 0;
4991+
4992+fail:
4993+ CloseMmalDeinterlace(filter);
4994+ return ret;
4995 }
4996
4997-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4998-{
4999- filter_t *filter = (filter_t *)port->userdata;
5000- filter_sys_t *sys = filter->p_sys;
5001- picture_t *picture;
5002+vlc_module_begin()
5003+ set_shortname(N_("MMAL deinterlace"))
5004+ set_description(N_("MMAL-based deinterlace filter plugin"))
5005+ set_capability("video filter", 900)
5006+ set_category(CAT_VIDEO)
5007+ set_subcategory(SUBCAT_VIDEO_VFILTER)
5008+ set_callbacks(OpenMmalDeinterlace, CloseMmalDeinterlace)
5009+ add_shortcut("deinterlace")
5010+ add_bool(MMAL_DEINTERLACE_NO_QPU, false, MMAL_DEINTERLACE_NO_QPU_TEXT,
5011+ MMAL_DEINTERLACE_NO_QPU_LONGTEXT, true);
5012+ add_bool(MMAL_DEINTERLACE_ADV, false, MMAL_DEINTERLACE_ADV_TEXT,
5013+ MMAL_DEINTERLACE_ADV_LONGTEXT, true);
5014+ add_bool(MMAL_DEINTERLACE_FAST, false, MMAL_DEINTERLACE_FAST_TEXT,
5015+ MMAL_DEINTERLACE_FAST_LONGTEXT, true);
5016+ add_bool(MMAL_DEINTERLACE_NONE, false, MMAL_DEINTERLACE_NONE_TEXT,
5017+ MMAL_DEINTERLACE_NONE_LONGTEXT, true);
5018+ add_bool(MMAL_DEINTERLACE_HALF_RATE, false, MMAL_DEINTERLACE_HALF_RATE_TEXT,
5019+ MMAL_DEINTERLACE_HALF_RATE_LONGTEXT, true);
5020+ add_bool(MMAL_DEINTERLACE_FULL_RATE, false, MMAL_DEINTERLACE_FULL_RATE_TEXT,
5021+ MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true);
5022+
5023+vlc_module_end()
5024+
5025
5026- if (buffer->cmd == 0) {
5027- if (buffer->length > 0) {
5028- atomic_store(&sys->started, true);
5029- mmal_queue_put(sys->filtered_pictures, buffer);
5030- picture = (picture_t *)buffer->user_data;
5031- } else {
5032- picture = (picture_t *)buffer->user_data;
5033- picture_Release(picture);
5034- }
5035-
5036- atomic_fetch_sub(&sys->output_in_transit, 1);
5037- vlc_sem_post(&sys->sem);
5038- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
5039- msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled");
5040- mmal_buffer_header_release(buffer);
5041- } else {
5042- mmal_buffer_header_release(buffer);
5043- }
5044-}
5045--- /dev/null
5046+++ b/modules/hw/mmal/mmal_avcodec.c
5047@@ -0,0 +1,2175 @@
5048+/*****************************************************************************
5049+ * video.c: video decoder using the libavcodec library
5050+ *****************************************************************************
5051+ * Copyright (C) 1999-2001 VLC authors and VideoLAN
5052+ * $Id$
5053+ *
5054+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>
5055+ * Gildas Bazin <gbazin@videolan.org>
5056+ *
5057+ * This program is free software; you can redistribute it and/or modify it
5058+ * under the terms of the GNU Lesser General Public License as published by
5059+ * the Free Software Foundation; either version 2.1 of the License, or
5060+ * (at your option) any later version.
5061+ *
5062+ * This program is distributed in the hope that it will be useful,
5063+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
5064+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5065+ * GNU Lesser General Public License for more details.
5066+ *
5067+ * You should have received a copy of the GNU Lesser General Public License
5068+ * along with this program; if not, write to the Free Software Foundation,
5069+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
5070+ *****************************************************************************/
5071+
5072+/*****************************************************************************
5073+ * Preamble
5074+ *****************************************************************************/
5075+#include "config.h"
5076+
5077+#include <vlc_common.h>
5078+#include <vlc_codec.h>
5079+#include <vlc_avcodec.h>
5080+#include <vlc_cpu.h>
5081+#include <vlc_atomic.h>
5082+#include <assert.h>
5083+
5084+#include <libavcodec/avcodec.h>
5085+#include <libavutil/mem.h>
5086+#include <libavutil/pixdesc.h>
5087+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
5088+#include <libavutil/mastering_display_metadata.h>
5089+#endif
5090+
5091+//#include "avcodec.h"
5092+//#include "va.h"
5093+
5094+#include <vlc_plugin.h>
5095+#include <libavutil/rpi_sand_fns.h>
5096+#include <libavcodec/rpi_zc.h>
5097+#include "../../codec/cc.h"
5098+#include "../../codec/avcodec/avcommon.h" // ??? Beware over inclusion
5099+#include "mmal_cma.h"
5100+#include "mmal_picture.h"
5101+
5102+#define TRACE_ALL 0
5103+
5104+#define BUFFERS_IN_FLIGHT 5 // Default max value for in flight buffers
5105+#define BUFFERS_IN_FLIGHT_UHD 3 // Fewer if very big
5106+
5107+#define MMAL_AVCODEC_BUFFERS "mmal-avcodec-buffers"
5108+#define MMAL_AVCODEC_BUFFERS_TEXT N_("In flight buffer count before blocking.")
5109+#define MMAL_AVCODEC_BUFFERS_LONGTEXT N_("In flight buffer count before blocking. " \
5110+"Beware that incautious changing of this can lead to lockup. " \
5111+"Zero will disable the module.")
5112+
5113+
5114+// Fwd declarations required due to wanting to avoid reworking the original
5115+// code too much
5116+static void MmalAvcodecCloseDecoder( vlc_object_t *obj );
5117+
5118+
5119+/*****************************************************************************
5120+ * decoder_sys_t : decoder descriptor
5121+ *****************************************************************************/
5122+struct decoder_sys_t
5123+{
5124+ AVCodecContext *p_context;
5125+ const AVCodec *p_codec;
5126+
5127+ /* Video decoder specific part */
5128+ date_t pts;
5129+
5130+ /* Closed captions for decoders */
5131+ cc_data_t cc;
5132+
5133+ /* for frame skipping algo */
5134+ bool b_hurry_up;
5135+ bool b_show_corrupted;
5136+ bool b_from_preroll;
5137+ enum AVDiscard i_skip_frame;
5138+
5139+ /* how many decoded frames are late */
5140+ int i_late_frames;
5141+ mtime_t i_late_frames_start;
5142+ mtime_t i_last_late_delay;
5143+
5144+ /* for direct rendering */
5145+ bool b_direct_rendering;
5146+ atomic_bool b_dr_failure;
5147+
5148+ /* Hack to force display of still pictures */
5149+ bool b_first_frame;
5150+
5151+
5152+ /* */
5153+ bool palette_sent;
5154+
5155+ /* VA API */
5156+// vlc_va_t *p_va;
5157+ enum PixelFormat pix_fmt;
5158+ int profile;
5159+ int level;
5160+
5161+ vlc_sem_t sem_mt;
5162+
5163+ // Rpi vars
5164+ cma_buf_pool_t * cma_pool;
5165+ bool pool_alloc_1;
5166+ vcsm_init_type_t vcsm_init_type;
5167+ int cma_in_flight_max;
5168+ // Debug
5169+ decoder_t * p_dec;
5170+};
5171+
5172+
5173+static vlc_fourcc_t
5174+ZcFindVlcChroma(const int i_ffmpeg_chroma)
5175+{
5176+ switch (i_ffmpeg_chroma)
5177+ {
5178+ // This is all we claim to deal with
5179+ // In theory RGB should be doable within our current framework
5180+ case AV_PIX_FMT_YUV420P:
5181+ return VLC_CODEC_MMAL_ZC_I420;
5182+ case AV_PIX_FMT_SAND128:
5183+ case AV_PIX_FMT_RPI4_8:
5184+ return VLC_CODEC_MMAL_ZC_SAND8;
5185+ case AV_PIX_FMT_SAND64_10:
5186+ return VLC_CODEC_MMAL_ZC_SAND10;
5187+ case AV_PIX_FMT_RPI4_10:
5188+ return VLC_CODEC_MMAL_ZC_SAND30;
5189+ default:
5190+ break;
5191+ }
5192+ return 0;
5193+}
5194+
5195+// Pix Fmt conv for MMal
5196+// video_fromat from ffmpeg pic_fmt
5197+static int
5198+ZcGetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma )
5199+{
5200+ fmt->i_rmask = 0;
5201+ fmt->i_gmask = 0;
5202+ fmt->i_bmask = 0;
5203+ fmt->i_chroma = ZcFindVlcChroma(i_ffmpeg_chroma);
5204+
5205+ return fmt->i_chroma == 0 ? -1 : 0;
5206+}
5207+
5208+
5209+// Format chooser is way simpler than vlc
5210+static enum PixelFormat
5211+ZcGetFormat(AVCodecContext *p_context, const enum PixelFormat *pi_fmt)
5212+{
5213+ enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt);
5214+ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++)
5215+ {
5216+ if (ZcFindVlcChroma(pi_fmt[i]) != 0)
5217+ return pi_fmt[i];
5218+ }
5219+ return swfmt;
5220+}
5221+
5222+
5223+static void cma_avbuf_pool_free(void * v)
5224+{
5225+ cma_buf_unref(v);
5226+}
5227+
5228+static unsigned int zc_buf_vcsm_handle(void * v)
5229+{
5230+ return cma_buf_vcsm_handle(v);
5231+}
5232+
5233+static unsigned int zc_buf_vc_handle(void * v)
5234+{
5235+ return cma_buf_vc_handle(v);
5236+}
5237+
5238+static void * zc_buf_map_arm(void * v)
5239+{
5240+ return cma_buf_addr(v);
5241+}
5242+
5243+static unsigned int zc_buf_map_vc(void * v)
5244+{
5245+ return cma_buf_vc_addr(v);
5246+}
5247+
5248+
5249+
5250+static const av_rpi_zc_buf_fn_tab_t zc_buf_fn_tab = {
5251+ .free = cma_avbuf_pool_free,
5252+
5253+ .vcsm_handle = zc_buf_vcsm_handle,
5254+ .vc_handle = zc_buf_vc_handle,
5255+ .map_arm = zc_buf_map_arm,
5256+ .map_vc = zc_buf_map_vc
5257+};
5258+
5259+
5260+static AVBufferRef *
5261+zc_alloc_buf(void * v, size_t size, const AVRpiZcFrameGeometry * geo)
5262+{
5263+ decoder_t * const dec = v;
5264+ decoder_sys_t * const sys = dec->p_sys;
5265+
5266+ VLC_UNUSED(geo);
5267+
5268+ assert(sys != NULL);
5269+
5270+ const unsigned int dec_pool_req = av_rpi_zc_get_decoder_pool_size(sys->p_context->opaque);
5271+ if (dec_pool_req != 0)
5272+ {
5273+ cma_buf_pool_resize(sys->cma_pool, dec_pool_req + sys->cma_in_flight_max, sys->cma_in_flight_max);
5274+
5275+ if (!sys->pool_alloc_1)
5276+ {
5277+ sys->pool_alloc_1 = true;
5278+ msg_Dbg(dec, "Pool size: (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size);
5279+ if (cma_buf_pool_fill(sys->cma_pool, size) != 0)
5280+ msg_Warn(dec, "Failed to preallocate decoder pool (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size);
5281+ }
5282+ }
5283+
5284+ void * const cmabuf = cma_buf_pool_alloc_buf(sys->cma_pool, size);
5285+
5286+ if (cmabuf == NULL)
5287+ {
5288+ msg_Err(dec, "CMA buf pool alloc buf failed");
5289+ return NULL;
5290+ }
5291+
5292+ AVBufferRef *const avbuf = av_rpi_zc_buf(cma_buf_size(cmabuf), 0, cmabuf, &zc_buf_fn_tab);
5293+
5294+ if (avbuf == NULL)
5295+ {
5296+ msg_Err(dec, "av_rpi_zc_buf failed");
5297+ cma_buf_unref(cmabuf);
5298+ return NULL;
5299+ }
5300+
5301+ return avbuf;
5302+}
5303+
5304+static void
5305+zc_free_pool(void * v)
5306+{
5307+ decoder_t * const dec = v;
5308+ cma_buf_pool_delete(dec->p_sys->cma_pool);
5309+}
5310+
5311+
5312+static const uint8_t shift_01[] = {0,1,1,1};
5313+static const uint8_t pb_1[] = {1,1,1,1};
5314+static const uint8_t pb_12[] = {1,2,2,2};
5315+static const uint8_t pb_24[] = {2,4,4,4};
5316+static const uint8_t pb_4[] = {4,4,4,4};
5317+
5318+static int set_pic_from_frame(picture_t * const pic, const AVFrame * const frame)
5319+{
5320+ const uint8_t * hs = shift_01;
5321+ const uint8_t * ws = shift_01;
5322+ const uint8_t * pb = pb_1;
5323+
5324+ switch (pic->format.i_chroma)
5325+ {
5326+ case VLC_CODEC_MMAL_ZC_RGB32:
5327+ pic->i_planes = 1;
5328+ pb = pb_4;
5329+ break;
5330+ case VLC_CODEC_MMAL_ZC_I420:
5331+ pic->i_planes = 3;
5332+ break;
5333+ case VLC_CODEC_MMAL_ZC_SAND8:
5334+ pic->i_planes = 2;
5335+ pb = pb_12;
5336+ break;
5337+ case VLC_CODEC_MMAL_ZC_SAND10:
5338+ case VLC_CODEC_MMAL_ZC_SAND30: // Lies: SAND30 is "special"
5339+ pic->i_planes = 2;
5340+ pb = pb_24;
5341+ break;
5342+ default:
5343+ return VLC_EGENERIC;
5344+ }
5345+
5346+ const cma_buf_t * const cb = cma_buf_pic_get(pic);
5347+ uint8_t * const data = cma_buf_addr(cb);
5348+ if (data == NULL) {
5349+ return VLC_ENOMEM;
5350+ }
5351+
5352+ uint8_t * frame_end = frame->data[0] + cma_buf_size(cb);
5353+ for (int i = 0; i != pic->i_planes; ++i) {
5354+ // Calculate lines from gap between planes
5355+ // This will give us an accurate "height" for later use by MMAL
5356+ const int lines = ((i + 1 == pic->i_planes ? frame_end : frame->data[i + 1]) -
5357+ frame->data[i]) / frame->linesize[i];
5358+ pic->p[i] = (plane_t){
5359+ .p_pixels = data + (frame->data[i] - frame->data[0]),
5360+ .i_lines = lines,
5361+ .i_pitch = frame->linesize[i],
5362+ .i_pixel_pitch = pb[i],
5363+ .i_visible_lines = av_frame_cropped_height(frame) >> hs[i],
5364+ .i_visible_pitch = av_frame_cropped_width(frame) >> ws[i]
5365+ };
5366+ }
5367+ return 0;
5368+}
5369+
5370+
5371+//============================================================================
5372+//
5373+// Nicked from avcodec/fourcc.c
5374+//
5375+// * Really we should probably use that directly
5376+
5377+/*
5378+ * Video Codecs
5379+ */
5380+
5381+struct vlc_avcodec_fourcc
5382+{
5383+ vlc_fourcc_t i_fourcc;
5384+ unsigned i_codec;
5385+};
5386+
5387+
5388+static const struct vlc_avcodec_fourcc video_codecs[] =
5389+{
5390+ { VLC_CODEC_MP1V, AV_CODEC_ID_MPEG1VIDEO },
5391+ { VLC_CODEC_MP2V, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */
5392+ { VLC_CODEC_MPGV, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */
5393+ /* AV_CODEC_ID_MPEG2VIDEO_XVMC */
5394+ { VLC_CODEC_H261, AV_CODEC_ID_H261 },
5395+ { VLC_CODEC_H263, AV_CODEC_ID_H263 },
5396+ { VLC_CODEC_RV10, AV_CODEC_ID_RV10 },
5397+ { VLC_CODEC_RV13, AV_CODEC_ID_RV10 },
5398+ { VLC_CODEC_RV20, AV_CODEC_ID_RV20 },
5399+ { VLC_CODEC_MJPG, AV_CODEC_ID_MJPEG },
5400+ { VLC_CODEC_MJPGB, AV_CODEC_ID_MJPEGB },
5401+ { VLC_CODEC_LJPG, AV_CODEC_ID_LJPEG },
5402+ { VLC_CODEC_SP5X, AV_CODEC_ID_SP5X },
5403+ { VLC_CODEC_JPEGLS, AV_CODEC_ID_JPEGLS },
5404+ { VLC_CODEC_MP4V, AV_CODEC_ID_MPEG4 },
5405+ /* AV_CODEC_ID_RAWVIDEO */
5406+ { VLC_CODEC_DIV1, AV_CODEC_ID_MSMPEG4V1 },
5407+ { VLC_CODEC_DIV2, AV_CODEC_ID_MSMPEG4V2 },
5408+ { VLC_CODEC_DIV3, AV_CODEC_ID_MSMPEG4V3 },
5409+ { VLC_CODEC_WMV1, AV_CODEC_ID_WMV1 },
5410+ { VLC_CODEC_WMV2, AV_CODEC_ID_WMV2 },
5411+ { VLC_CODEC_H263P, AV_CODEC_ID_H263P },
5412+ { VLC_CODEC_H263I, AV_CODEC_ID_H263I },
5413+ { VLC_CODEC_FLV1, AV_CODEC_ID_FLV1 },
5414+ { VLC_CODEC_SVQ1, AV_CODEC_ID_SVQ1 },
5415+ { VLC_CODEC_SVQ3, AV_CODEC_ID_SVQ3 },
5416+ { VLC_CODEC_DV, AV_CODEC_ID_DVVIDEO },
5417+ { VLC_CODEC_HUFFYUV, AV_CODEC_ID_HUFFYUV },
5418+ { VLC_CODEC_CYUV, AV_CODEC_ID_CYUV },
5419+ { VLC_CODEC_H264, AV_CODEC_ID_H264 },
5420+ { VLC_CODEC_INDEO3, AV_CODEC_ID_INDEO3 },
5421+ { VLC_CODEC_VP3, AV_CODEC_ID_VP3 },
5422+ { VLC_CODEC_THEORA, AV_CODEC_ID_THEORA },
5423+#if ( !defined( WORDS_BIGENDIAN ) )
5424+ /* Asus Video (Another thing that doesn't work on PPC) */
5425+ { VLC_CODEC_ASV1, AV_CODEC_ID_ASV1 },
5426+ { VLC_CODEC_ASV2, AV_CODEC_ID_ASV2 },
5427+#endif
5428+ { VLC_CODEC_FFV1, AV_CODEC_ID_FFV1 },
5429+ { VLC_CODEC_4XM, AV_CODEC_ID_4XM },
5430+ { VLC_CODEC_VCR1, AV_CODEC_ID_VCR1 },
5431+ { VLC_CODEC_CLJR, AV_CODEC_ID_CLJR },
5432+ { VLC_CODEC_MDEC, AV_CODEC_ID_MDEC },
5433+ { VLC_CODEC_ROQ, AV_CODEC_ID_ROQ },
5434+ { VLC_CODEC_INTERPLAY, AV_CODEC_ID_INTERPLAY_VIDEO },
5435+ { VLC_CODEC_XAN_WC3, AV_CODEC_ID_XAN_WC3 },
5436+ { VLC_CODEC_XAN_WC4, AV_CODEC_ID_XAN_WC4 },
5437+ { VLC_CODEC_RPZA, AV_CODEC_ID_RPZA },
5438+ { VLC_CODEC_CINEPAK, AV_CODEC_ID_CINEPAK },
5439+ { VLC_CODEC_WS_VQA, AV_CODEC_ID_WS_VQA },
5440+ { VLC_CODEC_MSRLE, AV_CODEC_ID_MSRLE },
5441+ { VLC_CODEC_MSVIDEO1, AV_CODEC_ID_MSVIDEO1 },
5442+ { VLC_CODEC_IDCIN, AV_CODEC_ID_IDCIN },
5443+ { VLC_CODEC_8BPS, AV_CODEC_ID_8BPS },
5444+ { VLC_CODEC_SMC, AV_CODEC_ID_SMC },
5445+ { VLC_CODEC_FLIC, AV_CODEC_ID_FLIC },
5446+ { VLC_CODEC_TRUEMOTION1, AV_CODEC_ID_TRUEMOTION1 },
5447+ { VLC_CODEC_VMDVIDEO, AV_CODEC_ID_VMDVIDEO },
5448+ { VLC_CODEC_LCL_MSZH, AV_CODEC_ID_MSZH },
5449+ { VLC_CODEC_LCL_ZLIB, AV_CODEC_ID_ZLIB },
5450+ { VLC_CODEC_QTRLE, AV_CODEC_ID_QTRLE },
5451+ { VLC_CODEC_TSCC, AV_CODEC_ID_TSCC },
5452+ { VLC_CODEC_ULTI, AV_CODEC_ID_ULTI },
5453+ { VLC_CODEC_QDRAW, AV_CODEC_ID_QDRAW },
5454+ { VLC_CODEC_VIXL, AV_CODEC_ID_VIXL },
5455+ { VLC_CODEC_QPEG, AV_CODEC_ID_QPEG },
5456+ { VLC_CODEC_PNG, AV_CODEC_ID_PNG },
5457+ { VLC_CODEC_PPM, AV_CODEC_ID_PPM },
5458+ /* AV_CODEC_ID_PBM */
5459+ { VLC_CODEC_PGM, AV_CODEC_ID_PGM },
5460+ { VLC_CODEC_PGMYUV, AV_CODEC_ID_PGMYUV },
5461+ { VLC_CODEC_PAM, AV_CODEC_ID_PAM },
5462+ { VLC_CODEC_FFVHUFF, AV_CODEC_ID_FFVHUFF },
5463+ { VLC_CODEC_RV30, AV_CODEC_ID_RV30 },
5464+ { VLC_CODEC_RV40, AV_CODEC_ID_RV40 },
5465+ { VLC_CODEC_VC1, AV_CODEC_ID_VC1 },
5466+ { VLC_CODEC_WMVA, AV_CODEC_ID_VC1 },
5467+ { VLC_CODEC_WMV3, AV_CODEC_ID_WMV3 },
5468+ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3 },
5469+ { VLC_CODEC_LOCO, AV_CODEC_ID_LOCO },
5470+ { VLC_CODEC_WNV1, AV_CODEC_ID_WNV1 },
5471+ { VLC_CODEC_AASC, AV_CODEC_ID_AASC },
5472+ { VLC_CODEC_INDEO2, AV_CODEC_ID_INDEO2 },
5473+ { VLC_CODEC_FRAPS, AV_CODEC_ID_FRAPS },
5474+ { VLC_CODEC_TRUEMOTION2, AV_CODEC_ID_TRUEMOTION2 },
5475+ { VLC_CODEC_BMP, AV_CODEC_ID_BMP },
5476+ { VLC_CODEC_CSCD, AV_CODEC_ID_CSCD },
5477+ { VLC_CODEC_MMVIDEO, AV_CODEC_ID_MMVIDEO },
5478+ { VLC_CODEC_ZMBV, AV_CODEC_ID_ZMBV },
5479+ { VLC_CODEC_AVS, AV_CODEC_ID_AVS },
5480+ { VLC_CODEC_SMACKVIDEO, AV_CODEC_ID_SMACKVIDEO },
5481+ { VLC_CODEC_NUV, AV_CODEC_ID_NUV },
5482+ { VLC_CODEC_KMVC, AV_CODEC_ID_KMVC },
5483+ { VLC_CODEC_FLASHSV, AV_CODEC_ID_FLASHSV },
5484+ { VLC_CODEC_CAVS, AV_CODEC_ID_CAVS },
5485+ { VLC_CODEC_JPEG2000, AV_CODEC_ID_JPEG2000 },
5486+ { VLC_CODEC_VMNC, AV_CODEC_ID_VMNC },
5487+ { VLC_CODEC_VP5, AV_CODEC_ID_VP5 },
5488+ { VLC_CODEC_VP6, AV_CODEC_ID_VP6 },
5489+ { VLC_CODEC_VP6F, AV_CODEC_ID_VP6F },
5490+ { VLC_CODEC_TARGA, AV_CODEC_ID_TARGA },
5491+ { VLC_CODEC_DSICINVIDEO, AV_CODEC_ID_DSICINVIDEO },
5492+ { VLC_CODEC_TIERTEXSEQVIDEO, AV_CODEC_ID_TIERTEXSEQVIDEO },
5493+ { VLC_CODEC_TIFF, AV_CODEC_ID_TIFF },
5494+ { VLC_CODEC_GIF, AV_CODEC_ID_GIF },
5495+ { VLC_CODEC_DXA, AV_CODEC_ID_DXA },
5496+ { VLC_CODEC_DNXHD, AV_CODEC_ID_DNXHD },
5497+ { VLC_CODEC_THP, AV_CODEC_ID_THP },
5498+ { VLC_CODEC_SGI, AV_CODEC_ID_SGI },
5499+ { VLC_CODEC_C93, AV_CODEC_ID_C93 },
5500+ { VLC_CODEC_BETHSOFTVID, AV_CODEC_ID_BETHSOFTVID },
5501+ /* AV_CODEC_ID_PTX */
5502+ { VLC_CODEC_TXD, AV_CODEC_ID_TXD },
5503+ { VLC_CODEC_VP6A, AV_CODEC_ID_VP6A },
5504+ { VLC_CODEC_AMV, AV_CODEC_ID_AMV },
5505+ { VLC_CODEC_VB, AV_CODEC_ID_VB },
5506+ { VLC_CODEC_PCX, AV_CODEC_ID_PCX },
5507+ /* AV_CODEC_ID_SUNRAST */
5508+ { VLC_CODEC_INDEO4, AV_CODEC_ID_INDEO4 },
5509+ { VLC_CODEC_INDEO5, AV_CODEC_ID_INDEO5 },
5510+ { VLC_CODEC_MIMIC, AV_CODEC_ID_MIMIC },
5511+ { VLC_CODEC_RL2, AV_CODEC_ID_RL2 },
5512+ { VLC_CODEC_ESCAPE124, AV_CODEC_ID_ESCAPE124 },
5513+ { VLC_CODEC_DIRAC, AV_CODEC_ID_DIRAC },
5514+ { VLC_CODEC_BFI, AV_CODEC_ID_BFI },
5515+ { VLC_CODEC_CMV, AV_CODEC_ID_CMV },
5516+ { VLC_CODEC_MOTIONPIXELS, AV_CODEC_ID_MOTIONPIXELS },
5517+ { VLC_CODEC_TGV, AV_CODEC_ID_TGV },
5518+ { VLC_CODEC_TGQ, AV_CODEC_ID_TGQ },
5519+ { VLC_CODEC_TQI, AV_CODEC_ID_TQI },
5520+ { VLC_CODEC_AURA, AV_CODEC_ID_AURA },
5521+ /* AV_CODEC_ID_AURA2 */
5522+ /* AV_CODEC_ID_V210X */
5523+ { VLC_CODEC_TMV, AV_CODEC_ID_TMV },
5524+ { VLC_CODEC_V210, AV_CODEC_ID_V210 },
5525+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 54, 50, 100 ) && LIBAVCODEC_VERSION_MICRO >= 100
5526+ { VLC_CODEC_VUYA, AV_CODEC_ID_AYUV },
5527+#endif
5528+ /* AV_CODEC_ID_DPX */
5529+ { VLC_CODEC_MAD, AV_CODEC_ID_MAD },
5530+ { VLC_CODEC_FRWU, AV_CODEC_ID_FRWU },
5531+ { VLC_CODEC_FLASHSV2, AV_CODEC_ID_FLASHSV2 },
5532+ /* AV_CODEC_ID_CDGRAPHICS */
5533+ /* AV_CODEC_ID_R210 */
5534+ { VLC_CODEC_ANM, AV_CODEC_ID_ANM },
5535+ { VLC_CODEC_BINKVIDEO, AV_CODEC_ID_BINKVIDEO },
5536+ /* AV_CODEC_ID_IFF_ILBM */
5537+ /* AV_CODEC_ID_IFF_BYTERUN1 */
5538+ { VLC_CODEC_KGV1, AV_CODEC_ID_KGV1 },
5539+ { VLC_CODEC_YOP, AV_CODEC_ID_YOP },
5540+ { VLC_CODEC_VP8, AV_CODEC_ID_VP8 },
5541+ /* AV_CODEC_ID_PICTOR */
5542+ /* AV_CODEC_ID_ANSI */
5543+ /* AV_CODEC_ID_A64_MULTI */
5544+ /* AV_CODEC_ID_A64_MULTI5 */
5545+ /* AV_CODEC_ID_R10K */
5546+ { VLC_CODEC_MXPEG, AV_CODEC_ID_MXPEG },
5547+ { VLC_CODEC_LAGARITH, AV_CODEC_ID_LAGARITH },
5548+ { VLC_CODEC_PRORES, AV_CODEC_ID_PRORES },
5549+ { VLC_CODEC_JV, AV_CODEC_ID_JV },
5550+ { VLC_CODEC_DFA, AV_CODEC_ID_DFA },
5551+ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3IMAGE },
5552+ { VLC_CODEC_WMVP2, AV_CODEC_ID_VC1IMAGE },
5553+ { VLC_CODEC_UTVIDEO, AV_CODEC_ID_UTVIDEO },
5554+ { VLC_CODEC_BMVVIDEO, AV_CODEC_ID_BMV_VIDEO },
5555+ { VLC_CODEC_VBLE, AV_CODEC_ID_VBLE },
5556+ { VLC_CODEC_DXTORY, AV_CODEC_ID_DXTORY },
5557+ /* AV_CODEC_ID_V410 */
5558+ /* AV_CODEC_ID_XWD */
5559+ { VLC_CODEC_CDXL, AV_CODEC_ID_CDXL },
5560+ /* AV_CODEC_ID_XBM */
5561+ /* AV_CODEC_ID_ZEROCODEC */
5562+ { VLC_CODEC_MSS1, AV_CODEC_ID_MSS1 },
5563+ { VLC_CODEC_MSA1, AV_CODEC_ID_MSA1 },
5564+ { VLC_CODEC_TSC2, AV_CODEC_ID_TSCC2 },
5565+ { VLC_CODEC_MTS2, AV_CODEC_ID_MTS2 },
5566+ { VLC_CODEC_CLLC, AV_CODEC_ID_CLLC },
5567+ { VLC_CODEC_MSS2, AV_CODEC_ID_MSS2 },
5568+ { VLC_CODEC_VP9, AV_CODEC_ID_VP9 },
5569+#if LIBAVCODEC_VERSION_CHECK( 57, 26, 0, 83, 101 )
5570+ { VLC_CODEC_AV1, AV_CODEC_ID_AV1 },
5571+#endif
5572+ { VLC_CODEC_ICOD, AV_CODEC_ID_AIC },
5573+ /* AV_CODEC_ID_ESCAPE130 */
5574+ { VLC_CODEC_G2M4, AV_CODEC_ID_G2M },
5575+ { VLC_CODEC_G2M2, AV_CODEC_ID_G2M },
5576+ { VLC_CODEC_G2M3, AV_CODEC_ID_G2M },
5577+ /* AV_CODEC_ID_WEBP */
5578+ { VLC_CODEC_HNM4_VIDEO, AV_CODEC_ID_HNM4_VIDEO },
5579+ { VLC_CODEC_HEVC, AV_CODEC_ID_HEVC },
5580+
5581+ { VLC_CODEC_FIC , AV_CODEC_ID_FIC },
5582+ /* AV_CODEC_ID_ALIAS_PIX */
5583+ /* AV_CODEC_ID_BRENDER_PIX */
5584+ /* AV_CODEC_ID_PAF_VIDEO */
5585+ /* AV_CODEC_ID_EXR */
5586+
5587+ { VLC_CODEC_VP7 , AV_CODEC_ID_VP7 },
5588+ /* AV_CODEC_ID_SANM */
5589+ /* AV_CODEC_ID_SGIRLE */
5590+ /* AV_CODEC_ID_MVC1 */
5591+ /* AV_CODEC_ID_MVC2 */
5592+ { VLC_CODEC_HQX, AV_CODEC_ID_HQX },
5593+
5594+ { VLC_CODEC_TDSC, AV_CODEC_ID_TDSC },
5595+
5596+ { VLC_CODEC_HQ_HQA, AV_CODEC_ID_HQ_HQA },
5597+
5598+ { VLC_CODEC_HAP, AV_CODEC_ID_HAP },
5599+ /* AV_CODEC_ID_DDS */
5600+
5601+ { VLC_CODEC_DXV, AV_CODEC_ID_DXV },
5602+
5603+ /* ffmpeg only: AV_CODEC_ID_BRENDER_PIX */
5604+ /* ffmpeg only: AV_CODEC_ID_Y41P */
5605+ /* ffmpeg only: AV_CODEC_ID_EXR */
5606+ /* ffmpeg only: AV_CODEC_ID_AVRP */
5607+ /* ffmpeg only: AV_CODEC_ID_012V */
5608+ /* ffmpeg only: AV_CODEC_ID_AVUI */
5609+ /* ffmpeg only: AV_CODEC_ID_TARGA_Y216 */
5610+ /* ffmpeg only: AV_CODEC_ID_V308 */
5611+ /* ffmpeg only: AV_CODEC_ID_V408 */
5612+ /* ffmpeg only: AV_CODEC_ID_YUV4 */
5613+ /* ffmpeg only: AV_CODEC_ID_SANM */
5614+ /* ffmpeg only: AV_CODEC_ID_PAF_VIDEO */
5615+ /* ffmpeg only: AV_CODEC_ID_AVRN */
5616+ /* ffmpeg only: AV_CODEC_ID_CPIA */
5617+ /* ffmpeg only: AV_CODEC_ID_XFACE */
5618+ /* ffmpeg only: AV_CODEC_ID_SGIRLE */
5619+ /* ffmpeg only: AV_CODEC_ID_MVC1 */
5620+ /* ffmpeg only: AV_CODEC_ID_MVC2 */
5621+ /* ffmpeg only: AV_CODEC_ID_SNOW */
5622+ /* ffmpeg only: AV_CODEC_ID_SMVJPEG */
5623+
5624+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 24, 102 )
5625+ { VLC_CODEC_CINEFORM, AV_CODEC_ID_CFHD },
5626+#endif
5627+
5628+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 70, 100 )
5629+ { VLC_CODEC_PIXLET, AV_CODEC_ID_PIXLET },
5630+#endif
5631+
5632+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 101 )
5633+ { VLC_CODEC_SPEEDHQ, AV_CODEC_ID_SPEEDHQ },
5634+#endif
5635+
5636+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 79, 100 )
5637+ { VLC_CODEC_FMVC, AV_CODEC_ID_FMVC },
5638+#endif
5639+};
5640+
5641+// *** Really we should probably use GetFfmpegCodec with a pre-kludge for the bits we care about
5642+static bool
5643+ZcGetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc,
5644+ unsigned *pi_ffmpeg_codec, const char **ppsz_name )
5645+{
5646+ const struct vlc_avcodec_fourcc *base;
5647+ size_t count;
5648+
5649+ base = video_codecs;
5650+ count = ARRAY_SIZE(video_codecs);
5651+ i_fourcc = vlc_fourcc_GetCodec( cat, i_fourcc );
5652+
5653+ for( size_t i = 0; i < count; i++ )
5654+ {
5655+ if( base[i].i_fourcc == i_fourcc )
5656+ {
5657+ if( pi_ffmpeg_codec != NULL )
5658+ *pi_ffmpeg_codec = base[i].i_codec;
5659+ if( ppsz_name )
5660+ *ppsz_name = vlc_fourcc_GetDescription( cat, i_fourcc );
5661+ return true;
5662+ }
5663+ }
5664+ return false;
5665+}
5666+
5667+
5668+
5669+//============================================================================
5670+// Derived from codec/avcodec/avcodec.c
5671+
5672+static AVCodecContext *
5673+ZcFfmpeg_AllocContext( decoder_t *p_dec,
5674+ const AVCodec **restrict codecp )
5675+{
5676+ unsigned i_codec_id;
5677+ const char *psz_namecodec;
5678+ const AVCodec *p_codec = NULL;
5679+
5680+ /* *** determine codec type *** */
5681+ if( !ZcGetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec,
5682+ &i_codec_id, &psz_namecodec ) )
5683+ return NULL;
5684+
5685+ msg_Dbg( p_dec, "using %s %s", AVPROVIDER(LIBAVCODEC), LIBAVCODEC_IDENT );
5686+
5687+ /* Initialization must be done before avcodec_find_decoder() */
5688+ vlc_init_avcodec(VLC_OBJECT(p_dec));
5689+
5690+ /* *** ask ffmpeg for a decoder *** */
5691+ char *psz_decoder = var_InheritString( p_dec, "avcodec-codec" );
5692+ if( psz_decoder != NULL )
5693+ {
5694+ p_codec = avcodec_find_decoder_by_name( psz_decoder );
5695+ if( !p_codec )
5696+ msg_Err( p_dec, "Decoder `%s' not found", psz_decoder );
5697+ else if( p_codec->id != i_codec_id )
5698+ {
5699+ msg_Err( p_dec, "Decoder `%s' can't handle %4.4s",
5700+ psz_decoder, (char*)&p_dec->fmt_in.i_codec );
5701+ p_codec = NULL;
5702+ }
5703+ free( psz_decoder );
5704+ }
5705+ if( !p_codec )
5706+// p_codec = avcodec_find_decoder( i_codec_id );
5707+ {
5708+ if( p_dec->fmt_in.i_codec != VLC_CODEC_HEVC )
5709+ p_codec = avcodec_find_decoder(i_codec_id);
5710+ else
5711+ {
5712+ psz_namecodec = rpi_is_model_pi4() ? "hevc" : "hevc_rpi";
5713+ msg_Info(p_dec, "Looking for HEVC decoder '%s'", psz_namecodec);
5714+ p_codec = avcodec_find_decoder_by_name(psz_namecodec);
5715+ }
5716+ }
5717+
5718+ if( !p_codec )
5719+ {
5720+ msg_Dbg( p_dec, "codec not found (%s)", psz_namecodec );
5721+ return NULL;
5722+ }
5723+
5724+ *codecp = p_codec;
5725+
5726+ /* *** get a p_context *** */
5727+ AVCodecContext *avctx = avcodec_alloc_context3(p_codec);
5728+ if( unlikely(avctx == NULL) )
5729+ return NULL;
5730+
5731+ avctx->debug = var_InheritInteger( p_dec, "avcodec-debug" );
5732+ avctx->opaque = p_dec;
5733+ return avctx;
5734+}
5735+
5736+/*****************************************************************************
5737+ * ffmpeg_OpenCodec:
5738+ *****************************************************************************/
5739+
5740+static int
5741+ZcFfmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx,
5742+ const AVCodec *codec )
5743+{
5744+ char *psz_opts = var_InheritString( p_dec, "avcodec-options" );
5745+ AVDictionary *options = NULL;
5746+ int ret;
5747+
5748+ if (psz_opts) {
5749+ vlc_av_get_options(psz_opts, &options);
5750+ free(psz_opts);
5751+ }
5752+
5753+ if (av_rpi_zc_init2(ctx, p_dec, zc_alloc_buf, zc_free_pool) != 0)
5754+ {
5755+ msg_Err(p_dec, "Failed to init AV ZC");
5756+ return VLC_EGENERIC;
5757+ }
5758+
5759+ vlc_avcodec_lock();
5760+ ret = avcodec_open2( ctx, codec, options ? &options : NULL );
5761+ vlc_avcodec_unlock();
5762+
5763+ AVDictionaryEntry *t = NULL;
5764+ while ((t = av_dict_get(options, "", t, AV_DICT_IGNORE_SUFFIX))) {
5765+ msg_Err( p_dec, "Unknown option \"%s\"", t->key );
5766+ }
5767+ av_dict_free(&options);
5768+
5769+ if( ret < 0 )
5770+ {
5771+ msg_Err( p_dec, "cannot start codec (%s)", codec->name );
5772+ return VLC_EGENERIC;
5773+ }
5774+
5775+ msg_Dbg( p_dec, "codec (%s) started", codec->name );
5776+ return VLC_SUCCESS;
5777+}
5778+
5779+//============================================================================
5780+// Derived from 3.0.7.1 codec/avcodec/video.c
5781+
5782+static inline void wait_mt(decoder_sys_t *sys)
5783+{
5784+#if 1
5785+ // As we only ever update the output in our main thread this lock is
5786+ // redundant
5787+ VLC_UNUSED(sys);
5788+#else
5789+ vlc_sem_wait(&sys->sem_mt);
5790+#endif
5791+}
5792+
5793+static inline void post_mt(decoder_sys_t *sys)
5794+{
5795+#if 1
5796+ // As we only ever update the output in our main thread this lock is
5797+ // redundant
5798+ VLC_UNUSED(sys);
5799+#else
5800+ vlc_sem_post(&sys->sem_mt);
5801+#endif
5802+}
5803+
5804+/*****************************************************************************
5805+ * Local prototypes
5806+ *****************************************************************************/
5807+static void ffmpeg_InitCodec ( decoder_t * );
5808+static int DecodeVideo( decoder_t *, block_t * );
5809+static void Flush( decoder_t * );
5810+
5811+static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc )
5812+{
5813+ uint8_t *p = (uint8_t*)&fcc;
5814+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
5815+}
5816+
5817+/*****************************************************************************
5818+ * Local Functions
5819+ *****************************************************************************/
5820+
5821+/**
5822+ * Sets the decoder output format.
5823+ */
5824+static int lavc_GetVideoFormat(decoder_t *dec, video_format_t *restrict fmt,
5825+ AVCodecContext *ctx, enum AVPixelFormat pix_fmt,
5826+ enum AVPixelFormat sw_pix_fmt)
5827+{
5828+ int width = ctx->coded_width;
5829+ int height = ctx->coded_height;
5830+
5831+ video_format_Init(fmt, 0);
5832+
5833+#if 1
5834+ VLC_UNUSED(sw_pix_fmt);
5835+ if ((fmt->i_chroma = ZcFindVlcChroma(pix_fmt)) == 0)
5836+ return -1;
5837+#else
5838+ if (pix_fmt == sw_pix_fmt)
5839+ { /* software decoding */
5840+ int aligns[AV_NUM_DATA_POINTERS];
5841+
5842+ if (GetVlcChroma(fmt, pix_fmt))
5843+ return -1;
5844+
5845+ /* The libavcodec palette can only be fetched when the first output
5846+ * frame is decoded. Assume that the current chroma is RGB32 while we
5847+ * are waiting for a valid palette. Indeed, fmt_out.video.p_palette
5848+ * doesn't trigger a new vout request, but a new chroma yes. */
5849+ if (pix_fmt == AV_PIX_FMT_PAL8 && !dec->fmt_out.video.p_palette)
5850+ fmt->i_chroma = VLC_CODEC_RGB32;
5851+
5852+ avcodec_align_dimensions2(ctx, &width, &height, aligns);
5853+ }
5854+ else /* hardware decoding */
5855+ fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt);
5856+#endif
5857+
5858+ if( width == 0 || height == 0 || width > 8192 || height > 8192 ||
5859+ width < ctx->width || height < ctx->height )
5860+ {
5861+ msg_Err(dec, "Invalid frame size %dx%d vsz %dx%d",
5862+ width, height, ctx->width, ctx->height );
5863+ return -1; /* invalid display size */
5864+ }
5865+
5866+ fmt->i_width = width;
5867+ fmt->i_height = height;
5868+ fmt->i_visible_width = ctx->width;
5869+ fmt->i_visible_height = ctx->height;
5870+
5871+ /* If an aspect-ratio was specified in the input format then force it */
5872+ if (dec->fmt_in.video.i_sar_num > 0 && dec->fmt_in.video.i_sar_den > 0)
5873+ {
5874+ fmt->i_sar_num = dec->fmt_in.video.i_sar_num;
5875+ fmt->i_sar_den = dec->fmt_in.video.i_sar_den;
5876+ }
5877+ else
5878+ {
5879+ fmt->i_sar_num = ctx->sample_aspect_ratio.num;
5880+ fmt->i_sar_den = ctx->sample_aspect_ratio.den;
5881+
5882+ if (fmt->i_sar_num == 0 || fmt->i_sar_den == 0)
5883+ fmt->i_sar_num = fmt->i_sar_den = 1;
5884+ }
5885+
5886+ if (dec->fmt_in.video.i_frame_rate > 0
5887+ && dec->fmt_in.video.i_frame_rate_base > 0)
5888+ {
5889+ fmt->i_frame_rate = dec->fmt_in.video.i_frame_rate;
5890+ fmt->i_frame_rate_base = dec->fmt_in.video.i_frame_rate_base;
5891+ }
5892+ else if (ctx->framerate.num > 0 && ctx->framerate.den > 0)
5893+ {
5894+ fmt->i_frame_rate = ctx->framerate.num;
5895+ fmt->i_frame_rate_base = ctx->framerate.den;
5896+# if LIBAVCODEC_VERSION_MICRO < 100
5897+ // for some reason libav don't thinkg framerate presents actually same thing as in ffmpeg
5898+ fmt->i_frame_rate_base *= __MAX(ctx->ticks_per_frame, 1);
5899+# endif
5900+ }
5901+ else if (ctx->time_base.num > 0 && ctx->time_base.den > 0)
5902+ {
5903+ fmt->i_frame_rate = ctx->time_base.den;
5904+ fmt->i_frame_rate_base = ctx->time_base.num
5905+ * __MAX(ctx->ticks_per_frame, 1);
5906+ }
5907+
5908+ /* FIXME we should only set the known values and let the core decide
5909+ * later of fallbacks, but we can't do that with a boolean */
5910+ switch ( ctx->color_range )
5911+ {
5912+ case AVCOL_RANGE_JPEG:
5913+ fmt->b_color_range_full = true;
5914+ break;
5915+ case AVCOL_RANGE_UNSPECIFIED:
5916+ fmt->b_color_range_full = !vlc_fourcc_IsYUV( fmt->i_chroma );
5917+ break;
5918+ case AVCOL_RANGE_MPEG:
5919+ default:
5920+ fmt->b_color_range_full = false;
5921+ break;
5922+ }
5923+
5924+ switch( ctx->colorspace )
5925+ {
5926+ case AVCOL_SPC_BT709:
5927+ fmt->space = COLOR_SPACE_BT709;
5928+ break;
5929+ case AVCOL_SPC_SMPTE170M:
5930+ case AVCOL_SPC_BT470BG:
5931+ fmt->space = COLOR_SPACE_BT601;
5932+ break;
5933+ case AVCOL_SPC_BT2020_NCL:
5934+ case AVCOL_SPC_BT2020_CL:
5935+ fmt->space = COLOR_SPACE_BT2020;
5936+ break;
5937+ default:
5938+ break;
5939+ }
5940+
5941+ switch( ctx->color_trc )
5942+ {
5943+ case AVCOL_TRC_LINEAR:
5944+ fmt->transfer = TRANSFER_FUNC_LINEAR;
5945+ break;
5946+ case AVCOL_TRC_GAMMA22:
5947+ fmt->transfer = TRANSFER_FUNC_SRGB;
5948+ break;
5949+ case AVCOL_TRC_BT709:
5950+ fmt->transfer = TRANSFER_FUNC_BT709;
5951+ break;
5952+ case AVCOL_TRC_SMPTE170M:
5953+ case AVCOL_TRC_BT2020_10:
5954+ case AVCOL_TRC_BT2020_12:
5955+ fmt->transfer = TRANSFER_FUNC_BT2020;
5956+ break;
5957+#if LIBAVUTIL_VERSION_CHECK( 55, 14, 0, 31, 100)
5958+ case AVCOL_TRC_ARIB_STD_B67:
5959+ fmt->transfer = TRANSFER_FUNC_ARIB_B67;
5960+ break;
5961+#endif
5962+#if LIBAVUTIL_VERSION_CHECK( 55, 17, 0, 37, 100)
5963+ case AVCOL_TRC_SMPTE2084:
5964+ fmt->transfer = TRANSFER_FUNC_SMPTE_ST2084;
5965+ break;
5966+ case AVCOL_TRC_SMPTE240M:
5967+ fmt->transfer = TRANSFER_FUNC_SMPTE_240;
5968+ break;
5969+ case AVCOL_TRC_GAMMA28:
5970+ fmt->transfer = TRANSFER_FUNC_BT470_BG;
5971+ break;
5972+#endif
5973+ default:
5974+ break;
5975+ }
5976+
5977+ switch( ctx->color_primaries )
5978+ {
5979+ case AVCOL_PRI_BT709:
5980+ fmt->primaries = COLOR_PRIMARIES_BT709;
5981+ break;
5982+ case AVCOL_PRI_BT470BG:
5983+ fmt->primaries = COLOR_PRIMARIES_BT601_625;
5984+ break;
5985+ case AVCOL_PRI_SMPTE170M:
5986+ case AVCOL_PRI_SMPTE240M:
5987+ fmt->primaries = COLOR_PRIMARIES_BT601_525;
5988+ break;
5989+ case AVCOL_PRI_BT2020:
5990+ fmt->primaries = COLOR_PRIMARIES_BT2020;
5991+ break;
5992+ default:
5993+ break;
5994+ }
5995+
5996+ switch( ctx->chroma_sample_location )
5997+ {
5998+ case AVCHROMA_LOC_LEFT:
5999+ fmt->chroma_location = CHROMA_LOCATION_LEFT;
6000+ break;
6001+ case AVCHROMA_LOC_CENTER:
6002+ fmt->chroma_location = CHROMA_LOCATION_CENTER;
6003+ break;
6004+ case AVCHROMA_LOC_TOPLEFT:
6005+ fmt->chroma_location = CHROMA_LOCATION_TOP_LEFT;
6006+ break;
6007+ default:
6008+ break;
6009+ }
6010+
6011+ return 0;
6012+}
6013+
6014+static int lavc_UpdateVideoFormat(decoder_t *dec, AVCodecContext *ctx,
6015+ enum AVPixelFormat fmt,
6016+ enum AVPixelFormat swfmt)
6017+{
6018+ video_format_t fmt_out;
6019+ int val;
6020+#if TRACE_ALL
6021+ msg_Dbg(dec, "<<< %s", __func__);
6022+#endif
6023+ val = lavc_GetVideoFormat(dec, &fmt_out, ctx, fmt, swfmt);
6024+ if (val)
6025+ {
6026+ msg_Dbg(dec, "Failed to get format");
6027+ return val;
6028+ }
6029+
6030+ /* always have date in fields/ticks units */
6031+ if(dec->p_sys->pts.i_divider_num)
6032+ date_Change(&dec->p_sys->pts, fmt_out.i_frame_rate *
6033+ __MAX(ctx->ticks_per_frame, 1),
6034+ fmt_out.i_frame_rate_base);
6035+ else
6036+ date_Init(&dec->p_sys->pts, fmt_out.i_frame_rate *
6037+ __MAX(ctx->ticks_per_frame, 1),
6038+ fmt_out.i_frame_rate_base);
6039+
6040+ fmt_out.p_palette = dec-> fmt_out.video.p_palette;
6041+ dec->fmt_out.video.p_palette = NULL;
6042+
6043+ es_format_Change(&dec->fmt_out, VIDEO_ES, fmt_out.i_chroma);
6044+ dec->fmt_out.video = fmt_out;
6045+ dec->fmt_out.video.orientation = dec->fmt_in.video.orientation;
6046+ dec->fmt_out.video.projection_mode = dec->fmt_in.video.projection_mode;
6047+ dec->fmt_out.video.multiview_mode = dec->fmt_in.video.multiview_mode;
6048+ dec->fmt_out.video.pose = dec->fmt_in.video.pose;
6049+ if ( dec->fmt_in.video.mastering.max_luminance )
6050+ dec->fmt_out.video.mastering = dec->fmt_in.video.mastering;
6051+ dec->fmt_out.video.lighting = dec->fmt_in.video.lighting;
6052+
6053+ val = decoder_UpdateVideoFormat(dec);
6054+#if TRACE_ALL
6055+ msg_Dbg(dec, ">>> %s: rv=%d", __func__, val);
6056+#endif
6057+ return val;
6058+}
6059+
6060+static int OpenVideoCodec( decoder_t *p_dec )
6061+{
6062+ decoder_sys_t *p_sys = p_dec->p_sys;
6063+ AVCodecContext *ctx = p_sys->p_context;
6064+ const AVCodec *codec = p_sys->p_codec;
6065+ int ret;
6066+
6067+ if( ctx->extradata_size <= 0 )
6068+ {
6069+ if( codec->id == AV_CODEC_ID_VC1 ||
6070+ codec->id == AV_CODEC_ID_THEORA )
6071+ {
6072+ msg_Warn( p_dec, "waiting for extra data for codec %s",
6073+ codec->name );
6074+ return 1;
6075+ }
6076+ }
6077+
6078+ ctx->width = p_dec->fmt_in.video.i_visible_width;
6079+ ctx->height = p_dec->fmt_in.video.i_visible_height;
6080+
6081+ ctx->coded_width = p_dec->fmt_in.video.i_width;
6082+ ctx->coded_height = p_dec->fmt_in.video.i_height;
6083+
6084+ ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel;
6085+ p_sys->pix_fmt = AV_PIX_FMT_NONE;
6086+ p_sys->profile = -1;
6087+ p_sys->level = -1;
6088+ cc_Init( &p_sys->cc );
6089+
6090+ set_video_color_settings( &p_dec->fmt_in.video, ctx );
6091+ if( p_dec->fmt_in.video.i_frame_rate_base &&
6092+ p_dec->fmt_in.video.i_frame_rate &&
6093+ (double) p_dec->fmt_in.video.i_frame_rate /
6094+ p_dec->fmt_in.video.i_frame_rate_base < 6 )
6095+ {
6096+ ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
6097+ }
6098+
6099+ post_mt( p_sys );
6100+ ret = ZcFfmpeg_OpenCodec( p_dec, ctx, codec );
6101+ wait_mt( p_sys );
6102+ if( ret < 0 )
6103+ return ret;
6104+
6105+ switch( ctx->active_thread_type )
6106+ {
6107+ case FF_THREAD_FRAME:
6108+ msg_Dbg( p_dec, "using frame thread mode with %d threads",
6109+ ctx->thread_count );
6110+ break;
6111+ case FF_THREAD_SLICE:
6112+ msg_Dbg( p_dec, "using slice thread mode with %d threads",
6113+ ctx->thread_count );
6114+ break;
6115+ case 0:
6116+ if( ctx->thread_count > 1 )
6117+ msg_Warn( p_dec, "failed to enable threaded decoding" );
6118+ break;
6119+ default:
6120+ msg_Warn( p_dec, "using unknown thread mode with %d threads",
6121+ ctx->thread_count );
6122+ break;
6123+ }
6124+ return 0;
6125+}
6126+
6127+/*****************************************************************************
6128+ * InitVideo: initialize the video decoder
6129+ *****************************************************************************
6130+ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet
6131+ * opened (done after the first decoded frame).
6132+ *****************************************************************************/
6133+static int MmalAvcodecOpenDecoder( vlc_object_t *obj )
6134+{
6135+ decoder_t *p_dec = (decoder_t *)obj;
6136+ const AVCodec *p_codec;
6137+
6138+ int extra_buffers = var_InheritInteger(p_dec, MMAL_AVCODEC_BUFFERS);
6139+
6140+ if (extra_buffers < 0)
6141+ {
6142+ extra_buffers = p_dec->fmt_in.video.i_height * p_dec->fmt_in.video.i_width >= 1920 * 1088 ?
6143+ BUFFERS_IN_FLIGHT_UHD : BUFFERS_IN_FLIGHT;
6144+ }
6145+
6146+ if (extra_buffers <= 0)
6147+ {
6148+ msg_Dbg(p_dec, "%s: extra_buffers=%d - cannot use module", __func__, extra_buffers);
6149+ return VLC_EGENERIC;
6150+ }
6151+
6152+ const vcsm_init_type_t vcsm_type = cma_vcsm_init();
6153+ const int vcsm_size =
6154+ vcsm_type == VCSM_INIT_LEGACY ? hw_mmal_get_gpu_mem() : 512 << 20;
6155+
6156+#if 1
6157+ {
6158+ char buf1[5], buf2[5], buf2a[5];
6159+ char buf3[5], buf4[5];
6160+ uint32_t in_fcc = 0;
6161+ msg_Dbg(p_dec, "%s: <<< (%s/%s)[%s] %dx%d -> (%s/%s) %dx%d [%s/%d] xb:%d", __func__,
6162+ str_fourcc(buf1, p_dec->fmt_in.i_codec),
6163+ str_fourcc(buf2, p_dec->fmt_in.video.i_chroma),
6164+ str_fourcc(buf2a, in_fcc),
6165+ p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height,
6166+ str_fourcc(buf3, p_dec->fmt_out.i_codec),
6167+ str_fourcc(buf4, p_dec->fmt_out.video.i_chroma),
6168+ p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height,
6169+ cma_vcsm_init_str(vcsm_type), vcsm_size, extra_buffers);
6170+ }
6171+#endif
6172+
6173+ if( vcsm_type == VCSM_INIT_NONE )
6174+ return VLC_EGENERIC;
6175+#if 1
6176+ if( (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC &&
6177+ (vcsm_type == VCSM_INIT_CMA || vcsm_size < (96 << 20))) ||
6178+ (p_dec->fmt_in.i_codec == VLC_CODEC_HEVC &&
6179+ vcsm_size < (128 << 20)))
6180+ {
6181+ cma_vcsm_exit(vcsm_type);
6182+ return VLC_EGENERIC;
6183+ }
6184+#endif
6185+
6186+ AVCodecContext *p_context = ZcFfmpeg_AllocContext( p_dec, &p_codec );
6187+ if( p_context == NULL )
6188+ {
6189+ cma_vcsm_exit(vcsm_type);
6190+ return VLC_EGENERIC;
6191+ }
6192+
6193+ int i_val;
6194+
6195+ /* Allocate the memory needed to store the decoder's structure */
6196+ decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) );
6197+ if( unlikely(p_sys == NULL) )
6198+ {
6199+ avcodec_free_context( &p_context );
6200+ cma_vcsm_exit(vcsm_type);
6201+ return VLC_ENOMEM;
6202+ }
6203+
6204+ p_dec->p_sys = p_sys;
6205+ p_sys->p_context = p_context;
6206+ p_sys->p_codec = p_codec;
6207+ p_sys->p_dec = p_dec;
6208+// p_sys->p_va = NULL;
6209+ p_sys->cma_in_flight_max = extra_buffers;
6210+ p_sys->vcsm_init_type = vcsm_type;
6211+ vlc_sem_init( &p_sys->sem_mt, 0 );
6212+
6213+ /* ***** Fill p_context with init values ***** */
6214+ p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ?
6215+ p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec );
6216+
6217+ /* ***** Get configuration of ffmpeg plugin ***** */
6218+ p_context->workaround_bugs =
6219+ var_InheritInteger( p_dec, "avcodec-workaround-bugs" );
6220+ p_context->err_recognition =
6221+ var_InheritInteger( p_dec, "avcodec-error-resilience" );
6222+
6223+ if( var_CreateGetBool( p_dec, "grayscale" ) )
6224+ p_context->flags |= AV_CODEC_FLAG_GRAY;
6225+
6226+ /* ***** Output always the frames ***** */
6227+ p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
6228+
6229+ i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" );
6230+ if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL;
6231+ else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY;
6232+ else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR;
6233+ else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF;
6234+ else p_context->skip_loop_filter = AVDISCARD_DEFAULT;
6235+
6236+ if( var_CreateGetBool( p_dec, "avcodec-fast" ) )
6237+ p_context->flags2 |= AV_CODEC_FLAG2_FAST;
6238+
6239+ /* ***** libavcodec frame skipping ***** */
6240+ p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" );
6241+ p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" );
6242+
6243+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" );
6244+ if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL;
6245+ else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY;
6246+ else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR;
6247+ else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF;
6248+ else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE;
6249+ else p_sys->i_skip_frame = AVDISCARD_DEFAULT;
6250+ p_context->skip_frame = p_sys->i_skip_frame;
6251+
6252+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" );
6253+ if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL;
6254+ else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY;
6255+ else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR;
6256+ else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF;
6257+ else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE;
6258+ else p_context->skip_idct = AVDISCARD_DEFAULT;
6259+
6260+ /* ***** libavcodec direct rendering ***** */
6261+ p_sys->b_direct_rendering = false;
6262+ atomic_init(&p_sys->b_dr_failure, false);
6263+ if( var_CreateGetBool( p_dec, "avcodec-dr" ) &&
6264+ (p_codec->capabilities & AV_CODEC_CAP_DR1) &&
6265+ /* No idea why ... but this fixes flickering on some TSCC streams */
6266+ p_sys->p_codec->id != AV_CODEC_ID_TSCC &&
6267+ p_sys->p_codec->id != AV_CODEC_ID_CSCD &&
6268+ p_sys->p_codec->id != AV_CODEC_ID_CINEPAK )
6269+ {
6270+ /* Some codecs set pix_fmt only after the 1st frame has been decoded,
6271+ * so we need to do another check in ffmpeg_GetFrameBuf() */
6272+ p_sys->b_direct_rendering = true;
6273+ }
6274+
6275+ p_context->get_format = ZcGetFormat;
6276+#if 0
6277+ p_context->get_format = ffmpeg_GetFormat;
6278+ /* Always use our get_buffer wrapper so we can calculate the
6279+ * PTS correctly */
6280+ p_context->get_buffer2 = lavc_GetFrame;
6281+ p_context->opaque = p_dec;
6282+#endif
6283+
6284+ int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" );
6285+ if( i_thread_count <= 0 )
6286+#if 1
6287+ {
6288+ // Pick 5 threads for everything on Pi except for HEVC where the h/w
6289+ // really limits the useful size to 3
6290+ i_thread_count = p_codec->id == AV_CODEC_ID_HEVC ? 3 : 5;
6291+ }
6292+#else
6293+ {
6294+ i_thread_count = vlc_GetCPUCount();
6295+ if( i_thread_count > 1 )
6296+ i_thread_count++;
6297+
6298+ //FIXME: take in count the decoding time
6299+#if VLC_WINSTORE_APP
6300+ i_thread_count = __MIN( i_thread_count, 6 );
6301+#else
6302+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 );
6303+#endif
6304+ }
6305+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 );
6306+#endif
6307+ msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count );
6308+ p_context->thread_count = i_thread_count;
6309+ p_context->thread_safe_callbacks = true;
6310+
6311+ switch( p_codec->id )
6312+ {
6313+ case AV_CODEC_ID_MPEG4:
6314+ case AV_CODEC_ID_H263:
6315+ p_context->thread_type = 0;
6316+ break;
6317+ case AV_CODEC_ID_MPEG1VIDEO:
6318+ case AV_CODEC_ID_MPEG2VIDEO:
6319+ p_context->thread_type &= ~FF_THREAD_SLICE;
6320+ /* fall through */
6321+# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0))
6322+ case AV_CODEC_ID_H264:
6323+ case AV_CODEC_ID_VC1:
6324+ case AV_CODEC_ID_WMV3:
6325+ p_context->thread_type &= ~FF_THREAD_FRAME;
6326+# endif
6327+ default:
6328+ break;
6329+ }
6330+
6331+ if( p_context->thread_type & FF_THREAD_FRAME )
6332+ p_dec->i_extra_picture_buffers = 2 * p_context->thread_count;
6333+
6334+ /* ***** misc init ***** */
6335+ date_Init(&p_sys->pts, 1, 30001);
6336+ date_Set(&p_sys->pts, VLC_TS_INVALID);
6337+ p_sys->b_first_frame = true;
6338+ p_sys->i_late_frames = 0;
6339+ p_sys->b_from_preroll = false;
6340+
6341+ /* Set output properties */
6342+ if( ZcGetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS )
6343+ {
6344+ /* we are doomed. but not really, because most codecs set their pix_fmt later on */
6345+// p_dec->fmt_out.i_codec = VLC_CODEC_I420;
6346+ p_dec->fmt_out.i_codec = VLC_CODEC_MMAL_ZC_I420;
6347+ }
6348+ p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma;
6349+
6350+ p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation;
6351+
6352+ if( p_dec->fmt_in.video.p_palette ) {
6353+ p_sys->palette_sent = false;
6354+ p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) );
6355+ if( p_dec->fmt_out.video.p_palette )
6356+ *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette;
6357+ } else
6358+ p_sys->palette_sent = true;
6359+
6360+ 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)
6361+ {
6362+ msg_Err(p_dec, "CMA pool alloc failure");
6363+ goto fail;
6364+ }
6365+
6366+ /* ***** init this codec with special data ***** */
6367+ ffmpeg_InitCodec( p_dec );
6368+
6369+ /* ***** Open the codec ***** */
6370+ if( OpenVideoCodec( p_dec ) < 0 )
6371+ {
6372+ vlc_sem_destroy( &p_sys->sem_mt );
6373+ free( p_sys );
6374+ avcodec_free_context( &p_context );
6375+ return VLC_EGENERIC;
6376+ }
6377+
6378+ p_dec->pf_decode = DecodeVideo;
6379+ p_dec->pf_flush = Flush;
6380+
6381+ /* XXX: Writing input format makes little sense. */
6382+ if( p_context->profile != FF_PROFILE_UNKNOWN )
6383+ p_dec->fmt_in.i_profile = p_context->profile;
6384+ if( p_context->level != FF_LEVEL_UNKNOWN )
6385+ p_dec->fmt_in.i_level = p_context->level;
6386+
6387+#if 1
6388+ // Most of the time we have nothing useful by way of a format here
6389+ // wait till we've decoded something
6390+#else
6391+ // Update output format
6392+ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
6393+ p_context->pix_fmt) != 0)
6394+ {
6395+ msg_Err(p_dec, "Unable to update format: pix_fmt=%d", p_context->pix_fmt);
6396+// goto fail;
6397+ }
6398+#endif
6399+
6400+#if TRACE_ALL
6401+ msg_Dbg(p_dec, "<<< %s: OK", __func__);
6402+#endif
6403+ return VLC_SUCCESS;
6404+
6405+fail:
6406+ MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec));
6407+
6408+#if TRACE_ALL
6409+ msg_Dbg(p_dec, "<<< %s: FAIL", __func__);
6410+#endif
6411+
6412+ return VLC_EGENERIC;
6413+}
6414+
6415+/*****************************************************************************
6416+ * Flush:
6417+ *****************************************************************************/
6418+static void Flush( decoder_t *p_dec )
6419+{
6420+ decoder_sys_t *p_sys = p_dec->p_sys;
6421+ AVCodecContext *p_context = p_sys->p_context;
6422+
6423+#if TRACE_ALL
6424+ msg_Dbg(p_dec, "<<< %s", __func__);
6425+#endif
6426+
6427+ date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */
6428+ p_sys->i_late_frames = 0;
6429+ cc_Flush( &p_sys->cc );
6430+
6431+ /* Abort pictures in order to unblock all avcodec workers threads waiting
6432+ * for a picture. This will avoid a deadlock between avcodec_flush_buffers
6433+ * and workers threads */
6434+// It would probably be good to use AbortPicture but that often deadlocks on close
6435+// and given that we wait for pics in the main thread it should be unneeded (whereas
6436+// cma is alloced in the depths of ffmpeg on its own threads)
6437+// decoder_AbortPictures( p_dec, true );
6438+ cma_buf_pool_cancel(p_sys->cma_pool);
6439+
6440+ post_mt( p_sys );
6441+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
6442+ if( avcodec_is_open( p_context ) )
6443+ avcodec_flush_buffers( p_context );
6444+ wait_mt( p_sys );
6445+
6446+ /* Reset cancel state to false */
6447+ cma_buf_pool_uncancel(p_sys->cma_pool);
6448+// decoder_AbortPictures( p_dec, false );
6449+
6450+#if TRACE_ALL
6451+ msg_Dbg(p_dec, ">>> %s", __func__);
6452+#endif
6453+
6454+}
6455+
6456+static bool check_block_validity( decoder_sys_t *p_sys, block_t *block )
6457+{
6458+ if( !block)
6459+ return true;
6460+
6461+ if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )
6462+ {
6463+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
6464+ cc_Flush( &p_sys->cc );
6465+
6466+ p_sys->i_late_frames = 0;
6467+ if( block->i_flags & BLOCK_FLAG_CORRUPTED )
6468+ {
6469+ block_Release( block );
6470+ return false;
6471+ }
6472+ }
6473+ return true;
6474+}
6475+
6476+static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time)
6477+{
6478+ if( !block )
6479+ return false;
6480+ if( block->i_flags & BLOCK_FLAG_PREROLL )
6481+ {
6482+ /* Do not care about late frames when prerolling
6483+ * TODO avoid decoding of non reference frame
6484+ * (ie all B except for H264 where it depends only on nal_ref_idc) */
6485+ p_sys->i_late_frames = 0;
6486+ p_sys->b_from_preroll = true;
6487+ p_sys->i_last_late_delay = INT64_MAX;
6488+ }
6489+
6490+ if( p_sys->i_late_frames <= 0 )
6491+ return false;
6492+
6493+ if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ))
6494+ {
6495+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
6496+ block_Release( block );
6497+ p_sys->i_late_frames--;
6498+ return true;
6499+ }
6500+ return false;
6501+}
6502+
6503+static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture )
6504+{
6505+ if( p_sys->i_late_frames <= 4)
6506+ return false;
6507+
6508+ *b_need_output_picture = false;
6509+ if( p_sys->i_late_frames < 12 )
6510+ {
6511+ p_context->skip_frame =
6512+ (p_sys->i_skip_frame <= AVDISCARD_NONREF) ?
6513+ AVDISCARD_NONREF : p_sys->i_skip_frame;
6514+ }
6515+ else
6516+ {
6517+ /* picture too late, won't decode
6518+ * but break picture until a new I, and for mpeg4 ...*/
6519+ p_sys->i_late_frames--; /* needed else it will never be decrease */
6520+ return true;
6521+ }
6522+ return false;
6523+}
6524+
6525+static mtime_t interpolate_next_pts( decoder_t *p_dec, AVFrame *frame )
6526+{
6527+ decoder_sys_t *p_sys = p_dec->p_sys;
6528+ AVCodecContext *p_context = p_sys->p_context;
6529+
6530+ if( date_Get( &p_sys->pts ) == VLC_TS_INVALID ||
6531+ p_sys->pts.i_divider_num == 0 )
6532+ return VLC_TS_INVALID;
6533+
6534+ int i_tick = p_context->ticks_per_frame;
6535+ if( i_tick <= 0 )
6536+ i_tick = 1;
6537+
6538+ /* interpolate the next PTS */
6539+ return date_Increment( &p_sys->pts, i_tick + frame->repeat_pict );
6540+}
6541+
6542+static void update_late_frame_count( decoder_t *p_dec, block_t *p_block,
6543+ mtime_t current_time, mtime_t i_pts,
6544+ mtime_t i_next_pts )
6545+{
6546+ decoder_sys_t *p_sys = p_dec->p_sys;
6547+ /* Update frame late count (except when doing preroll) */
6548+ mtime_t i_display_date = VLC_TS_INVALID;
6549+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
6550+ i_display_date = decoder_GetDisplayDate( p_dec, i_pts );
6551+
6552+ mtime_t i_threshold = i_next_pts != VLC_TS_INVALID ? (i_next_pts - i_pts) / 2 : 20000;
6553+
6554+ if( i_display_date > VLC_TS_INVALID && i_display_date + i_threshold <= current_time )
6555+ {
6556+ /* Out of preroll, consider only late frames on rising delay */
6557+ if( p_sys->b_from_preroll )
6558+ {
6559+ if( p_sys->i_last_late_delay > current_time - i_display_date )
6560+ {
6561+ p_sys->i_last_late_delay = current_time - i_display_date;
6562+ return;
6563+ }
6564+ p_sys->b_from_preroll = false;
6565+ }
6566+
6567+ p_sys->i_late_frames++;
6568+ if( p_sys->i_late_frames == 1 )
6569+ p_sys->i_late_frames_start = current_time;
6570+
6571+ }
6572+ else
6573+ {
6574+ p_sys->i_late_frames = 0;
6575+ }
6576+}
6577+
6578+
6579+static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic )
6580+{
6581+ decoder_sys_t *p_sys = p_dec->p_sys;
6582+ bool format_changed = false;
6583+
6584+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
6585+#define FROM_AVRAT(default_factor, avrat) \
6586+(uint64_t)(default_factor) * (avrat).num / (avrat).den
6587+ const AVFrameSideData *metadata =
6588+ av_frame_get_side_data( frame,
6589+ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA );
6590+ if ( metadata )
6591+ {
6592+ const AVMasteringDisplayMetadata *hdr_meta =
6593+ (const AVMasteringDisplayMetadata *) metadata->data;
6594+ if ( hdr_meta->has_luminance )
6595+ {
6596+#define ST2086_LUMA_FACTOR 10000
6597+ p_pic->format.mastering.max_luminance =
6598+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance);
6599+ p_pic->format.mastering.min_luminance =
6600+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance);
6601+ }
6602+ if ( hdr_meta->has_primaries )
6603+ {
6604+#define ST2086_RED 2
6605+#define ST2086_GREEN 0
6606+#define ST2086_BLUE 1
6607+#define LAV_RED 0
6608+#define LAV_GREEN 1
6609+#define LAV_BLUE 2
6610+#define ST2086_PRIM_FACTOR 50000
6611+ p_pic->format.mastering.primaries[ST2086_RED*2 + 0] =
6612+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]);
6613+ p_pic->format.mastering.primaries[ST2086_RED*2 + 1] =
6614+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]);
6615+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] =
6616+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]);
6617+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] =
6618+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]);
6619+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] =
6620+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]);
6621+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] =
6622+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]);
6623+ p_pic->format.mastering.white_point[0] =
6624+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]);
6625+ p_pic->format.mastering.white_point[1] =
6626+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]);
6627+ }
6628+
6629+ if ( memcmp( &p_dec->fmt_out.video.mastering,
6630+ &p_pic->format.mastering,
6631+ sizeof(p_pic->format.mastering) ) )
6632+ {
6633+ p_dec->fmt_out.video.mastering = p_pic->format.mastering;
6634+ format_changed = true;
6635+ }
6636+#undef FROM_AVRAT
6637+ }
6638+#endif
6639+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) )
6640+ const AVFrameSideData *metadata_lt =
6641+ av_frame_get_side_data( frame,
6642+ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL );
6643+ if ( metadata_lt )
6644+ {
6645+ const AVContentLightMetadata *light_meta =
6646+ (const AVContentLightMetadata *) metadata_lt->data;
6647+ p_pic->format.lighting.MaxCLL = light_meta->MaxCLL;
6648+ p_pic->format.lighting.MaxFALL = light_meta->MaxFALL;
6649+ if ( memcmp( &p_dec->fmt_out.video.lighting,
6650+ &p_pic->format.lighting,
6651+ sizeof(p_pic->format.lighting) ) )
6652+ {
6653+ p_dec->fmt_out.video.lighting = p_pic->format.lighting;
6654+ format_changed = true;
6655+ }
6656+ }
6657+#endif
6658+
6659+ if (format_changed && decoder_UpdateVideoFormat( p_dec ))
6660+ return -1;
6661+
6662+ const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC );
6663+ if( p_avcc )
6664+ {
6665+ cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size );
6666+ if( p_sys->cc.b_reorder || p_sys->cc.i_data )
6667+ {
6668+ block_t *p_cc = block_Alloc( p_sys->cc.i_data );
6669+ if( p_cc )
6670+ {
6671+ memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
6672+ if( p_sys->cc.b_reorder )
6673+ p_cc->i_dts = p_cc->i_pts = p_pic->date;
6674+ else
6675+ p_cc->i_pts = p_cc->i_dts;
6676+ decoder_cc_desc_t desc;
6677+ desc.i_608_channels = p_sys->cc.i_608channels;
6678+ desc.i_708_channels = p_sys->cc.i_708channels;
6679+ desc.i_reorder_depth = 4;
6680+ decoder_QueueCc( p_dec, p_cc, &desc );
6681+ }
6682+ cc_Flush( &p_sys->cc );
6683+ }
6684+ }
6685+ return 0;
6686+}
6687+
6688+/*****************************************************************************
6689+ * DecodeBlock: Called to decode one or more frames
6690+ *****************************************************************************/
6691+
6692+static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error )
6693+{
6694+ decoder_sys_t *p_sys = p_dec->p_sys;
6695+ AVCodecContext *p_context = p_sys->p_context;
6696+ /* Boolean if we assume that we should get valid pic as result */
6697+ bool b_need_output_picture = true;
6698+
6699+ /* Boolean for END_OF_SEQUENCE */
6700+ bool eos_spotted = false;
6701+
6702+#if TRACE_ALL
6703+ msg_Dbg(p_dec, "<<< %s: (buf_size=%d)", __func__, pp_block == NULL || *pp_block == NULL ? 0 : (*pp_block)->i_buffer);
6704+#endif
6705+
6706+ block_t *p_block;
6707+ mtime_t current_time;
6708+ picture_t *p_pic = NULL;
6709+ AVFrame *frame = NULL;
6710+
6711+ // By default we are OK
6712+ *error = false;
6713+
6714+ if( !p_context->extradata_size && p_dec->fmt_in.i_extra )
6715+ {
6716+ ffmpeg_InitCodec( p_dec );
6717+ if( !avcodec_is_open( p_context ) )
6718+ OpenVideoCodec( p_dec );
6719+ }
6720+
6721+ p_block = pp_block ? *pp_block : NULL;
6722+ if(!p_block && !(p_sys->p_codec->capabilities & AV_CODEC_CAP_DELAY) )
6723+ return NULL;
6724+
6725+ if( !avcodec_is_open( p_context ) )
6726+ {
6727+ if( p_block )
6728+ block_Release( p_block );
6729+ return NULL;
6730+ }
6731+
6732+ if( !check_block_validity( p_sys, p_block ) )
6733+ return NULL;
6734+
6735+ current_time = mdate();
6736+ if( p_dec->b_frame_drop_allowed && check_block_being_late( p_sys, p_block, current_time) )
6737+ {
6738+ msg_Err( p_dec, "more than 5 seconds of late video -> "
6739+ "dropping frame (computer too slow ?)" );
6740+ return NULL;
6741+ }
6742+
6743+
6744+ /* A good idea could be to decode all I pictures and see for the other */
6745+
6746+ /* Defaults that if we aren't in prerolling, we want output picture
6747+ same for if we are flushing (p_block==NULL) */
6748+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
6749+ b_need_output_picture = true;
6750+ else
6751+ b_need_output_picture = false;
6752+
6753+ /* Change skip_frame config only if hurry_up is enabled */
6754+ if( p_sys->b_hurry_up )
6755+ {
6756+ p_context->skip_frame = p_sys->i_skip_frame;
6757+
6758+ /* Check also if we should/can drop the block and move to next block
6759+ as trying to catchup the speed*/
6760+ if( p_dec->b_frame_drop_allowed &&
6761+ check_frame_should_be_dropped( p_sys, p_context, &b_need_output_picture ) )
6762+ {
6763+ if( p_block )
6764+ block_Release( p_block );
6765+ msg_Warn( p_dec, "More than 11 late frames, dropping frame" );
6766+ return NULL;
6767+ }
6768+ }
6769+ if( !b_need_output_picture )
6770+ {
6771+ p_context->skip_frame = __MAX( p_context->skip_frame,
6772+ AVDISCARD_NONREF );
6773+ }
6774+
6775+ /*
6776+ * Do the actual decoding now */
6777+
6778+ /* Don't forget that libavcodec requires a little more bytes
6779+ * that the real frame size */
6780+ if( p_block && p_block->i_buffer > 0 )
6781+ {
6782+ eos_spotted = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0;
6783+
6784+ p_block = block_Realloc( p_block, 0,
6785+ p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE );
6786+ if( !p_block )
6787+ return NULL;
6788+ p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
6789+ *pp_block = p_block;
6790+ memset( p_block->p_buffer + p_block->i_buffer, 0,
6791+ FF_INPUT_BUFFER_PADDING_SIZE );
6792+ }
6793+
6794+ while( !p_block || p_block->i_buffer > 0 || eos_spotted )
6795+ {
6796+ int i_used;
6797+ AVPacket pkt;
6798+
6799+ post_mt( p_sys );
6800+
6801+ av_init_packet( &pkt );
6802+ if( p_block && p_block->i_buffer > 0 )
6803+ {
6804+ pkt.data = p_block->p_buffer;
6805+ pkt.size = p_block->i_buffer;
6806+ pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE;
6807+ pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE;
6808+ }
6809+ else
6810+ {
6811+ /* Return delayed frames if codec has CODEC_CAP_DELAY */
6812+ pkt.data = NULL;
6813+ pkt.size = 0;
6814+ }
6815+
6816+ if( !p_sys->palette_sent )
6817+ {
6818+ uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
6819+ if (pal) {
6820+ memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE);
6821+ p_sys->palette_sent = true;
6822+ }
6823+ }
6824+
6825+ /* Make sure we don't reuse the same timestamps twice */
6826+ if( p_block )
6827+ {
6828+ p_block->i_pts =
6829+ p_block->i_dts = VLC_TS_INVALID;
6830+ }
6831+
6832+ int ret = avcodec_send_packet(p_context, &pkt);
6833+ if( ret != 0 && ret != AVERROR(EAGAIN) )
6834+ {
6835+ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
6836+ {
6837+ msg_Err(p_dec, "avcodec_send_packet critical error");
6838+ *error = true;
6839+ }
6840+ av_packet_unref( &pkt );
6841+ break;
6842+ }
6843+ i_used = ret != AVERROR(EAGAIN) ? pkt.size : 0;
6844+ av_packet_unref( &pkt );
6845+
6846+ frame = av_frame_alloc();
6847+ if (unlikely(frame == NULL))
6848+ {
6849+ *error = true;
6850+ break;
6851+ }
6852+
6853+ ret = avcodec_receive_frame(p_context, frame);
6854+ if( ret != 0 && ret != AVERROR(EAGAIN) )
6855+ {
6856+ msg_Dbg(p_dec, "No receive");
6857+ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
6858+ {
6859+ msg_Err(p_dec, "avcodec_receive_frame critical error");
6860+ *error = true;
6861+ }
6862+ av_frame_free(&frame);
6863+ /* After draining, we need to reset decoder with a flush */
6864+ if( ret == AVERROR_EOF )
6865+ avcodec_flush_buffers( p_sys->p_context );
6866+ break;
6867+ }
6868+ bool not_received_frame = ret;
6869+
6870+ wait_mt( p_sys );
6871+
6872+ if( eos_spotted )
6873+ p_sys->b_first_frame = true;
6874+
6875+ if( p_block )
6876+ {
6877+ if( p_block->i_buffer <= 0 )
6878+ eos_spotted = false;
6879+
6880+ /* Consumed bytes */
6881+ p_block->p_buffer += i_used;
6882+ p_block->i_buffer -= i_used;
6883+ }
6884+
6885+ /* Nothing to display */
6886+ if( not_received_frame )
6887+ {
6888+// msg_Dbg(p_dec, "No rx: used=%d", i_used);
6889+ av_frame_free(&frame);
6890+ if( i_used == 0 ) break;
6891+ continue;
6892+ }
6893+
6894+ /* Compute the PTS */
6895+#ifdef FF_API_PKT_PTS
6896+ mtime_t i_pts = frame->pts;
6897+#else
6898+ mtime_t i_pts = frame->pkt_pts;
6899+#endif
6900+ if (i_pts == AV_NOPTS_VALUE )
6901+ i_pts = frame->pkt_dts;
6902+
6903+ if( i_pts == AV_NOPTS_VALUE )
6904+ i_pts = date_Get( &p_sys->pts );
6905+
6906+ /* Interpolate the next PTS */
6907+ if( i_pts > VLC_TS_INVALID )
6908+ date_Set( &p_sys->pts, i_pts );
6909+
6910+ const mtime_t i_next_pts = interpolate_next_pts(p_dec, frame);
6911+
6912+ update_late_frame_count( p_dec, p_block, current_time, i_pts, i_next_pts);
6913+
6914+ if( !b_need_output_picture ||
6915+// ( !p_sys->p_va && !frame->linesize[0] ) ||
6916+ ( !frame->linesize[0] ) ||
6917+ ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) &&
6918+ !p_sys->b_show_corrupted ) )
6919+ {
6920+ av_frame_free(&frame);
6921+// msg_Dbg(p_dec, "Bad frame");
6922+ continue;
6923+ }
6924+
6925+ if( p_context->pix_fmt == AV_PIX_FMT_PAL8
6926+ && !p_dec->fmt_out.video.p_palette )
6927+ {
6928+ /* See AV_PIX_FMT_PAL8 comment in avc_GetVideoFormat(): update the
6929+ * fmt_out palette and change the fmt_out chroma to request a new
6930+ * vout */
6931+ assert( p_dec->fmt_out.video.i_chroma != VLC_CODEC_RGBP );
6932+
6933+ video_palette_t *p_palette;
6934+ p_palette = p_dec->fmt_out.video.p_palette
6935+ = malloc( sizeof(video_palette_t) );
6936+ if( !p_palette )
6937+ {
6938+ *error = true;
6939+ av_frame_free(&frame);
6940+ break;
6941+ }
6942+ static_assert( sizeof(p_palette->palette) == AVPALETTE_SIZE,
6943+ "Palette size mismatch between vlc and libavutil" );
6944+ assert( frame->data[1] != NULL );
6945+ memcpy( p_palette->palette, frame->data[1], AVPALETTE_SIZE );
6946+ p_palette->i_entries = AVPALETTE_COUNT;
6947+ p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBP;
6948+ if( decoder_UpdateVideoFormat( p_dec ) )
6949+ {
6950+ av_frame_free(&frame);
6951+ continue;
6952+ }
6953+ }
6954+
6955+#if 1
6956+ {
6957+ cma_buf_t * const cb = av_rpi_zc_buf_v(frame->buf[0]);
6958+
6959+ if (cb == NULL)
6960+ {
6961+ msg_Err(p_dec, "Frame has no attached CMA buffer");
6962+ goto fail;
6963+ }
6964+
6965+ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
6966+ p_context->pix_fmt) != 0)
6967+ {
6968+ msg_Err(p_dec, "Failed to update format");
6969+ goto fail;
6970+ }
6971+
6972+ if ((p_pic = decoder_NewPicture(p_dec)) == NULL)
6973+ {
6974+ msg_Err(p_dec, "Failed to allocate pic");
6975+ goto fail;
6976+ }
6977+
6978+ if (cma_buf_pic_attach(cma_buf_ref(cb), p_pic) != 0)
6979+ {
6980+ cma_buf_unref(cb); // Undo the in_flight
6981+ char dbuf0[5];
6982+ msg_Err(p_dec, "Failed to attach bufs to pic: fmt=%s", str_fourcc(dbuf0, p_pic->format.i_chroma));
6983+ goto fail;
6984+ }
6985+
6986+ // ****** Set planes etc.
6987+ set_pic_from_frame(p_pic, frame);
6988+ }
6989+#else
6990+ picture_t *p_pic = frame->opaque;
6991+ if( p_pic == NULL )
6992+ { /* When direct rendering is not used, get_format() and get_buffer()
6993+ * might not be called. The output video format must be set here
6994+ * then picture buffer can be allocated. */
6995+ if (p_sys->p_va == NULL
6996+ && lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
6997+ p_context->pix_fmt) == 0)
6998+ p_pic = decoder_NewPicture(p_dec);
6999+
7000+ if( !p_pic )
7001+ {
7002+ av_frame_free(&frame);
7003+ break;
7004+ }
7005+
7006+ /* Fill picture_t from AVFrame */
7007+ if( lavc_CopyPicture( p_dec, p_pic, frame ) != VLC_SUCCESS )
7008+ {
7009+ av_frame_free(&frame);
7010+ picture_Release( p_pic );
7011+ break;
7012+ }
7013+ }
7014+ else
7015+ {
7016+ /* Some codecs can return the same frame multiple times. By the
7017+ * time that the same frame is returned a second time, it will be
7018+ * too late to clone the underlying picture. So clone proactively.
7019+ * A single picture CANNOT be queued multiple times.
7020+ */
7021+ p_pic = picture_Clone( p_pic );
7022+ if( unlikely(p_pic == NULL) )
7023+ {
7024+ av_frame_free(&frame);
7025+ break;
7026+ }
7027+ }
7028+#endif
7029+
7030+ if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den )
7031+ {
7032+ /* Fetch again the aspect ratio in case it changed */
7033+ p_dec->fmt_out.video.i_sar_num
7034+ = p_context->sample_aspect_ratio.num;
7035+ p_dec->fmt_out.video.i_sar_den
7036+ = p_context->sample_aspect_ratio.den;
7037+
7038+ if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den )
7039+ {
7040+ p_dec->fmt_out.video.i_sar_num = 1;
7041+ p_dec->fmt_out.video.i_sar_den = 1;
7042+ }
7043+ }
7044+
7045+ p_pic->date = i_pts;
7046+ /* Hack to force display of still pictures */
7047+ p_pic->b_force = p_sys->b_first_frame;
7048+ p_pic->i_nb_fields = 2 + frame->repeat_pict;
7049+ p_pic->b_progressive = !frame->interlaced_frame;
7050+ p_pic->b_top_field_first = frame->top_field_first;
7051+
7052+ if (DecodeSidedata(p_dec, frame, p_pic))
7053+ i_pts = VLC_TS_INVALID;
7054+
7055+ av_frame_free(&frame);
7056+
7057+ /* Send decoded frame to vout */
7058+ if (i_pts > VLC_TS_INVALID)
7059+ {
7060+ p_sys->b_first_frame = false;
7061+#if TRACE_ALL
7062+ msg_Dbg(p_dec, ">>> %s: Got pic", __func__);
7063+#endif
7064+ return p_pic;
7065+ }
7066+ else
7067+ picture_Release( p_pic );
7068+ }
7069+
7070+ if( p_block )
7071+ block_Release( p_block );
7072+
7073+#if TRACE_ALL
7074+ msg_Dbg(p_dec, ">>> %s: NULL", __func__);
7075+#endif
7076+ return NULL;
7077+
7078+fail:
7079+#if TRACE_ALL
7080+ msg_Dbg(p_dec, ">>> %s: FAIL", __func__);
7081+#endif
7082+ av_frame_free(&frame);
7083+ if (p_pic != NULL)
7084+ picture_Release(p_pic);
7085+ if (p_block != NULL)
7086+ block_Release(p_block);
7087+ *error = true;
7088+ return NULL;
7089+}
7090+
7091+static int DecodeVideo( decoder_t *p_dec, block_t *p_block )
7092+{
7093+ block_t **pp_block = p_block ? &p_block : NULL;
7094+ picture_t *p_pic;
7095+ bool error = false;
7096+ while( ( p_pic = DecodeBlock( p_dec, pp_block, &error ) ) != NULL )
7097+ decoder_QueueVideo( p_dec, p_pic );
7098+ return VLCDEC_SUCCESS;
7099+// Easiest to just ignore all errors - returning a real error seems to
7100+// kill output forever
7101+// return error ? VLCDEC_ECRITICAL : VLCDEC_SUCCESS;
7102+}
7103+
7104+/*****************************************************************************
7105+ * EndVideo: decoder destruction
7106+ *****************************************************************************
7107+ * This function is called when the thread ends after a successful
7108+ * initialization.
7109+ *****************************************************************************/
7110+static void MmalAvcodecCloseDecoder( vlc_object_t *obj )
7111+{
7112+ decoder_t *p_dec = (decoder_t *)obj;
7113+ decoder_sys_t *p_sys = p_dec->p_sys;
7114+ AVCodecContext *ctx = p_sys->p_context;
7115+// void *hwaccel_context;
7116+
7117+ msg_Dbg(obj, "<<< %s", __func__);
7118+
7119+ post_mt( p_sys );
7120+
7121+ cma_buf_pool_cancel(p_sys->cma_pool); // Abort any pending frame allocs
7122+
7123+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
7124+ if( avcodec_is_open( ctx ) )
7125+ avcodec_flush_buffers( ctx );
7126+
7127+ av_rpi_zc_uninit2(ctx);
7128+
7129+ wait_mt( p_sys );
7130+
7131+ cc_Flush( &p_sys->cc );
7132+
7133+// hwaccel_context = ctx->hwaccel_context;
7134+ avcodec_free_context( &ctx );
7135+
7136+// if( p_sys->p_va )
7137+// vlc_va_Delete( p_sys->p_va, &hwaccel_context );
7138+
7139+ cma_vcsm_exit(p_sys->vcsm_init_type);
7140+
7141+ vlc_sem_destroy( &p_sys->sem_mt );
7142+ free( p_sys );
7143+}
7144+
7145+/*****************************************************************************
7146+ * ffmpeg_InitCodec: setup codec extra initialization data for ffmpeg
7147+ *****************************************************************************/
7148+static void ffmpeg_InitCodec( decoder_t *p_dec )
7149+{
7150+ decoder_sys_t *p_sys = p_dec->p_sys;
7151+ size_t i_size = p_dec->fmt_in.i_extra;
7152+
7153+ if( !i_size ) return;
7154+
7155+ if( p_sys->p_codec->id == AV_CODEC_ID_SVQ3 )
7156+ {
7157+ uint8_t *p;
7158+
7159+ p_sys->p_context->extradata_size = i_size + 12;
7160+ p = p_sys->p_context->extradata =
7161+ av_malloc( p_sys->p_context->extradata_size +
7162+ FF_INPUT_BUFFER_PADDING_SIZE );
7163+ if( !p )
7164+ return;
7165+
7166+ memcpy( &p[0], "SVQ3", 4 );
7167+ memset( &p[4], 0, 8 );
7168+ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size );
7169+
7170+ /* Now remove all atoms before the SMI one */
7171+ if( p_sys->p_context->extradata_size > 0x5a &&
7172+ strncmp( (char*)&p[0x56], "SMI ", 4 ) )
7173+ {
7174+ uint8_t *psz = &p[0x52];
7175+
7176+ while( psz < &p[p_sys->p_context->extradata_size - 8] )
7177+ {
7178+ uint_fast32_t atom_size = GetDWBE( psz );
7179+ if( atom_size <= 1 )
7180+ {
7181+ /* FIXME handle 1 as long size */
7182+ break;
7183+ }
7184+ if( !strncmp( (char*)&psz[4], "SMI ", 4 ) )
7185+ {
7186+ memmove( &p[0x52], psz,
7187+ &p[p_sys->p_context->extradata_size] - psz );
7188+ break;
7189+ }
7190+
7191+ psz += atom_size;
7192+ }
7193+ }
7194+ }
7195+ else
7196+ {
7197+ p_sys->p_context->extradata_size = i_size;
7198+ p_sys->p_context->extradata =
7199+ av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );
7200+ if( p_sys->p_context->extradata )
7201+ {
7202+ memcpy( p_sys->p_context->extradata,
7203+ p_dec->fmt_in.p_extra, i_size );
7204+ memset( p_sys->p_context->extradata + i_size,
7205+ 0, FF_INPUT_BUFFER_PADDING_SIZE );
7206+ }
7207+ }
7208+}
7209+
7210+
7211+vlc_module_begin()
7212+ set_category( CAT_INPUT )
7213+ set_subcategory( SUBCAT_INPUT_VCODEC )
7214+ set_shortname(N_("MMAL avcodec"))
7215+ set_description(N_("MMAL buffered avcodec "))
7216+ set_capability("video decoder", 80)
7217+ add_shortcut("mmal_avcodec")
7218+ add_integer(MMAL_AVCODEC_BUFFERS, -1, MMAL_AVCODEC_BUFFERS_TEXT,
7219+ MMAL_AVCODEC_BUFFERS_LONGTEXT, true)
7220+ set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder)
7221+vlc_module_end()
7222+
7223--- /dev/null
7224+++ b/modules/hw/mmal/mmal_cma.c
7225@@ -0,0 +1,668 @@
7226+#ifdef HAVE_CONFIG_H
7227+# include "config.h"
7228+#endif
7229+
7230+#include <stdatomic.h>
7231+#include <unistd.h>
7232+#include <fcntl.h>
7233+#include <sys/ioctl.h>
7234+#include <sys/mman.h>
7235+
7236+#include <interface/vcsm/user-vcsm.h>
7237+
7238+#include <vlc_common.h>
7239+#include <vlc_picture.h>
7240+
7241+#include "mmal_cma.h"
7242+#include "mmal_picture.h"
7243+
7244+#include <assert.h>
7245+
7246+#define TRACE_ALL 0
7247+
7248+//-----------------------------------------------------------------------------
7249+//
7250+// Generic pool functions
7251+// Knows nothing about pool entries
7252+
7253+typedef void * cma_pool_alloc_fn(void * v, size_t size);
7254+typedef void cma_pool_free_fn(void * v, void * el, size_t size);
7255+
7256+#if TRACE_ALL
7257+static atomic_int pool_seq;
7258+#endif
7259+
7260+// Pool structure
7261+// Ref count is held by pool owner and pool els that have been got
7262+// Els in the pool do not count towards its ref count
7263+struct cma_pool_fixed_s
7264+{
7265+ atomic_int ref_count;
7266+
7267+ vlc_mutex_t lock;
7268+ unsigned int n_in;
7269+ unsigned int n_out;
7270+ unsigned int pool_size;
7271+ int flight_size;
7272+ size_t el_size;
7273+ void ** pool;
7274+
7275+ bool cancel;
7276+ int in_flight;
7277+ vlc_cond_t flight_cond;
7278+
7279+ void * alloc_v;
7280+ cma_pool_alloc_fn * el_alloc_fn;
7281+ cma_pool_free_fn * el_free_fn;
7282+ cma_pool_on_delete_fn * on_delete_fn;
7283+
7284+ const char * name;
7285+#if TRACE_ALL
7286+ int seq;
7287+#endif
7288+};
7289+
7290+static inline unsigned int inc_mod(const unsigned int n, const unsigned int m)
7291+{
7292+ return n + 1 >= m ? 0 : n + 1;
7293+}
7294+
7295+static void free_pool(const cma_pool_fixed_t * const p, void ** const pool,
7296+ const unsigned int pool_size, const size_t el_size)
7297+{
7298+ if (pool == NULL)
7299+ return;
7300+
7301+ for (unsigned int n = 0; n != pool_size; ++n)
7302+ if (pool[n] != NULL)
7303+ p->el_free_fn(p->alloc_v, pool[n], el_size);
7304+ free(pool);
7305+}
7306+
7307+// Just kill this - no checks
7308+static void cma_pool_fixed_delete(cma_pool_fixed_t * const p)
7309+{
7310+ cma_pool_on_delete_fn *const on_delete_fn = p->on_delete_fn;
7311+ void *const v = p->alloc_v;
7312+
7313+ free_pool(p, p->pool, p->pool_size, p->el_size);
7314+
7315+ if (p->name != NULL)
7316+ free((void *)p->name); // Discard const
7317+
7318+ vlc_cond_destroy(&p->flight_cond);
7319+ vlc_mutex_destroy(&p->lock);
7320+ free(p);
7321+
7322+ // Inform our container that we are dead (if it cares)
7323+ if (on_delete_fn)
7324+ on_delete_fn(v);
7325+}
7326+
7327+static void cma_pool_fixed_unref(cma_pool_fixed_t * const p)
7328+{
7329+ if (atomic_fetch_sub(&p->ref_count, 1) <= 1)
7330+ cma_pool_fixed_delete(p);
7331+}
7332+
7333+static void cma_pool_fixed_ref(cma_pool_fixed_t * const p)
7334+{
7335+ atomic_fetch_add(&p->ref_count, 1);
7336+}
7337+
7338+static void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p)
7339+{
7340+ vlc_mutex_lock(&p->lock);
7341+ ++p->in_flight;
7342+ vlc_mutex_unlock(&p->lock);
7343+}
7344+
7345+static void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p)
7346+{
7347+ vlc_mutex_lock(&p->lock);
7348+ if (--p->in_flight == 0)
7349+ vlc_cond_signal(&p->flight_cond);
7350+ vlc_mutex_unlock(&p->lock);
7351+}
7352+
7353+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)
7354+{
7355+ void * v = NULL;
7356+
7357+ vlc_mutex_lock(&p->lock);
7358+
7359+ for (;;)
7360+ {
7361+ if (req_el_size != p->el_size)
7362+ {
7363+ void ** const deadpool = p->pool;
7364+ const size_t dead_size = p->el_size;
7365+ const unsigned int dead_n = p->pool_size;
7366+
7367+ p->pool = NULL;
7368+ p->n_in = 0;
7369+ p->n_out = 0;
7370+ p->el_size = req_el_size;
7371+
7372+ if (deadpool != NULL)
7373+ {
7374+ vlc_mutex_unlock(&p->lock);
7375+ // Do the free old op outside the mutex in case the free is slow
7376+ free_pool(p, deadpool, dead_n, dead_size);
7377+ vlc_mutex_lock(&p->lock);
7378+ continue;
7379+ }
7380+ }
7381+
7382+ // Late abort if flush or cancel so we can still kill the pool
7383+ if (req_el_size == 0 || p->cancel)
7384+ {
7385+ vlc_mutex_unlock(&p->lock);
7386+ return NULL;
7387+ }
7388+
7389+ if (p->pool != NULL && !no_pool)
7390+ {
7391+ v = p->pool[p->n_in];
7392+ if (v != NULL)
7393+ {
7394+ p->pool[p->n_in] = NULL;
7395+ p->n_in = inc_mod(p->n_in, p->pool_size);
7396+ break;
7397+ }
7398+ }
7399+
7400+ if (p->in_flight <= 0)
7401+ break;
7402+
7403+ vlc_cond_wait(&p->flight_cond, &p->lock);
7404+ }
7405+
7406+ if (inc_flight)
7407+ ++p->in_flight;
7408+
7409+ vlc_mutex_unlock(&p->lock);
7410+
7411+ if (v == NULL && req_el_size != 0)
7412+ v = p->el_alloc_fn(p->alloc_v, req_el_size);
7413+
7414+ // Tag ref
7415+ if (v != NULL)
7416+ cma_pool_fixed_ref(p);
7417+ // Remove flight if we set it and error
7418+ else if (inc_flight)
7419+ cma_pool_fixed_dec_in_flight(p);
7420+
7421+ return v;
7422+}
7423+
7424+static void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight)
7425+{
7426+ vlc_mutex_lock(&p->lock);
7427+
7428+ if (el_size == p->el_size && (p->pool == NULL || p->pool[p->n_out] == NULL))
7429+ {
7430+ if (p->pool == NULL)
7431+ p->pool = calloc(p->pool_size, sizeof(void*));
7432+
7433+ p->pool[p->n_out] = v;
7434+ p->n_out = inc_mod(p->n_out, p->pool_size);
7435+ v = NULL;
7436+ }
7437+
7438+ if (was_in_flight)
7439+ --p->in_flight;
7440+
7441+ vlc_mutex_unlock(&p->lock);
7442+
7443+ vlc_cond_signal(&p->flight_cond);
7444+
7445+ if (v != NULL)
7446+ p->el_free_fn(p->alloc_v, v, el_size);
7447+
7448+ cma_pool_fixed_unref(p);
7449+}
7450+
7451+static int cma_pool_fixed_resize(cma_pool_fixed_t * const p,
7452+ const unsigned int new_pool_size, const int new_flight_size)
7453+{
7454+ void ** dead_pool = NULL;
7455+ size_t dead_size = 0;
7456+ unsigned int dead_n = 0;
7457+
7458+ // This makes this non-reentrant but saves us a lot of time in the normal
7459+ // "nothing happens" case
7460+ if (p->pool_size == new_pool_size && p->flight_size == new_flight_size)
7461+ return 0;
7462+
7463+ vlc_mutex_lock(&p->lock);
7464+
7465+ if (p->pool != NULL && new_pool_size != p->pool_size)
7466+ {
7467+ void ** const new_pool = calloc(new_pool_size, sizeof(void*));
7468+ unsigned int d, s;
7469+ dead_pool = p->pool;
7470+ dead_size = p->el_size;
7471+ dead_n = p->pool_size;
7472+
7473+ if (new_pool == NULL)
7474+ {
7475+ vlc_mutex_unlock(&p->lock);
7476+ return -1;
7477+ }
7478+
7479+ 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))
7480+ dead_pool[s] = NULL;
7481+
7482+ p->n_out = 0;
7483+ p->n_in = (d != new_pool_size) ? d : 0;
7484+ p->pool = new_pool;
7485+ }
7486+
7487+ p->pool_size = new_pool_size;
7488+ if (new_flight_size > p->flight_size)
7489+ vlc_cond_broadcast(&p->flight_cond); // Lock still active so nothing happens till we release it
7490+ p->in_flight += p->flight_size - new_flight_size;
7491+ p->flight_size = new_flight_size;
7492+
7493+ vlc_mutex_unlock(&p->lock);
7494+
7495+ free_pool(p, dead_pool, dead_n, dead_size);
7496+ return 0;
7497+}
7498+
7499+static int cma_pool_fixed_fill(cma_pool_fixed_t * const p, const size_t el_size)
7500+{
7501+ for (;;)
7502+ {
7503+ vlc_mutex_lock(&p->lock);
7504+ bool done = el_size == p->el_size && p->pool != NULL && p->pool[p->n_out] != NULL;
7505+ vlc_mutex_unlock(&p->lock);
7506+ if (done)
7507+ break;
7508+ void * buf = cma_pool_fixed_get(p, el_size, false, true);
7509+ if (buf == NULL)
7510+ return -ENOMEM;
7511+ cma_pool_fixed_put(p, buf, el_size, false);
7512+ }
7513+ return 0;
7514+}
7515+
7516+static void cma_pool_fixed_cancel(cma_pool_fixed_t * const p)
7517+{
7518+ vlc_mutex_lock(&p->lock);
7519+ p->cancel = true;
7520+ vlc_cond_broadcast(&p->flight_cond);
7521+ vlc_mutex_unlock(&p->lock);
7522+}
7523+
7524+static void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p)
7525+{
7526+ vlc_mutex_lock(&p->lock);
7527+ p->cancel = false;
7528+ vlc_mutex_unlock(&p->lock);
7529+}
7530+
7531+
7532+// Purge pool & unref
7533+static void cma_pool_fixed_kill(cma_pool_fixed_t * const p)
7534+{
7535+ if (p == NULL)
7536+ return;
7537+
7538+ // This flush is not strictly needed but it reclaims what memory we can reclaim asap
7539+ cma_pool_fixed_get(p, 0, false, false);
7540+ cma_pool_fixed_unref(p);
7541+}
7542+
7543+// Create a new pool
7544+static cma_pool_fixed_t*
7545+cma_pool_fixed_new(const unsigned int pool_size,
7546+ const int flight_size,
7547+ void * const alloc_v,
7548+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn,
7549+ cma_pool_on_delete_fn * const on_delete_fn,
7550+ const char * const name)
7551+{
7552+ cma_pool_fixed_t* const p = calloc(1, sizeof(cma_pool_fixed_t));
7553+ if (p == NULL)
7554+ return NULL;
7555+
7556+ atomic_store(&p->ref_count, 1);
7557+ vlc_mutex_init(&p->lock);
7558+ vlc_cond_init(&p->flight_cond);
7559+
7560+ p->pool_size = pool_size;
7561+ p->flight_size = flight_size;
7562+ p->in_flight = -flight_size;
7563+
7564+ p->alloc_v = alloc_v;
7565+ p->el_alloc_fn = alloc_fn;
7566+ p->el_free_fn = free_fn;
7567+ p->on_delete_fn = on_delete_fn;
7568+ p->name = name == NULL ? NULL : strdup(name);
7569+#if TRACE_ALL
7570+ p->seq = atomic_fetch_add(&pool_seq, 1);
7571+#endif
7572+
7573+ return p;
7574+}
7575+
7576+// ---------------------------------------------------------------------------
7577+//
7578+// CMA buffer functions - uses cma_pool_fixed for pooling
7579+
7580+struct cma_buf_pool_s {
7581+ cma_pool_fixed_t * pool;
7582+ vcsm_init_type_t init_type;
7583+
7584+ bool all_in_flight;
7585+#if TRACE_ALL
7586+ size_t alloc_n;
7587+ size_t alloc_size;
7588+#endif
7589+};
7590+
7591+typedef struct cma_buf_s {
7592+ atomic_int ref_count;
7593+ cma_buf_pool_t * cbp;
7594+ bool in_flight;
7595+ size_t size;
7596+ unsigned int vcsm_h; // VCSM handle from initial alloc
7597+ unsigned int vc_h; // VC handle for ZC mmal buffers
7598+ unsigned int vc_addr; // VC addr - unused by us but wanted by FFmpeg
7599+ int fd; // dmabuf handle for GL
7600+ void * mmap; // ARM mapped address
7601+ picture_context_t *ctx2;
7602+} cma_buf_t;
7603+
7604+static void cma_pool_delete(cma_buf_t * const cb)
7605+{
7606+ assert(atomic_load(&cb->ref_count) == 0);
7607+#if TRACE_ALL
7608+ cb->cbp->alloc_size -= cb->size;
7609+ --cb->cbp->alloc_n;
7610+ 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);
7611+#endif
7612+
7613+ if (cb->ctx2 != NULL)
7614+ cb->ctx2->destroy(cb->ctx2);
7615+
7616+ if (cb->mmap != MAP_FAILED)
7617+ {
7618+ if (cb->cbp->init_type == VCSM_INIT_CMA)
7619+ munmap(cb->mmap, cb->size);
7620+ else
7621+ vcsm_unlock_hdl(cb->vcsm_h);
7622+ }
7623+ if (cb->fd != -1)
7624+ close(cb->fd);
7625+ if (cb->vcsm_h != 0)
7626+ vcsm_free(cb->vcsm_h);
7627+ free(cb);
7628+}
7629+
7630+static void cma_pool_free_cb(void * v, void * el, size_t size)
7631+{
7632+ VLC_UNUSED(v);
7633+ VLC_UNUSED(size);
7634+
7635+ cma_pool_delete(el);
7636+}
7637+
7638+static void * cma_pool_alloc_cb(void * v, size_t size)
7639+{
7640+ cma_buf_pool_t * const cbp = v;
7641+
7642+ cma_buf_t * const cb = malloc(sizeof(cma_buf_t));
7643+ if (cb == NULL)
7644+ return NULL;
7645+
7646+ *cb = (cma_buf_t){
7647+ .ref_count = ATOMIC_VAR_INIT(0),
7648+ .cbp = cbp,
7649+ .in_flight = 0,
7650+ .size = size,
7651+ .vcsm_h = 0,
7652+ .vc_h = 0,
7653+ .fd = -1,
7654+ .mmap = MAP_FAILED,
7655+ .ctx2 = NULL
7656+ };
7657+#if TRACE_ALL
7658+ cb->cbp->alloc_size += cb->size;
7659+ ++cb->cbp->alloc_n;
7660+ fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cbp->pool->seq, cbp->pool->name, cbp->alloc_n, cbp->alloc_size);
7661+#endif
7662+
7663+ // 0x80 is magic value to force full ARM-side mapping - otherwise
7664+ // cache requests can cause kernel crashes
7665+ if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST | 0x80, "VLC frame")) == 0)
7666+ {
7667+#if TRACE_ALL
7668+ fprintf(stderr, "vcsm_malloc_cache fail\n");
7669+#endif
7670+ goto fail;
7671+ }
7672+
7673+ if ((cb->vc_h = vcsm_vc_hdl_from_hdl(cb->vcsm_h)) == 0)
7674+ {
7675+#if TRACE_ALL
7676+ fprintf(stderr, "vcsm_vc_hdl_from_hdl fail\n");
7677+#endif
7678+ goto fail;
7679+ }
7680+
7681+ if (cbp->init_type == VCSM_INIT_CMA)
7682+ {
7683+ if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1)
7684+ {
7685+#if TRACE_ALL
7686+ fprintf(stderr, "vcsm_export_dmabuf fail\n");
7687+#endif
7688+ goto fail;
7689+ }
7690+
7691+ if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED)
7692+ goto fail;
7693+ }
7694+ else
7695+ {
7696+ void * arm_addr;
7697+ if ((arm_addr = vcsm_lock(cb->vcsm_h)) == NULL)
7698+ {
7699+#if TRACE_ALL
7700+ fprintf(stderr, "vcsm_lock fail\n");
7701+#endif
7702+ goto fail;
7703+ }
7704+ cb->mmap = arm_addr;
7705+ }
7706+
7707+ cb->vc_addr = vcsm_vc_addr_from_hdl(cb->vcsm_h);
7708+
7709+ return cb;
7710+
7711+fail:
7712+ cma_pool_delete(cb);
7713+ return NULL;
7714+}
7715+
7716+// Pool has died - safe now to exit vcsm
7717+static void cma_buf_pool_on_delete_cb(void * v)
7718+{
7719+ cma_buf_pool_t * const cbp = v;
7720+
7721+ cma_vcsm_exit(cbp->init_type);
7722+ free(cbp);
7723+}
7724+
7725+void cma_buf_pool_cancel(cma_buf_pool_t * const cbp)
7726+{
7727+ if (cbp == NULL || cbp->pool == NULL)
7728+ return;
7729+
7730+ cma_pool_fixed_cancel(cbp->pool);
7731+}
7732+
7733+void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp)
7734+{
7735+ if (cbp == NULL || cbp->pool == NULL)
7736+ return;
7737+
7738+ cma_pool_fixed_uncancel(cbp->pool);
7739+}
7740+
7741+// User finished with pool
7742+void cma_buf_pool_delete(cma_buf_pool_t * const cbp)
7743+{
7744+ if (cbp == NULL)
7745+ return;
7746+
7747+ if (cbp->pool != NULL)
7748+ {
7749+ // We will call cma_buf_pool_on_delete_cb when the pool finally dies
7750+ // (might be now) which will free up our env.
7751+ cma_pool_fixed_kill(cbp->pool);
7752+ }
7753+ else
7754+ {
7755+ // Had no pool for some reason (error) but must still finish cleanup
7756+ cma_buf_pool_on_delete_cb(cbp);
7757+ }
7758+}
7759+
7760+int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size)
7761+{
7762+ return cma_pool_fixed_fill(cbp->pool, el_size);
7763+}
7764+
7765+int cma_buf_pool_resize(cma_buf_pool_t * const cbp,
7766+ const unsigned int new_pool_size, const int new_flight_size)
7767+{
7768+ return cma_pool_fixed_resize(cbp->pool, new_pool_size, new_flight_size);
7769+}
7770+
7771+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)
7772+{
7773+ vcsm_init_type_t const init_type = cma_vcsm_init();
7774+ if (init_type == VCSM_INIT_NONE)
7775+ return NULL;
7776+
7777+ cma_buf_pool_t * const cbp = calloc(1, sizeof(cma_buf_pool_t));
7778+ if (cbp == NULL)
7779+ return NULL;
7780+
7781+ cbp->init_type = init_type;
7782+ cbp->all_in_flight = all_in_flight;
7783+
7784+ 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)
7785+ goto fail;
7786+ return cbp;
7787+
7788+fail:
7789+ cma_buf_pool_delete(cbp);
7790+ return NULL;
7791+}
7792+
7793+
7794+void cma_buf_in_flight(cma_buf_t * const cb)
7795+{
7796+ if (!cb->cbp->all_in_flight)
7797+ {
7798+ assert(!cb->in_flight);
7799+ cb->in_flight = true;
7800+ cma_pool_fixed_inc_in_flight(cb->cbp->pool);
7801+ }
7802+}
7803+
7804+void cma_buf_end_flight(cma_buf_t * const cb)
7805+{
7806+ if (cb != NULL && !cb->cbp->all_in_flight && cb->in_flight)
7807+ {
7808+ cb->in_flight = false;
7809+ cma_pool_fixed_dec_in_flight(cb->cbp->pool);
7810+ }
7811+}
7812+
7813+
7814+// Return vcsm handle
7815+unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb)
7816+{
7817+ return cb->vcsm_h;
7818+}
7819+
7820+size_t cma_buf_size(const cma_buf_t * const cb)
7821+{
7822+ return cb->size;
7823+}
7824+
7825+int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2)
7826+{
7827+ if (cb->ctx2 != NULL)
7828+ return VLC_EGENERIC;
7829+
7830+ cb->ctx2 = ctx2;
7831+ return VLC_SUCCESS;
7832+}
7833+
7834+unsigned int cma_buf_vc_handle(const cma_buf_t *const cb)
7835+{
7836+ return cb->vc_h;
7837+}
7838+
7839+int cma_buf_fd(const cma_buf_t *const cb)
7840+{
7841+ return cb->fd;
7842+}
7843+
7844+void * cma_buf_addr(const cma_buf_t *const cb)
7845+{
7846+ return cb->mmap;
7847+}
7848+
7849+unsigned int cma_buf_vc_addr(const cma_buf_t *const cb)
7850+{
7851+ return cb->vc_addr;
7852+}
7853+
7854+
7855+picture_context_t * cma_buf_context2(const cma_buf_t *const cb)
7856+{
7857+ return cb->ctx2;
7858+}
7859+
7860+
7861+void cma_buf_unref(cma_buf_t * const cb)
7862+{
7863+ if (cb == NULL)
7864+ return;
7865+ if (atomic_fetch_sub(&cb->ref_count, 1) <= 1)
7866+ {
7867+ const bool was_in_flight = cb->in_flight;
7868+ cb->in_flight = false;
7869+ cma_pool_fixed_put(cb->cbp->pool, cb, cb->size, was_in_flight);
7870+ }
7871+}
7872+
7873+cma_buf_t * cma_buf_ref(cma_buf_t * const cb)
7874+{
7875+ if (cb == NULL)
7876+ return NULL;
7877+ atomic_fetch_add(&cb->ref_count, 1);
7878+ return cb;
7879+}
7880+
7881+cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const cbp, const size_t size)
7882+{
7883+ cma_buf_t *const cb = cma_pool_fixed_get(cbp->pool, size, cbp->all_in_flight, false);
7884+
7885+ if (cb == NULL)
7886+ return NULL;
7887+
7888+ cb->in_flight = cbp->all_in_flight;
7889+ // When 1st allocated or retrieved from the pool the block will have a
7890+ // ref count of 0 so ref here
7891+ return cma_buf_ref(cb);
7892+}
7893+
7894--- /dev/null
7895+++ b/modules/hw/mmal/mmal_cma.h
7896@@ -0,0 +1,71 @@
7897+#ifndef VLC_MMAL_MMAL_CMA_H_
7898+#define VLC_MMAL_MMAL_CMA_H_
7899+
7900+
7901+struct cma_pool_fixed_s;
7902+typedef struct cma_pool_fixed_s cma_pool_fixed_t;
7903+
7904+typedef void * cma_pool_alloc_fn(void * v, size_t size);
7905+typedef void cma_pool_free_fn(void * v, void * el, size_t size);
7906+typedef void cma_pool_on_delete_fn(void * v);
7907+
7908+#if 0
7909+void cma_pool_fixed_unref(cma_pool_fixed_t * const p);
7910+void cma_pool_fixed_ref(cma_pool_fixed_t * const p);
7911+void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool in_flight);
7912+void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight);
7913+void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p);
7914+void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p);
7915+void cma_pool_fixed_cancel(cma_pool_fixed_t * const p);
7916+void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p);
7917+void cma_pool_fixed_kill(cma_pool_fixed_t * const p);
7918+int cma_pool_fixed_resize(cma_pool_fixed_t * const p,
7919+ const unsigned int new_pool_size, const int new_flight_size);
7920+cma_pool_fixed_t * cma_pool_fixed_new(const unsigned int pool_size,
7921+ const int flight_size,
7922+ void * const alloc_v,
7923+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn,
7924+ cma_pool_on_delete_fn * const on_delete_fn,
7925+ const char * const name);
7926+#endif
7927+
7928+struct cma_buf_s;
7929+typedef struct cma_buf_s cma_buf_t;
7930+
7931+void cma_buf_in_flight(cma_buf_t * const cb);
7932+void cma_buf_end_flight(cma_buf_t * const cb);
7933+unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb);
7934+size_t cma_buf_size(const cma_buf_t * const cb);
7935+int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2);
7936+unsigned int cma_buf_vc_handle(const cma_buf_t *const cb);
7937+int cma_buf_fd(const cma_buf_t *const cb);
7938+void * cma_buf_addr(const cma_buf_t *const cb);
7939+unsigned int cma_buf_vc_addr(const cma_buf_t *const cb);
7940+picture_context_t * cma_buf_context2(const cma_buf_t *const cb);
7941+
7942+void cma_buf_unref(cma_buf_t * const cb);
7943+cma_buf_t * cma_buf_ref(cma_buf_t * const cb);
7944+
7945+struct cma_buf_pool_s;
7946+typedef struct cma_buf_pool_s cma_buf_pool_t;
7947+
7948+cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const p, const size_t size);
7949+void cma_buf_pool_cancel(cma_buf_pool_t * const cbp);
7950+void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp);
7951+void cma_buf_pool_delete(cma_buf_pool_t * const p);
7952+int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size);
7953+int cma_buf_pool_resize(cma_buf_pool_t * const cbp,
7954+ const unsigned int new_pool_size, const int new_flight_size);
7955+cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size,
7956+ const bool all_in_flight, const char * const name);
7957+
7958+static inline void cma_buf_pool_deletez(cma_buf_pool_t ** const pp)
7959+{
7960+ cma_buf_pool_t * const p = *pp;
7961+ if (p != NULL) {
7962+ *pp = NULL;
7963+ cma_buf_pool_delete(p);
7964+ }
7965+}
7966+
7967+#endif // VLC_MMAL_MMAL_CMA_H_
7968--- /dev/null
7969+++ b/modules/hw/mmal/mmal_gl.h
7970@@ -0,0 +1,45 @@
7971+// Trim this include list!
7972+
7973+#include <libdrm/drm.h>
7974+#include <libdrm/drm_mode.h>
7975+#include <libdrm/drm_fourcc.h>
7976+//#include <xf86drm.h>
7977+//#include <xf86drmMode.h>
7978+#include <X11/Xlib.h>
7979+#include <X11/Xutil.h>
7980+#include <X11/Xlib-xcb.h>
7981+#include <epoxy/gl.h>
7982+#include <epoxy/egl.h>
7983+#include <xcb/xcb.h>
7984+#include <xcb/dri3.h>
7985+
7986+struct mmal_gl_converter_s;
7987+
7988+typedef struct cma_buf_s {
7989+ struct mmal_gl_converter_s * sys;
7990+
7991+ size_t size;
7992+ __u32 h_dumb;
7993+ int fd;
7994+ unsigned int h_vcsm;
7995+ void * mapped_addr;
7996+ GLuint texture;
7997+} cma_buf_t;
7998+
7999+typedef struct cma_pic_sys_s {
8000+ cma_buf_t * cmabuf;
8001+} cma_pic_sys_t;
8002+
8003+static inline unsigned int
8004+hw_mmal_h_vcsm(const picture_t * const pic)
8005+{
8006+ const cma_pic_sys_t *const pic_sys = (cma_pic_sys_t *)pic->p_sys;
8007+
8008+ if (pic->format.i_chroma != VLC_CODEC_MMAL_GL_RGB32 ||
8009+ pic_sys == NULL || pic_sys->cmabuf == NULL) {
8010+ return 0;
8011+ }
8012+
8013+ return pic_sys->cmabuf->h_vcsm;
8014+}
8015+
8016--- /dev/null
8017+++ b/modules/hw/mmal/mmal_piccpy_neon.S
8018@@ -0,0 +1,105 @@
8019+// Copy pix
8020+
8021+ .syntax unified
8022+ .arm
8023+// .thumb
8024+ .text
8025+ .align 16
8026+ .arch armv7-a
8027+ .fpu neon-vfpv4
8028+
8029+
8030+.macro function name
8031+ .global \name
8032+#ifdef __ELF__
8033+ .type \name, %function
8034+#endif
8035+\name:
8036+.endm
8037+
8038+
8039+.macro piccpy_to_8, bit_depth
8040+ subs r2, #128
8041+ vpush {q4-q7}
8042+ blt 2f
8043+1:
8044+ vldm r1!, {q0-q7}
8045+ subs r2, #128
8046+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8047+ vqrshrn.u16 d1, q1, #\bit_depth - 8
8048+ vqrshrn.u16 d2, q2, #\bit_depth - 8
8049+ vqrshrn.u16 d3, q3, #\bit_depth - 8
8050+ vldm r1!, {q8-q15}
8051+ vqrshrn.u16 d4, q4, #\bit_depth - 8
8052+ vqrshrn.u16 d5, q5, #\bit_depth - 8
8053+ vqrshrn.u16 d6, q6, #\bit_depth - 8
8054+ vqrshrn.u16 d7, q7, #\bit_depth - 8
8055+ vqrshrn.u16 d8, q8, #\bit_depth - 8
8056+ vqrshrn.u16 d9, q9, #\bit_depth - 8
8057+ vqrshrn.u16 d10, q10, #\bit_depth - 8
8058+ vqrshrn.u16 d11, q11, #\bit_depth - 8
8059+ vqrshrn.u16 d12, q12, #\bit_depth - 8
8060+ vqrshrn.u16 d13, q13, #\bit_depth - 8
8061+ vqrshrn.u16 d14, q14, #\bit_depth - 8
8062+ vqrshrn.u16 d15, q15, #\bit_depth - 8
8063+ vstm r0!, {q0-q7}
8064+ bge 1b
8065+2:
8066+ adds r2, #64
8067+ blt 1f
8068+
8069+ vldm r1!, {q0-q7}
8070+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8071+ vqrshrn.u16 d1, q1, #\bit_depth - 8
8072+ vqrshrn.u16 d2, q2, #\bit_depth - 8
8073+ vqrshrn.u16 d3, q3, #\bit_depth - 8
8074+ vqrshrn.u16 d4, q4, #\bit_depth - 8
8075+ vqrshrn.u16 d5, q5, #\bit_depth - 8
8076+ vqrshrn.u16 d6, q6, #\bit_depth - 8
8077+ vqrshrn.u16 d7, q7, #\bit_depth - 8
8078+ vstm r0!, {q0-q3}
8079+1:
8080+ adds r2, #32
8081+ blt 1f
8082+
8083+ vldm r1!, {q0-q3}
8084+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8085+ vqrshrn.u16 d1, q1, #\bit_depth - 8
8086+ vqrshrn.u16 d2, q2, #\bit_depth - 8
8087+ vqrshrn.u16 d3, q3, #\bit_depth - 8
8088+ vstm r0!, {q0-q1}
8089+1:
8090+ adds r2, #16
8091+ blt 1f
8092+
8093+ vldm r1!, {q0-q1}
8094+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8095+ vqrshrn.u16 d1, q1, #\bit_depth - 8
8096+ vstm r0!, {q0}
8097+1:
8098+ adds r2, #8
8099+ blt 1f
8100+
8101+ vldm r1!, {q0}
8102+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8103+ vstr d0, [r0]
8104+ add r0, #8
8105+1:
8106+ adds r2, #4
8107+ blt 1f
8108+
8109+ vldr d0, [r1]
8110+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8111+ vstr s0, [r0]
8112+1:
8113+ vpop {q4-q7}
8114+ bx lr
8115+.endm
8116+
8117+
8118+@ [r0] Dest
8119+@ [r1] Src
8120+@ r2 Pels
8121+function mmal_piccpy_10_to_8_neon
8122+ piccpy_to_8 10
8123+
8124--- a/modules/hw/mmal/mmal_picture.c
8125+++ b/modules/hw/mmal/mmal_picture.c
8126@@ -21,25 +21,1542 @@
8127 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
8128 *****************************************************************************/
8129
8130+// We would really like to use vlc_thread.h but the detach thread stuff can't be
8131+// used here :-(
8132+#include <pthread.h>
8133+
8134+#include <stdatomic.h>
8135+#include <unistd.h>
8136+#include <fcntl.h>
8137+
8138 #include <vlc_common.h>
8139+#include <vlc_cpu.h>
8140 #include <vlc_picture.h>
8141+
8142+#pragma GCC diagnostic push
8143+#pragma GCC diagnostic ignored "-Wbad-function-cast"
8144+#include <bcm_host.h>
8145+#pragma GCC diagnostic pop
8146 #include <interface/mmal/mmal.h>
8147+#include <interface/mmal/util/mmal_util.h>
8148+#include <interface/mmal/util/mmal_default_components.h>
8149+#include <interface/vmcs_host/vcgencmd.h>
8150+#include <interface/vcsm/user-vcsm.h>
8151
8152+#include "mmal_cma.h"
8153 #include "mmal_picture.h"
8154+#include "transform_ops.h"
8155+
8156+#define TRACE_TRANSFORMS 0
8157+
8158+#define UINT64_SIZE(s) (((s) + sizeof(uint64_t) - 1)/sizeof(uint64_t))
8159+
8160+static inline char safe_char(const unsigned int c0)
8161+{
8162+ const unsigned int c = c0 & 0xff;
8163+ return c > ' ' && c < 0x7f ? c : '.';
8164+}
8165+
8166+const char * str_fourcc(char * const buf, const unsigned int fcc)
8167+{
8168+ if (fcc == 0)
8169+ return "----";
8170+ buf[0] = safe_char(fcc >> 0);
8171+ buf[1] = safe_char(fcc >> 8);
8172+ buf[2] = safe_char(fcc >> 16);
8173+ buf[3] = safe_char(fcc >> 24);
8174+ buf[4] = 0;
8175+ return buf;
8176+}
8177+
8178+// WB + Inv
8179+static inline void flush_range(void * const start, const size_t len)
8180+{
8181+ uint64_t buf[UINT64_SIZE(sizeof(struct vcsm_user_clean_invalid2_s) + sizeof(struct vcsm_user_clean_invalid2_block_s))];
8182+ struct vcsm_user_clean_invalid2_s * const b = (struct vcsm_user_clean_invalid2_s *)buf;
8183+
8184+ *b = (struct vcsm_user_clean_invalid2_s){
8185+ .op_count = 1
8186+ };
8187+
8188+ b->s[0] = (struct vcsm_user_clean_invalid2_block_s){
8189+ .invalidate_mode = 3, // wb + invalidate
8190+ .block_count = 1,
8191+ .start_address = start, // Rely on clean inv to fix up align & size boundries
8192+ .block_size = len,
8193+ .inter_block_stride = 0
8194+ };
8195+
8196+ vcsm_clean_invalid2(b);
8197+}
8198+
8199+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs)
8200+{
8201+ switch (vlc_cs)
8202+ {
8203+ case COLOR_SPACE_BT601:
8204+ return MMAL_COLOR_SPACE_ITUR_BT601;
8205+ case COLOR_SPACE_BT709:
8206+ return MMAL_COLOR_SPACE_ITUR_BT709;
8207+ default:
8208+ break;
8209+ }
8210+ return MMAL_COLOR_SPACE_UNKNOWN;
8211+}
8212+
8213+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc)
8214+{
8215+ switch (vf_vlc->i_chroma) {
8216+ case VLC_CODEC_MMAL_ZC_RGB32:
8217+ case VLC_CODEC_RGB32:
8218+ {
8219+ // VLC RGB32 aka RV32 means we have to look at the mask values
8220+ const uint32_t r = vf_vlc->i_rmask;
8221+ const uint32_t g = vf_vlc->i_gmask;
8222+ const uint32_t b = vf_vlc->i_bmask;
8223+ if (r == 0xff0000 && g == 0xff00 && b == 0xff)
8224+ return MMAL_ENCODING_BGRA;
8225+ if (r == 0xff && g == 0xff00 && b == 0xff0000)
8226+ return MMAL_ENCODING_RGBA;
8227+ if (r == 0xff000000 && g == 0xff0000 && b == 0xff00)
8228+ return MMAL_ENCODING_ABGR;
8229+ if (r == 0xff00 && g == 0xff0000 && b == 0xff000000)
8230+ return MMAL_ENCODING_ARGB;
8231+ break;
8232+ }
8233+ case VLC_CODEC_RGB16:
8234+ {
8235+ // VLC RGB16 aka RV16 means we have to look at the mask values
8236+ const uint32_t r = vf_vlc->i_rmask;
8237+ const uint32_t g = vf_vlc->i_gmask;
8238+ const uint32_t b = vf_vlc->i_bmask;
8239+ if (r == 0xf800 && g == 0x7e0 && b == 0x1f)
8240+ return MMAL_ENCODING_RGB16;
8241+ break;
8242+ }
8243+ case VLC_CODEC_I420:
8244+ case VLC_CODEC_MMAL_ZC_I420:
8245+ return MMAL_ENCODING_I420;
8246+ case VLC_CODEC_RGBA:
8247+ return MMAL_ENCODING_RGBA;
8248+ case VLC_CODEC_BGRA:
8249+ return MMAL_ENCODING_BGRA;
8250+ case VLC_CODEC_ARGB:
8251+ return MMAL_ENCODING_ARGB;
8252+ // VLC_CODEC_ABGR does not exist in VLC
8253+ case VLC_CODEC_MMAL_OPAQUE:
8254+ return MMAL_ENCODING_OPAQUE;
8255+ case VLC_CODEC_MMAL_ZC_SAND8:
8256+ return MMAL_ENCODING_YUVUV128;
8257+ case VLC_CODEC_MMAL_ZC_SAND10:
8258+ return MMAL_ENCODING_YUVUV64_10;
8259+ case VLC_CODEC_MMAL_ZC_SAND30:
8260+ return MMAL_ENCODING_YUV10_COL;
8261+ default:
8262+ break;
8263+ }
8264+ return 0;
8265+}
8266+
8267+static void vlc_fmt_to_video_format(MMAL_VIDEO_FORMAT_T *const vf_mmal, const video_frame_format_t * const vf_vlc)
8268+{
8269+ const unsigned int wmask = (vf_vlc->i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
8270+ vf_vlc->i_chroma == VLC_CODEC_I420) ? 31 : 15;
8271+
8272+ vf_mmal->width = (vf_vlc->i_width + wmask) & ~wmask;
8273+ vf_mmal->height = (vf_vlc->i_height + 15) & ~15;
8274+ vf_mmal->crop.x = vf_vlc->i_x_offset;
8275+ vf_mmal->crop.y = vf_vlc->i_y_offset;
8276+ vf_mmal->crop.width = vf_vlc->i_visible_width;
8277+ vf_mmal->crop.height = vf_vlc->i_visible_height;
8278+ if (vf_vlc->i_sar_num == 0 || vf_vlc->i_sar_den == 0) {
8279+ vf_mmal->par.num = 1;
8280+ vf_mmal->par.den = 1;
8281+ } else {
8282+ vf_mmal->par.num = vf_vlc->i_sar_num;
8283+ vf_mmal->par.den = vf_vlc->i_sar_den;
8284+ }
8285+ vf_mmal->frame_rate.num = vf_vlc->i_frame_rate;
8286+ vf_mmal->frame_rate.den = vf_vlc->i_frame_rate_base;
8287+ vf_mmal->color_space = vlc_to_mmal_color_space(vf_vlc->space);
8288+}
8289+
8290+
8291+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc)
8292+{
8293+ vlc_fmt_to_video_format(&es_fmt->es->video, vf_vlc);
8294+}
8295+
8296+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic)
8297+{
8298+ MMAL_VIDEO_FORMAT_T vf_new_ss;
8299+ MMAL_VIDEO_FORMAT_T *const vf_old = &es_fmt->es->video;
8300+ MMAL_VIDEO_FORMAT_T *const vf_new = &vf_new_ss;
8301+
8302+ vlc_fmt_to_video_format(vf_new, &pic->format);
8303+
8304+ // If we have a format that might have come from ffmpeg then rework for
8305+ // a better guess as to layout. All sand stuff is "special" with regards to
8306+ // width/height vs real layout so leave as is if that
8307+ if ((pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
8308+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) &&
8309+ pic->p[0].i_pixel_pitch != 0)
8310+ {
8311+ // Now overwrite width/height with a better guess as to actual layout info
8312+ vf_new->height = pic->p[0].i_lines;
8313+ vf_new->width = pic->p[0].i_pitch / pic->p[0].i_pixel_pitch;
8314+ }
8315+
8316+ if (
8317+ vf_new->width != vf_old->width ||
8318+ vf_new->height != vf_old->height ||
8319+ vf_new->crop.x != vf_old->crop.x ||
8320+ vf_new->crop.y != vf_old->crop.y ||
8321+ vf_new->crop.width != vf_old->crop.width ||
8322+ vf_new->crop.height != vf_old->crop.height ||
8323+ vf_new->par.num != vf_old->par.num ||
8324+ vf_new->par.den != vf_old->par.den ||
8325+ // Frame rate ignored
8326+ vf_new->color_space != vf_old->color_space)
8327+ {
8328+#if 0
8329+ char dbuf0[5], dbuf1[5];
8330+ printf("%dx%d (%d,%d %dx%d) par:%d/%d %s -> %dx%d (%d,%d %dx%d) par:%d/%d %s\n",
8331+ vf_old->width ,
8332+ vf_old->height ,
8333+ vf_old->crop.x ,
8334+ vf_old->crop.y ,
8335+ vf_old->crop.width ,
8336+ vf_old->crop.height ,
8337+ vf_old->par.num ,
8338+ vf_old->par.den ,
8339+ str_fourcc(dbuf0, vf_old->color_space) ,
8340+ vf_new->width ,
8341+ vf_new->height ,
8342+ vf_new->crop.x ,
8343+ vf_new->crop.y ,
8344+ vf_new->crop.width ,
8345+ vf_new->crop.height ,
8346+ vf_new->par.num ,
8347+ vf_new->par.den ,
8348+ str_fourcc(dbuf1, vf_new->color_space) );
8349+#endif
8350+ *vf_old = *vf_new;
8351+ return true;
8352+ }
8353+ return false;
8354+}
8355+
8356+
8357+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
8358+ const unsigned int headers, const uint32_t payload_size)
8359+{
8360+ hw_mmal_port_pool_ref_t * ppr = calloc(1, sizeof(hw_mmal_port_pool_ref_t));
8361+ if (ppr == NULL)
8362+ return NULL;
8363+
8364+ if ((ppr->pool = mmal_port_pool_create(port, headers, payload_size)) == NULL)
8365+ goto fail;
8366+
8367+ ppr->port = port;
8368+ atomic_store(&ppr->refs, 1);
8369+ return ppr;
8370+
8371+fail:
8372+ free(ppr);
8373+ return NULL;
8374+}
8375+
8376+static void do_detached(void *(*fn)(void *), void * v)
8377+{
8378+ pthread_t dothread;
8379+ pthread_create(&dothread, NULL, fn, v);
8380+ pthread_detach(dothread);
8381+}
8382+
8383+// Destroy a ppr - aranged s.t. it has the correct prototype for a pthread
8384+static void * kill_ppr(void * v)
8385+{
8386+ hw_mmal_port_pool_ref_t * const ppr = v;
8387+ if (ppr->port->is_enabled)
8388+ mmal_port_disable(ppr->port); // Avoid annoyed messages from MMAL when we kill the pool
8389+ mmal_port_pool_destroy(ppr->port, ppr->pool);
8390+ free(ppr);
8391+ return NULL;
8392+}
8393+
8394+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb)
8395+{
8396+ if (ppr == NULL)
8397+ return;
8398+ if (atomic_fetch_sub(&ppr->refs, 1) != 1)
8399+ return;
8400+ if (in_cb)
8401+ do_detached(kill_ppr, ppr);
8402+ else
8403+ kill_ppr(ppr);
8404+}
8405+
8406+// Put buffer in port if possible - if not then release to pool
8407+// Returns true if sent, false if recycled
8408+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf)
8409+{
8410+ mmal_buffer_header_reset(buf);
8411+ buf->user_data = NULL;
8412+
8413+ if (mmal_port_send_buffer(ppr->port, buf) == MMAL_SUCCESS)
8414+ return true;
8415+ mmal_buffer_header_release(buf);
8416+ return false;
8417+}
8418+
8419+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr)
8420+{
8421+ MMAL_BUFFER_HEADER_T * buf;
8422+ MMAL_STATUS_T err = MMAL_SUCCESS;
8423+
8424+ while ((buf = mmal_queue_get(ppr->pool->queue)) != NULL) {
8425+ if ((err = mmal_port_send_buffer(ppr->port, buf)) != MMAL_SUCCESS)
8426+ {
8427+ mmal_queue_put_back(ppr->pool->queue, buf);
8428+ break;
8429+ }
8430+ }
8431+ return err;
8432+}
8433+
8434+
8435+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
8436+ hw_mmal_port_pool_ref_t ** pppr,
8437+ MMAL_PORT_T * const port,
8438+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback)
8439+{
8440+ MMAL_STATUS_T status;
8441+
8442+ port->userdata = (struct MMAL_PORT_USERDATA_T *)obj;
8443+
8444+ status = port_parameter_set_uint32(port, MMAL_PARAMETER_EXTRA_BUFFERS, extra_buffers);
8445+ if (status != MMAL_SUCCESS) {
8446+ msg_Err(obj, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
8447+ status, mmal_status_to_string(status));
8448+ return status;
8449+ }
8450+
8451+ status = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, 1);
8452+ if (status != MMAL_SUCCESS) {
8453+ msg_Err(obj, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
8454+ port->name, status, mmal_status_to_string(status));
8455+ return status;
8456+ }
8457+
8458+ port->format->encoding = MMAL_ENCODING_OPAQUE;
8459+ port->format->encoding_variant = 0;
8460+ if ((status = mmal_port_format_commit(port)) != MMAL_SUCCESS)
8461+ {
8462+ msg_Err(obj, "Failed to commit format on port %s (status=%"PRIx32" %s)",
8463+ port->name, status, mmal_status_to_string(status));
8464+ return status;
8465+ }
8466+
8467+ port->buffer_num = 30;
8468+ port->buffer_size = port->buffer_size_recommended;
8469+
8470+ if ((*pppr = hw_mmal_port_pool_ref_create(port, port->buffer_num, port->buffer_size)) == NULL) {
8471+ msg_Err(obj, "Failed to create output pool");
8472+ return status;
8473+ }
8474+
8475+ status = mmal_port_enable(port, callback);
8476+ if (status != MMAL_SUCCESS) {
8477+ hw_mmal_port_pool_ref_release(*pppr, false);
8478+ *pppr = NULL;
8479+ msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)",
8480+ port->name, status, mmal_status_to_string(status));
8481+ return status;
8482+ }
8483+
8484+ return MMAL_SUCCESS;
8485+}
8486+
8487+
8488+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn)
8489+{
8490+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
8491+ unsigned int i;
8492+
8493+ for (i = 0; i != ctx->buf_count; ++i) {
8494+ if (ctx->bufs[i] != NULL)
8495+ mmal_buffer_header_release(ctx->bufs[i]);
8496+ }
8497+
8498+ cma_buf_end_flight(ctx->cb);
8499+ cma_buf_unref(ctx->cb);
8500+
8501+ free(ctx);
8502+}
8503+
8504+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn)
8505+{
8506+ const pic_ctx_mmal_t * const src_ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
8507+ pic_ctx_mmal_t * const dst_ctx = calloc(1, sizeof(*dst_ctx));
8508+ unsigned int i;
8509+
8510+ if (dst_ctx == NULL)
8511+ return NULL;
8512+
8513+ // Copy
8514+ dst_ctx->cmn = src_ctx->cmn;
8515+
8516+ dst_ctx->cb = cma_buf_ref(src_ctx->cb);
8517+
8518+ dst_ctx->buf_count = src_ctx->buf_count;
8519+ for (i = 0; i != src_ctx->buf_count; ++i) {
8520+ dst_ctx->bufs[i] = src_ctx->bufs[i];
8521+ if (dst_ctx->bufs[i] != NULL)
8522+ mmal_buffer_header_acquire(dst_ctx->bufs[i]);
8523+ }
8524+
8525+ return &dst_ctx->cmn;
8526+}
8527+
8528+static MMAL_BOOL_T
8529+buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata)
8530+{
8531+ hw_mmal_port_pool_ref_t * const ppr = userdata;
8532+
8533+ // Kill the callback - otherwise we will go in circles!
8534+ mmal_buffer_header_pre_release_cb_set(buf, (MMAL_BH_PRE_RELEASE_CB_T)0, NULL);
8535+ mmal_buffer_header_acquire(buf); // Ref it again
8536+
8537+ // As we have re-acquired the buffer we need a full release
8538+ // (not continue) to zap the ref count back to zero
8539+ // This is "safe" 'cos we have already reset the cb
8540+ hw_mmal_port_pool_ref_recycle(ppr, buf);
8541+ hw_mmal_port_pool_ref_release(ppr, true); // Assume in callback
8542+
8543+ return MMAL_TRUE;
8544+}
8545+
8546+// Buffer belongs to context on successful return from this fn
8547+// is still valid on failure
8548+picture_context_t *
8549+hw_mmal_gen_context(MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr)
8550+{
8551+ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
8552+
8553+ if (ctx == NULL)
8554+ return NULL;
8555+
8556+ // If we have an associated ppr then ref & set appropriate callbacks
8557+ if (ppr != NULL) {
8558+ hw_mmal_port_pool_ref_acquire(ppr);
8559+ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr);
8560+ buf->user_data = NULL;
8561+ }
8562+
8563+ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
8564+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
8565+
8566+ ctx->buf_count = 1;
8567+ ctx->bufs[0] = buf;
8568+
8569+ return &ctx->cmn;
8570+}
8571+
8572+// n is els
8573+// * Make NEON!
8574+typedef void piccpy_fn(void * dest, const void * src, size_t n);
8575+
8576+extern piccpy_fn mmal_piccpy_10_to_8_neon;
8577+
8578+static void piccpy_10_to_8_c(void * dest, const void * src, size_t n)
8579+{
8580+ uint8_t * d = dest;
8581+ const uint16_t * s = src;
8582+ while (n-- != 0)
8583+ *d++ = *s++ >> 2;
8584+}
8585+
8586+// Do a stride converting copy - if the strides are the same and line_len is
8587+// close then do a single block copy - we don't expect to have to preserve
8588+// pixels in the output frame
8589+static void mem_copy_2d(uint8_t * d_ptr, const size_t d_stride,
8590+ const uint8_t * s_ptr, const size_t s_stride,
8591+ size_t lines, const size_t line_len)
8592+{
8593+ if (s_stride == d_stride && d_stride < line_len + 32)
8594+ {
8595+ memcpy(d_ptr, s_ptr, d_stride * lines);
8596+ }
8597+ else
8598+ {
8599+ while (lines-- != 0) {
8600+ memcpy(d_ptr, s_ptr, line_len);
8601+ d_ptr += d_stride;
8602+ s_ptr += s_stride;
8603+ }
8604+ }
8605+}
8606+
8607+// line_len in D units
8608+static void mem_copy_2d_10_to_8(uint8_t * d_ptr, const size_t d_stride,
8609+ const uint8_t * s_ptr, const size_t s_stride,
8610+ size_t lines, const size_t line_len)
8611+{
8612+ piccpy_fn * const docpy = vlc_CPU_ARM_NEON() ? mmal_piccpy_10_to_8_neon : piccpy_10_to_8_c;
8613+ if (s_stride == d_stride * 2 && d_stride < line_len + 32)
8614+ {
8615+ docpy(d_ptr, s_ptr, d_stride * lines);
8616+ }
8617+ else
8618+ {
8619+ while (lines-- != 0) {
8620+ docpy(d_ptr, s_ptr, line_len);
8621+ d_ptr += d_stride;
8622+ s_ptr += s_stride;
8623+ }
8624+ }
8625+}
8626+
8627+
8628+int hw_mmal_copy_pic_to_buf(void * const buf_data,
8629+ uint32_t * const pLength,
8630+ const MMAL_ES_FORMAT_T * const fmt,
8631+ const picture_t * const pic)
8632+{
8633+ const MMAL_VIDEO_FORMAT_T *const video = &fmt->es->video;
8634+ uint8_t * const dest = buf_data;
8635+ size_t length = 0;
8636+
8637+ //**** Worry about x/y_offsets
8638+
8639+ assert(fmt->encoding == MMAL_ENCODING_I420);
8640+
8641+ switch (pic->format.i_chroma) {
8642+ case VLC_CODEC_I420:
8643+ {
8644+ const size_t y_size = video->width * video->height;
8645+ mem_copy_2d(dest, video->width,
8646+ pic->p[0].p_pixels, pic->p[0].i_pitch,
8647+ video->crop.height,
8648+ video->crop.width);
8649+
8650+ mem_copy_2d(dest + y_size, video->width / 2,
8651+ pic->p[1].p_pixels, pic->p[1].i_pitch,
8652+ video->crop.height / 2,
8653+ video->crop.width / 2);
8654+
8655+ mem_copy_2d(dest + y_size + y_size / 4, video->width / 2,
8656+ pic->p[2].p_pixels, pic->p[2].i_pitch,
8657+ video->crop.height / 2,
8658+ video->crop.width / 2);
8659+
8660+ // And make sure it is actually in memory
8661+ length = y_size + y_size / 2;
8662+ break;
8663+ }
8664+
8665+ case VLC_CODEC_I420_10L:
8666+ {
8667+ const size_t y_size = video->width * video->height;
8668+ mem_copy_2d_10_to_8(dest, video->width,
8669+ pic->p[0].p_pixels, pic->p[0].i_pitch,
8670+ video->crop.height,
8671+ video->crop.width);
8672+
8673+ mem_copy_2d_10_to_8(dest + y_size, video->width / 2,
8674+ pic->p[1].p_pixels, pic->p[1].i_pitch,
8675+ video->crop.height / 2,
8676+ video->crop.width / 2);
8677+
8678+ mem_copy_2d_10_to_8(dest + y_size + y_size / 4, video->width / 2,
8679+ pic->p[2].p_pixels, pic->p[2].i_pitch,
8680+ video->crop.height / 2,
8681+ video->crop.width / 2);
8682+
8683+ // And make sure it is actually in memory
8684+ length = y_size + y_size / 2;
8685+ break;
8686+ }
8687+
8688+ default:
8689+ if (pLength != NULL)
8690+ *pLength = 0;
8691+ return VLC_EBADVAR;
8692+ }
8693+
8694+ if (cma_vcsm_type() == VCSM_INIT_LEGACY) { // ** CMA is currently always uncached
8695+ flush_range(dest, length);
8696+ }
8697+
8698+ if (pLength != NULL)
8699+ *pLength = (uint32_t)length;
8700+
8701+ return VLC_SUCCESS;
8702+}
8703+
8704+
8705+static MMAL_BOOL_T rep_buf_free_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
8706+{
8707+ cma_buf_t * const cb = userdata;
8708+ VLC_UNUSED(header);
8709+
8710+ cma_buf_unref(cb);
8711+ return MMAL_FALSE;
8712+}
8713+
8714+static int cma_buf_buf_attach(MMAL_BUFFER_HEADER_T * const buf, cma_buf_t * const cb)
8715+{
8716+ // Just a CMA buffer - fill in new buffer
8717+ const uintptr_t vc_h = cma_buf_vc_handle(cb);
8718+ if (vc_h == 0)
8719+ return VLC_EGENERIC;
8720+
8721+ mmal_buffer_header_reset(buf);
8722+ buf->data = (uint8_t *)vc_h;
8723+ buf->alloc_size = cma_buf_size(cb);
8724+ buf->length = buf->alloc_size;
8725+ // Ensure cb remains valid for the duration of this buffer
8726+ mmal_buffer_header_pre_release_cb_set(buf, rep_buf_free_cb, cma_buf_ref(cb));
8727+ return VLC_SUCCESS;
8728+}
8729+
8730+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic,
8731+ MMAL_POOL_T * const rep_pool,
8732+ MMAL_PORT_T * const port,
8733+ cma_buf_pool_t * const cbp)
8734+{
8735+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(rep_pool->queue);
8736+ if (buf == NULL)
8737+ goto fail0;
8738+
8739+ cma_buf_t * const cb = cma_buf_pool_alloc_buf(cbp, port->buffer_size);
8740+ if (cb == NULL)
8741+ goto fail1;
8742+
8743+ if (cma_buf_buf_attach(buf, cb) != VLC_SUCCESS)
8744+ goto fail2;
8745+
8746+ pic_to_buf_copy_props(buf, pic);
8747+
8748+ if (hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), &buf->length, port->format, pic) != VLC_SUCCESS)
8749+ goto fail2;
8750+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
8751+
8752+ cma_buf_unref(cb);
8753+ return buf;
8754+
8755+fail2:
8756+ cma_buf_unref(cb);
8757+fail1:
8758+ mmal_buffer_header_release(buf);
8759+fail0:
8760+ return NULL;
8761+}
8762+
8763+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool)
8764+{
8765+ pic_ctx_mmal_t *const ctx = (pic_ctx_mmal_t *)pic->context;
8766+ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
8767+
8768+ if (rep_buf == NULL)
8769+ return NULL;
8770+
8771+ if (ctx->bufs[0] != NULL)
8772+ {
8773+ // Existing buffer - replicate it
8774+ if (mmal_buffer_header_replicate(rep_buf, ctx->bufs[0]) != MMAL_SUCCESS)
8775+ goto fail;
8776+ }
8777+ else if (ctx->cb != NULL)
8778+ {
8779+ // Just a CMA buffer - fill in new buffer
8780+ if (cma_buf_buf_attach(rep_buf, ctx->cb) != 0)
8781+ goto fail;
8782+ }
8783+ else
8784+ goto fail;
8785+
8786+ pic_to_buf_copy_props(rep_buf, pic);
8787+ return rep_buf;
8788+
8789+fail:
8790+ mmal_buffer_header_release(rep_buf);
8791+ return NULL;
8792+}
8793+
8794+
8795+
8796+
8797+int hw_mmal_get_gpu_mem(void) {
8798+ static int stashed_val = -2;
8799+ VCHI_INSTANCE_T vchi_instance;
8800+ VCHI_CONNECTION_T *vchi_connection = NULL;
8801+ char rbuf[1024] = { 0 };
8802+
8803+ if (stashed_val >= -1)
8804+ return stashed_val;
8805+
8806+ if (vchi_initialise(&vchi_instance) != 0)
8807+ goto fail0;
8808+
8809+ //create a vchi connection
8810+ if (vchi_connect(NULL, 0, vchi_instance) != 0)
8811+ goto fail0;
8812+
8813+ vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1);
8814+
8815+ //send the gencmd for the argument
8816+ if (vc_gencmd_send("get_mem gpu") != 0)
8817+ goto fail;
8818+
8819+ if (vc_gencmd_read_response(rbuf, sizeof(rbuf) - 1) != 0)
8820+ goto fail;
8821+
8822+ if (strncmp(rbuf, "gpu=", 4) != 0)
8823+ goto fail;
8824+
8825+ char *p;
8826+ unsigned long m = strtoul(rbuf + 4, &p, 10);
8827+
8828+ if (p[0] != 'M' || p[1] != '\0')
8829+ stashed_val = -1;
8830+ else
8831+ stashed_val = (int)m << 20;
8832+
8833+ vc_gencmd_stop();
8834+
8835+ //close the vchi connection
8836+ vchi_disconnect(vchi_instance);
8837+
8838+ return stashed_val;
8839+
8840+fail:
8841+ vc_gencmd_stop();
8842+ vchi_disconnect(vchi_instance);
8843+fail0:
8844+ stashed_val = -1;
8845+ return -1;
8846+};
8847+
8848+// ===========================================================================
8849+
8850+typedef struct pool_ent_s
8851+{
8852+ struct pool_ent_s * next;
8853+ struct pool_ent_s * prev;
8854+
8855+ atomic_int ref_count;
8856+ unsigned int seq;
8857+
8858+ size_t size;
8859+
8860+ int vcsm_hdl;
8861+ int vc_hdl;
8862+ void * buf;
8863+
8864+ unsigned int width;
8865+ unsigned int height;
8866+ MMAL_FOURCC_T enc_type;
8867+
8868+ picture_t * pic;
8869+} pool_ent_t;
8870+
8871+
8872+typedef struct ent_list_hdr_s
8873+{
8874+ pool_ent_t * ents;
8875+ pool_ent_t * tail;
8876+ unsigned int n;
8877+} ent_list_hdr_t;
8878+
8879+#define ENT_LIST_HDR_INIT (ent_list_hdr_t){ \
8880+ .ents = NULL, \
8881+ .tail = NULL, \
8882+ .n = 0 \
8883+}
8884+
8885+struct vzc_pool_ctl_s
8886+{
8887+ atomic_int ref_count;
8888+
8889+ ent_list_hdr_t ent_pool;
8890+ ent_list_hdr_t ents_cur;
8891+ ent_list_hdr_t ents_prev;
8892+
8893+ unsigned int max_n;
8894+ unsigned int seq;
8895+
8896+ vlc_mutex_t lock;
8897+
8898+ MMAL_POOL_T * buf_pool;
8899+
8900+ vcsm_init_type_t vcsm_init_type;
8901+};
8902+
8903+typedef struct vzc_subbuf_ent_s
8904+{
8905+ pool_ent_t * ent;
8906+ MMAL_RECT_T pic_rect;
8907+ MMAL_RECT_T orig_dest_rect;
8908+ MMAL_DISPLAYREGION_T dreg;
8909+} vzc_subbuf_ent_t;
8910+
8911+
8912+static pool_ent_t * ent_extract(ent_list_hdr_t * const elh, pool_ent_t * const ent)
8913+{
8914+// printf("List %p [%d]: Ext %p\n", elh, elh->n, ent);
8915+
8916+ if (ent == NULL)
8917+ return NULL;
8918+
8919+ if (ent->next == NULL)
8920+ elh->tail = ent->prev;
8921+ else
8922+ ent->next->prev = ent->prev;
8923+
8924+ if (ent->prev == NULL)
8925+ elh->ents = ent->next;
8926+ else
8927+ ent->prev->next = ent->next;
8928+
8929+ ent->prev = ent->next = NULL;
8930+
8931+ --elh->n;
8932+
8933+ return ent; // For convienience
8934+}
8935+
8936+static inline pool_ent_t * ent_extract_tail(ent_list_hdr_t * const elh)
8937+{
8938+ return ent_extract(elh, elh->tail);
8939+}
8940+
8941+static void ent_add_head(ent_list_hdr_t * const elh, pool_ent_t * const ent)
8942+{
8943+// printf("List %p [%d]: Add %p\n", elh, elh->n, ent);
8944+
8945+ if ((ent->next = elh->ents) == NULL)
8946+ elh->tail = ent;
8947+ else
8948+ ent->next->prev = ent;
8949+
8950+ ent->prev = NULL;
8951+ elh->ents = ent;
8952+ ++elh->n;
8953+}
8954+
8955+static void ent_free(pool_ent_t * const ent)
8956+{
8957+// printf("Free ent: %p\n", ent);
8958+ if (ent != NULL) {
8959+ // If we still have a ref to a pic - kill it now
8960+ if (ent->pic != NULL)
8961+ picture_Release(ent->pic);
8962+
8963+ // Free contents
8964+ vcsm_unlock_hdl(ent->vcsm_hdl);
8965+
8966+ vcsm_free(ent->vcsm_hdl);
8967+
8968+ free(ent);
8969+ }
8970+}
8971+
8972+static void ent_free_list(ent_list_hdr_t * const elh)
8973+{
8974+ pool_ent_t * ent = elh->ents;
8975+
8976+// printf("Free list: %p [%d]\n", elh, elh->n);
8977+
8978+ *elh = ENT_LIST_HDR_INIT;
8979+
8980+ while (ent != NULL) {
8981+ pool_ent_t * const t = ent;
8982+ ent = t->next;
8983+ ent_free(t);
8984+ }
8985+}
8986+
8987+static void ent_list_move(ent_list_hdr_t * const dst, ent_list_hdr_t * const src)
8988+{
8989+// printf("Move %p->%p\n", src, dst);
8990+
8991+ *dst = *src;
8992+ *src = ENT_LIST_HDR_INIT;
8993+}
8994+
8995+// Scans "backwards" as that should give us the fastest match if we are
8996+// presented with pics in the same order each time
8997+static pool_ent_t * ent_list_extract_pic_ent(ent_list_hdr_t * const elh, picture_t * const pic)
8998+{
8999+ pool_ent_t *ent = elh->tail;
9000+
9001+// printf("Find list: %p [%d]; pic:%p\n", elh, elh->n, pic);
9002+
9003+ while (ent != NULL) {
9004+// printf("Check ent: %p, pic:%p\n", ent, ent->pic);
9005+
9006+ if (ent->pic == pic)
9007+ return ent_extract(elh, ent);
9008+ ent = ent->prev;
9009+ }
9010+ return NULL;
9011+}
9012+
9013+#define POOL_ENT_ALLOC_BLOCK 0x10000
9014+
9015+static pool_ent_t * pool_ent_alloc_new(size_t req_size)
9016+{
9017+ pool_ent_t * ent = calloc(1, sizeof(*ent));
9018+ const size_t alloc_size = (req_size + POOL_ENT_ALLOC_BLOCK - 1) & ~(POOL_ENT_ALLOC_BLOCK - 1);
9019+
9020+ if (ent == NULL)
9021+ return NULL;
9022+
9023+ ent->next = ent->prev = NULL;
9024+
9025+ // Alloc from vcsm
9026+ if ((ent->vcsm_hdl = vcsm_malloc_cache(alloc_size, VCSM_CACHE_TYPE_HOST, (char *)"vlc-subpic")) == -1)
9027+ goto fail1;
9028+ if ((ent->vc_hdl = vcsm_vc_hdl_from_hdl(ent->vcsm_hdl)) == 0)
9029+ goto fail2;
9030+ if ((ent->buf = vcsm_lock(ent->vcsm_hdl)) == NULL)
9031+ goto fail2;
9032+
9033+ ent->size = alloc_size;
9034+ return ent;
9035+
9036+fail2:
9037+ vcsm_free(ent->vcsm_hdl);
9038+fail1:
9039+ free(ent);
9040+ return NULL;
9041+}
9042+
9043+static inline pool_ent_t * pool_ent_ref(pool_ent_t * const ent)
9044+{
9045+// int n = atomic_fetch_add(&ent->ref_count, 1) + 1;
9046+// printf("Ref: %p: %d\n", ent, n);
9047+ atomic_fetch_add(&ent->ref_count, 1);
9048+ return ent;
9049+}
9050+
9051+static void pool_recycle(vzc_pool_ctl_t * const pc, pool_ent_t * const ent)
9052+{
9053+ pool_ent_t * xs = NULL;
9054+ int n;
9055+
9056+ if (ent == NULL)
9057+ return;
9058+
9059+ n = atomic_fetch_sub(&ent->ref_count, 1) - 1;
9060+
9061+// printf("%s: Pool: %p: Ent: %p: %d\n", __func__, &pc->ent_pool, ent, n);
9062+
9063+ if (n != 0)
9064+ return;
9065+
9066+ if (ent->pic != NULL) {
9067+ picture_Release(ent->pic);
9068+ ent->pic = NULL;
9069+ }
9070+
9071+ vlc_mutex_lock(&pc->lock);
9072+
9073+ // If we have a full pool then extract the LRU and free it
9074+ // Free done outside mutex
9075+ if (pc->ent_pool.n >= pc->max_n)
9076+ xs = ent_extract_tail(&pc->ent_pool);
9077+
9078+ ent_add_head(&pc->ent_pool, ent);
9079+
9080+ vlc_mutex_unlock(&pc->lock);
9081+
9082+ ent_free(xs);
9083+}
9084+
9085+// * This could be made more efficient, but this is easy
9086+static void pool_recycle_list(vzc_pool_ctl_t * const pc, ent_list_hdr_t * const elh)
9087+{
9088+ pool_ent_t * ent;
9089+ while ((ent = ent_extract_tail(elh)) != NULL) {
9090+ pool_recycle(pc, ent);
9091+ }
9092+}
9093+
9094+static pool_ent_t * pool_best_fit(vzc_pool_ctl_t * const pc, size_t req_size)
9095+{
9096+ pool_ent_t * best = NULL;
9097+
9098+ vlc_mutex_lock(&pc->lock);
9099+
9100+ {
9101+ pool_ent_t * ent = pc->ent_pool.ents;
9102+
9103+ // Simple scan
9104+ while (ent != NULL) {
9105+ if (ent->size >= req_size && ent->size <= req_size * 2 + POOL_ENT_ALLOC_BLOCK &&
9106+ (best == NULL || best->size > ent->size))
9107+ best = ent;
9108+ ent = ent->next;
9109+ }
9110+
9111+ // extract best from chain if we've found it
9112+ ent_extract(&pc->ent_pool, best);
9113+ }
9114+
9115+ vlc_mutex_unlock(&pc->lock);
9116+
9117+ if (best == NULL)
9118+ best = pool_ent_alloc_new(req_size);
9119+
9120+ if ((best->seq = ++pc->seq) == 0)
9121+ best->seq = ++pc->seq; // Never allow to be zero
9122+
9123+ atomic_store(&best->ref_count, 1);
9124+ return best;
9125+}
9126+
9127+
9128+const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[] = { VLC_CODEC_RGBA, VLC_CODEC_BGRA, VLC_CODEC_ARGB, 0 };
9129+
9130+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH)
9131+{
9132+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
9133+ *pW = ent->width;
9134+ *pH = ent->height;
9135+}
9136+
9137+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt)
9138+{
9139+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
9140+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
9141+
9142+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
9143+ es_fmt->encoding = ent->enc_type;
9144+ es_fmt->encoding_variant = 0;
9145+
9146+ v_fmt->width = ent->width;
9147+ v_fmt->height = ent->height;
9148+ v_fmt->crop.x = 0;
9149+ v_fmt->crop.y = 0;
9150+ v_fmt->crop.width = ent->width;
9151+ v_fmt->crop.height = ent->height;
9152+
9153+ return true;
9154+}
9155+
9156+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
9157+ uint32_t * const pWidth, uint32_t * const pHeight)
9158+{
9159+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
9160+ *pWidth = ent->width;
9161+ *pHeight = ent->height;
9162+}
9163+
9164+
9165+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf)
9166+{
9167+ vzc_subbuf_ent_t * sb = buf->user_data;
9168+ return &sb->dreg;
9169+}
9170+
9171+static inline int rescale_x(int x, int mul, int div)
9172+{
9173+ return div == 0 ? x * mul : (x * mul + div/2) / div;
9174+}
9175+
9176+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)
9177+{
9178+ d->x = rescale_x(s->x - div_rect->x, mul_rect->width, div_rect->width) + mul_rect->x;
9179+ d->y = rescale_x(s->y - div_rect->y, mul_rect->height, div_rect->height) + mul_rect->y;
9180+ d->width = rescale_x(s->width, mul_rect->width, div_rect->width);
9181+ d->height = rescale_x(s->height, mul_rect->height, div_rect->height);
9182+#if TRACE_TRANSFORMS
9183+ fprintf(stderr, "(%d,%d %dx%d) * (%d,%d %dx%d) / (%d,%d %dx%d) -> (%d,%d %dx%d)\n",
9184+ s->x, s->y, s->width, s->height,
9185+ mul_rect->x, mul_rect->y, mul_rect->width, mul_rect->height,
9186+ div_rect->x, div_rect->y, div_rect->width, div_rect->height,
9187+ d->x, d->y, d->width, d->height);
9188+#endif
9189+}
9190+
9191+static MMAL_RECT_T
9192+rect_untransform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t)
9193+{
9194+#if TRACE_TRANSFORMS
9195+ fprintf(stderr, "t=%d, s=%d,%d:%dx%d, c=%d,%d:%dx%d -> ", (int)t,
9196+ s.x,s.y,s.width,s.height,
9197+ c.x,c.y,c.width,c.height);
9198+#endif
9199+ if (is_transform_hflip(t))
9200+ s = rect_hflip(s, c);
9201+ if (is_transform_vflip(t) != 0)
9202+ s = rect_vflip(s, c);
9203+ if (is_transform_transpose(t) != 0)
9204+ s = rect_transpose(s);
9205+#if TRACE_TRANSFORMS
9206+ fprintf(stderr, "s=%d,%d:%dx%d\n",
9207+ s.x,s.y,s.width,s.height);
9208+#endif
9209+ return s;
9210+}
9211+
9212+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)
9213+{
9214+ vzc_subbuf_ent_t * sb = buf->user_data;
9215+ if (scale_rect == NULL) {
9216+ sb->dreg.dest_rect = sb->orig_dest_rect;
9217+ sb->dreg.transform = MMAL_DISPLAY_ROT0;
9218+ }
9219+ else
9220+ {
9221+ // The scale rect has been transposed if we have a transposing
9222+ // transform - untranspose so we are the same way up as the source
9223+ const MMAL_RECT_T c = (scale_transform & 4) == 0 ? *scale_rect : rect_transpose(*scale_rect);
9224+ rescale_rect(&sb->dreg.dest_rect, &sb->orig_dest_rect,
9225+ &c, &sb->pic_rect);
9226+ sb->dreg.dest_rect = rect_untransform(sb->dreg.dest_rect, c, scale_transform);
9227+ sb->dreg.transform = scale_transform;
9228+ }
9229+}
9230+
9231+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf)
9232+{
9233+ vzc_subbuf_ent_t * sb = buf->user_data;
9234+ return sb->ent->seq;
9235+}
9236+
9237+
9238+// The intent with the ents_cur & ents_last stuff is to remember the buffers
9239+// we used on the last frame and reuse them on the current one if they are the
9240+// same. Unfortunately detection of "is_first" is only a heuristic (there are
9241+// no rules governing the order in which things are blended) so we must deal
9242+// (fairly) gracefully with it never (or always) being set.
9243+
9244+// dst_fmt gives the number space in which the destination pixels are specified
9245+
9246+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc,
9247+ picture_t * const pic,
9248+ const MMAL_RECT_T dst_pic_rect,
9249+ const int x_offset, const int y_offset,
9250+ const unsigned int alpha,
9251+ const bool is_first)
9252+{
9253+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(pc->buf_pool->queue);
9254+ vzc_subbuf_ent_t * sb;
9255+
9256+ if (buf == NULL)
9257+ return NULL;
9258+
9259+ if ((sb = calloc(1, sizeof(*sb))) == NULL)
9260+ goto fail1;
9261+
9262+ // If first or we've had a lot of stuff move everything to the last list
9263+ // (we could deal more gracefully with the "too many" case but it shouldn't
9264+ // really happen)
9265+ if (is_first || pc->ents_cur.n >= CTX_BUFS_MAX) {
9266+ pool_recycle_list(pc, &pc->ents_prev);
9267+ ent_list_move(&pc->ents_prev, &pc->ents_cur);
9268+ }
9269+
9270+ sb->dreg.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
9271+ sb->dreg.hdr.size = sizeof(sb->dreg);
9272+ buf->user_data = sb;
9273+
9274+ {
9275+ // ?? Round start offset as well as length
9276+ const video_format_t *const fmt = &pic->format;
9277+
9278+ const unsigned int bpp = (fmt->i_bits_per_pixel + 7) >> 3;
9279+ const unsigned int xl = (fmt->i_x_offset & ~15);
9280+ const unsigned int xr = (fmt->i_x_offset + fmt->i_visible_width + 15) & ~15;
9281+ const size_t dst_stride = (xr - xl) * bpp;
9282+ const size_t dst_lines = ((fmt->i_visible_height + 15) & ~15);
9283+ const size_t dst_size = dst_stride * dst_lines;
9284+
9285+ pool_ent_t * ent = ent_list_extract_pic_ent(&pc->ents_prev, pic);
9286+ bool needs_copy = false;
9287+
9288+ // If we didn't find ent in last then look in cur in case is_first
9289+ // isn't working
9290+ if (ent == NULL)
9291+ ent = ent_list_extract_pic_ent(&pc->ents_cur, pic);
9292+
9293+// printf("ent_found: %p\n", ent);
9294
9295-int mmal_picture_lock(picture_t *picture)
9296+ if (ent == NULL)
9297+ {
9298+ // Need a new ent
9299+ needs_copy = true;
9300+
9301+ if ((ent = pool_best_fit(pc, dst_size)) == NULL)
9302+ goto fail2;
9303+ if ((ent->enc_type = vlc_to_mmal_video_fourcc(&pic->format)) == 0)
9304+ goto fail2;
9305+
9306+ ent->pic = picture_Hold(pic);
9307+ }
9308+
9309+ ent_add_head(&pc->ents_cur, ent);
9310+
9311+ sb->ent = pool_ent_ref(ent);
9312+ hw_mmal_vzc_pool_ref(pc);
9313+
9314+ // Copy data
9315+ buf->next = NULL;
9316+ buf->cmd = 0;
9317+ buf->data = (uint8_t *)(ent->vc_hdl);
9318+ buf->alloc_size = buf->length = dst_size;
9319+ buf->offset = 0;
9320+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
9321+ buf->pts = buf->dts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
9322+ buf->type->video = (MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T){
9323+ .planes = 1,
9324+ .pitch = { dst_stride }
9325+ };
9326+
9327+ // Remember offsets
9328+ sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT |
9329+ MMAL_DISPLAY_SET_DEST_RECT |
9330+ MMAL_DISPLAY_SET_FULLSCREEN |
9331+ MMAL_DISPLAY_SET_TRANSFORM |
9332+ MMAL_DISPLAY_SET_ALPHA;
9333+
9334+ sb->dreg.fullscreen = 0;
9335+
9336+ // Will be set later - zero now to avoid any confusion
9337+ sb->dreg.transform = MMAL_DISPLAY_ROT0;
9338+ sb->dreg.dest_rect = (MMAL_RECT_T){0, 0, 0, 0};
9339+
9340+ sb->dreg.alpha = (uint32_t)(alpha & 0xff) | MMAL_DISPLAY_ALPHA_FLAGS_MIX;
9341+
9342+// 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);
9343+
9344+ sb->dreg.src_rect = (MMAL_RECT_T){
9345+ .x = (fmt->i_x_offset - xl),
9346+ .y = 0,
9347+ .width = fmt->i_visible_width,
9348+ .height = fmt->i_visible_height
9349+ };
9350+
9351+ sb->pic_rect = dst_pic_rect;
9352+
9353+ sb->orig_dest_rect = (MMAL_RECT_T){
9354+ .x = x_offset,
9355+ .y = y_offset,
9356+ .width = fmt->i_visible_width,
9357+ .height = fmt->i_visible_height
9358+ };
9359+
9360+ if (needs_copy)
9361+ {
9362+ ent->width = dst_stride / bpp;
9363+ ent->height = dst_lines;
9364+
9365+ // 2D copy
9366+ {
9367+ uint8_t *d = ent->buf;
9368+ const uint8_t *s = pic->p[0].p_pixels + xl * bpp + fmt->i_y_offset * pic->p[0].i_pitch;
9369+
9370+ mem_copy_2d(d, dst_stride, s, pic->p[0].i_pitch, fmt->i_visible_height, dst_stride);
9371+
9372+ // And make sure it is actually in memory
9373+ if (pc->vcsm_init_type != VCSM_INIT_CMA) { // ** CMA is currently always uncached
9374+ flush_range(ent->buf, dst_stride * fmt->i_visible_height);
9375+ }
9376+ }
9377+ }
9378+ }
9379+
9380+ return buf;
9381+
9382+fail2:
9383+ free(sb);
9384+fail1:
9385+ mmal_buffer_header_release(buf);
9386+ return NULL;
9387+}
9388+
9389+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc)
9390+{
9391+ pool_recycle_list(pc, &pc->ents_prev);
9392+ pool_recycle_list(pc, &pc->ents_cur);
9393+}
9394+
9395+static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc)
9396+{
9397+
9398+// printf("<<< %s\n", __func__);
9399+
9400+ hw_mmal_vzc_pool_flush(pc);
9401+
9402+ ent_free_list(&pc->ent_pool);
9403+
9404+ if (pc->buf_pool != NULL)
9405+ mmal_pool_destroy(pc->buf_pool);
9406+
9407+ vlc_mutex_destroy(&pc->lock);
9408+
9409+ cma_vcsm_exit(pc->vcsm_init_type);
9410+
9411+// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash
9412+ free (pc);
9413+
9414+ // printf(">>> %s\n", __func__);
9415+}
9416+
9417+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc)
9418+{
9419+ int n;
9420+
9421+ if (pc == NULL)
9422+ return;
9423+
9424+ n = atomic_fetch_sub(&pc->ref_count, 1) - 1;
9425+
9426+ if (n != 0)
9427+ return;
9428+
9429+ hw_mmal_vzc_pool_delete(pc);
9430+}
9431+
9432+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc)
9433+{
9434+ atomic_fetch_add(&pc->ref_count, 1);
9435+}
9436+
9437+static MMAL_BOOL_T vcz_pool_release_cb(MMAL_POOL_T * buf_pool, MMAL_BUFFER_HEADER_T *buf, void *userdata)
9438+{
9439+ vzc_pool_ctl_t * const pc = userdata;
9440+ vzc_subbuf_ent_t * const sb = buf->user_data;
9441+
9442+ VLC_UNUSED(buf_pool);
9443+
9444+// printf("<<< %s\n", __func__);
9445+
9446+ if (sb != NULL) {
9447+ buf->user_data = NULL;
9448+ pool_recycle(pc, sb->ent);
9449+ hw_mmal_vzc_pool_release(pc);
9450+ free(sb);
9451+ }
9452+
9453+// printf(">>> %s\n", __func__);
9454+
9455+ return MMAL_TRUE;
9456+}
9457+
9458+vzc_pool_ctl_t * hw_mmal_vzc_pool_new()
9459+{
9460+ vzc_pool_ctl_t * const pc = calloc(1, sizeof(*pc));
9461+
9462+ if (pc == NULL)
9463+ return NULL;
9464+
9465+ if ((pc->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
9466+ {
9467+ free(pc);
9468+ return NULL;
9469+ }
9470+
9471+ pc->max_n = 8;
9472+ vlc_mutex_init(&pc->lock); // Must init before potential destruction
9473+
9474+ if ((pc->buf_pool = mmal_pool_create(64, 0)) == NULL)
9475+ {
9476+ hw_mmal_vzc_pool_delete(pc);
9477+ return NULL;
9478+ }
9479+
9480+ atomic_store(&pc->ref_count, 1);
9481+
9482+ mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc);
9483+
9484+ return pc;
9485+}
9486+
9487+//----------------------------------------------------------------------------
9488+
9489+
9490+static const uint8_t shift_00[] = {0,0,0,0};
9491+static const uint8_t shift_01[] = {0,1,1,1};
9492+
9493+int cma_pic_set_data(picture_t * const pic,
9494+ const MMAL_ES_FORMAT_T * const mm_esfmt,
9495+ const MMAL_BUFFER_HEADER_T * const buf)
9496 {
9497- picture_sys_t *pic_sys = picture->p_sys;
9498- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
9499+ const MMAL_VIDEO_FORMAT_T * const mm_fmt = &mm_esfmt->es->video;
9500+ const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = (buf == NULL) ? NULL : &buf->type->video;
9501+ cma_buf_t *const cb = cma_buf_pic_get(pic);
9502+ unsigned int planes = 1;
9503+
9504+ uint8_t * const data = cma_buf_addr(cb);
9505+ if (data == NULL) {
9506+ return VLC_ENOMEM;
9507+ }
9508+
9509+ const uint8_t * ws = shift_00;
9510+ const uint8_t * hs = shift_00;
9511+ int pb = 1;
9512+
9513+ switch (mm_esfmt->encoding)
9514+ {
9515+ case MMAL_ENCODING_ARGB:
9516+ case MMAL_ENCODING_ABGR:
9517+ case MMAL_ENCODING_RGBA:
9518+ case MMAL_ENCODING_BGRA:
9519+ case MMAL_ENCODING_RGB32:
9520+ case MMAL_ENCODING_BGR32:
9521+ pb = 4;
9522+ break;
9523+ case MMAL_ENCODING_RGB16:
9524+ pb = 2;
9525+ break;
9526
9527- int offset = 0;
9528- picture->p[0].p_pixels = buffer->data;
9529- for (int i = 1; i < picture->i_planes; i++) {
9530- offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines;
9531- picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset;
9532+ case MMAL_ENCODING_I420:
9533+ ws = shift_01;
9534+ hs = shift_01;
9535+ planes = 3;
9536+ break;
9537+
9538+ case MMAL_ENCODING_YUVUV128:
9539+ hs = shift_01;
9540+ planes = 2;
9541+ break;
9542+
9543+ default:
9544+// msg_Err(p_filter, "%s: Unexpected format", __func__);
9545+ return VLC_EGENERIC;
9546 }
9547
9548- pic_sys->displayed = false;
9549+ // Fix up SAR if unset
9550+ if (pic->format.i_sar_den == 0 || pic->format.i_sar_num == 0) {
9551+ pic->format.i_sar_den = mm_fmt->par.den;
9552+ pic->format.i_sar_num = mm_fmt->par.num;
9553+ }
9554
9555+ pic->i_planes = planes;
9556+ unsigned int offset = 0;
9557+ for (unsigned int i = 0; i != planes; ++i) {
9558+ pic->p[i] = (plane_t){
9559+ .p_pixels = data + (buf_vid != NULL ? buf_vid->offset[i] : offset),
9560+ .i_lines = mm_fmt->height >> hs[i],
9561+ .i_pitch = buf_vid != NULL ? buf_vid->pitch[i] : mm_fmt->width * pb,
9562+ .i_pixel_pitch = pb,
9563+ .i_visible_lines = mm_fmt->crop.height >> hs[i],
9564+ .i_visible_pitch = mm_fmt->crop.width >> ws[i]
9565+ };
9566+ offset += pic->p[i].i_pitch * pic->p[i].i_lines;
9567+ }
9568 return VLC_SUCCESS;
9569 }
9570+
9571+int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic)
9572+{
9573+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma))
9574+ return VLC_EGENERIC;
9575+ if (pic->context != NULL)
9576+ return VLC_EBADVAR;
9577+
9578+ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
9579+
9580+ if (ctx == NULL)
9581+ return VLC_ENOMEM;
9582+
9583+ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
9584+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
9585+ ctx->buf_count = 1; // cb takes the place of the 1st buf
9586+ ctx->cb = cb;
9587+
9588+ cma_buf_in_flight(cb);
9589+
9590+ pic->context = &ctx->cmn;
9591+ return VLC_SUCCESS;
9592+}
9593+
9594+cma_buf_t * cma_buf_pic_get(picture_t * const pic)
9595+{
9596+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9597+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? 0 : ctx->cb;
9598+}
9599+
9600+
9601+//----------------------------------------------------------------------------
9602+
9603+/* Returns the type of the Pi being used
9604+*/
9605+bool rpi_is_model_pi4(void) {
9606+ return bcm_host_is_model_pi4();
9607+}
9608+
9609+// Preferred mode - none->cma on Pi4 otherwise legacy
9610+static volatile vcsm_init_type_t last_vcsm_type = VCSM_INIT_NONE;
9611+
9612+vcsm_init_type_t cma_vcsm_type(void)
9613+{
9614+ return last_vcsm_type;
9615+}
9616+
9617+vcsm_init_type_t cma_vcsm_init(void)
9618+{
9619+ vcsm_init_type_t rv = VCSM_INIT_NONE;
9620+ // We don't bother locking - taking a copy here should be good enough
9621+ vcsm_init_type_t try_type = last_vcsm_type;
9622+
9623+ if (try_type == VCSM_INIT_NONE) {
9624+ if (bcm_host_is_fkms_active())
9625+ try_type = VCSM_INIT_CMA;
9626+ else
9627+ try_type = VCSM_INIT_LEGACY;
9628+ }
9629+
9630+ if (try_type == VCSM_INIT_CMA) {
9631+ if (vcsm_init_ex(1, -1) == 0)
9632+ rv = VCSM_INIT_CMA;
9633+ else if (vcsm_init_ex(0, -1) == 0)
9634+ rv = VCSM_INIT_LEGACY;
9635+ }
9636+ else
9637+ {
9638+ if (vcsm_init_ex(0, -1) == 0)
9639+ rv = VCSM_INIT_LEGACY;
9640+ else if (vcsm_init_ex(1, -1) == 0)
9641+ rv = VCSM_INIT_CMA;
9642+ }
9643+
9644+ // Just in case this affects vcsm init do after that
9645+ if (rv != VCSM_INIT_NONE)
9646+ bcm_host_init();
9647+
9648+ last_vcsm_type = rv;
9649+ return rv;
9650+}
9651+
9652+void cma_vcsm_exit(const vcsm_init_type_t init_mode)
9653+{
9654+ if (init_mode != VCSM_INIT_NONE)
9655+ {
9656+ vcsm_exit();
9657+ bcm_host_deinit(); // Does nothing but add in case it ever does
9658+ }
9659+}
9660+
9661+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode)
9662+{
9663+ switch (init_mode)
9664+ {
9665+ case VCSM_INIT_CMA:
9666+ return "CMA";
9667+ case VCSM_INIT_LEGACY:
9668+ return "Legacy";
9669+ case VCSM_INIT_NONE:
9670+ return "none";
9671+ default:
9672+ break;
9673+ }
9674+ return "???";
9675+}
9676+
9677+
9678--- a/modules/hw/mmal/mmal_picture.h
9679+++ b/modules/hw/mmal/mmal_picture.h
9680@@ -24,19 +24,298 @@
9681 #ifndef VLC_MMAL_MMAL_PICTURE_H_
9682 #define VLC_MMAL_MMAL_PICTURE_H_
9683
9684+#include <stdatomic.h>
9685+
9686 #include <vlc_common.h>
9687 #include <interface/mmal/mmal.h>
9688
9689+#include "mmal_cma.h"
9690+
9691 /* Think twice before changing this. Incorrect values cause havoc. */
9692 #define NUM_ACTUAL_OPAQUE_BUFFERS 30
9693
9694-struct picture_sys_t {
9695- vlc_object_t *owner;
9696+#ifndef VLC_TICK_INVALID
9697+#define VLC_TICK_INVALID VLC_TS_INVALID
9698+#define VLC_VER_3 1
9699+#else
9700+#define VLC_VER_3 0
9701+#endif
9702+
9703+typedef struct mmal_port_pool_ref_s
9704+{
9705+ atomic_uint refs;
9706+ MMAL_POOL_T * pool;
9707+ MMAL_PORT_T * port;
9708+} hw_mmal_port_pool_ref_t;
9709+
9710+typedef struct pic_ctx_subpic_s {
9711+ picture_t * subpic;
9712+ int x, y;
9713+ int alpha;
9714+} pic_ctx_subpic_t;
9715+
9716+
9717+#define CTX_BUFS_MAX 4
9718+typedef struct pic_ctx_mmal_s {
9719+ picture_context_t cmn; // PARENT: Common els at start
9720+
9721+ cma_buf_t * cb;
9722+
9723+ unsigned int buf_count;
9724+ MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX];
9725+
9726+} pic_ctx_mmal_t;
9727+
9728+const char * str_fourcc(char * const buf, const unsigned int fcc);
9729+
9730+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc);
9731+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs);
9732+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc);
9733+// Returns true if fmt_changed
9734+// frame_rate ignored for compare, but is set if something else is updated
9735+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic);
9736+
9737+// Copy pic contents into an existing buffer
9738+int hw_mmal_copy_pic_to_buf(void * const buf_data, uint32_t * const pLength,
9739+ const MMAL_ES_FORMAT_T * const fmt, const picture_t * const pic);
9740+
9741+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
9742+ const unsigned int headers, const uint32_t payload_size);
9743+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb);
9744+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf);
9745+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr);
9746+static inline void hw_mmal_port_pool_ref_acquire(hw_mmal_port_pool_ref_t * const ppr)
9747+{
9748+ atomic_fetch_add(&ppr->refs, 1);
9749+}
9750+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
9751+ hw_mmal_port_pool_ref_t ** pppr,
9752+ MMAL_PORT_T * const port,
9753+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback);
9754+
9755+static inline int hw_mmal_pic_has_sub_bufs(picture_t * const pic)
9756+{
9757+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9758+ return ctx->buf_count > 1;
9759+}
9760+
9761+static inline void hw_mmal_pic_sub_buf_add(picture_t * const pic, MMAL_BUFFER_HEADER_T * const sub)
9762+{
9763+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9764+
9765+ if (ctx->buf_count >= CTX_BUFS_MAX) {
9766+ mmal_buffer_header_release(sub);
9767+ return;
9768+ }
9769+
9770+ ctx->bufs[ctx->buf_count++] = sub;
9771+}
9772+
9773+static inline MMAL_BUFFER_HEADER_T * hw_mmal_pic_sub_buf_get(picture_t * const pic, const unsigned int n)
9774+{
9775+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9776+
9777+ return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1];
9778+}
9779+
9780+static inline bool hw_mmal_chroma_is_mmal(const vlc_fourcc_t chroma)
9781+{
9782+ return
9783+ chroma == VLC_CODEC_MMAL_OPAQUE ||
9784+ chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
9785+ chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
9786+ chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
9787+ chroma == VLC_CODEC_MMAL_ZC_I420 ||
9788+ chroma == VLC_CODEC_MMAL_ZC_RGB32;
9789+}
9790+
9791+static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic)
9792+{
9793+ return hw_mmal_chroma_is_mmal(pic->format.i_chroma);
9794+}
9795+
9796+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn);
9797+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn);
9798+picture_context_t * hw_mmal_gen_context(
9799+ MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr);
9800+
9801+int hw_mmal_get_gpu_mem(void);
9802+
9803+
9804+static inline MMAL_STATUS_T port_parameter_set_uint32(MMAL_PORT_T * port, uint32_t id, uint32_t val)
9805+{
9806+ const MMAL_PARAMETER_UINT32_T param = {
9807+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_UINT32_T)},
9808+ .value = val
9809+ };
9810+ return mmal_port_parameter_set(port, &param.hdr);
9811+}
9812+
9813+static inline MMAL_STATUS_T port_parameter_set_bool(MMAL_PORT_T * const port, const uint32_t id, const bool val)
9814+{
9815+ const MMAL_PARAMETER_BOOLEAN_T param = {
9816+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_BOOLEAN_T)},
9817+ .enable = val
9818+ };
9819+ return mmal_port_parameter_set(port, &param.hdr);
9820+}
9821+
9822+static inline MMAL_STATUS_T port_send_replicated(MMAL_PORT_T * const port, MMAL_POOL_T * const rep_pool,
9823+ MMAL_BUFFER_HEADER_T * const src_buf,
9824+ const uint64_t seq)
9825+{
9826+ MMAL_STATUS_T err;
9827+ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
9828+
9829+ if (rep_buf == NULL)
9830+ return MMAL_ENOSPC;
9831+
9832+ if ((err = mmal_buffer_header_replicate(rep_buf, src_buf)) != MMAL_SUCCESS)
9833+ return err;
9834+
9835+ rep_buf->pts = seq;
9836+
9837+ if ((err = mmal_port_send_buffer(port, rep_buf)) != MMAL_SUCCESS)
9838+ {
9839+ mmal_buffer_header_release(rep_buf);
9840+ return err;
9841+ }
9842+
9843+ return MMAL_SUCCESS;
9844+}
9845+
9846+
9847+static inline void pic_to_buf_copy_props(MMAL_BUFFER_HEADER_T * const buf, const picture_t * const pic)
9848+{
9849+ if (!pic->b_progressive)
9850+ {
9851+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9852+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9853+ }
9854+ else
9855+ {
9856+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9857+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9858+ }
9859+ if (pic->b_top_field_first)
9860+ {
9861+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9862+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9863+ }
9864+ else
9865+ {
9866+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9867+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9868+ }
9869+ buf->pts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
9870+ buf->dts = buf->pts;
9871+}
9872+
9873+static inline void buf_to_pic_copy_props(picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf)
9874+{
9875+ // Contrary to docn the interlace & tff flags turn up in the header flags rather than the
9876+ // video specific flags (which appear to be currently unused).
9877+ pic->b_progressive = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED) == 0;
9878+ pic->b_top_field_first = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST) != 0;
9879+
9880+ pic->date = buf->pts != MMAL_TIME_UNKNOWN ? buf->pts :
9881+ buf->dts != MMAL_TIME_UNKNOWN ? buf->dts :
9882+ VLC_TICK_INVALID;
9883+}
9884+
9885+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic,
9886+ MMAL_POOL_T * const rep_pool,
9887+ MMAL_PORT_T * const port,
9888+ cma_buf_pool_t * const cbp);
9889+
9890+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool);
9891+
9892+struct vzc_pool_ctl_s;
9893+typedef struct vzc_pool_ctl_s vzc_pool_ctl_t;
9894+
9895+// At the moment we cope with any mono-planar RGBA thing
9896+// We could cope with many other things but they currently don't occur
9897+extern const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[];
9898+static inline bool hw_mmal_vzc_subpic_fmt_valid(const video_frame_format_t * const vf_vlc)
9899+{
9900+ const vlc_fourcc_t vfcc_src = vf_vlc->i_chroma;
9901+ for (const vlc_fourcc_t * p = hw_mmal_vzc_subpicture_chromas; *p != 0; ++p)
9902+ if (*p == vfcc_src)
9903+ return true;
9904+
9905+ return false;
9906+}
9907+
9908+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt);
9909+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf);
9910+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);
9911+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH);
9912+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf);
9913+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic,
9914+ const MMAL_RECT_T dst_pic_rect,
9915+ const int x_offset, const int y_offset,
9916+ const unsigned int alpha, const bool is_first);
9917+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
9918+ uint32_t * const pWidth, uint32_t * const pHeight);
9919+
9920+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc);
9921+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc);
9922+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc);
9923+vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void);
9924+
9925+
9926+static inline MMAL_RECT_T vis_mmal_rect(const video_format_t * const fmt)
9927+{
9928+ return (MMAL_RECT_T){
9929+ .x = fmt->i_x_offset,
9930+ .y = fmt->i_y_offset,
9931+ .width = fmt->i_visible_width,
9932+ .height = fmt->i_visible_height
9933+ };
9934+}
9935+
9936+int cma_pic_set_data(picture_t * const pic,
9937+ const MMAL_ES_FORMAT_T * const mm_esfmt,
9938+ const MMAL_BUFFER_HEADER_T * const buf);
9939+
9940+// Attaches cma buf to pic
9941+// Marks in_flight if not all_in_flight anyway
9942+int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic);
9943+// Returns a pointer to the cma_buf attached to the pic
9944+// Just a pointer - doesn't add a ref
9945+cma_buf_t * cma_buf_pic_get(picture_t * const pic);
9946+
9947+static inline bool is_cma_buf_pic_chroma(const uint32_t chroma)
9948+{
9949+ return chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
9950+ chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
9951+ chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
9952+ chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
9953+ chroma == VLC_CODEC_MMAL_ZC_I420;
9954+}
9955+
9956+
9957+int rpi_get_model_type(void);
9958+bool rpi_is_model_pi4(void);
9959+bool rpi_is_fkms_active(void);
9960+
9961+typedef enum vcsm_init_type_e {
9962+ VCSM_INIT_NONE = 0,
9963+ VCSM_INIT_LEGACY,
9964+ VCSM_INIT_CMA
9965+} vcsm_init_type_t;
9966+
9967+vcsm_init_type_t cma_vcsm_init(void);
9968+void cma_vcsm_exit(const vcsm_init_type_t init_mode);
9969+vcsm_init_type_t cma_vcsm_type(void);
9970+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode);
9971+
9972
9973- MMAL_BUFFER_HEADER_T *buffer;
9974- bool displayed;
9975-};
9976+#define VOUT_DISPLAY_CHANGE_MMAL_BASE 1024
9977+#define VOUT_DISPLAY_CHANGE_MMAL_HIDE (VOUT_DISPLAY_CHANGE_MMAL_BASE + 0)
9978
9979-int mmal_picture_lock(picture_t *picture);
9980+#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize"
9981+#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp"
9982+#define MMAL_COMPONENT_HVS "vc.ril.hvs"
9983
9984 #endif
9985--- /dev/null
9986+++ b/modules/hw/mmal/rpi_prof.h
9987@@ -0,0 +1,110 @@
9988+#ifndef RPI_PROFILE_H
9989+#define RPI_PROFILE_H
9990+
9991+#include <stdint.h>
9992+#include <inttypes.h>
9993+
9994+#ifndef RPI_PROFILE
9995+#define RPI_PROFILE 0
9996+#endif
9997+
9998+#if RPI_PROFILE
9999+
10000+#include "v7_pmu.h"
10001+
10002+#ifdef RPI_PROC_ALLOC
10003+#define X volatile
10004+#define Z =0
10005+#else
10006+#define X extern volatile
10007+#define Z
10008+#endif
10009+
10010+X uint64_t av_rpi_prof0_cycles Z;
10011+X unsigned int av_rpi_prof0_cnt Z;
10012+#define RPI_prof0_MAX_DURATION 100000
10013+
10014+X uint64_t av_rpi_prof1_cycles Z;
10015+X unsigned int av_rpi_prof1_cnt Z;
10016+#define RPI_prof1_MAX_DURATION 100000
10017+
10018+X uint64_t av_rpi_prof2_cycles Z;
10019+X unsigned int av_rpi_prof2_cnt Z;
10020+#define RPI_prof2_MAX_DURATION 10000
10021+
10022+X uint64_t av_rpi_prof_n_cycles[128];
10023+X unsigned int av_rpi_prof_n_cnt[128];
10024+#define RPI_prof_n_MAX_DURATION 10000
10025+
10026+
10027+#undef X
10028+#undef Z
10029+
10030+#define PROFILE_INIT()\
10031+do {\
10032+ enable_pmu();\
10033+ enable_ccnt();\
10034+} while (0)
10035+
10036+#define PROFILE_START()\
10037+do {\
10038+ volatile uint32_t perf_1 = read_ccnt();\
10039+ volatile uint32_t perf_2
10040+
10041+
10042+#define PROFILE_ACC(x)\
10043+ perf_2 = read_ccnt();\
10044+ {\
10045+ const uint32_t duration = perf_2 - perf_1;\
10046+ if (duration < RPI_##x##_MAX_DURATION)\
10047+ {\
10048+ av_rpi_##x##_cycles += duration;\
10049+ av_rpi_##x##_cnt += 1;\
10050+ }\
10051+ }\
10052+} while(0)
10053+
10054+
10055+#define PROFILE_ACC_N(n)\
10056+ if ((n) >= 0) {\
10057+ perf_2 = read_ccnt();\
10058+ {\
10059+ const uint32_t duration = perf_2 - perf_1;\
10060+ if (duration < RPI_prof_n_MAX_DURATION)\
10061+ {\
10062+ av_rpi_prof_n_cycles[n] += duration;\
10063+ av_rpi_prof_n_cnt[n] += 1;\
10064+ }\
10065+ }\
10066+ }\
10067+} while(0)
10068+
10069+#define PROFILE_PRINTF(x)\
10070+ printf("%-20s cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", #x, av_rpi_##x##_cycles, av_rpi_##x##_cnt,\
10071+ av_rpi_##x##_cnt == 0 ? (uint64_t)0 : av_rpi_##x##_cycles / (uint64_t)av_rpi_##x##_cnt)
10072+
10073+#define PROFILE_PRINTF_N(n)\
10074+ 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],\
10075+ av_rpi_prof_n_cnt[n] == 0 ? (uint64_t)0 : av_rpi_prof_n_cycles[n] / (uint64_t)av_rpi_prof_n_cnt[n])
10076+
10077+#define PROFILE_CLEAR_N(n) \
10078+do {\
10079+ av_rpi_prof_n_cycles[n] = 0;\
10080+ av_rpi_prof_n_cnt[n] = 0;\
10081+} while(0)
10082+
10083+#else
10084+
10085+// No profile
10086+#define PROFILE_INIT()
10087+#define PROFILE_START()
10088+#define PROFILE_ACC(x)
10089+#define PROFILE_ACC_N(x)
10090+#define PROFILE_PRINTF(x)
10091+#define PROFILE_PRINTF_N(x)
10092+#define PROFILE_CLEAR_N(n)
10093+
10094+#endif
10095+
10096+#endif
10097+
10098--- /dev/null
10099+++ b/modules/hw/mmal/subpic.c
10100@@ -0,0 +1,257 @@
10101+/*****************************************************************************
10102+ * mmal.c: MMAL-based decoder plugin for Raspberry Pi
10103+ *****************************************************************************
10104+ * Authors: jc@kynesim.co.uk
10105+ *
10106+ * This program is free software; you can redistribute it and/or modify it
10107+ * under the terms of the GNU Lesser General Public License as published by
10108+ * the Free Software Foundation; either version 2.1 of the License, or
10109+ * (at your option) any later version.
10110+ *
10111+ * This program is distributed in the hope that it will be useful,
10112+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10113+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10114+ * GNU Lesser General Public License for more details.
10115+ *
10116+ * You should have received a copy of the GNU Lesser General Public License
10117+ * along with this program; if not, write to the Free Software Foundation,
10118+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
10119+ *****************************************************************************/
10120+
10121+#ifdef HAVE_CONFIG_H
10122+#include "config.h"
10123+#endif
10124+
10125+#include <stdatomic.h>
10126+
10127+#include <vlc_common.h>
10128+#include <vlc_plugin.h>
10129+#include <vlc_codec.h>
10130+#include <vlc_filter.h>
10131+#include <vlc_threads.h>
10132+
10133+#include <bcm_host.h>
10134+#include <interface/mmal/mmal.h>
10135+#include <interface/mmal/util/mmal_util.h>
10136+#include <interface/mmal/util/mmal_default_components.h>
10137+
10138+#include "mmal_picture.h"
10139+#include "subpic.h"
10140+
10141+
10142+#define TRACE_ALL 0
10143+
10144+static inline bool cmp_rect(const MMAL_RECT_T * const a, const MMAL_RECT_T * const b)
10145+{
10146+ return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height;
10147+}
10148+
10149+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const sub)
10150+{
10151+ VLC_UNUSED(p_filter);
10152+ if (sub->port != NULL && sub->port->is_enabled)
10153+ mmal_port_disable(sub->port);
10154+ sub->seq = 0;
10155+}
10156+
10157+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe)
10158+{
10159+ hw_mmal_subpic_flush(p_filter, spe);
10160+
10161+ if (spe->pool != NULL)
10162+ mmal_pool_destroy(spe->pool);
10163+
10164+ // Zap to avoid any accidental reuse
10165+ *spe = (subpic_reg_stash_t){NULL};
10166+}
10167+
10168+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port,
10169+ const int display_id, const unsigned int layer)
10170+{
10171+ MMAL_STATUS_T err;
10172+
10173+ // Start by zapping all to zero
10174+ *spe = (subpic_reg_stash_t){NULL};
10175+
10176+ if ((err = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
10177+ {
10178+ msg_Err(p_filter, "Failed to set sub port zero copy");
10179+ return err;
10180+ }
10181+
10182+ if ((spe->pool = mmal_pool_create(30, 0)) == NULL)
10183+ {
10184+ msg_Err(p_filter, "Failed to create sub pool");
10185+ return MMAL_ENOMEM;
10186+ }
10187+
10188+ port->userdata = (void *)p_filter;
10189+ spe->port = port;
10190+ spe->display_id = display_id;
10191+ spe->layer = layer;
10192+
10193+ return MMAL_SUCCESS;
10194+}
10195+
10196+static void conv_subpic_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
10197+{
10198+#if TRACE_ALL
10199+ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, user=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
10200+ __func__, buf->cmd, buf->user_data, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
10201+#else
10202+ VLC_UNUSED(port);
10203+#endif
10204+
10205+ mmal_buffer_header_release(buf); // Will extract & release pic in pool callback
10206+}
10207+
10208+static int
10209+subpic_send_empty(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, const uint64_t pts)
10210+{
10211+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(spe->pool->queue);
10212+ MMAL_STATUS_T err;
10213+
10214+ if (buf == NULL) {
10215+ msg_Err(p_filter, "Buffer get for subpic failed");
10216+ return -1;
10217+ }
10218+#if TRACE_ALL
10219+ msg_Dbg(p_filter, "Remove pic for sub %d", spe->seq);
10220+#endif
10221+ buf->cmd = 0;
10222+ buf->data = NULL;
10223+ buf->alloc_size = 0;
10224+ buf->offset = 0;
10225+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
10226+ buf->pts = pts;
10227+ buf->dts = MMAL_TIME_UNKNOWN;
10228+ buf->user_data = NULL;
10229+
10230+ if ((err = mmal_port_send_buffer(spe->port, buf)) != MMAL_SUCCESS)
10231+ {
10232+ msg_Err(p_filter, "Send buffer to subput failed");
10233+ mmal_buffer_header_release(buf);
10234+ return -1;
10235+ }
10236+ return 0;
10237+}
10238+
10239+// < 0 Error
10240+// 0 Done & stop
10241+// 1 Done & continue
10242+
10243+int hw_mmal_subpic_update(vlc_object_t * const p_filter,
10244+ MMAL_BUFFER_HEADER_T * const sub_buf,
10245+ subpic_reg_stash_t * const spe,
10246+ const video_format_t * const fmt,
10247+ const MMAL_RECT_T * const scale_out,
10248+ const MMAL_DISPLAYTRANSFORM_T transform_out,
10249+ const uint64_t pts)
10250+{
10251+ MMAL_STATUS_T err;
10252+
10253+ if (sub_buf == NULL)
10254+ {
10255+ if (spe->port->is_enabled && spe->seq != 0)
10256+ {
10257+ subpic_send_empty(p_filter, spe, pts);
10258+ spe->seq = 0;
10259+ }
10260+ }
10261+ else
10262+ {
10263+ const unsigned int seq = hw_mmal_vzc_buf_seq(sub_buf);
10264+ bool needs_update = (spe->seq != seq);
10265+
10266+ hw_mmal_vzc_buf_scale_dest_rect(sub_buf, scale_out, transform_out);
10267+
10268+ if (hw_mmal_vzc_buf_set_format(sub_buf, spe->port->format))
10269+ {
10270+ MMAL_DISPLAYREGION_T * const dreg = hw_mmal_vzc_buf_region(sub_buf);
10271+ MMAL_VIDEO_FORMAT_T *const v_fmt = &spe->port->format->es->video;
10272+
10273+ v_fmt->frame_rate.den = fmt->i_frame_rate_base;
10274+ v_fmt->frame_rate.num = fmt->i_frame_rate;
10275+ v_fmt->par.den = fmt->i_sar_den;
10276+ v_fmt->par.num = fmt->i_sar_num;
10277+ v_fmt->color_space = MMAL_COLOR_SPACE_UNKNOWN;
10278+
10279+ if (needs_update || dreg->alpha != spe->alpha || !cmp_rect(&dreg->dest_rect, &spe->dest_rect)) {
10280+
10281+ spe->alpha = dreg->alpha;
10282+ spe->dest_rect = dreg->dest_rect;
10283+ needs_update = true;
10284+
10285+ if (spe->display_id >= 0)
10286+ {
10287+ dreg->display_num = spe->display_id;
10288+ dreg->set |= MMAL_DISPLAY_SET_NUM;
10289+ }
10290+ dreg->layer = spe->layer;
10291+ dreg->set |= MMAL_DISPLAY_SET_LAYER;
10292+
10293+#if TRACE_ALL
10294+ msg_Dbg(p_filter, "%s: Update region: Set=%x, dest=%dx%d @ (%d,%d), src=%dx%d @ (%d,%d), layer=%d, alpha=%#x",
10295+ __func__, dreg->set,
10296+ dreg->dest_rect.width, dreg->dest_rect.height, dreg->dest_rect.x, dreg->dest_rect.y,
10297+ dreg->src_rect.width, dreg->src_rect.height, dreg->src_rect.x, dreg->src_rect.y,
10298+ dreg->layer, dreg->alpha);
10299+#endif
10300+
10301+ // If now completely offscreen just flush this & return
10302+ // We only do -ve as (a) that is easy and (b) it seems to be
10303+ // something that can confuse mmal
10304+ if (dreg->dest_rect.y + dreg->dest_rect.height <= 0 ||
10305+ dreg->dest_rect.x + dreg->dest_rect.width <= 0)
10306+ {
10307+ if (spe->port->is_enabled)
10308+ subpic_send_empty(p_filter, spe, pts);
10309+ spe->seq = seq;
10310+ return 1;
10311+ }
10312+
10313+ if ((err = mmal_port_parameter_set(spe->port, &dreg->hdr)) != MMAL_SUCCESS)
10314+ {
10315+ msg_Err(p_filter, "Set display region on subput failed");
10316+ return -1;
10317+ }
10318+
10319+ if ((err = mmal_port_format_commit(spe->port)) != MMAL_SUCCESS)
10320+ {
10321+ msg_Dbg(p_filter, "%s: Subpic commit fail: %d", __func__, err);
10322+ return -1;
10323+ }
10324+ }
10325+ }
10326+
10327+ if (!spe->port->is_enabled)
10328+ {
10329+ spe->port->buffer_num = 30;
10330+ spe->port->buffer_size = spe->port->buffer_size_recommended; // Not used but shuts up the error checking
10331+
10332+ if ((err = mmal_port_enable(spe->port, conv_subpic_cb)) != MMAL_SUCCESS)
10333+ {
10334+ msg_Dbg(p_filter, "%s: Subpic enable fail: %d", __func__, err);
10335+ return -1;
10336+ }
10337+ }
10338+
10339+ if (needs_update)
10340+ {
10341+#if TRACE_ALL
10342+ msg_Dbg(p_filter, "Update pic for sub %d", spe->seq);
10343+#endif
10344+ if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS)
10345+ {
10346+ msg_Err(p_filter, "Send buffer to subput failed");
10347+ return -1;
10348+ }
10349+
10350+ spe->seq = seq;
10351+ }
10352+ }
10353+ return 1;
10354+}
10355+
10356+
10357+
10358--- /dev/null
10359+++ b/modules/hw/mmal/subpic.h
10360@@ -0,0 +1,33 @@
10361+#ifndef VLC_HW_MMAL_SUBPIC_H_
10362+#define VLC_HW_MMAL_SUBPIC_H_
10363+
10364+typedef struct subpic_reg_stash_s
10365+{
10366+ MMAL_PORT_T * port;
10367+ MMAL_POOL_T * pool;
10368+ int display_id; // -1 => do not set
10369+ unsigned int layer;
10370+ // Shadow vars so we can tell if stuff has changed
10371+ MMAL_RECT_T dest_rect;
10372+ unsigned int alpha;
10373+ unsigned int seq;
10374+} subpic_reg_stash_t;
10375+
10376+int hw_mmal_subpic_update(vlc_object_t * const p_filter,
10377+ MMAL_BUFFER_HEADER_T * const sub_buf,
10378+ subpic_reg_stash_t * const spe,
10379+ const video_format_t * const fmt,
10380+ const MMAL_RECT_T * const scale_out,
10381+ const MMAL_DISPLAYTRANSFORM_T transform_out,
10382+ const uint64_t pts);
10383+
10384+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
10385+
10386+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
10387+
10388+// If display id is -1 it will be unset
10389+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port,
10390+ const int display_id, const unsigned int layer);
10391+
10392+#endif
10393+
10394--- /dev/null
10395+++ b/modules/hw/mmal/transform_ops.h
10396@@ -0,0 +1,99 @@
10397+#ifndef VLC_MMAL_TRANSFORM_OPS_H
10398+#define VLC_MMAL_TRANSFORM_OPS_H
10399+
10400+#include <vlc_common.h>
10401+#include <vlc_picture.h>
10402+#include <interface/mmal/mmal.h>
10403+
10404+
10405+// These are enums with the same order so simply coerce
10406+static inline MMAL_DISPLAYTRANSFORM_T vlc_to_mmal_transform(const video_orientation_t orientation){
10407+ return (MMAL_DISPLAYTRANSFORM_T)orientation;
10408+}
10409+
10410+// MMAL headers comment these (getting 2 a bit wrong) but do not give
10411+// defines
10412+#define XFORM_H_SHIFT 0 // Hflip
10413+#define XFORM_V_SHIFT 1 // Vflip
10414+#define XFORM_T_SHIFT 2 // Transpose
10415+#define XFORM_H_BIT (1 << XFORM_H_SHIFT)
10416+#define XFORM_V_BIT (1 << XFORM_V_SHIFT)
10417+#define XFORM_T_BIT (1 << XFORM_T_SHIFT)
10418+
10419+static inline bool
10420+is_transform_transpose(const MMAL_DISPLAYTRANSFORM_T t)
10421+{
10422+ return ((unsigned int)t & XFORM_T_BIT) != 0;
10423+}
10424+
10425+static inline bool
10426+is_transform_hflip(const MMAL_DISPLAYTRANSFORM_T t)
10427+{
10428+ return ((unsigned int)t & XFORM_H_BIT) != 0;
10429+}
10430+
10431+static inline bool
10432+is_transform_vflip(const MMAL_DISPLAYTRANSFORM_T t)
10433+{
10434+ return ((unsigned int)t & XFORM_V_BIT) != 0;
10435+}
10436+
10437+static inline MMAL_DISPLAYTRANSFORM_T
10438+swap_transform_hv(const MMAL_DISPLAYTRANSFORM_T x)
10439+{
10440+ return (((x >> XFORM_H_SHIFT) & 1) << XFORM_V_SHIFT) |
10441+ (((x >> XFORM_V_SHIFT) & 1) << XFORM_H_SHIFT) |
10442+ (x & XFORM_T_BIT);
10443+}
10444+
10445+static inline MMAL_DISPLAYTRANSFORM_T
10446+transform_inverse(const MMAL_DISPLAYTRANSFORM_T x)
10447+{
10448+ return is_transform_transpose(x) ? swap_transform_hv(x) : x;
10449+}
10450+
10451+// Transform generated by A then B
10452+// All ops are self inverse so can simply be XORed on their own
10453+// H & V flips after a transpose need to be swapped
10454+static inline MMAL_DISPLAYTRANSFORM_T
10455+combine_transform(const MMAL_DISPLAYTRANSFORM_T a, const MMAL_DISPLAYTRANSFORM_T b)
10456+{
10457+ return a ^ (is_transform_transpose(a) ? swap_transform_hv(b) : b);
10458+}
10459+
10460+static inline MMAL_RECT_T
10461+rect_transpose(const MMAL_RECT_T s)
10462+{
10463+ return (MMAL_RECT_T){
10464+ .x = s.y,
10465+ .y = s.x,
10466+ .width = s.height,
10467+ .height = s.width
10468+ };
10469+}
10470+
10471+// hflip s in c
10472+static inline MMAL_RECT_T rect_hflip(const MMAL_RECT_T s, const MMAL_RECT_T c)
10473+{
10474+ return (MMAL_RECT_T){
10475+ .x = c.x + (c.x + c.width) - (s.x + s.width),
10476+ .y = s.y,
10477+ .width = s.width,
10478+ .height = s.height
10479+ };
10480+}
10481+
10482+// vflip s in c
10483+static inline MMAL_RECT_T rect_vflip(const MMAL_RECT_T s, const MMAL_RECT_T c)
10484+{
10485+ return (MMAL_RECT_T){
10486+ .x = s.x,
10487+ .y = (c.y + c.height) - (s.y - c.y) - s.height,
10488+ .width = s.width,
10489+ .height = s.height
10490+ };
10491+}
10492+
10493+
10494+#endif
10495+
10496--- /dev/null
10497+++ b/modules/hw/mmal/v7_pmu.S
10498@@ -0,0 +1,263 @@
10499+/*------------------------------------------------------------
10500+Performance Monitor Block
10501+------------------------------------------------------------*/
10502+ .arm @ Make sure we are in ARM mode.
10503+ .text
10504+ .align 2
10505+ .global getPMN @ export this function for the linker
10506+
10507+/* Returns the number of progammable counters uint32_t getPMN(void) */
10508+
10509+getPMN:
10510+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC Register */
10511+ MOV r0, r0, LSR #11 /* Shift N field down to bit 0 */
10512+ AND r0, r0, #0x1F /* Mask to leave just the 5 N bits */
10513+ BX lr
10514+
10515+
10516+
10517+ .global pmn_config @ export this function for the linker
10518+ /* Sets the event for a programmable counter to record */
10519+ /* void pmn_config(unsigned counter, uint32_t event) */
10520+ /* counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1 */
10521+ /* event = r1 = The event code */
10522+pmn_config:
10523+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */
10524+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */
10525+ MCR p15, 0, r1, c9, c13, 1 /* Write EVTSELx Register */
10526+ BX lr
10527+
10528+
10529+
10530+ .global ccnt_divider @ export this function for the linker
10531+ /* Enables/disables the divider (1/64) on CCNT */
10532+ /* void ccnt_divider(int divider) */
10533+ /* divider = r0 = If 0 disable divider, else enable dvider */
10534+ccnt_divider:
10535+ MRC p15, 0, r1, c9, c12, 0 /* Read PMNC */
10536+
10537+ CMP r0, #0x0 /* IF (r0 == 0) */
10538+ BICEQ r1, r1, #0x08 /* THEN: Clear the D bit (disables the */
10539+ ORRNE r1, r1, #0x08 /* ELSE: Set the D bit (enables the di */
10540+
10541+ MCR p15, 0, r1, c9, c12, 0 /* Write PMNC */
10542+ BX lr
10543+
10544+
10545+ /* --------------------------------------------------------------- */
10546+ /* Enable/Disable */
10547+ /* --------------------------------------------------------------- */
10548+
10549+ .global enable_pmu @ export this function for the linker
10550+ /* Global PMU enable */
10551+ /* void enable_pmu(void) */
10552+enable_pmu:
10553+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
10554+ ORR r0, r0, #0x01 /* Set E bit */
10555+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
10556+ BX lr
10557+
10558+
10559+
10560+ .global disable_pmu @ export this function for the linker
10561+ /* Global PMU disable */
10562+ /* void disable_pmu(void) */
10563+disable_pmu:
10564+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
10565+ BIC r0, r0, #0x01 /* Clear E bit */
10566+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
10567+ BX lr
10568+
10569+
10570+
10571+ .global enable_ccnt @ export this function for the linker
10572+ /* Enable the CCNT */
10573+ /* void enable_ccnt(void) */
10574+enable_ccnt:
10575+ MOV r0, #0x80000000 /* Set C bit */
10576+ MCR p15, 0, r0, c9, c12, 1 /* Write CNTENS Register */
10577+ BX lr
10578+
10579+
10580+
10581+ .global disable_ccnt @ export this function for the linker
10582+ /* Disable the CCNT */
10583+ /* void disable_ccnt(void) */
10584+disable_ccnt:
10585+ MOV r0, #0x80000000 /* Clear C bit */
10586+ MCR p15, 0, r0, c9, c12, 2 /* Write CNTENC Register */
10587+ BX lr
10588+
10589+
10590+
10591+ .global enable_pmn @ export this function for the linker
10592+ /* Enable PMN{n} */
10593+ /* void enable_pmn(uint32_t counter) */
10594+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10595+enable_pmn: */
10596+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */
10597+ MOV r1, r1, LSL r0
10598+
10599+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */
10600+ BX lr
10601+
10602+
10603+
10604+ .global disable_pmn @ export this function for the linker
10605+ /* Enable PMN{n} */
10606+ /* void disable_pmn(uint32_t counter) */
10607+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10608+disable_pmn: */
10609+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */
10610+ MOV r1, r1, LSL r0
10611+
10612+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */
10613+ BX lr
10614+
10615+
10616+
10617+ .global enable_pmu_user_access @ export this function for the linker
10618+ /* Enables User mode access to the PMU (must be called in a priviledge */
10619+ /* void enable_pmu_user_access(void) */
10620+enable_pmu_user_access:
10621+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */
10622+ ORR r0, r0, #0x01 /* Set EN bit (bit 0) */
10623+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */
10624+ BX lr
10625+
10626+
10627+
10628+ .global disable_pmu_user_access @ export this function for the linke
10629+ /* Disables User mode access to the PMU (must be called in a priviledg */
10630+ /* void disable_pmu_user_access(void) */
10631+disable_pmu_user_access:
10632+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */
10633+ BIC r0, r0, #0x01 /* Clear EN bit (bit 0) */
10634+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */
10635+ BX lr
10636+
10637+
10638+ /* --------------------------------------------------------------- */
10639+ /* Counter read registers */
10640+ /* --------------------------------------------------------------- */
10641+
10642+ .global read_ccnt @ export this function for the linker
10643+ /* Returns the value of CCNT */
10644+ /* uint32_t read_ccnt(void) */
10645+read_ccnt:
10646+ MRC p15, 0, r0, c9, c13, 0 /* Read CCNT Register */
10647+ BX lr
10648+
10649+
10650+ .global read_pmn @ export this function for the linker
10651+ /* Returns the value of PMN{n} */
10652+ /* uint32_t read_pmn(uint32_t counter) */
10653+ /* counter = r0 = The counter to read (e.g. 0 for PMN0, 1 for PMN1) *
10654+read_pmn: */
10655+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */
10656+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */
10657+ MRC p15, 0, r0, c9, c13, 2 /* Read current PMNx Register */
10658+ BX lr
10659+
10660+
10661+ /* --------------------------------------------------------------- */
10662+ /* Software Increment */
10663+ /* --------------------------------------------------------------- */
10664+
10665+ .global pmu_software_increment @ export this function for the linker
10666+ /* Writes to software increment register */
10667+ /* void pmu_software_increment(uint32_t counter) */
10668+ /* counter = r0 = The counter to increment (e.g. 0 for PMN0, 1 for PMN
10669+pmu_software_increment: */
10670+ MOV r1, #0x01
10671+ MOV r1, r1, LSL r0
10672+ MCR p15, 0, r1, c9, c12, 4 /* Write SWINCR Register */
10673+ BX lr
10674+
10675+ /* --------------------------------------------------------------- */
10676+ /* Overflow & Interrupt Generation */
10677+ /* --------------------------------------------------------------- */
10678+
10679+ .global read_flags @ export this function for the linker
10680+ /* Returns the value of the overflow flags */
10681+ /* uint32_t read_flags(void) */
10682+read_flags:
10683+ MRC p15, 0, r0, c9, c12, 3 /* Read FLAG Register */
10684+ BX lr
10685+
10686+
10687+ .global write_flags @ export this function for the linker
10688+ /* Writes the overflow flags */
10689+ /* void write_flags(uint32_t flags) */
10690+write_flags:
10691+ MCR p15, 0, r0, c9, c12, 3 /* Write FLAG Register */
10692+ BX lr
10693+
10694+
10695+ .global enable_ccnt_irq @ export this function for the linker
10696+ /* Enables interrupt generation on overflow of the CCNT */
10697+ /* void enable_ccnt_irq(void) */
10698+enable_ccnt_irq:
10699+ MOV r0, #0x80000000
10700+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */
10701+ BX lr
10702+
10703+ .global disable_ccnt_irq @ export this function for the linker
10704+ /* Disables interrupt generation on overflow of the CCNT */
10705+ /* void disable_ccnt_irq(void) */
10706+disable_ccnt_irq:
10707+ MOV r0, #0x80000000
10708+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */
10709+ BX lr
10710+
10711+
10712+ .global enable_pmn_irq @ export this function for the linker
10713+ /* Enables interrupt generation on overflow of PMN{x} */
10714+ /* void enable_pmn_irq(uint32_t counter) */
10715+ /* counter = r0 = The counter to enable the interrupt for (e.g. 0 for
10716+enable_pmn_irq: */
10717+ MOV r1, #0x1 /* Use arg (r0) to set which counter */
10718+ MOV r0, r1, LSL r0
10719+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */
10720+ BX lr
10721+
10722+ .global disable_pmn_irq @ export this function for the linker
10723+ /* Disables interrupt generation on overflow of PMN{x} */
10724+ /* void disable_pmn_irq(uint32_t counter) */
10725+ /* counter = r0 = The counter to disable the interrupt for (e.g. 0 fo
10726+disable_pmn_irq: */
10727+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */
10728+ MOV r0, r1, LSL r0
10729+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */
10730+ BX lr
10731+
10732+ /* --------------------------------------------------------------- */
10733+ /* Reset Functions */
10734+ /* --------------------------------------------------------------- */
10735+
10736+ .global reset_pmn @ export this function for the linker
10737+ /* Resets the programmable counters */
10738+ /* void reset_pmn(void) */
10739+reset_pmn:
10740+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
10741+ ORR r0, r0, #0x02 /* Set P bit (Event Counter Reset) */
10742+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
10743+ BX lr
10744+
10745+
10746+ .global reset_ccnt @ export this function for the linker
10747+ /* Resets the CCNT */
10748+ /* void reset_ccnt(void) */
10749+reset_ccnt:
10750+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
10751+ ORR r0, r0, #0x04 /* Set C bit (Event Counter Reset) */
10752+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
10753+ BX lr
10754+
10755+
10756+ .end @end of code, this line is optional.
10757+/* ------------------------------------------------------------ */
10758+/* End of v7_pmu.s */
10759+/* ------------------------------------------------------------ */
10760+
10761+
10762--- /dev/null
10763+++ b/modules/hw/mmal/v7_pmu.h
10764@@ -0,0 +1,113 @@
10765+// ------------------------------------------------------------
10766+// PMU for Cortex-A/R (v7-A/R)
10767+// ------------------------------------------------------------
10768+
10769+#ifndef _V7_PMU_H
10770+#define _V7_PMU_H
10771+
10772+// Returns the number of progammable counters
10773+unsigned int getPMN(void);
10774+
10775+// Sets the event for a programmable counter to record
10776+// counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1)
10777+// event = r1 = The event code (from appropiate TRM or ARM Architecture Reference Manual)
10778+void pmn_config(unsigned int counter, unsigned int event);
10779+
10780+// Enables/disables the divider (1/64) on CCNT
10781+// divider = r0 = If 0 disable divider, else enable dvider
10782+void ccnt_divider(int divider);
10783+
10784+//
10785+// Enables and disables
10786+//
10787+
10788+// Global PMU enable
10789+// On ARM11 this enables the PMU, and the counters start immediately
10790+// On Cortex this enables the PMU, there are individual enables for the counters
10791+void enable_pmu(void);
10792+
10793+// Global PMU disable
10794+// On Cortex, this overrides the enable state of the individual counters
10795+void disable_pmu(void);
10796+
10797+// Enable the CCNT
10798+void enable_ccnt(void);
10799+
10800+// Disable the CCNT
10801+void disable_ccnt(void);
10802+
10803+// Enable PMN{n}
10804+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10805+void enable_pmn(unsigned int counter);
10806+
10807+// Enable PMN{n}
10808+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10809+void disable_pmn(unsigned int counter);
10810+
10811+//
10812+// Read counter values
10813+//
10814+
10815+// Returns the value of CCNT
10816+unsigned int read_ccnt(void);
10817+
10818+// Returns the value of PMN{n}
10819+// counter = The counter to read (e.g. 0 for PMN0, 1 for PMN1)
10820+unsigned int read_pmn(unsigned int counter);
10821+
10822+//
10823+// Overflow and interrupts
10824+//
10825+
10826+// Returns the value of the overflow flags
10827+unsigned int read_flags(void);
10828+
10829+// Writes the overflow flags
10830+void write_flags(unsigned int flags);
10831+
10832+// Enables interrupt generation on overflow of the CCNT
10833+void enable_ccnt_irq(void);
10834+
10835+// Disables interrupt generation on overflow of the CCNT
10836+void disable_ccnt_irq(void);
10837+
10838+// Enables interrupt generation on overflow of PMN{x}
10839+// counter = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
10840+void enable_pmn_irq(unsigned int counter);
10841+
10842+// Disables interrupt generation on overflow of PMN{x}
10843+// counter = r0 = The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
10844+void disable_pmn_irq(unsigned int counter);
10845+
10846+//
10847+// Counter reset functions
10848+//
10849+
10850+// Resets the programmable counters
10851+void reset_pmn(void);
10852+
10853+// Resets the CCNT
10854+void reset_ccnt(void);
10855+
10856+//
10857+// Software Increment
10858+
10859+// Writes to software increment register
10860+// counter = The counter to increment (e.g. 0 for PMN0, 1 for PMN1)
10861+void pmu_software_increment(unsigned int counter);
10862+
10863+//
10864+// User mode access
10865+//
10866+
10867+// Enables User mode access to the PMU (must be called in a priviledged mode)
10868+void enable_pmu_user_access(void);
10869+
10870+// Disables User mode access to the PMU (must be called in a priviledged mode)
10871+void disable_pmu_user_access(void);
10872+
10873+#endif
10874+// ------------------------------------------------------------
10875+// End of v7_pmu.h
10876+// ------------------------------------------------------------
10877+
10878--- a/modules/hw/mmal/vout.c
10879+++ b/modules/hw/mmal/vout.c
10880@@ -27,21 +27,28 @@
10881 #endif
10882
10883 #include <math.h>
10884+#include <stdatomic.h>
10885
10886 #include <vlc_common.h>
10887-#include <vlc_atomic.h>
10888 #include <vlc_plugin.h>
10889 #include <vlc_threads.h>
10890 #include <vlc_vout_display.h>
10891+#include <vlc_modules.h>
10892
10893-#include "mmal_picture.h"
10894-
10895+#pragma GCC diagnostic push
10896+#pragma GCC diagnostic ignored "-Wbad-function-cast"
10897 #include <bcm_host.h>
10898+#pragma GCC diagnostic pop
10899 #include <interface/mmal/mmal.h>
10900 #include <interface/mmal/util/mmal_util.h>
10901 #include <interface/mmal/util/mmal_default_components.h>
10902 #include <interface/vmcs_host/vc_tvservice.h>
10903-#include <interface/vmcs_host/vc_dispmanx.h>
10904+
10905+#include "mmal_picture.h"
10906+#include "subpic.h"
10907+#include "transform_ops.h"
10908+
10909+#define TRACE_ALL 0
10910
10911 #define MAX_BUFFERS_IN_TRANSIT 1
10912 #define VC_TV_MAX_MODE_IDS 127
10913@@ -50,10 +57,28 @@
10914 #define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.")
10915 #define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.")
10916
10917-#define MMAL_BLANK_BACKGROUND_NAME "mmal-blank-background"
10918-#define MMAL_BLANK_BACKGROUND_TEXT N_("Blank screen below video.")
10919-#define MMAL_BLANK_BACKGROUND_LONGTEXT N_("Render blank screen below video. " \
10920- "Increases VideoCore load.")
10921+#define MMAL_DISPLAY_NAME "mmal-display"
10922+#define MMAL_DISPLAY_TEXT N_("Output device for Rpi fullscreen.")
10923+#define MMAL_DISPLAY_LONGTEXT N_("Output device for Rpi fullscreen. " \
10924+"Valid values are HDMI-1,HDMI-2. By default if qt-fullscreen-screennumber " \
10925+"is specified (or set by Fullscreen Output Device in Preferences) " \
10926+"HDMI-<qt-fullscreen-screennumber+1> will be used, otherwise HDMI-1.")
10927+
10928+#define MMAL_VOUT_TRANSFORM_NAME "mmal-vout-transform"
10929+#define MMAL_VOUT_TRANSFORM_TEXT N_("Video transform for Rpi fullscreen.")
10930+#define MMAL_VOUT_TRANSFORM_LONGTEXT N_("Video transform for Rpi fullscreen."\
10931+"Transforms availible: auto, 0, 90, 180, 270, hflip, vflip, transpose, antitranspose")
10932+
10933+#define MMAL_VOUT_WINDOW_NAME "mmal-vout-window"
10934+#define MMAL_VOUT_WINDOW_TEXT N_("Display window for Rpi fullscreen")
10935+#define MMAL_VOUT_WINDOW_LONGTEXT N_("Display window for Rpi fullscreen."\
10936+"fullscreen|<width>x<height>+<x>+<y>")
10937+
10938+#define MMAL_VOUT_TRANSPARENT_NAME "mmal-vout-transparent"
10939+#define MMAL_VOUT_TRANSPARENT_TEXT N_("Enable layers beneeth the vodeo layer.")
10940+#define MMAL_VOUT_TRANSPARENT_LONGTEXT N_("Enable layers beneath the video layer."\
10941+" By default these are disabled."\
10942+" Having the lower layers enabled can impact video performance")
10943
10944 #define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate"
10945 #define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.")
10946@@ -68,332 +93,628 @@
10947 #define PHASE_OFFSET_TARGET ((double)0.25)
10948 #define PHASE_CHECK_INTERVAL 100
10949
10950-static int Open(vlc_object_t *);
10951-static void Close(vlc_object_t *);
10952-
10953-vlc_module_begin()
10954- set_shortname(N_("MMAL vout"))
10955- set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
10956- set_capability("vout display", 90)
10957- add_shortcut("mmal_vout")
10958- add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
10959- add_bool(MMAL_BLANK_BACKGROUND_NAME, true, MMAL_BLANK_BACKGROUND_TEXT,
10960- MMAL_BLANK_BACKGROUND_LONGTEXT, true);
10961- add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
10962- MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
10963- add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
10964- MMAL_NATIVE_INTERLACE_LONGTEXT, false)
10965- set_callbacks(Open, Close)
10966-vlc_module_end()
10967+#define SUBS_MAX 4
10968
10969-struct dmx_region_t {
10970- struct dmx_region_t *next;
10971- picture_t *picture;
10972- VC_RECT_T bmp_rect;
10973- VC_RECT_T src_rect;
10974- VC_RECT_T dst_rect;
10975- VC_DISPMANX_ALPHA_T alpha;
10976- DISPMANX_ELEMENT_HANDLE_T element;
10977- DISPMANX_RESOURCE_HANDLE_T resource;
10978- int32_t pos_x;
10979- int32_t pos_y;
10980-};
10981+typedef struct vout_subpic_s {
10982+ MMAL_COMPONENT_T *component;
10983+ subpic_reg_stash_t sub;
10984+} vout_subpic_t;
10985
10986 struct vout_display_sys_t {
10987- vlc_cond_t buffer_cond;
10988- vlc_mutex_t buffer_mutex;
10989 vlc_mutex_t manage_mutex;
10990
10991- plane_t planes[3]; /* Depending on video format up to 3 planes are used */
10992- picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */
10993- picture_pool_t *picture_pool;
10994-
10995+ vcsm_init_type_t init_type;
10996 MMAL_COMPONENT_T *component;
10997 MMAL_PORT_T *input;
10998 MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/
10999- struct dmx_region_t *dmx_region;
11000 int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */
11001
11002- uint32_t buffer_size; /* size of actual mmal buffers */
11003 int buffers_in_transit; /* number of buffers currently pushed to mmal component */
11004 unsigned num_buffers; /* number of buffers allocated at mmal port */
11005
11006- DISPMANX_DISPLAY_HANDLE_T dmx_handle;
11007- DISPMANX_ELEMENT_HANDLE_T bkg_element;
11008- DISPMANX_RESOURCE_HANDLE_T bkg_resource;
11009- unsigned display_width;
11010- unsigned display_height;
11011+ int display_id;
11012+ MMAL_RECT_T win_rect; // Window rect after transform(s)
11013+ MMAL_RECT_T display_rect; // Actual shape of display (x, y always 0)
11014+ MMAL_RECT_T req_win; // User requested window (w=0 => fullscreen)
11015+
11016+ MMAL_RECT_T spu_rect; // Output rectangle in cfg coords (for subpic placement)
11017+ MMAL_RECT_T dest_rect; // Output rectangle in display coords
11018+ MMAL_DISPLAYTRANSFORM_T dest_transform; // Dest window coord transform
11019+ MMAL_DISPLAYTRANSFORM_T display_transform; // "Native" display transform
11020+ MMAL_DISPLAYTRANSFORM_T video_transform; // Combined config+native transform
11021
11022- int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
11023- int i_frame_rate;
11024+ unsigned int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
11025+ unsigned int i_frame_rate;
11026
11027 int next_phase_check; /* lowpass for phase check frequency */
11028 int phase_offset; /* currently applied offset to presentation time in ns */
11029 int layer; /* the dispman layer (z-index) used for video rendering */
11030+ bool transparent; // Do not disable layers beneath ours
11031
11032 bool need_configure_display; /* indicates a required display reconfigure to main thread */
11033 bool adjust_refresh_rate;
11034 bool native_interlaced;
11035 bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */
11036 bool b_progressive;
11037- bool opaque; /* indicated use of opaque picture format (zerocopy) */
11038-};
11039+ bool force_config;
11040
11041-static const vlc_fourcc_t subpicture_chromas[] = {
11042- VLC_CODEC_RGBA,
11043- 0
11044-};
11045+ vout_subpic_t subs[SUBS_MAX];
11046+ // Stash for subpics derived from the passed subpicture rather than
11047+ // included with the main pic
11048+ MMAL_BUFFER_HEADER_T * subpic_bufs[SUBS_MAX];
11049+
11050+ picture_pool_t * pic_pool;
11051+
11052+ struct vout_isp_conf_s {
11053+ MMAL_COMPONENT_T *component;
11054+ MMAL_PORT_T * input;
11055+ MMAL_PORT_T * output;
11056+ MMAL_QUEUE_T * out_q;
11057+ MMAL_POOL_T * in_pool;
11058+ MMAL_POOL_T * out_pool;
11059+ bool pending;
11060+ } isp;
11061
11062-/* Utility functions */
11063-static inline uint32_t align(uint32_t x, uint32_t y);
11064-static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
11065- const video_format_t *fmt);
11066+ MMAL_POOL_T * copy_pool;
11067+ MMAL_BUFFER_HEADER_T * copy_buf;
11068
11069-/* VLC vout display callbacks */
11070-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count);
11071-static void vd_prepare(vout_display_t *vd, picture_t *picture,
11072- subpicture_t *subpicture);
11073-static void vd_display(vout_display_t *vd, picture_t *picture,
11074- subpicture_t *subpicture);
11075-static int vd_control(vout_display_t *vd, int query, va_list args);
11076-static void vd_manage(vout_display_t *vd);
11077-
11078-/* MMAL callbacks */
11079-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
11080-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
11081+ // Subpic blend if we have to do it here
11082+ vzc_pool_ctl_t * vzc;
11083+};
11084
11085-/* TV service */
11086-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height);
11087-static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
11088- uint32_t param2);
11089-static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
11090-static int set_latency_target(vout_display_t *vd, bool enable);
11091
11092-/* DispManX */
11093-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture);
11094-static void close_dmx(vout_display_t *vd);
11095-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
11096- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region);
11097-static void dmx_region_update(struct dmx_region_t *dmx_region,
11098- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture);
11099-static void dmx_region_delete(struct dmx_region_t *dmx_region,
11100- DISPMANX_UPDATE_HANDLE_T update);
11101-static void show_background(vout_display_t *vd, bool enable);
11102-static void maintain_phase_sync(vout_display_t *vd);
11103+// ISP setup
11104
11105-static int Open(vlc_object_t *object)
11106+static inline bool want_isp(const vout_display_t * const vd)
11107 {
11108- vout_display_t *vd = (vout_display_t *)object;
11109- vout_display_sys_t *sys;
11110- uint32_t buffer_pitch, buffer_height;
11111- vout_display_place_t place;
11112- MMAL_DISPLAYREGION_T display_region;
11113- MMAL_STATUS_T status;
11114- int ret = VLC_SUCCESS;
11115- unsigned i;
11116+ return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10);
11117+}
11118
11119- if (vout_display_IsWindowed(vd))
11120- return VLC_EGENERIC;
11121+static inline bool want_copy(const vout_display_t * const vd)
11122+{
11123+ return (vd->fmt.i_chroma == VLC_CODEC_I420 || vd->fmt.i_chroma == VLC_CODEC_I420_10L);
11124+}
11125
11126- sys = calloc(1, sizeof(struct vout_display_sys_t));
11127- if (!sys)
11128- return VLC_ENOMEM;
11129- vd->sys = sys;
11130+static inline vlc_fourcc_t req_chroma(const vout_display_t * const vd)
11131+{
11132+ return !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma) && !want_copy(vd) ?
11133+ VLC_CODEC_I420 :
11134+ vd->fmt.i_chroma;
11135+}
11136
11137- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
11138- bcm_host_init();
11139+static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc)
11140+{
11141+ switch (fcc){
11142+ case VLC_CODEC_MMAL_OPAQUE:
11143+ return MMAL_ENCODING_OPAQUE;
11144+ case VLC_CODEC_MMAL_ZC_SAND8:
11145+ return MMAL_ENCODING_YUVUV128;
11146+ case VLC_CODEC_MMAL_ZC_SAND10:
11147+ return MMAL_ENCODING_YUVUV64_10;
11148+ case VLC_CODEC_MMAL_ZC_SAND30:
11149+ return MMAL_ENCODING_YUV10_COL;
11150+ case VLC_CODEC_MMAL_ZC_I420:
11151+ case VLC_CODEC_I420:
11152+ return MMAL_ENCODING_I420;
11153+ default:
11154+ break;
11155+ }
11156+ return MMAL_ENCODING_I420;
11157+}
11158
11159- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE;
11160+static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate)
11161+{
11162+ const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ;
11163+ const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height;
11164+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
11165
11166- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
11167- if (status != MMAL_SUCCESS) {
11168- msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
11169- MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
11170- ret = VLC_EGENERIC;
11171- goto out;
11172+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
11173+ es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
11174+ es_fmt->encoding_variant = 0;
11175+
11176+ v_fmt->width = (w + 31) & ~31;
11177+ v_fmt->height = (h + 15) & ~15;
11178+ v_fmt->crop.x = 0;
11179+ v_fmt->crop.y = 0;
11180+ v_fmt->crop.width = w;
11181+ v_fmt->crop.height = h;
11182+ if (vd->fmt.i_sar_num == 0 || vd->fmt.i_sar_den == 0) {
11183+ v_fmt->par.num = 1;
11184+ v_fmt->par.den = 1;
11185+ } else {
11186+ v_fmt->par.num = vd->fmt.i_sar_num;
11187+ v_fmt->par.den = vd->fmt.i_sar_den;
11188 }
11189+ v_fmt->frame_rate.num = vd->fmt.i_frame_rate;
11190+ v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base;
11191+ v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space);
11192
11193- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
11194- status = mmal_port_enable(sys->component->control, control_port_cb);
11195- if (status != MMAL_SUCCESS) {
11196- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
11197- sys->component->control->name, status, mmal_status_to_string(status));
11198- ret = VLC_EGENERIC;
11199- goto out;
11200+ msg_Dbg(vd, "WxH: %dx%d, Crop: %dx%d", v_fmt->width, v_fmt->height, v_fmt->crop.width, v_fmt->crop.height);
11201+}
11202+
11203+static MMAL_RECT_T
11204+display_src_rect(const vout_display_t * const vd, const video_format_t * const src)
11205+{
11206+ const bool wants_isp = want_isp(vd);
11207+
11208+ // Scale source derived cropping to actual picture shape
11209+ return (MMAL_RECT_T){
11210+ .x = wants_isp ? 0 : src->i_x_offset * vd->fmt.i_width / src->i_width,
11211+ .y = wants_isp ? 0 : src->i_y_offset * vd->fmt.i_height / src->i_height,
11212+ .width = src->i_visible_width * vd->fmt.i_width / src->i_width,
11213+ .height = src->i_visible_height * vd->fmt.i_height / src->i_height
11214+ };
11215+}
11216+
11217+static void isp_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
11218+{
11219+#if TRACE_ALL
11220+ vout_display_t * const vd = (vout_display_t *)port->userdata;
11221+ pic_ctx_mmal_t * ctx = buf->user_data;
11222+ msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf,
11223+ buf->flags, (long long)buf->pts);
11224+#else
11225+ VLC_UNUSED(port);
11226+#endif
11227+
11228+ mmal_buffer_header_release(buf);
11229+
11230+#if TRACE_ALL
11231+ msg_Dbg(vd, ">>> %s", __func__);
11232+#endif
11233+}
11234+
11235+static void isp_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
11236+{
11237+ vout_display_t *vd = (vout_display_t *)port->userdata;
11238+ MMAL_STATUS_T status;
11239+
11240+ if (buffer->cmd == MMAL_EVENT_ERROR) {
11241+ status = *(uint32_t *)buffer->data;
11242+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
11243 }
11244
11245- sys->input = sys->component->input[0];
11246- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
11247+ mmal_buffer_header_release(buffer);
11248+}
11249
11250- if (sys->opaque) {
11251- sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
11252- sys->i_planes = 1;
11253- sys->buffer_size = sys->input->buffer_size_recommended;
11254- } else {
11255- sys->input->format->encoding = MMAL_ENCODING_I420;
11256- vd->fmt.i_chroma = VLC_CODEC_I420;
11257- buffer_pitch = align(vd->fmt.i_width, 32);
11258- buffer_height = align(vd->fmt.i_height, 16);
11259- sys->i_planes = 3;
11260- sys->buffer_size = 3 * buffer_pitch * buffer_height / 2;
11261- }
11262-
11263- sys->input->format->es->video.width = vd->fmt.i_width;
11264- sys->input->format->es->video.height = vd->fmt.i_height;
11265- sys->input->format->es->video.crop.x = 0;
11266- sys->input->format->es->video.crop.y = 0;
11267- sys->input->format->es->video.crop.width = vd->fmt.i_width;
11268- sys->input->format->es->video.crop.height = vd->fmt.i_height;
11269- sys->input->format->es->video.par.num = vd->source.i_sar_num;
11270- sys->input->format->es->video.par.den = vd->source.i_sar_den;
11271+static void isp_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
11272+{
11273+ if (buf->cmd == 0 && buf->length != 0)
11274+ {
11275+ // The filter structure etc. should always exist if we have contents
11276+ // but might not on later flushes as we shut down
11277+ vout_display_t * const vd = (vout_display_t *)port->userdata;
11278+ struct vout_isp_conf_s *const isp = &vd->sys->isp;
11279
11280- status = mmal_port_format_commit(sys->input);
11281- if (status != MMAL_SUCCESS) {
11282- msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
11283- sys->input->name, status, mmal_status_to_string(status));
11284- ret = VLC_EGENERIC;
11285- goto out;
11286+#if TRACE_ALL
11287+ msg_Dbg(vd, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts);
11288+#endif
11289+ mmal_queue_put(isp->out_q, buf);
11290+#if TRACE_ALL
11291+ msg_Dbg(vd, ">>> %s: out Q len=%d", __func__, mmal_queue_length(isp->out_q));
11292+#endif
11293 }
11294- sys->input->buffer_size = sys->input->buffer_size_recommended;
11295+ else
11296+ {
11297+ mmal_buffer_header_reset(buf);
11298+ mmal_buffer_header_release(buf);
11299+ }
11300+}
11301
11302- vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
11303- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
11304- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
11305- display_region.fullscreen = MMAL_FALSE;
11306- display_region.src_rect.x = vd->fmt.i_x_offset;
11307- display_region.src_rect.y = vd->fmt.i_y_offset;
11308- display_region.src_rect.width = vd->fmt.i_visible_width;
11309- display_region.src_rect.height = vd->fmt.i_visible_height;
11310- display_region.dest_rect.x = place.x;
11311- display_region.dest_rect.y = place.y;
11312- display_region.dest_rect.width = place.width;
11313- display_region.dest_rect.height = place.height;
11314- display_region.layer = sys->layer;
11315- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
11316- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
11317- status = mmal_port_parameter_set(sys->input, &display_region.hdr);
11318- if (status != MMAL_SUCCESS) {
11319- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
11320- status, mmal_status_to_string(status));
11321- ret = VLC_EGENERIC;
11322- goto out;
11323+static void isp_empty_out_q(struct vout_isp_conf_s * const isp)
11324+{
11325+ MMAL_BUFFER_HEADER_T * buf;
11326+ // We can be called as part of error recovery so allow for missing Q
11327+ if (isp->out_q == NULL)
11328+ return;
11329+
11330+ while ((buf = mmal_queue_get(isp->out_q)) != NULL)
11331+ mmal_buffer_header_release(buf);
11332+}
11333+
11334+static void isp_flush(struct vout_isp_conf_s * const isp)
11335+{
11336+ if (!isp->input->is_enabled)
11337+ mmal_port_disable(isp->input);
11338+
11339+ if (isp->output->is_enabled)
11340+ mmal_port_disable(isp->output);
11341+
11342+ isp_empty_out_q(isp);
11343+ isp->pending = false;
11344+}
11345+
11346+static MMAL_STATUS_T isp_prepare(vout_display_t * const vd, struct vout_isp_conf_s * const isp)
11347+{
11348+ MMAL_STATUS_T err;
11349+ MMAL_BUFFER_HEADER_T * buf;
11350+
11351+ if (!isp->output->is_enabled) {
11352+ if ((err = mmal_port_enable(isp->output, isp_output_cb)) != MMAL_SUCCESS)
11353+ {
11354+ msg_Err(vd, "ISP output port enable failed");
11355+ return err;
11356+ }
11357 }
11358
11359- for (i = 0; i < sys->i_planes; ++i) {
11360- sys->planes[i].i_lines = buffer_height;
11361- sys->planes[i].i_pitch = buffer_pitch;
11362- sys->planes[i].i_visible_lines = vd->fmt.i_visible_height;
11363- sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width;
11364+ while ((buf = mmal_queue_get(isp->out_pool->queue)) != NULL) {
11365+ if ((err = mmal_port_send_buffer(isp->output, buf)) != MMAL_SUCCESS)
11366+ {
11367+ msg_Err(vd, "ISP output port stuff failed");
11368+ return err;
11369+ }
11370+ }
11371
11372- if (i > 0) {
11373- sys->planes[i].i_lines /= 2;
11374- sys->planes[i].i_pitch /= 2;
11375- sys->planes[i].i_visible_lines /= 2;
11376- sys->planes[i].i_visible_pitch /= 2;
11377+ if (!isp->input->is_enabled) {
11378+ if ((err = mmal_port_enable(isp->input, isp_input_cb)) != MMAL_SUCCESS)
11379+ {
11380+ msg_Err(vd, "ISP input port enable failed");
11381+ return err;
11382 }
11383 }
11384+ return MMAL_SUCCESS;
11385+}
11386
11387- vlc_mutex_init(&sys->buffer_mutex);
11388- vlc_cond_init(&sys->buffer_cond);
11389- vlc_mutex_init(&sys->manage_mutex);
11390+static void isp_close(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
11391+{
11392+ struct vout_isp_conf_s * const isp = &vd_sys->isp;
11393+ VLC_UNUSED(vd);
11394
11395- vd->pool = vd_pool;
11396- vd->prepare = vd_prepare;
11397- vd->display = vd_display;
11398- vd->control = vd_control;
11399- vd->manage = vd_manage;
11400+ if (isp->component == NULL)
11401+ return;
11402
11403- vc_tv_register_callback(tvservice_cb, vd);
11404+ isp_flush(isp);
11405
11406- if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) {
11407- vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height);
11408- } else {
11409- sys->display_width = vd->cfg->display.width;
11410- sys->display_height = vd->cfg->display.height;
11411+ if (isp->component->control->is_enabled)
11412+ mmal_port_disable(isp->component->control);
11413+
11414+ if (isp->out_q != NULL) {
11415+ // 1st junk anything lying around
11416+ isp_empty_out_q(isp);
11417+
11418+ mmal_queue_destroy(isp->out_q);
11419+ isp->out_q = NULL;
11420 }
11421
11422- sys->dmx_handle = vc_dispmanx_display_open(0);
11423- vd->info.subpicture_chromas = subpicture_chromas;
11424+ if (isp->out_pool != NULL) {
11425+ mmal_port_pool_destroy(isp->output, isp->out_pool);
11426+ isp->out_pool = NULL;
11427+ }
11428
11429- vout_display_DeleteWindow(vd, NULL);
11430+ isp->input = NULL;
11431+ isp->output = NULL;
11432
11433-out:
11434- if (ret != VLC_SUCCESS)
11435- Close(object);
11436+ mmal_component_release(isp->component);
11437+ isp->component = NULL;
11438
11439- return ret;
11440+ return;
11441 }
11442
11443-static void Close(vlc_object_t *object)
11444+// Restuff into output rather than return to pool is we can
11445+static MMAL_BOOL_T isp_out_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata)
11446 {
11447- vout_display_t *vd = (vout_display_t *)object;
11448- vout_display_sys_t *sys = vd->sys;
11449- char response[20]; /* answer is hvs_update_fields=%1d */
11450- unsigned i;
11451+ struct vout_isp_conf_s * const isp = userdata;
11452+ VLC_UNUSED(pool);
11453+ if (isp->output->is_enabled) {
11454+ mmal_buffer_header_reset(buffer);
11455+ if (mmal_port_send_buffer(isp->output, buffer) == MMAL_SUCCESS)
11456+ return MMAL_FALSE;
11457+ }
11458+ return MMAL_TRUE;
11459+}
11460
11461- vc_tv_unregister_callback_full(tvservice_cb, vd);
11462+static MMAL_STATUS_T isp_setup(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
11463+{
11464+ struct vout_isp_conf_s * const isp = &vd_sys->isp;
11465+ MMAL_STATUS_T err;
11466
11467- if (sys->dmx_handle)
11468- close_dmx(vd);
11469+ if ((err = mmal_component_create(MMAL_COMPONENT_ISP_RESIZER, &isp->component)) != MMAL_SUCCESS) {
11470+ msg_Err(vd, "Cannot create ISP component");
11471+ return err;
11472+ }
11473+ isp->input = isp->component->input[0];
11474+ isp->output = isp->component->output[0];
11475
11476- if (sys->component && sys->component->control->is_enabled)
11477- mmal_port_disable(sys->component->control);
11478+ isp->component->control->userdata = (void *)vd;
11479+ if ((err = mmal_port_enable(isp->component->control, isp_control_port_cb)) != MMAL_SUCCESS) {
11480+ msg_Err(vd, "Failed to enable ISP control port");
11481+ goto fail;
11482+ }
11483
11484- if (sys->input && sys->input->is_enabled)
11485- mmal_port_disable(sys->input);
11486+ isp->input->userdata = (void *)vd;
11487+ display_set_format(vd, isp->input->format, false);
11488
11489- if (sys->component && sys->component->is_enabled)
11490- mmal_component_disable(sys->component);
11491+ if ((err = port_parameter_set_bool(isp->input, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
11492+ goto fail;
11493
11494- if (sys->pool)
11495- mmal_port_pool_destroy(sys->input, sys->pool);
11496+ if ((err = mmal_port_format_commit(isp->input)) != MMAL_SUCCESS) {
11497+ msg_Err(vd, "Failed to set ISP input format");
11498+ goto fail;
11499+ }
11500
11501- if (sys->component)
11502- mmal_component_release(sys->component);
11503+ isp->input->buffer_size = isp->input->buffer_size_recommended;
11504+ isp->input->buffer_num = 30;
11505
11506- if (sys->picture_pool)
11507- picture_pool_Release(sys->picture_pool);
11508- else
11509- for (i = 0; i < sys->num_buffers; ++i)
11510- if (sys->pictures[i]) {
11511- mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer);
11512- picture_Release(sys->pictures[i]);
11513- }
11514+ if ((isp->in_pool = mmal_pool_create(isp->input->buffer_num, 0)) == NULL)
11515+ {
11516+ msg_Err(vd, "Failed to create input pool");
11517+ goto fail;
11518+ }
11519
11520- vlc_mutex_destroy(&sys->buffer_mutex);
11521- vlc_cond_destroy(&sys->buffer_cond);
11522- vlc_mutex_destroy(&sys->manage_mutex);
11523+ if ((isp->out_q = mmal_queue_create()) == NULL)
11524+ {
11525+ err = MMAL_ENOMEM;
11526+ goto fail;
11527+ }
11528
11529- if (sys->native_interlaced) {
11530- if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
11531- response[18] != '0')
11532- msg_Warn(vd, "Could not reset hvs field mode");
11533+ display_set_format(vd, isp->output->format, true);
11534+
11535+ if ((err = port_parameter_set_bool(isp->output, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
11536+ goto fail;
11537+
11538+ if ((err = mmal_port_format_commit(isp->output)) != MMAL_SUCCESS) {
11539+ msg_Err(vd, "Failed to set ISP input format");
11540+ goto fail;
11541 }
11542
11543- free(sys->pictures);
11544- free(sys);
11545+ isp->output->buffer_size = isp->output->buffer_size_recommended;
11546+ isp->output->buffer_num = 2;
11547+ isp->output->userdata = (void *)vd;
11548+
11549+ if ((isp->out_pool = mmal_port_pool_create(isp->output, isp->output->buffer_num, isp->output->buffer_size)) == NULL)
11550+ {
11551+ msg_Err(vd, "Failed to make ISP port pool");
11552+ goto fail;
11553+ }
11554+
11555+ mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp);
11556+
11557+ if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS)
11558+ goto fail;
11559+
11560+ return MMAL_SUCCESS;
11561
11562- bcm_host_deinit();
11563+fail:
11564+ isp_close(vd, vd_sys);
11565+ return err;
11566 }
11567
11568-static inline uint32_t align(uint32_t x, uint32_t y) {
11569- uint32_t mod = x % y;
11570- if (mod == 0)
11571- return x;
11572+static MMAL_STATUS_T isp_check(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
11573+{
11574+ struct vout_isp_conf_s *const isp = &vd_sys->isp;
11575+ const bool has_isp = (isp->component != NULL);
11576+ const bool wants_isp = want_isp(vd);
11577+
11578+ if (has_isp == wants_isp)
11579+ {
11580+ // All OK - do nothing
11581+ }
11582+ else if (has_isp)
11583+ {
11584+ // ISP active but we don't want it
11585+ isp_flush(isp);
11586+
11587+ // Check we have everything back and then kill it
11588+ if (mmal_queue_length(isp->out_pool->queue) == isp->output->buffer_num)
11589+ isp_close(vd, vd_sys);
11590+ }
11591 else
11592- return x + y - mod;
11593+ {
11594+ // ISP closed but we want it
11595+ return isp_setup(vd, vd_sys);
11596+ }
11597+
11598+ return MMAL_SUCCESS;
11599+}
11600+
11601+/* TV service */
11602+static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
11603+ uint32_t param2);
11604+static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
11605+static int set_latency_target(vout_display_t *vd, bool enable);
11606+
11607+// Mmal
11608+static void maintain_phase_sync(vout_display_t *vd);
11609+
11610+
11611+
11612+static void vd_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
11613+{
11614+#if TRACE_ALL
11615+ vout_display_t * const vd = (vout_display_t *)port->userdata;
11616+ pic_ctx_mmal_t * ctx = buf->user_data;
11617+ msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf,
11618+ buf->flags, (long long)buf->pts);
11619+#else
11620+ VLC_UNUSED(port);
11621+#endif
11622+
11623+ mmal_buffer_header_release(buf);
11624+
11625+#if TRACE_ALL
11626+ msg_Dbg(vd, ">>> %s", __func__);
11627+#endif
11628+}
11629+
11630+static int query_resolution(vout_display_t *vd, const int display_id, unsigned *width, unsigned *height)
11631+{
11632+ TV_DISPLAY_STATE_T display_state = {0};
11633+ int ret = 0;
11634+
11635+ if (vc_tv_get_display_state_id(display_id, &display_state) == 0) {
11636+ msg_Dbg(vd, "State=%#x", display_state.state);
11637+ if (display_state.state & 0xFF) {
11638+ msg_Dbg(vd, "HDMI: %dx%d", display_state.display.hdmi.width, display_state.display.hdmi.height);
11639+ *width = display_state.display.hdmi.width;
11640+ *height = display_state.display.hdmi.height;
11641+ } else if (display_state.state & 0xFF00) {
11642+ msg_Dbg(vd, "SDTV: %dx%d", display_state.display.sdtv.width, display_state.display.sdtv.height);
11643+ *width = display_state.display.sdtv.width;
11644+ *height = display_state.display.sdtv.height;
11645+ } else {
11646+ msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
11647+ ret = -1;
11648+ }
11649+ } else {
11650+ msg_Warn(vd, "Failed to query display resolution");
11651+ ret = -1;
11652+ }
11653+
11654+ return ret;
11655+}
11656+
11657+static inline MMAL_RECT_T
11658+place_to_mmal_rect(const vout_display_place_t place)
11659+{
11660+ return (MMAL_RECT_T){
11661+ .x = place.x,
11662+ .y = place.y,
11663+ .width = place.width,
11664+ .height = place.height
11665+ };
11666+}
11667+
11668+static MMAL_RECT_T
11669+place_out(const vout_display_cfg_t * cfg,
11670+ const video_format_t * fmt,
11671+ const MMAL_RECT_T r)
11672+{
11673+ video_format_t tfmt;
11674+ vout_display_cfg_t tcfg;
11675+ vout_display_place_t place;
11676+
11677+ // Fix SAR if unknown
11678+ if (fmt->i_sar_den == 0 || fmt->i_sar_num == 0) {
11679+ tfmt = *fmt;
11680+ tfmt.i_sar_den = 1;
11681+ tfmt.i_sar_num = 1;
11682+ fmt = &tfmt;
11683+ }
11684+
11685+ // Override what VLC thinks might be going on with display size
11686+ // if we know better
11687+ if (r.width != 0 && r.height != 0)
11688+ {
11689+ tcfg = *cfg;
11690+ tcfg.display.width = r.width;
11691+ tcfg.display.height = r.height;
11692+ cfg = &tcfg;
11693+ }
11694+
11695+ vout_display_PlacePicture(&place, fmt, cfg, false);
11696+
11697+ place.x += r.x;
11698+ place.y += r.y;
11699+
11700+ return place_to_mmal_rect(place);
11701+}
11702+
11703+static MMAL_RECT_T
11704+rect_transform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t)
11705+{
11706+ if (is_transform_transpose(t))
11707+ s = rect_transpose(s);
11708+ if (is_transform_hflip(t))
11709+ s = rect_hflip(s, c);
11710+ if (is_transform_vflip(t) != 0)
11711+ s = rect_vflip(s, c);
11712+ return s;
11713+}
11714+
11715+static void
11716+place_dest_rect(vout_display_t * const vd,
11717+ const vout_display_cfg_t * const cfg,
11718+ const video_format_t * fmt)
11719+{
11720+ vout_display_sys_t * const sys = vd->sys;
11721+ sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect),
11722+ sys->display_rect, sys->dest_transform);
11723+}
11724+
11725+static void
11726+place_spu_rect(vout_display_t * const vd,
11727+ const vout_display_cfg_t * const cfg,
11728+ const video_format_t * fmt)
11729+{
11730+ vout_display_sys_t * const sys = vd->sys;
11731+ static const MMAL_RECT_T r0 = {0};
11732+
11733+ sys->spu_rect = place_out(cfg, fmt, r0);
11734+ sys->spu_rect.x = 0;
11735+ sys->spu_rect.y = 0;
11736+
11737+ // Copy place override logic for spu pos from video_output.c
11738+ // This info doesn't appear to reside anywhere natively
11739+
11740+ if (fmt->i_width * fmt->i_height >= (unsigned int)(sys->spu_rect.width * sys->spu_rect.height)) {
11741+ sys->spu_rect.width = fmt->i_visible_width;
11742+ sys->spu_rect.height = fmt->i_visible_height;
11743+ }
11744+
11745+ if (ORIENT_IS_SWAP(fmt->orientation))
11746+ sys->spu_rect = rect_transpose(sys->spu_rect);
11747+}
11748+
11749+static void
11750+place_rects(vout_display_t * const vd,
11751+ const vout_display_cfg_t * const cfg,
11752+ const video_format_t * fmt)
11753+{
11754+ place_dest_rect(vd, cfg, fmt);
11755+ place_spu_rect(vd, cfg, fmt);
11756+}
11757+
11758+static int
11759+set_input_region(vout_display_t * const vd, const video_format_t * const fmt)
11760+{
11761+ const vout_display_sys_t * const sys = vd->sys;
11762+ MMAL_DISPLAYREGION_T display_region = {
11763+ .hdr = {
11764+ .id = MMAL_PARAMETER_DISPLAYREGION,
11765+ .size = sizeof(MMAL_DISPLAYREGION_T)
11766+ },
11767+ .display_num = sys->display_id,
11768+ .fullscreen = MMAL_FALSE,
11769+ .transform = sys->video_transform,
11770+ .dest_rect = sys->dest_rect,
11771+ .src_rect = display_src_rect(vd, fmt),
11772+ .noaspect = MMAL_TRUE,
11773+ .mode = MMAL_DISPLAY_MODE_FILL,
11774+ .layer = sys->layer,
11775+ .alpha = 0xff | (sys->transparent ? 0 : (1 << 29)),
11776+ .set =
11777+ MMAL_DISPLAY_SET_NUM |
11778+ MMAL_DISPLAY_SET_FULLSCREEN |
11779+ MMAL_DISPLAY_SET_TRANSFORM |
11780+ MMAL_DISPLAY_SET_DEST_RECT |
11781+ MMAL_DISPLAY_SET_SRC_RECT |
11782+ MMAL_DISPLAY_SET_NOASPECT |
11783+ MMAL_DISPLAY_SET_MODE |
11784+ MMAL_DISPLAY_SET_LAYER |
11785+ MMAL_DISPLAY_SET_ALPHA
11786+ };
11787+ MMAL_STATUS_T status = mmal_port_parameter_set(sys->input, &display_region.hdr);
11788+ if (status != MMAL_SUCCESS) {
11789+ msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
11790+ status, mmal_status_to_string(status));
11791+ return -EINVAL;
11792+ }
11793+ return 0;
11794 }
11795
11796 static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
11797 const video_format_t *fmt)
11798 {
11799- vout_display_sys_t *sys = vd->sys;
11800- vout_display_place_t place;
11801- MMAL_DISPLAYREGION_T display_region;
11802+ vout_display_sys_t * const sys = vd->sys;
11803 MMAL_STATUS_T status;
11804
11805 if (!cfg && !fmt)
11806+ {
11807+ msg_Err(vd, "%s: Missing cfg & fmt", __func__);
11808 return -EINVAL;
11809+ }
11810+
11811+ isp_check(vd, sys);
11812
11813 if (fmt) {
11814 sys->input->format->es->video.par.num = fmt->i_sar_num;
11815@@ -412,30 +733,14 @@ static int configure_display(vout_displa
11816 if (!cfg)
11817 cfg = vd->cfg;
11818
11819- vout_display_PlacePicture(&place, fmt, cfg, false);
11820+ sys->video_transform = combine_transform(
11821+ vlc_to_mmal_transform(fmt->orientation), sys->display_transform);
11822
11823- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
11824- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
11825- display_region.fullscreen = MMAL_FALSE;
11826- display_region.src_rect.x = fmt->i_x_offset;
11827- display_region.src_rect.y = fmt->i_y_offset;
11828- display_region.src_rect.width = fmt->i_visible_width;
11829- display_region.src_rect.height = fmt->i_visible_height;
11830- display_region.dest_rect.x = place.x;
11831- display_region.dest_rect.y = place.y;
11832- display_region.dest_rect.width = place.width;
11833- display_region.dest_rect.height = place.height;
11834- display_region.layer = sys->layer;
11835- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
11836- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
11837- status = mmal_port_parameter_set(sys->input, &display_region.hdr);
11838- if (status != MMAL_SUCCESS) {
11839- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
11840- status, mmal_status_to_string(status));
11841+ place_rects(vd, cfg, fmt);
11842+
11843+ if (set_input_region(vd, fmt) != 0)
11844 return -EINVAL;
11845- }
11846
11847- show_background(vd, var_InheritBool(vd, MMAL_BLANK_BACKGROUND_NAME));
11848 sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME);
11849 sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED);
11850 if (sys->adjust_refresh_rate) {
11851@@ -446,204 +751,217 @@ static int configure_display(vout_displa
11852 return 0;
11853 }
11854
11855+static void kill_pool(vout_display_sys_t * const sys)
11856+{
11857+ if (sys->pic_pool != NULL) {
11858+ picture_pool_Release(sys->pic_pool);
11859+ sys->pic_pool = NULL;
11860+ }
11861+}
11862+
11863+// Actual picture pool for MMAL opaques is just a set of trivial containers
11864 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
11865 {
11866- vout_display_sys_t *sys = vd->sys;
11867- picture_resource_t picture_res;
11868- picture_pool_configuration_t picture_pool_cfg;
11869- video_format_t fmt = vd->fmt;
11870- MMAL_STATUS_T status;
11871- unsigned i;
11872+ vout_display_sys_t * const sys = vd->sys;
11873
11874- if (sys->picture_pool) {
11875- if (sys->num_buffers < count)
11876- msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures",
11877- count, sys->num_buffers);
11878+ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__,
11879+ 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);
11880
11881- goto out;
11882+ if (sys->pic_pool == NULL) {
11883+ sys->pic_pool = picture_pool_NewFromFormat(&vd->fmt, count);
11884 }
11885+ return sys->pic_pool;
11886+}
11887
11888- if (sys->opaque) {
11889- if (count <= NUM_ACTUAL_OPAQUE_BUFFERS)
11890- count = NUM_ACTUAL_OPAQUE_BUFFERS;
11891+static inline bool
11892+check_shape(vout_display_t * const vd, const picture_t * const p_pic)
11893+{
11894+ if (vd->fmt.i_width == p_pic->format.i_width &&
11895+ vd->fmt.i_height == p_pic->format.i_height)
11896+ return true;
11897+ return false;
11898+}
11899
11900- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
11901- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
11902- 1
11903- };
11904+static void vd_display(vout_display_t *vd, picture_t *p_pic,
11905+ subpicture_t *subpicture)
11906+{
11907+ vout_display_sys_t * const sys = vd->sys;
11908+ MMAL_STATUS_T err;
11909
11910- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
11911- if (status != MMAL_SUCCESS) {
11912- msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
11913- sys->input->name, status, mmal_status_to_string(status));
11914- goto out;
11915- }
11916+#if TRACE_ALL
11917+ {
11918+ char dbuf0[5];
11919+ msg_Dbg(vd, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %dx%d@%d,%d", __func__,
11920+ str_fourcc(dbuf0, p_pic->format.i_chroma), p_pic->format.i_width, p_pic->format.i_height,
11921+ p_pic->format.i_x_offset, p_pic->format.i_y_offset,
11922+ p_pic->format.i_visible_width, p_pic->format.i_visible_height,
11923+ p_pic->format.i_sar_num, p_pic->format.i_sar_den,
11924+ sys->dest_rect.width, sys->dest_rect.height, sys->dest_rect.x, sys->dest_rect.y);
11925 }
11926-
11927- if (count < sys->input->buffer_num_recommended)
11928- count = sys->input->buffer_num_recommended;
11929-
11930-#ifndef NDEBUG
11931- msg_Dbg(vd, "Creating picture pool with %u pictures", count);
11932 #endif
11933
11934- sys->input->buffer_num = count;
11935- status = mmal_port_enable(sys->input, input_port_cb);
11936- if (status != MMAL_SUCCESS) {
11937- msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
11938- sys->input->name, status, mmal_status_to_string(status));
11939- goto out;
11940+ // If we had subpics then we have attached them to the main pic in prepare
11941+ // so all we have to do here is delete the refs
11942+ if (subpicture != NULL) {
11943+ subpicture_Delete(subpicture);
11944 }
11945
11946- status = mmal_component_enable(sys->component);
11947- if (status != MMAL_SUCCESS) {
11948- msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
11949- sys->component->name, status, mmal_status_to_string(status));
11950- goto out;
11951+ if (!check_shape(vd, p_pic))
11952+ {
11953+ msg_Err(vd, "Pic/fmt shape mismatch");
11954+ goto fail;
11955+ }
11956+
11957+ if (!sys->input->is_enabled &&
11958+ (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS)
11959+ {
11960+ msg_Err(vd, "Input port enable failed");
11961+ goto fail;
11962+ }
11963+ // Stuff into input
11964+ // We assume the BH is already set up with values reflecting pic date etc.
11965+ if (sys->copy_buf != NULL) {
11966+ MMAL_BUFFER_HEADER_T *const buf = sys->copy_buf;
11967+ sys->copy_buf = NULL;
11968+#if TRACE_ALL
11969+ msg_Dbg(vd, "--- %s: Copy stuff", __func__);
11970+#endif
11971+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
11972+ {
11973+ mmal_buffer_header_release(buf);
11974+ msg_Err(vd, "Send copy buffer to render input failed");
11975+ goto fail;
11976+ }
11977 }
11978-
11979- sys->num_buffers = count;
11980- sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers,
11981- sys->input->buffer_size);
11982- if (!sys->pool) {
11983- msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
11984- count, sys->input->buffer_size);
11985- goto out;
11986+ else if (sys->isp.pending) {
11987+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q);
11988+ sys->isp.pending = false;
11989+#if TRACE_ALL
11990+ msg_Dbg(vd, "--- %s: ISP stuff", __func__);
11991+#endif
11992+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
11993+ {
11994+ mmal_buffer_header_release(buf);
11995+ msg_Err(vd, "Send ISP buffer to render input failed");
11996+ goto fail;
11997+ }
11998 }
11999-
12000- memset(&picture_res, 0, sizeof(picture_resource_t));
12001- sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *));
12002- for (i = 0; i < sys->num_buffers; ++i) {
12003- picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
12004- picture_res.p_sys->owner = (vlc_object_t *)vd;
12005- picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue);
12006-
12007- sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res);
12008- if (!sys->pictures[i]) {
12009- msg_Err(vd, "Failed to create picture");
12010- free(picture_res.p_sys);
12011- goto out;
12012+ else
12013+ {
12014+ MMAL_BUFFER_HEADER_T *const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->pool);
12015+ if (pic_buf == NULL)
12016+ {
12017+ msg_Err(vd, "Replicated buffer get fail");
12018+ goto fail;
12019 }
12020
12021- sys->pictures[i]->i_planes = sys->i_planes;
12022- memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t));
12023- }
12024
12025- memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
12026- picture_pool_cfg.picture_count = sys->num_buffers;
12027- picture_pool_cfg.picture = sys->pictures;
12028- picture_pool_cfg.lock = mmal_picture_lock;
12029+ // If dimensions have chnaged then fix that
12030+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
12031+ {
12032+ msg_Dbg(vd, "Reset port format");
12033+
12034+ // HVS can deal with on-line dimension changes
12035+ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
12036+ msg_Warn(vd, "Input format commit failed");
12037+ }
12038
12039- sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
12040- if (!sys->picture_pool) {
12041- msg_Err(vd, "Failed to create picture pool");
12042- goto out;
12043+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
12044+ {
12045+ mmal_buffer_header_release(pic_buf);
12046+ msg_Err(vd, "Send buffer to input failed");
12047+ goto fail;
12048+ }
12049 }
12050
12051-out:
12052- return sys->picture_pool;
12053-}
12054-
12055-static void vd_prepare(vout_display_t *vd, picture_t *picture,
12056- subpicture_t *subpicture)
12057-{
12058- vout_display_sys_t *sys = vd->sys;
12059- picture_sys_t *pic_sys = picture->p_sys;
12060-
12061- if (!sys->adjust_refresh_rate || pic_sys->displayed)
12062- return;
12063-
12064- /* Apply the required phase_offset to the picture, so that vd_display()
12065- * will be called at the corrected time from the core */
12066- picture->date += sys->phase_offset;
12067-}
12068-
12069-static void vd_display(vout_display_t *vd, picture_t *picture,
12070- subpicture_t *subpicture)
12071-{
12072- vout_display_sys_t *sys = vd->sys;
12073- picture_sys_t *pic_sys = picture->p_sys;
12074- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
12075- MMAL_STATUS_T status;
12076-
12077- if (picture->format.i_frame_rate != sys->i_frame_rate ||
12078- picture->format.i_frame_rate_base != sys->i_frame_rate_base ||
12079- picture->b_progressive != sys->b_progressive ||
12080- picture->b_top_field_first != sys->b_top_field_first) {
12081- sys->b_top_field_first = picture->b_top_field_first;
12082- sys->b_progressive = picture->b_progressive;
12083- sys->i_frame_rate = picture->format.i_frame_rate;
12084- sys->i_frame_rate_base = picture->format.i_frame_rate_base;
12085- configure_display(vd, NULL, &picture->format);
12086- }
12087-
12088- if (!pic_sys->displayed || !sys->opaque) {
12089- buffer->cmd = 0;
12090- buffer->length = sys->input->buffer_size;
12091- buffer->user_data = picture;
12092-
12093- status = mmal_port_send_buffer(sys->input, buffer);
12094- if (status == MMAL_SUCCESS)
12095- atomic_fetch_add(&sys->buffers_in_transit, 1);
12096-
12097- if (status != MMAL_SUCCESS) {
12098- msg_Err(vd, "Failed to send buffer to input port. Frame dropped");
12099- picture_Release(picture);
12100+ {
12101+ unsigned int sub_no = 0;
12102+ MMAL_BUFFER_HEADER_T **psub_bufs2 = sys->subpic_bufs;
12103+ const bool is_mmal_pic = hw_mmal_pic_is_mmal(p_pic);
12104+
12105+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
12106+ int rv;
12107+ MMAL_BUFFER_HEADER_T * const sub_buf = !is_mmal_pic ? NULL :
12108+ hw_mmal_pic_sub_buf_get(p_pic, sub_no);
12109+
12110+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd),
12111+ sub_buf != NULL ? sub_buf : *psub_bufs2++,
12112+ &sys->subs[sub_no].sub,
12113+ &p_pic->format,
12114+ &sys->dest_rect,
12115+ sys->display_transform,
12116+ p_pic->date)) == 0)
12117+ break;
12118+ else if (rv < 0)
12119+ goto fail;
12120 }
12121-
12122- pic_sys->displayed = true;
12123- } else {
12124- picture_Release(picture);
12125 }
12126
12127- display_subpicture(vd, subpicture);
12128+fail:
12129+ for (unsigned int i = 0; i != SUBS_MAX && sys->subpic_bufs[i] != NULL; ++i) {
12130+ mmal_buffer_header_release(sys->subpic_bufs[i]);
12131+ sys->subpic_bufs[i] = NULL;
12132+ }
12133
12134- if (subpicture)
12135- subpicture_Delete(subpicture);
12136+ picture_Release(p_pic);
12137
12138 if (sys->next_phase_check == 0 && sys->adjust_refresh_rate)
12139 maintain_phase_sync(vd);
12140 sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL;
12141-
12142- if (sys->opaque) {
12143- vlc_mutex_lock(&sys->buffer_mutex);
12144- while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT)
12145- vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex);
12146- vlc_mutex_unlock(&sys->buffer_mutex);
12147- }
12148 }
12149
12150 static int vd_control(vout_display_t *vd, int query, va_list args)
12151 {
12152- vout_display_sys_t *sys = vd->sys;
12153- vout_display_cfg_t cfg;
12154- const vout_display_cfg_t *tmp_cfg;
12155+ vout_display_sys_t * const sys = vd->sys;
12156 int ret = VLC_EGENERIC;
12157+ VLC_UNUSED(args);
12158
12159 switch (query) {
12160- case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
12161- tmp_cfg = va_arg(args, const vout_display_cfg_t *);
12162- if (tmp_cfg->display.width == sys->display_width &&
12163- tmp_cfg->display.height == sys->display_height) {
12164- cfg = *vd->cfg;
12165- cfg.display.width = sys->display_width;
12166- cfg.display.height = sys->display_height;
12167- if (configure_display(vd, &cfg, NULL) >= 0)
12168- ret = VLC_SUCCESS;
12169- }
12170- break;
12171-
12172 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
12173 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
12174- if (configure_display(vd, NULL, &vd->source) >= 0)
12175+ if (configure_display(vd, vd->cfg, &vd->source) >= 0)
12176 ret = VLC_SUCCESS;
12177 break;
12178
12179- case VOUT_DISPLAY_RESET_PICTURES:
12180- vlc_assert_unreachable();
12181 case VOUT_DISPLAY_CHANGE_ZOOM:
12182- msg_Warn(vd, "Unsupported control query %d", query);
12183+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
12184+ case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
12185+ {
12186+ const vout_display_cfg_t * const cfg = va_arg(args, const vout_display_cfg_t *);
12187+
12188+ if (configure_display(vd, cfg, &vd->source) >= 0)
12189+ ret = VLC_SUCCESS;
12190+ break;
12191+ }
12192+
12193+ case VOUT_DISPLAY_RESET_PICTURES:
12194+ msg_Warn(vd, "Reset Pictures");
12195+ kill_pool(sys);
12196+ vd->fmt = vd->source; // Take (nearly) whatever source wants to give us
12197+ vd->fmt.i_chroma = req_chroma(vd); // Adjust chroma to something we can actaully deal with
12198+ ret = VLC_SUCCESS;
12199+ break;
12200+
12201+ case VOUT_DISPLAY_CHANGE_MMAL_HIDE:
12202+ {
12203+ MMAL_STATUS_T err;
12204+ unsigned int i;
12205+
12206+ msg_Dbg(vd, "Hide display");
12207+
12208+ for (i = 0; i != SUBS_MAX; ++i)
12209+ hw_mmal_subpic_flush(VLC_OBJECT(vd), &sys->subs[i].sub);
12210+
12211+ if (sys->input->is_enabled &&
12212+ (err = mmal_port_disable(sys->input)) != MMAL_SUCCESS)
12213+ {
12214+ msg_Err(vd, "Unable to disable port: err=%d", err);
12215+ break;
12216+ }
12217+ sys->force_config = true;
12218+ ret = VLC_SUCCESS;
12219 break;
12220+ }
12221
12222 default:
12223 msg_Warn(vd, "Unknown control query %d", query);
12224@@ -653,79 +971,207 @@ static int vd_control(vout_display_t *vd
12225 return ret;
12226 }
12227
12228+static void set_display_windows(vout_display_t *const vd, vout_display_sys_t *const sys)
12229+{
12230+ unsigned int width, height;
12231+ if (query_resolution(vd, sys->display_id, &width, &height) < 0) {
12232+ width = vd->cfg->display.width;
12233+ height = vd->cfg->display.height;
12234+ }
12235+ sys->display_rect = (MMAL_RECT_T){0, 0, width, height};
12236+
12237+ sys->win_rect = (sys->req_win.width != 0) ?
12238+ sys->req_win :
12239+ is_transform_transpose(sys->display_transform) ?
12240+ rect_transpose(sys->display_rect) : sys->display_rect;
12241+}
12242+
12243 static void vd_manage(vout_display_t *vd)
12244 {
12245- vout_display_sys_t *sys = vd->sys;
12246- unsigned width, height;
12247+ vout_display_sys_t *const sys = vd->sys;
12248
12249 vlc_mutex_lock(&sys->manage_mutex);
12250
12251 if (sys->need_configure_display) {
12252- close_dmx(vd);
12253- sys->dmx_handle = vc_dispmanx_display_open(0);
12254-
12255- if (query_resolution(vd, &width, &height) >= 0) {
12256- sys->display_width = width;
12257- sys->display_height = height;
12258- vout_display_SendEventDisplaySize(vd, width, height);
12259- }
12260-
12261 sys->need_configure_display = false;
12262+ set_display_windows(vd, sys);
12263 }
12264
12265 vlc_mutex_unlock(&sys->manage_mutex);
12266 }
12267
12268-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
12269+
12270+static int attach_subpics(vout_display_t * const vd, vout_display_sys_t * const sys,
12271+ subpicture_t * const subpicture)
12272 {
12273- vout_display_t *vd = (vout_display_t *)port->userdata;
12274- MMAL_STATUS_T status;
12275+ unsigned int n = 0;
12276
12277- if (buffer->cmd == MMAL_EVENT_ERROR) {
12278- status = *(uint32_t *)buffer->data;
12279- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
12280+ if (sys->vzc == NULL) {
12281+ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
12282+ {
12283+ msg_Err(vd, "Failed to allocate VZC");
12284+ return VLC_ENOMEM;
12285+ }
12286 }
12287
12288- mmal_buffer_header_release(buffer);
12289+ // Attempt to import the subpics
12290+ for (subpicture_t * spic = subpicture; spic != NULL; spic = spic->p_next)
12291+ {
12292+ for (subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) {
12293+ picture_t *const src = sreg->p_picture;
12294+
12295+#if TRACE_ALL
12296+ char dbuf0[5];
12297+ 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,
12298+ sreg->i_x, sreg->i_y,
12299+ sreg->i_max_width, sreg->i_max_height,
12300+ src->format.i_visible_width, src->format.i_visible_height,
12301+ src->format.i_width, src->format.i_height,
12302+ src->format.orientation,
12303+ sys->spu_rect.x, sys->spu_rect.y, sys->spu_rect.width, sys->spu_rect.height,
12304+ vd->fmt.i_visible_width, vd->fmt.i_visible_height,
12305+ vd->fmt.i_width, vd->fmt.i_height,
12306+ vd->source.i_visible_width, vd->source.i_visible_height,
12307+ vd->source.i_width, vd->source.i_height,
12308+ vd->cfg->display.width, vd->cfg->display.height,
12309+ vd->cfg->zoom.num, vd->cfg->zoom.den,
12310+ sreg->i_alpha,
12311+ str_fourcc(dbuf0, src->format.i_chroma));
12312+#endif
12313+
12314+ // At this point I think the subtitles are being placed in the
12315+ // coord space of the placed rectangle in the cfg display space
12316+ if ((sys->subpic_bufs[n] = hw_mmal_vzc_buf_from_pic(sys->vzc,
12317+ src,
12318+ (MMAL_RECT_T){.width = sys->spu_rect.width, .height=sys->spu_rect.height},
12319+ sreg->i_x, sreg->i_y,
12320+ sreg->i_alpha,
12321+ n == 0)) == NULL)
12322+ {
12323+ msg_Err(vd, "Failed to allocate vzc buffer for subpic");
12324+ return VLC_ENOMEM;
12325+ }
12326+
12327+ if (++n == SUBS_MAX)
12328+ return VLC_SUCCESS;
12329+ }
12330+ }
12331+ return VLC_SUCCESS;
12332 }
12333
12334-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
12335+
12336+static void vd_prepare(vout_display_t *vd, picture_t *p_pic,
12337+#if VLC_VER_3
12338+ subpicture_t *subpicture
12339+#else
12340+ subpicture_t *subpicture, vlc_tick_t date
12341+#endif
12342+ )
12343 {
12344- vout_display_t *vd = (vout_display_t *)port->userdata;
12345+ MMAL_STATUS_T err;
12346+ vout_display_sys_t * const sys = vd->sys;
12347+
12348+ vd_manage(vd);
12349+
12350+ if (!check_shape(vd, p_pic))
12351+ return;
12352+
12353+ if (sys->force_config ||
12354+ p_pic->format.i_frame_rate != sys->i_frame_rate ||
12355+ p_pic->format.i_frame_rate_base != sys->i_frame_rate_base ||
12356+ p_pic->b_progressive != sys->b_progressive ||
12357+ p_pic->b_top_field_first != sys->b_top_field_first)
12358+ {
12359+ sys->force_config = false;
12360+ sys->b_top_field_first = p_pic->b_top_field_first;
12361+ sys->b_progressive = p_pic->b_progressive;
12362+ sys->i_frame_rate = p_pic->format.i_frame_rate;
12363+ sys->i_frame_rate_base = p_pic->format.i_frame_rate_base;
12364+ configure_display(vd, NULL, &vd->source);
12365+ }
12366+
12367+ // Subpics can either turn up attached to the main pic or in the
12368+ // subpic list here - if they turn up here then process into temp
12369+ // buffers
12370+ if (subpicture != NULL) {
12371+ attach_subpics(vd, sys, subpicture);
12372+ }
12373+
12374+ // *****
12375+ if (want_copy(vd)) {
12376+ if (sys->copy_buf != NULL) {
12377+ msg_Err(vd, "Copy buf not NULL");
12378+ mmal_buffer_header_release(sys->copy_buf);
12379+ sys->copy_buf = NULL;
12380+ }
12381+
12382+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->copy_pool->queue);
12383+ // Copy 2d
12384+ hw_mmal_copy_pic_to_buf(buf->data, &buf->length, sys->input->format, p_pic);
12385+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
12386+
12387+ sys->copy_buf = buf;
12388+ }
12389+
12390+ if (isp_check(vd, sys) != MMAL_SUCCESS) {
12391+ return;
12392+ }
12393+
12394+ if (want_isp(vd))
12395+ {
12396+ struct vout_isp_conf_s * const isp = &sys->isp;
12397+ MMAL_BUFFER_HEADER_T * buf;
12398+
12399+ // This should be empty - make it so if it isn't
12400+ isp_empty_out_q(isp);
12401+ isp->pending = false;
12402+
12403+ // Stuff output
12404+ if (isp_prepare(vd, isp) != MMAL_SUCCESS)
12405+ return;
12406+
12407+ if ((buf = hw_mmal_pic_buf_replicated(p_pic, isp->in_pool)) == NULL)
12408+ {
12409+ msg_Err(vd, "Pic has no attached buffer");
12410+ return;
12411+ }
12412+
12413+ if ((err = mmal_port_send_buffer(isp->input, buf)) != MMAL_SUCCESS)
12414+ {
12415+ msg_Err(vd, "Send buffer to input failed");
12416+ mmal_buffer_header_release(buf);
12417+ return;
12418+ }
12419+
12420+ isp->pending = true;
12421+ }
12422+
12423+#if 0
12424+ VLC_UNUSED(date);
12425 vout_display_sys_t *sys = vd->sys;
12426- picture_t *picture = (picture_t *)buffer->user_data;
12427+ picture_sys_t *pic_sys = picture->p_sys;
12428
12429- if (picture)
12430- picture_Release(picture);
12431+ if (!sys->adjust_refresh_rate || pic_sys->displayed)
12432+ return;
12433
12434- vlc_mutex_lock(&sys->buffer_mutex);
12435- atomic_fetch_sub(&sys->buffers_in_transit, 1);
12436- vlc_cond_signal(&sys->buffer_cond);
12437- vlc_mutex_unlock(&sys->buffer_mutex);
12438+ /* Apply the required phase_offset to the picture, so that vd_display()
12439+ * will be called at the corrected time from the core */
12440+ picture->date += sys->phase_offset;
12441+#endif
12442 }
12443
12444-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
12445+
12446+static void vd_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
12447 {
12448- TV_DISPLAY_STATE_T display_state;
12449- int ret = 0;
12450+ vout_display_t *vd = (vout_display_t *)port->userdata;
12451+ MMAL_STATUS_T status;
12452
12453- if (vc_tv_get_display_state(&display_state) == 0) {
12454- if (display_state.state & 0xFF) {
12455- *width = display_state.display.hdmi.width;
12456- *height = display_state.display.hdmi.height;
12457- } else if (display_state.state & 0xFF00) {
12458- *width = display_state.display.sdtv.width;
12459- *height = display_state.display.sdtv.height;
12460- } else {
12461- msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
12462- ret = -1;
12463- }
12464- } else {
12465- msg_Warn(vd, "Failed to query display resolution");
12466- ret = -1;
12467+ if (buffer->cmd == MMAL_EVENT_ERROR) {
12468+ status = *(uint32_t *)buffer->data;
12469+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
12470 }
12471
12472- return ret;
12473+ mmal_buffer_header_release(buffer);
12474 }
12475
12476 static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
12477@@ -780,9 +1226,9 @@ static void adjust_refresh_rate(vout_dis
12478 double best_score, score;
12479 int i;
12480
12481- vc_tv_get_display_state(&display_state);
12482+ vc_tv_get_display_state_id(sys->display_id, &display_state);
12483 if(display_state.display.hdmi.mode != HDMI_MODE_OFF) {
12484- num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group,
12485+ num_modes = vc_tv_hdmi_get_supported_modes_new_id(sys->display_id, display_state.display.hdmi.group,
12486 supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL);
12487
12488 for (i = 0; i < num_modes; ++i) {
12489@@ -810,7 +1256,7 @@ static void adjust_refresh_rate(vout_dis
12490 if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) {
12491 msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32,
12492 supported_modes[best_id].frame_rate);
12493- vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI,
12494+ vc_tv_hdmi_power_on_explicit_new_id(sys->display_id, HDMI_MODE_HDMI,
12495 supported_modes[best_id].group,
12496 supported_modes[best_id].code);
12497 }
12498@@ -828,148 +1274,12 @@ static void adjust_refresh_rate(vout_dis
12499 }
12500 }
12501
12502-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture)
12503-{
12504- vout_display_sys_t *sys = vd->sys;
12505- struct dmx_region_t **dmx_region = &sys->dmx_region;
12506- struct dmx_region_t *unused_dmx_region;
12507- DISPMANX_UPDATE_HANDLE_T update = 0;
12508- picture_t *picture;
12509- video_format_t *fmt;
12510- struct dmx_region_t *dmx_region_next;
12511-
12512- if(subpicture) {
12513- subpicture_region_t *region = subpicture->p_region;
12514- while(region) {
12515- picture = region->p_picture;
12516- fmt = &region->fmt;
12517-
12518- if(!*dmx_region) {
12519- if(!update)
12520- update = vc_dispmanx_update_start(10);
12521- *dmx_region = dmx_region_new(vd, update, region);
12522- } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) ||
12523- ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) ||
12524- ((*dmx_region)->pos_x != region->i_x) ||
12525- ((*dmx_region)->pos_y != region->i_y) ||
12526- ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) {
12527- dmx_region_next = (*dmx_region)->next;
12528- if(!update)
12529- update = vc_dispmanx_update_start(10);
12530- dmx_region_delete(*dmx_region, update);
12531- *dmx_region = dmx_region_new(vd, update, region);
12532- (*dmx_region)->next = dmx_region_next;
12533- } else if((*dmx_region)->picture != picture) {
12534- if(!update)
12535- update = vc_dispmanx_update_start(10);
12536- dmx_region_update(*dmx_region, update, picture);
12537- }
12538-
12539- dmx_region = &(*dmx_region)->next;
12540- region = region->p_next;
12541- }
12542- }
12543-
12544- /* Remove remaining regions */
12545- unused_dmx_region = *dmx_region;
12546- while(unused_dmx_region) {
12547- dmx_region_next = unused_dmx_region->next;
12548- if(!update)
12549- update = vc_dispmanx_update_start(10);
12550- dmx_region_delete(unused_dmx_region, update);
12551- unused_dmx_region = dmx_region_next;
12552- }
12553- *dmx_region = NULL;
12554-
12555- if(update)
12556- vc_dispmanx_update_submit_sync(update);
12557-}
12558-
12559-static void close_dmx(vout_display_t *vd)
12560-{
12561- vout_display_sys_t *sys = vd->sys;
12562- DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10);
12563- struct dmx_region_t *dmx_region = sys->dmx_region;
12564- struct dmx_region_t *dmx_region_next;
12565-
12566- while(dmx_region) {
12567- dmx_region_next = dmx_region->next;
12568- dmx_region_delete(dmx_region, update);
12569- dmx_region = dmx_region_next;
12570- }
12571-
12572- vc_dispmanx_update_submit_sync(update);
12573- sys->dmx_region = NULL;
12574-
12575- show_background(vd, false);
12576-
12577- vc_dispmanx_display_close(sys->dmx_handle);
12578- sys->dmx_handle = DISPMANX_NO_HANDLE;
12579-}
12580-
12581-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
12582- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region)
12583-{
12584- vout_display_sys_t *sys = vd->sys;
12585- video_format_t *fmt = &region->fmt;
12586- struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t));
12587- uint32_t image_handle;
12588-
12589- dmx_region->pos_x = region->i_x;
12590- dmx_region->pos_y = region->i_y;
12591-
12592- vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width,
12593- fmt->i_visible_height);
12594- vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16,
12595- fmt->i_visible_height << 16);
12596- vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y,
12597- fmt->i_visible_width, fmt->i_visible_height);
12598-
12599- dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32,
12600- dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16),
12601- dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16),
12602- &image_handle);
12603- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
12604- region->p_picture->p[0].i_pitch,
12605- region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect);
12606-
12607- dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX;
12608- dmx_region->alpha.opacity = region->i_alpha;
12609- dmx_region->alpha.mask = DISPMANX_NO_HANDLE;
12610- dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle,
12611- sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource,
12612- &dmx_region->src_rect, DISPMANX_PROTECTION_NONE,
12613- &dmx_region->alpha, NULL, VC_IMAGE_ROT0);
12614-
12615- dmx_region->next = NULL;
12616- dmx_region->picture = region->p_picture;
12617-
12618- return dmx_region;
12619-}
12620-
12621-static void dmx_region_update(struct dmx_region_t *dmx_region,
12622- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
12623-{
12624- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
12625- picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect);
12626- vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource);
12627- dmx_region->picture = picture;
12628-}
12629-
12630-static void dmx_region_delete(struct dmx_region_t *dmx_region,
12631- DISPMANX_UPDATE_HANDLE_T update)
12632-{
12633- vc_dispmanx_element_remove(update, dmx_region->element);
12634- vc_dispmanx_resource_delete(dmx_region->resource);
12635- free(dmx_region);
12636-}
12637-
12638 static void maintain_phase_sync(vout_display_t *vd)
12639 {
12640 MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = {
12641 .hdr = { MMAL_PARAMETER_VIDEO_RENDER_STATS, sizeof(render_stats) },
12642 };
12643- int32_t frame_duration = 1000000 /
12644+ int32_t frame_duration = CLOCK_FREQ /
12645 ((double)vd->sys->i_frame_rate /
12646 vd->sys->i_frame_rate_base);
12647 vout_display_sys_t *sys = vd->sys;
12648@@ -1012,32 +1322,436 @@ static void maintain_phase_sync(vout_dis
12649 }
12650 }
12651
12652-static void show_background(vout_display_t *vd, bool enable)
12653+static void CloseMmalVout(vlc_object_t *object)
12654 {
12655- vout_display_sys_t *sys = vd->sys;
12656- uint32_t image_ptr, color = 0xFF000000;
12657- VC_RECT_T dst_rect, src_rect;
12658- DISPMANX_UPDATE_HANDLE_T update;
12659-
12660- if (enable && !sys->bkg_element) {
12661- sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1,
12662- &image_ptr);
12663- vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1);
12664- vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32,
12665- sizeof(color), &color, &dst_rect);
12666- vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16);
12667- vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0);
12668- update = vc_dispmanx_update_start(0);
12669- sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle,
12670- sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect,
12671- DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0);
12672- vc_dispmanx_update_submit_sync(update);
12673- } else if (!enable && sys->bkg_element) {
12674- update = vc_dispmanx_update_start(0);
12675- vc_dispmanx_element_remove(update, sys->bkg_element);
12676- vc_dispmanx_resource_delete(sys->bkg_resource);
12677- vc_dispmanx_update_submit_sync(update);
12678- sys->bkg_element = DISPMANX_NO_HANDLE;
12679- sys->bkg_resource = DISPMANX_NO_HANDLE;
12680+ vout_display_t * const vd = (vout_display_t *)object;
12681+ vout_display_sys_t * const sys = vd->sys;
12682+ char response[20]; /* answer is hvs_update_fields=%1d */
12683+
12684+#if TRACE_ALL
12685+ msg_Dbg(vd, "<<< %s", __func__);
12686+#endif
12687+
12688+ kill_pool(sys);
12689+
12690+ vc_tv_unregister_callback_full(tvservice_cb, vd);
12691+
12692+ // Shouldn't be anything here - but just in case
12693+ for (unsigned int i = 0; i != SUBS_MAX; ++i)
12694+ if (sys->subpic_bufs[i] != NULL)
12695+ mmal_buffer_header_release(sys->subpic_bufs[i]);
12696+
12697+ for (unsigned int i = 0; i != SUBS_MAX; ++i) {
12698+ vout_subpic_t * const sub = sys->subs + i;
12699+ if (sub->component != NULL) {
12700+ hw_mmal_subpic_close(VLC_OBJECT(vd), &sub->sub);
12701+ if (sub->component->control->is_enabled)
12702+ mmal_port_disable(sub->component->control);
12703+ if (sub->component->is_enabled)
12704+ mmal_component_disable(sub->component);
12705+ mmal_component_release(sub->component);
12706+ sub->component = NULL;
12707+ }
12708 }
12709+
12710+ if (sys->input && sys->input->is_enabled)
12711+ mmal_port_disable(sys->input);
12712+
12713+ if (sys->component && sys->component->control->is_enabled)
12714+ mmal_port_disable(sys->component->control);
12715+
12716+ if (sys->copy_buf != NULL)
12717+ mmal_buffer_header_release(sys->copy_buf);
12718+
12719+ if (sys->input != NULL && sys->copy_pool != NULL)
12720+ mmal_port_pool_destroy(sys->input, sys->copy_pool);
12721+
12722+ if (sys->component && sys->component->is_enabled)
12723+ mmal_component_disable(sys->component);
12724+
12725+ if (sys->pool)
12726+ mmal_pool_destroy(sys->pool);
12727+
12728+ if (sys->component)
12729+ mmal_component_release(sys->component);
12730+
12731+ isp_close(vd, sys);
12732+
12733+ hw_mmal_vzc_pool_release(sys->vzc);
12734+
12735+ vlc_mutex_destroy(&sys->manage_mutex);
12736+
12737+ if (sys->native_interlaced) {
12738+ if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
12739+ response[18] != '0')
12740+ msg_Warn(vd, "Could not reset hvs field mode");
12741+ }
12742+
12743+ cma_vcsm_exit(sys->init_type);;
12744+
12745+ free(sys);
12746+
12747+#if TRACE_ALL
12748+ msg_Dbg(vd, ">>> %s", __func__);
12749+#endif
12750+}
12751+
12752+
12753+static const struct {
12754+ const char * name;
12755+ int num;
12756+} display_name_to_num[] = {
12757+ {"auto", -1},
12758+ {"hdmi-1", DISPMANX_ID_HDMI0},
12759+ {"hdmi-2", DISPMANX_ID_HDMI1},
12760+ {NULL, -2}
12761+};
12762+
12763+static const struct {
12764+ const char * name;
12765+ int transform_num;
12766+} transform_name_to_num[] = {
12767+ {"auto", -1},
12768+ {"0", MMAL_DISPLAY_ROT0},
12769+ {"hflip", MMAL_DISPLAY_MIRROR_ROT0},
12770+ {"vflip", MMAL_DISPLAY_MIRROR_ROT180},
12771+ {"180", MMAL_DISPLAY_ROT180},
12772+ {"transpose", MMAL_DISPLAY_MIRROR_ROT90},
12773+ {"270", MMAL_DISPLAY_ROT270},
12774+ {"90", MMAL_DISPLAY_ROT90},
12775+ {"antitranspose", MMAL_DISPLAY_MIRROR_ROT270},
12776+ {NULL, -2}
12777+};
12778+
12779+static int find_display_num(const char * const name)
12780+{
12781+ unsigned int i;
12782+ for (i = 0; display_name_to_num[i].name != NULL && strcasecmp(display_name_to_num[i].name, name) != 0; ++i)
12783+ /* Loop */;
12784+ return display_name_to_num[i].num;
12785+}
12786+
12787+static int find_transform_num(const char * const name)
12788+{
12789+ unsigned int i;
12790+ for (i = 0; transform_name_to_num[i].name != NULL && strcasecmp(transform_name_to_num[i].name, name) != 0; ++i)
12791+ /* Loop */;
12792+ return transform_name_to_num[i].transform_num;
12793+}
12794+
12795+#if HAVE_X11_XLIB_H
12796+#include <X11/Xlib.h>
12797+#include <X11/extensions/Xrandr.h>
12798+static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd)
12799+{
12800+ Display * const x = XOpenDisplay(NULL);
12801+ Rotation cur_rot = 0;
12802+ MMAL_DISPLAYTRANSFORM_T trans;
12803+
12804+ if (x == NULL)
12805+ return MMAL_DISPLAY_ROT0;
12806+
12807+ XRRRotations(x, 0, &cur_rot);
12808+ XCloseDisplay(x);
12809+
12810+ // Convert to MMAL
12811+ // xrandr seems to rotate the other way to mmal
12812+
12813+ switch (cur_rot)
12814+ {
12815+ case 0:
12816+ case RR_Rotate_0:
12817+ trans = MMAL_DISPLAY_ROT0;
12818+ break;
12819+ case RR_Rotate_90:
12820+ trans = MMAL_DISPLAY_ROT270;
12821+ break;
12822+ case RR_Rotate_180:
12823+ trans = MMAL_DISPLAY_ROT180;
12824+ break;
12825+ case RR_Rotate_270:
12826+ trans = MMAL_DISPLAY_ROT90;
12827+ break;
12828+ case RR_Reflect_X:
12829+ trans = MMAL_DISPLAY_MIRROR_ROT0;
12830+ break;
12831+ case RR_Reflect_Y:
12832+ trans = MMAL_DISPLAY_MIRROR_ROT180;
12833+ break;
12834+ default:
12835+ msg_Info(vd, "Unexpected X rotation value: %#x", cur_rot);
12836+ trans = MMAL_DISPLAY_ROT0;
12837+ break;
12838+ }
12839+
12840+ return trans;
12841+}
12842+#else
12843+static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd)
12844+{
12845+ VLC_UNUSED(vd);
12846+ return MMAL_DISPLAY_ROT0;
12847+}
12848+#endif
12849+
12850+static MMAL_RECT_T str_to_rect(const char * s)
12851+{
12852+ MMAL_RECT_T rect = {0};
12853+ rect.width = strtoul(s, (char**)&s, 0);
12854+ if (*s == '\0')
12855+ return rect;
12856+ if (*s++ != 'x')
12857+ goto fail;
12858+ rect.height = strtoul(s, (char**)&s, 0);
12859+ if (*s == '\0')
12860+ return rect;
12861+ if (*s++ != '+')
12862+ goto fail;
12863+ rect.x = strtoul(s, (char**)&s, 0);
12864+ if (*s == '\0')
12865+ return rect;
12866+ if (*s++ != '+')
12867+ goto fail;
12868+ rect.y = strtoul(s, (char**)&s, 0);
12869+ if (*s != '\0')
12870+ goto fail;
12871+ return rect;
12872+
12873+fail:
12874+ return (MMAL_RECT_T){0,0,0,0};
12875+}
12876+
12877+static int OpenMmalVout(vlc_object_t *object)
12878+{
12879+ vout_display_t *vd = (vout_display_t *)object;
12880+ vout_display_sys_t *sys;
12881+ MMAL_STATUS_T status;
12882+ int ret = VLC_EGENERIC;
12883+ // At the moment all copy is via I420
12884+ const bool needs_copy = !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma);
12885+ const MMAL_FOURCC_T enc_in = needs_copy ? MMAL_ENCODING_I420 :
12886+ vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
12887+
12888+#if TRACE_ALL
12889+ msg_Dbg(vd, "<<< %s: o:%d", __func__, (int)vd->fmt.orientation);
12890+#endif
12891+
12892+ get_xrandr_rotation(vd);
12893+
12894+ sys = calloc(1, sizeof(struct vout_display_sys_t));
12895+ if (!sys)
12896+ return VLC_ENOMEM;
12897+ vd->sys = sys;
12898+
12899+ vlc_mutex_init(&sys->manage_mutex);
12900+
12901+ if ((sys->init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
12902+ {
12903+ msg_Err(vd, "VCSM init fail");
12904+ goto fail;
12905+ }
12906+
12907+ vc_tv_register_callback(tvservice_cb, vd);
12908+
12909+ sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
12910+ sys->transparent = var_InheritBool(vd, MMAL_VOUT_TRANSPARENT_NAME);
12911+
12912+ {
12913+ const char *display_name = var_InheritString(vd, MMAL_DISPLAY_NAME);
12914+ int qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber" );
12915+ int display_id = find_display_num(display_name);
12916+// sys->display_id = display_id < 0 ? vc_tv_get_default_display_id() : display_id;
12917+ sys->display_id = display_id >= 0 ? display_id :
12918+ qt_num == 1 ? DISPMANX_ID_HDMI1 : DISPMANX_ID_HDMI;
12919+ if (display_id < -1)
12920+ msg_Warn(vd, "Unknown display device: '%s'", display_name);
12921+ else
12922+ msg_Dbg(vd, "Display device: %s, qt=%d id=%d display=%d", display_name,
12923+ qt_num, display_id, sys->display_id);
12924+ }
12925+
12926+ {
12927+ const char *window_str = var_InheritString(vd, MMAL_VOUT_WINDOW_NAME);
12928+ sys->req_win = str_to_rect(window_str);
12929+ if (sys->req_win.width != 0)
12930+ msg_Dbg(vd, "Window: %dx%d @ %d,%d",
12931+ sys->req_win.width, sys->req_win.height,
12932+ sys->req_win.x, sys->req_win.y);
12933+ }
12934+
12935+ {
12936+ const char *transform_name = var_InheritString(vd, MMAL_VOUT_TRANSFORM_NAME);
12937+ int transform_num = find_transform_num(transform_name);
12938+ sys->display_transform = transform_num < 0 ?
12939+ get_xrandr_rotation(vd) :
12940+ (MMAL_DISPLAYTRANSFORM_T)transform_num;
12941+
12942+ if (transform_num < -1)
12943+ msg_Warn(vd, "Unknown vout transform: '%s'", transform_name);
12944+ else
12945+ msg_Dbg(vd, "Display transform: %s, mmal_display_transform=%d",
12946+ transform_name, (int)sys->display_transform);
12947+
12948+ sys->video_transform = combine_transform(
12949+ vlc_to_mmal_transform(vd->fmt.orientation), sys->display_transform);
12950+ sys->dest_transform = transform_inverse(sys->display_transform);
12951+ }
12952+
12953+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
12954+ if (status != MMAL_SUCCESS) {
12955+ msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
12956+ MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
12957+ goto fail;
12958+ }
12959+
12960+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
12961+ status = mmal_port_enable(sys->component->control, vd_control_port_cb);
12962+ if (status != MMAL_SUCCESS) {
12963+ msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
12964+ sys->component->control->name, status, mmal_status_to_string(status));
12965+ goto fail;
12966+ }
12967+
12968+ sys->input = sys->component->input[0];
12969+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
12970+
12971+ sys->input->format->encoding = enc_in;
12972+ sys->input->format->encoding_variant = 0;
12973+ sys->i_planes = 1;
12974+
12975+ display_set_format(vd, sys->input->format, want_isp(vd));
12976+
12977+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
12978+ if (status != MMAL_SUCCESS) {
12979+ msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
12980+ sys->input->name, status, mmal_status_to_string(status));
12981+ goto fail;
12982+ }
12983+
12984+ status = mmal_port_format_commit(sys->input);
12985+ if (status != MMAL_SUCCESS) {
12986+ msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
12987+ sys->input->name, status, mmal_status_to_string(status));
12988+ goto fail;
12989+ }
12990+
12991+ sys->input->buffer_size = sys->input->buffer_size_recommended;
12992+
12993+ if (!needs_copy) {
12994+ sys->input->buffer_num = 30;
12995+ }
12996+ else {
12997+ sys->input->buffer_num = 2;
12998+ if ((sys->copy_pool = mmal_port_pool_create(sys->input, 2, sys->input->buffer_size)) == NULL)
12999+ {
13000+ msg_Err(vd, "Cannot create copy pool");
13001+ goto fail;
13002+ }
13003+ }
13004+
13005+ set_display_windows(vd, sys);
13006+
13007+ configure_display(vd, vd->cfg, &vd->source);
13008+
13009+ status = mmal_port_enable(sys->input, vd_input_port_cb);
13010+ if (status != MMAL_SUCCESS) {
13011+ msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
13012+ sys->input->name, status, mmal_status_to_string(status));
13013+ goto fail;
13014+ }
13015+
13016+ status = mmal_component_enable(sys->component);
13017+ if (status != MMAL_SUCCESS) {
13018+ msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
13019+ sys->component->name, status, mmal_status_to_string(status));
13020+ goto fail;
13021+ }
13022+
13023+ if ((sys->pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
13024+ {
13025+ msg_Err(vd, "Failed to create input pool");
13026+ goto fail;
13027+ }
13028+
13029+ for (unsigned int i = 0; i != SUBS_MAX; ++i) {
13030+ vout_subpic_t * const sub = sys->subs + i;
13031+ if ((status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sub->component)) != MMAL_SUCCESS)
13032+ {
13033+ msg_Dbg(vd, "Failed to create subpic component %d", i);
13034+ goto fail;
13035+ }
13036+ sub->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
13037+ if ((status = mmal_port_enable(sub->component->control, vd_control_port_cb)) != MMAL_SUCCESS) {
13038+ msg_Err(vd, "Failed to enable control port %s on sub %d (status=%"PRIx32" %s)",
13039+ sys->component->control->name, i, status, mmal_status_to_string(status));
13040+ goto fail;
13041+ }
13042+ if ((status = hw_mmal_subpic_open(VLC_OBJECT(vd), &sub->sub, sub->component->input[0],
13043+ sys->display_id, sys->layer + i + 1)) != MMAL_SUCCESS) {
13044+ msg_Dbg(vd, "Failed to open subpic %d", i);
13045+ goto fail;
13046+ }
13047+ if ((status = mmal_component_enable(sub->component)) != MMAL_SUCCESS)
13048+ {
13049+ msg_Dbg(vd, "Failed to enable subpic component %d", i);
13050+ goto fail;
13051+ }
13052+ }
13053+
13054+ // If we can't deal with it directly ask for I420
13055+ vd->fmt.i_chroma = req_chroma(vd);
13056+
13057+ vd->info = (vout_display_info_t){
13058+ .is_slow = false,
13059+ .has_double_click = false,
13060+ .needs_hide_mouse = false,
13061+ .has_pictures_invalid = true,
13062+ .subpicture_chromas = hw_mmal_vzc_subpicture_chromas
13063+ };
13064+
13065+ vd->pool = vd_pool;
13066+ vd->prepare = vd_prepare;
13067+ vd->display = vd_display;
13068+ vd->control = vd_control;
13069+
13070+
13071+ msg_Dbg(vd, ">>> %s: ok", __func__);
13072+ return VLC_SUCCESS;
13073+
13074+fail:
13075+ CloseMmalVout(object);
13076+
13077+ msg_Dbg(vd, ">>> %s: rv=%d", __func__, ret);
13078+
13079+ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
13080 }
13081+
13082+vlc_module_begin()
13083+
13084+ add_submodule()
13085+
13086+ set_shortname(N_("MMAL vout"))
13087+ set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
13088+ set_capability("vout display", 16) // 1 point better than ASCII art
13089+ add_shortcut("mmal_vout")
13090+ set_category( CAT_VIDEO )
13091+ set_subcategory( SUBCAT_VIDEO_VOUT )
13092+
13093+ add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
13094+ add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
13095+ MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
13096+ add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
13097+ MMAL_NATIVE_INTERLACE_LONGTEXT, false)
13098+ add_string(MMAL_DISPLAY_NAME, "auto", MMAL_DISPLAY_TEXT,
13099+ MMAL_DISPLAY_LONGTEXT, false)
13100+ add_string(MMAL_VOUT_TRANSFORM_NAME, "auto", MMAL_VOUT_TRANSFORM_TEXT,
13101+ MMAL_VOUT_TRANSFORM_LONGTEXT, false)
13102+ add_string(MMAL_VOUT_WINDOW_NAME, "fullscreen", MMAL_VOUT_WINDOW_TEXT,
13103+ MMAL_VOUT_WINDOW_LONGTEXT, false)
13104+ add_bool(MMAL_VOUT_TRANSPARENT_NAME, false, MMAL_VOUT_TRANSPARENT_TEXT,
13105+ MMAL_VOUT_TRANSPARENT_LONGTEXT, false)
13106+ set_callbacks(OpenMmalVout, CloseMmalVout)
13107+
13108+vlc_module_end()
13109+
13110+
13111--- /dev/null
13112+++ b/modules/hw/mmal/xsplitter.c
13113@@ -0,0 +1,584 @@
13114+#ifdef HAVE_CONFIG_H
13115+#include "config.h"
13116+#endif
13117+
13118+#include <stdatomic.h>
13119+
13120+#include <vlc_common.h>
13121+#include <vlc_plugin.h>
13122+#include <vlc_threads.h>
13123+#include <vlc_vout_display.h>
13124+#include <vlc_modules.h>
13125+
13126+#include <bcm_host.h>
13127+#include <interface/mmal/mmal.h>
13128+#include <interface/mmal/util/mmal_util.h>
13129+#include <interface/mmal/util/mmal_default_components.h>
13130+
13131+#include "mmal_picture.h"
13132+
13133+#define TRACE_ALL 0
13134+
13135+typedef struct display_desc_s
13136+{
13137+ vout_display_t * vout;
13138+ unsigned int max_pels;
13139+} display_desc_t;
13140+
13141+typedef struct mmal_x11_sys_s
13142+{
13143+ bool use_mmal;
13144+ display_desc_t * cur_desc;
13145+ display_desc_t mmal_desc;
13146+ display_desc_t x_desc;
13147+ uint32_t changed;
13148+ vlc_fourcc_t subpicture_chromas[16];
13149+} mmal_x11_sys_t;
13150+
13151+#define MAX_GL_PELS (1920*1080)
13152+#define MAX_MMAL_PELS (4096*4096) // Should never be hit
13153+
13154+#if 0
13155+// Gen prog for the following table
13156+// Not done inline in case we end up pulling in FP libs we don't want
13157+#include <math.h>
13158+#include <stdio.h>
13159+
13160+int main(int argc, char *argv[])
13161+{
13162+ unsigned int i;
13163+ for (i = 0; i != 64; ++i)
13164+ {
13165+ printf(" [%2u]=%5u,", i, (unsigned int)(0.5 + (1/sqrt((i + 5)/4.0) * 65536.0)));
13166+ if (i % 4 == 3)
13167+ printf("\n");
13168+ }
13169+}
13170+#endif
13171+
13172+static const uint16_t sqrt_tab[64] = {
13173+ [ 0]=58617, [ 1]=53510, [ 2]=49541, [ 3]=46341,
13174+ [ 4]=43691, [ 5]=41449, [ 6]=39520, [ 7]=37837,
13175+ [ 8]=36353, [ 9]=35030, [10]=33843, [11]=32768,
13176+ [12]=31790, [13]=30894, [14]=30070, [15]=29309,
13177+ [16]=28602, [17]=27945, [18]=27330, [19]=26755,
13178+ [20]=26214, [21]=25705, [22]=25225, [23]=24770,
13179+ [24]=24339, [25]=23930, [26]=23541, [27]=23170,
13180+ [28]=22817, [29]=22479, [30]=22155, [31]=21845,
13181+ [32]=21548, [33]=21263, [34]=20988, [35]=20724,
13182+ [36]=20470, [37]=20225, [38]=19988, [39]=19760,
13183+ [40]=19539, [41]=19326, [42]=19119, [43]=18919,
13184+ [44]=18725, [45]=18536, [46]=18354, [47]=18176,
13185+ [48]=18004, [49]=17837, [50]=17674, [51]=17515,
13186+ [52]=17361, [53]=17211, [54]=17064, [55]=16921,
13187+ [56]=16782, [57]=16646, [58]=16514, [59]=16384,
13188+ [60]=16257, [61]=16134, [62]=16013, [63]=15895
13189+};
13190+#define SQRT_MAX (sizeof(sqrt_tab)/sizeof(sqrt_tab[0]) - 1)
13191+
13192+static bool cpy_fmt_limit_size(const display_desc_t * const dd,
13193+ video_format_t * const dst,
13194+ const video_format_t * const src)
13195+{
13196+ const unsigned int src_pel = src->i_visible_width * src->i_visible_height;
13197+
13198+ *dst = *src;
13199+
13200+ if (src_pel <= dd->max_pels)
13201+ return false;
13202+
13203+ // scaling factor sqrt(max_pel/cur_pel)
13204+ // sqrt done by lookup & 16 bit fixed-point maths - not exactly accurate but
13205+ // easily good enough & avoids floating point (which may be slow)
13206+ // src_pel > max_pel so n >= 0
13207+ // Rounding should be such that exact sqrts work and everything else rounds
13208+ // down
13209+ unsigned int n = ((src_pel * 4 - 1) / dd->max_pels) - 4;
13210+ unsigned int scale = sqrt_tab[n >= SQRT_MAX ? SQRT_MAX : n];
13211+
13212+ // Rescale width - rounding up to 16
13213+ unsigned int width = ((src->i_visible_width * scale + (16 << 16) - 1) >> 16) & ~15;
13214+ // Rescale height based on new width
13215+ unsigned int height = (src->i_visible_height * width + src->i_visible_width/2) / src->i_visible_width;
13216+
13217+// fprintf(stderr, "%dx%d -> %dx%d\n", src->i_visible_width, src->i_visible_height, width, height);
13218+
13219+ dst->i_width = width;
13220+ dst->i_visible_width = width;
13221+ dst->i_height = height;
13222+ dst->i_visible_height = height;
13223+ return true;
13224+}
13225+
13226+static void unload_display_module(vout_display_t * const x_vout)
13227+{
13228+ if (x_vout != NULL) {
13229+ if (x_vout->module != NULL) {
13230+ module_unneed(x_vout, x_vout->module);
13231+ }
13232+ vlc_object_release(x_vout);
13233+ }
13234+}
13235+
13236+static void CloseMmalX11(vlc_object_t *object)
13237+{
13238+ vout_display_t * const vd = (vout_display_t *)object;
13239+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13240+
13241+ msg_Dbg(vd, "<<< %s", __func__);
13242+
13243+ if (sys == NULL)
13244+ return;
13245+
13246+ unload_display_module(sys->x_desc.vout);
13247+
13248+ unload_display_module(sys->mmal_desc.vout);
13249+
13250+ free(sys);
13251+
13252+ msg_Dbg(vd, ">>> %s", __func__);
13253+}
13254+
13255+static void mmal_x11_event(vout_display_t * x_vd, int cmd, va_list args)
13256+{
13257+ vout_display_t * const vd = x_vd->owner.sys;
13258+#if TRACE_ALL
13259+ msg_Dbg(vd, "<<< %s (cmd=%d)", __func__, cmd);
13260+#endif
13261+
13262+ // Do not fall into the display assert if Invalid not supported
13263+ if (cmd == VOUT_DISPLAY_EVENT_PICTURES_INVALID &&
13264+ !vd->info.has_pictures_invalid)
13265+ return;
13266+
13267+ vd->owner.event(vd, cmd, args);
13268+}
13269+
13270+static vout_window_t * mmal_x11_window_new(vout_display_t * x_vd, unsigned type)
13271+{
13272+ vout_display_t * const vd = x_vd->owner.sys;
13273+#if TRACE_ALL
13274+ msg_Dbg(vd, "<<< %s (type=%d)", __func__, type);
13275+#endif
13276+ return vd->owner.window_new(vd, type);
13277+}
13278+
13279+static void mmal_x11_window_del(vout_display_t * x_vd, vout_window_t * win)
13280+{
13281+ vout_display_t * const vd = x_vd->owner.sys;
13282+#if TRACE_ALL
13283+ msg_Dbg(vd, "<<< %s", __func__);
13284+#endif
13285+ vd->owner.window_del(vd, win);
13286+}
13287+
13288+
13289+static int load_display_module(vout_display_t * const vd,
13290+ display_desc_t * const dd,
13291+ const char * const cap,
13292+ const char * const module_name)
13293+{
13294+ vout_display_t * const x_vout = vlc_object_create(vd, sizeof(*x_vout));
13295+
13296+ dd->vout = NULL;
13297+ if (!x_vout)
13298+ return -1;
13299+
13300+ x_vout->owner.sys = vd;
13301+ x_vout->owner.event = mmal_x11_event;
13302+ x_vout->owner.window_new = mmal_x11_window_new;
13303+ x_vout->owner.window_del = mmal_x11_window_del;
13304+
13305+ x_vout->cfg = vd->cfg;
13306+ x_vout->info = vd->info;
13307+ cpy_fmt_limit_size(dd, &x_vout->source, &vd->source);
13308+ cpy_fmt_limit_size(dd, &x_vout->fmt, &vd->fmt);
13309+
13310+ if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL)
13311+ {
13312+ msg_Err(vd, "Failed to open Xsplitter:%s module", module_name);
13313+ goto fail;
13314+ }
13315+
13316+ msg_Dbg(vd, "R/G/B: %08x/%08x/%08x", x_vout->fmt.i_rmask, x_vout->fmt.i_gmask, x_vout->fmt.i_bmask);
13317+
13318+ dd->vout = x_vout;
13319+ return 0;
13320+
13321+fail:
13322+ vlc_object_release(x_vout);
13323+ return -1;
13324+}
13325+
13326+
13327+/* Return a pointer over the current picture_pool_t* (mandatory).
13328+ *
13329+ * For performance reasons, it is best to provide at least count
13330+ * pictures but it is not mandatory.
13331+ * You can return NULL when you cannot/do not want to allocate
13332+ * pictures.
13333+ * The vout display module keeps the ownership of the pool and can
13334+ * destroy it only when closing or on invalid pictures control.
13335+ */
13336+static picture_pool_t * mmal_x11_pool(vout_display_t * vd, unsigned count)
13337+{
13338+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13339+ vout_display_t * const x_vd = sys->cur_desc->vout;
13340+#if TRACE_ALL
13341+ char buf0[5];
13342+ msg_Dbg(vd, "<<< %s (count=%d) %s:%dx%d->%s:%dx%d", __func__, count,
13343+ str_fourcc(buf0, vd->fmt.i_chroma),
13344+ vd->fmt.i_width, vd->fmt.i_height,
13345+ str_fourcc(buf0, x_vd->fmt.i_chroma),
13346+ x_vd->fmt.i_width, x_vd->fmt.i_height);
13347+#endif
13348+ picture_pool_t * pool = x_vd->pool(x_vd, count);
13349+#if TRACE_ALL
13350+ msg_Dbg(vd, ">>> %s: %p", __func__, pool);
13351+#endif
13352+ return pool;
13353+}
13354+
13355+/* Prepare a picture and an optional subpicture for display (optional).
13356+ *
13357+ * It is called before the next pf_display call to provide as much
13358+ * time as possible to prepare the given picture and the subpicture
13359+ * for display.
13360+ * You are guaranted that pf_display will always be called and using
13361+ * the exact same picture_t and subpicture_t.
13362+ * You cannot change the pixel content of the picture_t or of the
13363+ * subpicture_t.
13364+ */
13365+static void mmal_x11_prepare(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
13366+{
13367+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13368+ vout_display_t * const x_vd = sys->cur_desc->vout;
13369+#if TRACE_ALL
13370+ msg_Dbg(vd, "<<< %s", __func__);
13371+#endif
13372+ if (x_vd->prepare)
13373+ x_vd->prepare(x_vd, pic, sub);
13374+}
13375+
13376+/* Display a picture and an optional subpicture (mandatory).
13377+ *
13378+ * The picture and the optional subpicture must be displayed as soon as
13379+ * possible.
13380+ * You cannot change the pixel content of the picture_t or of the
13381+ * subpicture_t.
13382+ *
13383+ * This function gives away the ownership of the picture and of the
13384+ * subpicture, so you must release them as soon as possible.
13385+ */
13386+static void mmal_x11_display(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
13387+{
13388+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13389+ vout_display_t * const x_vd = sys->cur_desc->vout;
13390+
13391+#if TRACE_ALL
13392+ const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic);
13393+ 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,
13394+ is_mmal_pic, sys->use_mmal);
13395+#endif
13396+
13397+ if (x_vd->fmt.i_chroma != pic->format.i_chroma ||
13398+ x_vd->fmt.i_width != pic->format.i_width ||
13399+ x_vd->fmt.i_height != pic->format.i_height)
13400+ {
13401+ msg_Dbg(vd, "%s: Picture dropped", __func__);
13402+ picture_Release(pic);
13403+ if (sub != NULL)
13404+ subpicture_Delete(sub);
13405+ return;
13406+ }
13407+
13408+ x_vd->display(x_vd, pic, sub);
13409+}
13410+
13411+
13412+static int vout_display_Control(const display_desc_t * const dd, int query, ...)
13413+{
13414+ va_list args;
13415+ int result;
13416+
13417+ va_start(args, query);
13418+ result = dd->vout->control(dd->vout, query, args);
13419+ va_end(args);
13420+
13421+ return result;
13422+}
13423+
13424+static bool want_mmal_vout(vout_display_t * const vd, const mmal_x11_sys_t * const sys)
13425+{
13426+ return sys->mmal_desc.vout != NULL &&
13427+ (sys->x_desc.vout == NULL || var_InheritBool(vd, "fullscreen"));
13428+}
13429+
13430+static inline int
13431+up_rv(const int a, const int b)
13432+{
13433+ return a != 0 ? a : b;
13434+}
13435+
13436+static int
13437+reset_pictures(vout_display_t * const vd, const display_desc_t * const desc)
13438+{
13439+ int rv = 0;
13440+ VLC_UNUSED(vd);
13441+ if (desc->vout)
13442+ {
13443+ // If the display doesn't have has_pictures_invalid then it doesn't
13444+ // expect RESET_PICTURES
13445+ if (desc->vout->info.has_pictures_invalid)
13446+ vout_display_Control(desc, VOUT_DISPLAY_RESET_PICTURES);
13447+ }
13448+ return rv;
13449+}
13450+
13451+static int
13452+replay_controls(vout_display_t * const vd, const display_desc_t * const desc, const int32_t changed)
13453+{
13454+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_DISPLAY_FILLED)) != 0)
13455+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, vd->cfg);
13456+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_ZOOM)) != 0)
13457+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg);
13458+ if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) |
13459+ (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0)
13460+ cpy_fmt_limit_size(desc, &desc->vout->source, &vd->source);
13461+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT)) != 0)
13462+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT);
13463+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP)) != 0)
13464+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_CROP);
13465+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_VIEWPOINT)) != 0)
13466+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_VIEWPOINT, vd->cfg);
13467+ return 0;
13468+}
13469+
13470+/* Control on the module (mandatory) */
13471+static int mmal_x11_control(vout_display_t * vd, int ctl, va_list va)
13472+{
13473+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13474+ display_desc_t *x_desc = sys->cur_desc;
13475+ int rv;
13476+#if TRACE_ALL
13477+ msg_Dbg(vd, "<<< %s[%d] (ctl=%d)", __func__, sys->use_mmal, ctl);
13478+#endif
13479+ // Remember what we've told this vd - unwanted ctls ignored on replay
13480+ if (ctl >= 0 && ctl <= 31)
13481+ sys->changed |= (1 << ctl);
13482+
13483+ switch (ctl) {
13484+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
13485+ {
13486+ const vout_display_cfg_t * const cfg = va_arg(va, const vout_display_cfg_t *);
13487+ const bool want_mmal = want_mmal_vout(vd, sys);
13488+ const bool swap_vout = (sys->use_mmal != want_mmal);
13489+ display_desc_t * const new_desc = want_mmal ? &sys->mmal_desc : &sys->x_desc;
13490+
13491+ msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d",
13492+ cfg->display.width, cfg->display.height, sys->mmal_desc.vout, want_mmal,
13493+ var_InheritBool(vd, "fullscreen"));
13494+
13495+ // Repeat any control calls that we sent to the previous vd
13496+ if (swap_vout && sys->changed != 0) {
13497+ const uint32_t changed = sys->changed;
13498+ sys->changed = 0;
13499+ replay_controls(vd, new_desc, changed);
13500+ }
13501+
13502+ if (swap_vout) {
13503+ if (sys->use_mmal) {
13504+ vout_display_Control(x_desc, VOUT_DISPLAY_CHANGE_MMAL_HIDE);
13505+ }
13506+ vout_display_SendEventPicturesInvalid(vd);
13507+ }
13508+
13509+ rv = vout_display_Control(new_desc, ctl, cfg);
13510+ if (rv == VLC_SUCCESS) {
13511+ vd->fmt = new_desc->vout->fmt;
13512+ sys->cur_desc = new_desc;
13513+ sys->use_mmal = want_mmal;
13514+ }
13515+
13516+
13517+ break;
13518+ }
13519+
13520+ case VOUT_DISPLAY_RESET_PICTURES:
13521+ {
13522+ char dbuf0[5], dbuf1[5], dbuf2[5];
13523+ msg_Dbg(vd, "<<< %s: Pic reset: fmt: %s,%dx%d<-%s,%dx%d, source: %s,%dx%d/%dx%d", __func__,
13524+ str_fourcc(dbuf0, vd->fmt.i_chroma), vd->fmt.i_width, vd->fmt.i_height,
13525+ str_fourcc(dbuf1, x_desc->vout->fmt.i_chroma), x_desc->vout->fmt.i_width, x_desc->vout->fmt.i_height,
13526+ str_fourcc(dbuf2, vd->source.i_chroma), vd->source.i_width, vd->source.i_height, x_desc->vout->source.i_width,
13527+ x_desc->vout->source.i_height);
13528+ }
13529+ rv = reset_pictures(vd, &sys->x_desc);
13530+ rv = up_rv(rv, reset_pictures(vd, &sys->mmal_desc));
13531+
13532+ vd->fmt = x_desc->vout->fmt;
13533+ break;
13534+
13535+ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
13536+ case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
13537+ cpy_fmt_limit_size(x_desc, &x_desc->vout->source, &vd->source);
13538+
13539+ /* FALLTHRU */
13540+ default:
13541+ rv = x_desc->vout->control(x_desc->vout, ctl, va);
13542+// vd->fmt = x_vd->fmt;
13543+ break;
13544+ }
13545+#if TRACE_ALL
13546+ msg_Dbg(vd, ">>> %s (rv=%d)", __func__, rv);
13547+#endif
13548+ return rv;
13549+}
13550+
13551+#define DO_MANAGE 0
13552+
13553+#if DO_MANAGE
13554+/* Manage pending event (optional) */
13555+static void mmal_x11_manage(vout_display_t * vd)
13556+{
13557+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13558+ vout_display_t * const x_vd = sys->cur_desc->vout;
13559+#if TRACE_ALL
13560+ msg_Dbg(vd, "<<< %s", __func__);
13561+#endif
13562+ x_vd->manage(x_vd);
13563+}
13564+#endif
13565+
13566+static int OpenMmalX11(vlc_object_t *object)
13567+{
13568+ vout_display_t * const vd = (vout_display_t *)object;
13569+ mmal_x11_sys_t * const sys = calloc(1, sizeof(*sys));
13570+ int ret = VLC_SUCCESS;
13571+
13572+ if (sys == NULL) {
13573+ return VLC_EGENERIC;
13574+ }
13575+ vd->sys = (vout_display_sys_t *)sys;
13576+
13577+ vd->info = (vout_display_info_t){
13578+ .is_slow = false,
13579+ .has_double_click = false,
13580+ .needs_hide_mouse = false,
13581+ .has_pictures_invalid = true,
13582+ .subpicture_chromas = NULL
13583+ };
13584+
13585+ {
13586+ char dbuf0[5];
13587+ msg_Dbg(vd, ">>> %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
13588+ str_fourcc(dbuf0, vd->fmt.i_chroma),
13589+ vd->fmt.i_width, vd->fmt.i_height,
13590+ vd->fmt.i_x_offset, vd->fmt.i_y_offset,
13591+ vd->fmt.i_visible_width, vd->fmt.i_visible_height,
13592+ vd->fmt.i_sar_num, vd->fmt.i_sar_den);
13593+ }
13594+
13595+ sys->x_desc.max_pels = MAX_GL_PELS;
13596+ sys->mmal_desc.max_pels = MAX_MMAL_PELS;
13597+
13598+ if (load_display_module(vd, &sys->x_desc, "vout display", "opengles2") == 0)
13599+ {
13600+ msg_Dbg(vd, "Opengles2 output found");
13601+ }
13602+ else
13603+ {
13604+ sys->x_desc.max_pels = MAX_MMAL_PELS;
13605+ if (load_display_module(vd, &sys->x_desc, "vout display", "xcb_x11") == 0)
13606+ msg_Dbg(vd, "X11 XCB output found");
13607+ }
13608+
13609+ if ((load_display_module(vd, &sys->mmal_desc, "vout display", "mmal_vout")) == 0)
13610+ msg_Dbg(vd, "MMAL output found");
13611+
13612+ if (sys->mmal_desc.vout == NULL && sys->x_desc.vout == NULL) {
13613+ char dbuf0[5], dbuf1[5];
13614+ 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));
13615+ goto fail;
13616+ }
13617+
13618+ vd->pool = mmal_x11_pool;
13619+ vd->prepare = mmal_x11_prepare;
13620+ vd->display = mmal_x11_display;
13621+ vd->control = mmal_x11_control;
13622+#if DO_MANAGE
13623+ vd->manage = mmal_x11_manage;
13624+#endif
13625+
13626+ if (want_mmal_vout(vd, sys)) {
13627+ sys->cur_desc = &sys->mmal_desc;
13628+ sys->use_mmal = true;
13629+ }
13630+ else {
13631+ sys->cur_desc = &sys->x_desc;
13632+ sys->use_mmal = false;
13633+ }
13634+
13635+ if (sys->mmal_desc.vout == NULL || sys->x_desc.vout == NULL) {
13636+ vd->info = sys->cur_desc->vout->info;
13637+ vd->info.has_pictures_invalid = true; // Should make this unwanted
13638+ }
13639+ else {
13640+ // We have both - construct a combination
13641+ vd->info = (vout_display_info_t){
13642+ .is_slow = false,
13643+ .has_double_click = sys->mmal_desc.vout->info.has_double_click || sys->x_desc.vout->info.has_double_click,
13644+ .needs_hide_mouse = sys->mmal_desc.vout->info.needs_hide_mouse || sys->x_desc.vout->info.needs_hide_mouse,
13645+ .has_pictures_invalid = true,
13646+ };
13647+ // Construct intersection of subpicture chromas
13648+ // sys calloced so no need to add the terminating zero
13649+ if (sys->mmal_desc.vout->info.subpicture_chromas != NULL && sys->x_desc.vout->info.subpicture_chromas != NULL) {
13650+ unsigned int n = 0;
13651+ // N^2 - fix if we ever care
13652+ for (const vlc_fourcc_t * p1 = sys->mmal_desc.vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) {
13653+ for (const vlc_fourcc_t * p2 = sys->x_desc.vout->info.subpicture_chromas; *p2 != 0; ++p2) {
13654+ if (*p1 == *p2) {
13655+ sys->subpicture_chromas[n++] = *p1;
13656+ break;
13657+ }
13658+ }
13659+ }
13660+ if (n != 0)
13661+ vd->info.subpicture_chromas = sys->subpicture_chromas;
13662+ }
13663+ }
13664+ vd->fmt = sys->cur_desc->vout->fmt;
13665+
13666+#if TRACE_ALL
13667+ {
13668+ char dbuf0[5];
13669+ msg_Dbg(vd, ">>> %s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
13670+ module_get_name(sys->cur_desc->vout->module, false),
13671+ str_fourcc(dbuf0, vd->fmt.i_chroma),
13672+ vd->fmt.i_width, vd->fmt.i_height,
13673+ vd->fmt.i_x_offset, vd->fmt.i_y_offset,
13674+ vd->fmt.i_visible_width, vd->fmt.i_visible_height,
13675+ vd->fmt.i_sar_num, vd->fmt.i_sar_den);
13676+ }
13677+#endif
13678+ return VLC_SUCCESS;
13679+
13680+fail:
13681+ CloseMmalX11(VLC_OBJECT(vd));
13682+ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
13683+}
13684+
13685+
13686+
13687+
13688+vlc_module_begin()
13689+ set_shortname(N_("MMAL x11 splitter"))
13690+ set_description(N_("MMAL x11 splitter for Raspberry Pi"))
13691+ set_capability("vout display", 300) // Between GLES & GL
13692+ add_shortcut("mmal_x11")
13693+ set_category( CAT_VIDEO )
13694+ set_subcategory( SUBCAT_VIDEO_VOUT )
13695+ set_callbacks(OpenMmalX11, CloseMmalX11)
13696+vlc_module_end()
13697+
13698--- a/modules/video_output/opengl/egl.c
13699+++ b/modules/video_output/opengl/egl.c
13700@@ -43,6 +43,8 @@
13701 # include "../android/utils.h"
13702 #endif
13703
13704+#define REQUIRE_DMA_BUF_IMPORT 1
13705+
13706 typedef struct vlc_gl_sys_t
13707 {
13708 EGLDisplay display;
13709@@ -355,6 +357,14 @@ static int Open (vlc_object_t *obj, cons
13710 goto error;
13711 }
13712
13713+#if REQUIRE_DMA_BUF_IMPORT
13714+ if (!CheckToken(ext, "EGL_EXT_image_dma_buf_import"))
13715+ {
13716+ msg_Dbg(obj, "No dma_buf_import - fall back to X");
13717+ goto error;
13718+ }
13719+#endif
13720+
13721 const EGLint conf_attr[] = {
13722 EGL_RED_SIZE, 5,
13723 EGL_GREEN_SIZE, 5,
13724--- a/src/input/decoder.c
13725+++ b/src/input/decoder.c
13726@@ -1995,6 +1995,7 @@ void input_DecoderDelete( decoder_t *p_d
13727 vlc_mutex_lock( &p_owner->lock );
13728 p_owner->b_waiting = false;
13729 vlc_cond_signal( &p_owner->wait_request );
13730+ vlc_mutex_unlock( &p_owner->lock );
13731
13732 /* If the video output is paused or slow, or if the picture pool size was
13733 * under-estimated (e.g. greedy video filter, buggy decoder...), the
13734@@ -2005,7 +2006,6 @@ void input_DecoderDelete( decoder_t *p_d
13735 * worker threads (if any) and the decoder thread to terminate. */
13736 if( p_owner->p_vout != NULL )
13737 vout_Cancel( p_owner->p_vout, true );
13738- vlc_mutex_unlock( &p_owner->lock );
13739
13740 vlc_join( p_owner->thread, NULL );
13741
13742--- a/src/misc/fourcc.c
13743+++ b/src/misc/fourcc.c
13744@@ -755,8 +755,13 @@ static const struct
13745 { { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422,
13746 VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT },
13747 FAKE_FMT() },
13748- { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE,
13749- VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE },
13750+ { { VLC_CODEC_ANDROID_OPAQUE }, FAKE_FMT() },
13751+ { { VLC_CODEC_MMAL_OPAQUE, VLC_CODEC_MMAL_ZC_SAND30 },
13752+ FAKE_FMT() },
13753+ { { VLC_CODEC_MMAL_ZC_I420, VLC_CODEC_MMAL_ZC_SAND8,
13754+ VLC_CODEC_MMAL_ZC_SAND10, VLC_CODEC_MMAL_ZC_RGB32 },
13755+ FAKE_FMT() },
13756+ { { VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE },
13757 FAKE_FMT() },
13758 { { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B },
13759 FAKE_FMT() },
13760--- a/src/misc/picture.c
13761+++ b/src/misc/picture.c
13762@@ -365,10 +365,30 @@ void picture_CopyProperties( picture_t *
13763 p_dst->b_top_field_first = p_src->b_top_field_first;
13764 }
13765
13766+static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma)
13767+{
13768+ return i_chroma == VLC_CODEC_MMAL_OPAQUE ||
13769+ i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
13770+ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
13771+ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
13772+ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
13773+ i_chroma == VLC_CODEC_MMAL_ZC_SAND8;
13774+}
13775+
13776 void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src )
13777 {
13778- for( int i = 0; i < p_src->i_planes ; i++ )
13779- plane_CopyPixels( p_dst->p+i, p_src->p+i );
13780+ if( is_zc_chroma(p_src->format.i_chroma) )
13781+ {
13782+ assert(p_dst->i_planes == 0);
13783+ p_dst->i_planes = p_src->i_planes;
13784+ for( int i = 0; i < p_src->i_planes; i++ )
13785+ p_dst->p[i] = p_src->p[i];
13786+ }
13787+ else
13788+ {
13789+ for( int i = 0; i < p_src->i_planes; i++ )
13790+ plane_CopyPixels( p_dst->p+i, p_src->p+i );
13791+ }
13792
13793 assert( p_dst->context == NULL );
13794
13795--- a/src/video_output/video_output.c
13796+++ b/src/video_output/video_output.c
13797@@ -964,6 +964,17 @@ static picture_t *ConvertRGB32AndBlend(v
13798 return NULL;
13799 }
13800
13801+
13802+static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma)
13803+{
13804+ return i_chroma == VLC_CODEC_MMAL_OPAQUE ||
13805+ i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
13806+ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
13807+ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
13808+ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
13809+ i_chroma == VLC_CODEC_MMAL_ZC_SAND8;
13810+}
13811+
13812 static int ThreadDisplayRenderPicture(vout_thread_t *vout, bool is_forced)
13813 {
13814 vout_thread_sys_t *sys = vout->p;
13815@@ -1098,7 +1109,7 @@ static int ThreadDisplayRenderPicture(vo
13816 }
13817
13818 assert(vout_IsDisplayFiltered(vd) == !sys->display.use_dr);
13819- if (sys->display.use_dr && !is_direct) {
13820+ if (sys->display.use_dr && !is_direct && !is_zc_chroma(todisplay->format.i_chroma)) {
13821 picture_t *direct = NULL;
13822 if (likely(vout->p->display_pool != NULL))
13823 direct = picture_pool_Get(vout->p->display_pool);