blob: ab317303d96df1be732571f8e084de47e1b552d2 [file] [log] [blame]
Andrew Geissler517393d2023-01-13 08:55:19 -06001Upstream-Status: Inappropriate
2
3RPI-Distro repo forks original vlc and applies patches
4to enable raspiberry pi support.
5
6--- a/configure.ac
7+++ b/configure.ac
8@@ -3478,6 +3478,9 @@ dnl
9 AC_ARG_ENABLE(mmal,
10 AS_HELP_STRING([--enable-mmal],
11 [Multi-Media Abstraction Layer (MMAL) hardware plugin (default enable)]))
12+AC_ARG_ENABLE(mmal_avcodec,
13+ AS_HELP_STRING([--enable-mmal-avcodec],
14+ [Use MMAL enabled avcodec libs (default disable)]))
15 if test "${enable_mmal}" != "no"; then
16 VLC_SAVE_FLAGS
17 LDFLAGS="${LDFLAGS} -L/opt/vc/lib -lvchostif"
18@@ -3488,7 +3491,7 @@ if test "${enable_mmal}" != "no"; then
19 VLC_ADD_PLUGIN([mmal])
20 VLC_ADD_LDFLAGS([mmal],[ -L/opt/vc/lib ])
21 VLC_ADD_CFLAGS([mmal],[ -isystem /opt/vc/include -isystem /opt/vc/include/interface/vcos/pthreads -isystem /opt/vc/include/interface/vmcs_host/linux ])
22- VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif ]) ], [
23+ VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif -lvchiq_arm -lvcsm ]) ], [
24 AS_IF([test "${enable_mmal}" = "yes"],
25 [ AC_MSG_ERROR([Cannot find bcm library...]) ],
26 [ AC_MSG_WARN([Cannot find bcm library...]) ])
27@@ -3500,6 +3503,7 @@ if test "${enable_mmal}" != "no"; then
28 VLC_RESTORE_FLAGS
29 fi
30 AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"])
31+AM_CONDITIONAL([HAVE_MMAL_AVCODEC], [test "${enable_mmal_avcodec}" = "yes"])
32
33 dnl
34 dnl evas plugin
35--- a/include/vlc_fourcc.h
36+++ b/include/vlc_fourcc.h
37@@ -365,6 +365,11 @@
38
39 /* Broadcom MMAL opaque buffer type */
40 #define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L')
41+#define VLC_CODEC_MMAL_ZC_SAND8 VLC_FOURCC('Z','S','D','8')
42+#define VLC_CODEC_MMAL_ZC_SAND10 VLC_FOURCC('Z','S','D','0')
43+#define VLC_CODEC_MMAL_ZC_SAND30 VLC_FOURCC('Z','S','D','3')
44+#define VLC_CODEC_MMAL_ZC_I420 VLC_FOURCC('Z','4','2','0')
45+#define VLC_CODEC_MMAL_ZC_RGB32 VLC_FOURCC('Z','R','G','B')
46
47 /* DXVA2 opaque video surface for use with D3D9 */
48 #define VLC_CODEC_D3D9_OPAQUE VLC_FOURCC('D','X','A','9') /* 4:2:0 8 bpc */
49--- a/modules/hw/mmal/Makefile.am
50+++ b/modules/hw/mmal/Makefile.am
51@@ -1,23 +1,57 @@
52 include $(top_srcdir)/modules/common.am
53 mmaldir = $(pluginsdir)/mmal
54
55-AM_CFLAGS += $(CFLAGS_mmal)
56-AM_LDFLAGS += -rpath '$(mmaldir)' $(LDFLAGS_mmal)
57+AM_CFLAGS += -pthread $(CFLAGS_mmal)
58+AM_LDFLAGS += -pthread -rpath '$(mmaldir)' $(LDFLAGS_mmal)
59
60-libmmal_vout_plugin_la_SOURCES = vout.c mmal_picture.c mmal_picture.h
61+libmmal_vout_plugin_la_SOURCES = vout.c mmal_cma.c mmal_picture.c subpic.c\
62+ mmal_cma.h mmal_picture.h subpic.h transform_ops.h\
63+ mmal_piccpy_neon.S
64 libmmal_vout_plugin_la_CFLAGS = $(AM_CFLAGS)
65-libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm
66+libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm -lX11 -lXrandr
67 libmmal_vout_plugin_la_LIBADD = $(LIBS_mmal)
68 mmal_LTLIBRARIES = libmmal_vout_plugin.la
69
70-libmmal_codec_plugin_la_SOURCES = codec.c
71+libmmal_codec_plugin_la_SOURCES = codec.c mmal_cma.c mmal_picture.c subpic.c\
72+ mmal_cma.h mmal_picture.h subpic.h transform_ops.h\
73+ blend_rgba_neon.S mmal_piccpy_neon.S
74 libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS)
75 libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
76 libmmal_codec_plugin_la_LIBADD = $(LIBS_mmal)
77 mmal_LTLIBRARIES += libmmal_codec_plugin.la
78
79-libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c
80+libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c mmal_cma.c\
81+ mmal_cma.h mmal_picture.h transform_ops.h\
82+ mmal_piccpy_neon.S
83 libmmal_deinterlace_plugin_la_CFLAGS = $(AM_CFLAGS)
84 libmmal_deinterlace_plugin_la_LDFLAGS = $(AM_LDFLAGS)
85 libmmal_deinterlace_plugin_la_LIBADD = $(LIBS_mmal)
86 mmal_LTLIBRARIES += libmmal_deinterlace_plugin.la
87+
88+libmmal_xsplitter_plugin_la_SOURCES = xsplitter.c mmal_picture.c mmal_cma.c\
89+ mmal_cma.h mmal_picture.h transform_ops.h\
90+ mmal_piccpy_neon.S
91+libmmal_xsplitter_plugin_la_CFLAGS = $(AM_CFLAGS)
92+libmmal_xsplitter_plugin_la_LDFLAGS = $(AM_LDFLAGS)
93+libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal)
94+mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la
95+
96+libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c\
97+ mmal_cma.h mmal_picture.h transform_ops.h\
98+ mmal_piccpy_neon.S
99+libmmal_converter_plugin_la_CFLAGS = $(AM_CFLAGS)
100+libmmal_converter_plugin_la_LDFLAGS = $(AM_LDFLAGS)
101+libmmal_converter_plugin_la_LIBADD = $(LIBS_mmal)
102+mmal_LTLIBRARIES += libmmal_converter_plugin.la
103+
104+if HAVE_MMAL_AVCODEC
105+libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_cma.c mmal_picture.c\
106+ mmal_cma.h mmal_picture.h transform_ops.h\
107+ mmal_piccpy_neon.S
108+libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS)
109+libmmal_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
110+libmmal_avcodec_plugin_la_LIBADD = $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(LIBS_mmal)
111+mmal_LTLIBRARIES += libmmal_avcodec_plugin.la
112+endif
113+
114+
115--- /dev/null
116+++ b/modules/hw/mmal/blend_rgba_neon.S
117@@ -0,0 +1,197 @@
118+ .syntax unified
119+ .arm
120+// .thumb
121+ .text
122+ .align 16
123+ .arch armv7-a
124+ .fpu neon-vfpv4
125+
126+@ blend_rgbx_rgba_neon
127+
128+@ Implements /255 as ((x * 257) + 0x8000) >> 16
129+@ This generates something in the range [(x+126)/255, (x+127)/255] which is good enough
130+
131+@ There is advantage to aligning src and/or dest - dest gives a bit more due to being used twice
132+
133+
134+
135+@ [r0] RGBx dest loaded into d20-d23
136+@ [r1] RGBA src merge loaded into d16-d19
137+@ r2 plane alpha
138+@ r3 count (pixels)
139+
140+.macro blend_main sR, sG, sB, sA, dR, dG, dB, dA
141+
142+ push { r4, lr }
143+
144+ vdup.u8 d7, r2
145+
146+ subs r3, #8
147+ vmov.u8 d6, #0xff
148+
149+ blt 2f
150+
151+ @ If < 16 bytes to move then don't bother trying to align
152+ @ (a) This means the the align doesn't need to worry about r3 underflow
153+ @ (b) The overhead would be greater than any gain
154+ cmp r3, #8
155+ mov r4, r3
156+ ble 1f
157+
158+ @ Align r1 on a 32 byte boundary
159+ neg r3, r0
160+ ubfx r3, r3, #2, #3
161+
162+ cmp r3, #0
163+ blne 10f
164+
165+ sub r3, r4, r3
166+
167+1:
168+ vld4.8 {d16, d17, d18, d19}, [r1]
169+
170+1:
171+ vmull.u8 q15, \sA, d7
172+
173+ vld4.8 {d20, d21, d22, d23}, [r0]
174+
175+ vsra.u16 q15, q15, #8
176+ subs r3, #8
177+ vrshrn.u16 d31, q15, #8
178+ vsub.u8 d30, d6, d31
179+
180+ vmull.u8 q12, \sR, d31
181+ vmull.u8 q13, \sG, d31
182+ vmull.u8 q14, \sB, d31
183+ addge r1, #32
184+
185+ vmlal.u8 q12, \dR, d30
186+ vmlal.u8 q13, \dG, d30
187+ vmlal.u8 q14, \dB, d30
188+ vld4.8 {d16, d17, d18, d19}, [r1]
189+
190+ vsra.u16 q12, q12, #8 @ * 257/256
191+ vsra.u16 q13, q13, #8
192+ vsra.u16 q14, q14, #8
193+
194+ vrshrn.u16 \dR, q12, #8
195+ vrshrn.u16 \dG, q13, #8
196+ vrshrn.u16 \dB, q14, #8
197+ vmov.u8 \dA, #0xff
198+
199+ vst4.8 {d20, d21, d22, d23}, [r0]!
200+ bge 1b
201+ add r1, #32
202+
203+2:
204+ cmp r3, #-8
205+ blgt 10f
206+
207+ pop { r4, pc }
208+
209+
210+// Partial version
211+// Align @ start & deal with tail
212+10:
213+ lsls r2, r3, #30 @ b2 -> C, b1 -> N
214+ mov r2, r0
215+ bcc 1f
216+ vld4.8 {d16[0], d17[0], d18[0], d19[0]}, [r1]!
217+ vld4.8 {d20[0], d21[0], d22[0], d23[0]}, [r2]!
218+ vld4.8 {d16[1], d17[1], d18[1], d19[1]}, [r1]!
219+ vld4.8 {d20[1], d21[1], d22[1], d23[1]}, [r2]!
220+ vld4.8 {d16[2], d17[2], d18[2], d19[2]}, [r1]!
221+ vld4.8 {d20[2], d21[2], d22[2], d23[2]}, [r2]!
222+ vld4.8 {d16[3], d17[3], d18[3], d19[3]}, [r1]!
223+ vld4.8 {d20[3], d21[3], d22[3], d23[3]}, [r2]!
224+1:
225+ bpl 1f
226+ vld4.8 {d16[4], d17[4], d18[4], d19[4]}, [r1]!
227+ vld4.8 {d20[4], d21[4], d22[4], d23[4]}, [r2]!
228+ vld4.8 {d16[5], d17[5], d18[5], d19[5]}, [r1]!
229+ vld4.8 {d20[5], d21[5], d22[5], d23[5]}, [r2]!
230+1:
231+ tst r3, #1
232+ beq 1f
233+ vld4.8 {d16[6], d17[6], d18[6], d19[6]}, [r1]!
234+ vld4.8 {d20[6], d21[6], d22[6], d23[6]}, [r2]!
235+1:
236+ @ Set conditions for later
237+ lsls r2, r3, #30 @ b2 -> C, b1 -> N
238+
239+ vmull.u8 q15, \sA, d7
240+ vsra.u16 q15, q15, #8
241+ vrshrn.u16 d31, q15, #8
242+ vsub.u8 d30, d6, d31
243+
244+ vmull.u8 q12, \sR, d31
245+ vmull.u8 q13, \sG, d31
246+ vmull.u8 q14, \sB, d31
247+
248+ vmlal.u8 q12, \dR, d30
249+ vmlal.u8 q13, \dG, d30
250+ vmlal.u8 q14, \dB, d30
251+
252+ vsra.u16 q12, q12, #8
253+ vsra.u16 q13, q13, #8
254+ vsra.u16 q14, q14, #8
255+
256+ vrshrn.u16 \dR, q12, #8
257+ vrshrn.u16 \dG, q13, #8
258+ vrshrn.u16 \dB, q14, #8
259+ vmov.u8 \dA, #0xff
260+
261+ bcc 1f
262+ vst4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]!
263+ vst4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]!
264+ vst4.8 {d20[2], d21[2], d22[2], d23[2]}, [r0]!
265+ vst4.8 {d20[3], d21[3], d22[3], d23[3]}, [r0]!
266+1:
267+ bpl 1f
268+ vst4.8 {d20[4], d21[4], d22[4], d23[4]}, [r0]!
269+ vst4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]!
270+1:
271+ tst r3, #1
272+ bxeq lr
273+ vst4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]!
274+
275+ bx lr
276+
277+.endm
278+
279+
280+@ [r0] RGBx dest (Byte order: R, G, B, x)
281+@ [r1] RGBA src merge (Byte order: R, G, B, A)
282+@ r2 plane alpha
283+@ r3 count (pixels)
284+
285+@ Whilst specified as RGBx+RGBA the only important part is the position of
286+@ alpha, the other components are all treated the same
287+
288+@ [r0] RGBx dest (Byte order: R, G, B, x)
289+@ [r1] RGBA src merge (Byte order: R, G, B, A) - same as above
290+@ r2 plane alpha
291+@ r3 count (pixels)
292+ .align 16
293+ .global blend_rgbx_rgba_neon
294+#ifdef __ELF__
295+ .type blend_rgbx_rgba_neon, %function
296+#endif
297+blend_rgbx_rgba_neon:
298+ blend_main d16, d17, d18, d19, d20, d21, d22, d23
299+
300+
301+@ [r0] RGBx dest (Byte order: R, G, B, x)
302+@ [r1] RGBA src merge (Byte order: B, G, R, A) - B / R swapped
303+@ r2 plane alpha
304+@ r3 count (pixels)
305+ .align 16
306+ .global blend_bgrx_rgba_neon
307+#ifdef __ELF__
308+ .type blend_bgrx_rgba_neon, %function
309+#endif
310+blend_bgrx_rgba_neon:
311+ blend_main d18, d17, d16, d19, d20, d21, d22, d23
312+
313+
314+
315--- /dev/null
316+++ b/modules/hw/mmal/blend_rgba_neon.h
317@@ -0,0 +1,17 @@
318+#ifndef HW_MMAL_BLEND_RGBA_NEON_H
319+#define HW_MMAL_BLEND_RGBA_NEON_H
320+
321+#ifdef __cplusplus
322+extern "C" {
323+#endif
324+
325+typedef void blend_neon_fn(void * dest, const void * src, int alpha, unsigned int n);
326+extern blend_neon_fn blend_rgbx_rgba_neon;
327+extern blend_neon_fn blend_bgrx_rgba_neon;
328+
329+#ifdef __cplusplus
330+}
331+#endif
332+
333+#endif
334+
335--- /dev/null
336+++ b/modules/hw/mmal/blend_test.c
337@@ -0,0 +1,180 @@
338+#include <stdio.h>
339+#include <stdint.h>
340+#include <memory.h>
341+
342+#include "blend_rgba_neon.h"
343+
344+#define RPI_PROFILE 1
345+#define RPI_PROC_ALLOC 1
346+#include "rpi_prof.h"
347+
348+static inline unsigned div255(unsigned v)
349+{
350+ // This models what we we do in the asm for / 255
351+ // It generates something in the range [(i+126)/255, (i+127)/255] which is good enough
352+ return ((v * 257) + 0x8000) >> 16;
353+}
354+
355+static inline unsigned int a_merge(unsigned int dst, unsigned src, unsigned f)
356+{
357+ return div255((255 - f) * (dst) + src * f);
358+}
359+
360+
361+static void merge_line(void * dest, const void * src, int alpha, unsigned int n)
362+{
363+ unsigned int i;
364+ const uint8_t * s_data = src;
365+ uint8_t * d_data = dest;
366+
367+ for (i = 0; i != n; ++i) {
368+ const uint32_t s_pel = ((const uint32_t *)s_data)[i];
369+ const uint32_t d_pel = ((const uint32_t *)d_data)[i];
370+ const unsigned int a = div255(alpha * (s_pel >> 24));
371+ ((uint32_t *)d_data)[i] = 0xff000000 |
372+ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 16) & 0xff, a) << 16) |
373+ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) |
374+ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 0) & 0xff, a) << 0 );
375+ }
376+}
377+
378+
379+// Merge RGBA with BGRA
380+static void merge_line2(void * dest, const void * src, int alpha, unsigned int n)
381+{
382+ unsigned int i;
383+ const uint8_t * s_data = src;
384+ uint8_t * d_data = dest;
385+
386+ for (i = 0; i != n; ++i) {
387+ const uint32_t s_pel = ((const uint32_t *)s_data)[i];
388+ const uint32_t d_pel = ((const uint32_t *)d_data)[i];
389+ const unsigned int a = div255(alpha * (s_pel >> 24));
390+ ((uint32_t *)d_data)[i] = 0xff000000 |
391+ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 16) & 0xff, a) << 0 ) |
392+ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) |
393+ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 0) & 0xff, a) << 16);
394+ }
395+}
396+
397+#define BUF_SIZE 256
398+#define BUF_SLACK 16
399+#define BUF_ALIGN 64
400+#define BUF_ALLOC (BUF_SIZE + 2*BUF_SLACK + BUF_ALIGN)
401+
402+static void test_line(const uint32_t * const dx, const unsigned int d_off,
403+ const uint32_t * const sx, const unsigned int s_off,
404+ const unsigned int alpha, const unsigned int len, const int prof_no)
405+{
406+ uint32_t d0_buf[BUF_ALLOC];
407+ uint32_t d1_buf[BUF_ALLOC];
408+ const uint32_t * const s0 = sx + s_off;
409+
410+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
411+ uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
412+ unsigned int i;
413+
414+ memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4);
415+ memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4);
416+
417+ merge_line(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
418+
419+ PROFILE_START();
420+ blend_rgbx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
421+ PROFILE_ACC_N(prof_no);
422+
423+ for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) {
424+ if (d0[i] != d1[i]) {
425+ printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len);
426+ }
427+ }
428+}
429+
430+static void test_line2(const uint32_t * const dx, const unsigned int d_off,
431+ const uint32_t * const sx, const unsigned int s_off,
432+ const unsigned int alpha, const unsigned int len, const int prof_no)
433+{
434+ uint32_t d0_buf[BUF_ALLOC];
435+ uint32_t d1_buf[BUF_ALLOC];
436+ const uint32_t * const s0 = sx + s_off;
437+
438+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
439+ uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
440+ unsigned int i;
441+
442+ memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4);
443+ memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4);
444+
445+ merge_line2(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
446+
447+ PROFILE_START();
448+ blend_bgrx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
449+ PROFILE_ACC_N(prof_no);
450+
451+ for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) {
452+ if (d0[i] != d1[i]) {
453+ printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len);
454+ }
455+ }
456+}
457+
458+
459+
460+int main(int argc, char *argv[])
461+{
462+ unsigned int i, j;
463+ uint32_t d0_buf[BUF_ALLOC];
464+ uint32_t s0_buf[BUF_ALLOC];
465+
466+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + 63) & ~63) + 0;
467+ uint32_t * const s0 = (uint32_t *)(((uintptr_t)s0_buf + 63) & ~63) + 0;
468+
469+ PROFILE_INIT();
470+
471+ for (i = 0; i != 255*255; ++i) {
472+ unsigned int a = div255(i);
473+ unsigned int b = (i + 127)/255;
474+ unsigned int c = (i + 126)/255;
475+ if (a != b && a != c)
476+ printf("%d/255: %d != %d/%d\n", i, a, b, c);
477+ }
478+
479+ for (i = 0; i != BUF_ALLOC; ++i) {
480+ d0_buf[i] = 0xff00 | i;
481+ s0_buf[i] = (i << 24) | 0x40ffc0;
482+ }
483+
484+ for (i = 0; i != 256; ++i) {
485+ test_line(d0, 0, s0, 0, i, 256, -1);
486+ }
487+ for (i = 0; i != 256; ++i) {
488+ test_line(d0, 0, s0, 0, 128, i, -1);
489+ }
490+
491+ for (j = 0; j != 16; ++j) {
492+ for (i = 0; i != 256; ++i) {
493+ test_line(d0, j & 3, s0, j >> 2, i, 256, j);
494+ }
495+ PROFILE_PRINTF_N(j);
496+ PROFILE_CLEAR_N(j);
497+ }
498+ printf("Done 1\n");
499+
500+ for (i = 0; i != 256; ++i) {
501+ test_line2(d0, 0, s0, 0, i, 256, -1);
502+ }
503+ for (i = 0; i != 256; ++i) {
504+ test_line2(d0, 0, s0, 0, 128, i, -1);
505+ }
506+
507+ for (j = 0; j != 16; ++j) {
508+ for (i = 0; i != 256; ++i) {
509+ test_line2(d0, j & 3, s0, j >> 2, i, 256, j);
510+ }
511+ PROFILE_PRINTF_N(j);
512+ }
513+ printf("Done 2\n");
514+
515+ return 0;
516+}
517+
518--- a/modules/hw/mmal/codec.c
519+++ b/modules/hw/mmal/codec.c
520@@ -26,267 +26,443 @@
521 #include "config.h"
522 #endif
523
524+#include <stdatomic.h>
525+
526 #include <vlc_common.h>
527-#include <vlc_atomic.h>
528 #include <vlc_plugin.h>
529 #include <vlc_codec.h>
530+#include <vlc_filter.h>
531 #include <vlc_threads.h>
532
533-#include <bcm_host.h>
534 #include <interface/mmal/mmal.h>
535 #include <interface/mmal/util/mmal_util.h>
536 #include <interface/mmal/util/mmal_default_components.h>
537
538+#include <interface/vcsm/user-vcsm.h>
539+
540+#include "mmal_cma.h"
541 #include "mmal_picture.h"
542
543+#include "subpic.h"
544+#include "blend_rgba_neon.h"
545+
546+#define TRACE_ALL 0
547+
548+#define OPT_TO_FROM_ZC 0
549+
550 /*
551 * This seems to be a bit high, but reducing it causes instabilities
552 */
553 #define NUM_EXTRA_BUFFERS 5
554+//#define NUM_EXTRA_BUFFERS 10
555 #define NUM_DECODER_BUFFER_HEADERS 30
556
557-#define MIN_NUM_BUFFERS_IN_TRANSIT 2
558+#define CONVERTER_BUFFERS 4 // Buffers on the output of the converter
559+
560+#define MMAL_SLICE_HEIGHT 16
561+#define MMAL_ALIGN_W 32
562+#define MMAL_ALIGN_H 16
563
564 #define MMAL_OPAQUE_NAME "mmal-opaque"
565 #define MMAL_OPAQUE_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.")
566 #define MMAL_OPAQUE_LONGTEXT N_("Decode frames directly into RPI VideoCore instead of host memory. This option must only be used with the MMAL video output plugin.")
567
568-static int OpenDecoder(decoder_t *dec);
569-static void CloseDecoder(decoder_t *dec);
570-
571-vlc_module_begin()
572- set_shortname(N_("MMAL decoder"))
573- set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
574- set_capability("video decoder", 90)
575- add_shortcut("mmal_decoder")
576- add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
577- set_callbacks(OpenDecoder, CloseDecoder)
578-vlc_module_end()
579+#define MMAL_RESIZE_NAME "mmal-resize"
580+#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.")
581+#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.")
582+
583+#define MMAL_ISP_NAME "mmal-isp"
584+#define MMAL_ISP_TEXT N_("Use mmal isp rather than hvs.")
585+#define MMAL_ISP_LONGTEXT N_("Use mmal isp rather than hvs. This may be faster but has no blend.")
586
587-struct decoder_sys_t {
588- bool opaque;
589+typedef struct decoder_sys_t
590+{
591 MMAL_COMPONENT_T *component;
592 MMAL_PORT_T *input;
593 MMAL_POOL_T *input_pool;
594 MMAL_PORT_T *output;
595- MMAL_POOL_T *output_pool; /* only used for non-opaque mode */
596+ hw_mmal_port_pool_ref_t *ppr;
597 MMAL_ES_FORMAT_T *output_format;
598- vlc_sem_t sem;
599
600+ MMAL_STATUS_T err_stream;
601 bool b_top_field_first;
602 bool b_progressive;
603
604+ bool b_flushed;
605+
606+ vcsm_init_type_t vcsm_init_type;
607+
608+ // Lock to avoid pic update & allocate happenening simultainiously
609+ // * We should be able to arrange life s.t. this isn't needed
610+ // but while we are confused apply belt & braces
611+ vlc_mutex_t pic_lock;
612+
613 /* statistics */
614- int output_in_transit;
615- int input_in_transit;
616 atomic_bool started;
617-};
618+} decoder_sys_t;
619
620-/* Utilities */
621-static int change_output_format(decoder_t *dec);
622-static int send_output_buffer(decoder_t *dec);
623-static void fill_output_port(decoder_t *dec);
624-
625-/* VLC decoder callback */
626-static int decode(decoder_t *dec, block_t *block);
627-static void flush_decoder(decoder_t *dec);
628-
629-/* MMAL callbacks */
630-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
631-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
632-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
633
634-static int OpenDecoder(decoder_t *dec)
635-{
636- int ret = VLC_SUCCESS;
637- decoder_sys_t *sys;
638- MMAL_PARAMETER_UINT32_T extra_buffers;
639- MMAL_STATUS_T status;
640+typedef struct supported_mmal_enc_s {
641+ struct {
642+ MMAL_PARAMETER_HEADER_T header;
643+ MMAL_FOURCC_T encodings[64];
644+ } supported;
645+ int n;
646+} supported_mmal_enc_t;
647+
648+#define SUPPORTED_MMAL_ENC_INIT \
649+{ \
650+ {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, \
651+ -1 \
652+}
653
654- if (dec->fmt_in.i_codec != VLC_CODEC_MPGV &&
655- dec->fmt_in.i_codec != VLC_CODEC_H264)
656- return VLC_EGENERIC;
657+static supported_mmal_enc_t supported_decode_in_enc = SUPPORTED_MMAL_ENC_INIT;
658
659- sys = calloc(1, sizeof(decoder_sys_t));
660- if (!sys) {
661- ret = VLC_ENOMEM;
662- goto out;
663+static bool is_enc_supported(supported_mmal_enc_t * const support, const MMAL_FOURCC_T fcc)
664+{
665+ int i;
666+
667+ if (fcc == 0)
668+ return false;
669+ if (support->n == -1)
670+ return true; // Unknown - say OK
671+ for (i = 0; i < support->n; ++i) {
672+ if (support->supported.encodings[i] == fcc)
673+ return true;
674 }
675- dec->p_sys = sys;
676+ return false;
677+}
678
679- sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME);
680- bcm_host_init();
681+static bool set_and_test_enc_supported(supported_mmal_enc_t * const support, MMAL_PORT_T * port, const MMAL_FOURCC_T fcc)
682+{
683+ if (support->n >= 0)
684+ /* already done */;
685+ else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&support->supported) != MMAL_SUCCESS)
686+ support->n = 0;
687+ else
688+ support->n = (support->supported.header.size - sizeof(support->supported.header)) /
689+ sizeof(support->supported.encodings[0]);
690
691- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
692- if (status != MMAL_SUCCESS) {
693- msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
694- MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
695- ret = VLC_EGENERIC;
696- goto out;
697- }
698+ return is_enc_supported(support, fcc);
699+}
700
701- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
702- status = mmal_port_enable(sys->component->control, control_port_cb);
703- if (status != MMAL_SUCCESS) {
704- msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
705- sys->component->control->name, status, mmal_status_to_string(status));
706- ret = VLC_EGENERIC;
707- goto out;
708+static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc)
709+{
710+ switch (fcc){
711+ case VLC_CODEC_MJPG:
712+ return MMAL_ENCODING_MJPEG;
713+ case VLC_CODEC_MP1V:
714+ return MMAL_ENCODING_MP1V;
715+ case VLC_CODEC_MPGV:
716+ case VLC_CODEC_MP2V:
717+ return MMAL_ENCODING_MP2V;
718+ case VLC_CODEC_H263:
719+ return MMAL_ENCODING_H263;
720+ case VLC_CODEC_MP4V:
721+ return MMAL_ENCODING_MP4V;
722+ case VLC_CODEC_H264:
723+ return MMAL_ENCODING_H264;
724+ case VLC_CODEC_VP6:
725+ return MMAL_ENCODING_VP6;
726+ case VLC_CODEC_VP8:
727+ return MMAL_ENCODING_VP8;
728+ case VLC_CODEC_WMV1:
729+ return MMAL_ENCODING_WMV1;
730+ case VLC_CODEC_WMV2:
731+ return MMAL_ENCODING_WMV2;
732+ case VLC_CODEC_WMV3:
733+ return MMAL_ENCODING_WMV3;
734+ case VLC_CODEC_VC1:
735+ return MMAL_ENCODING_WVC1;
736+ case VLC_CODEC_THEORA:
737+ return MMAL_ENCODING_THEORA;
738+ default:
739+ break;
740 }
741+ return 0;
742+}
743
744- sys->input = sys->component->input[0];
745- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
746- if (dec->fmt_in.i_codec == VLC_CODEC_MPGV)
747- sys->input->format->encoding = MMAL_ENCODING_MP2V;
748- else
749- sys->input->format->encoding = MMAL_ENCODING_H264;
750+static MMAL_FOURCC_T pic_to_slice_mmal_fourcc(const MMAL_FOURCC_T fcc)
751+{
752+ switch (fcc){
753+ case MMAL_ENCODING_I420:
754+ return MMAL_ENCODING_I420_SLICE;
755+ case MMAL_ENCODING_I422:
756+ return MMAL_ENCODING_I422_SLICE;
757+ case MMAL_ENCODING_ARGB:
758+ return MMAL_ENCODING_ARGB_SLICE;
759+ case MMAL_ENCODING_RGBA:
760+ return MMAL_ENCODING_RGBA_SLICE;
761+ case MMAL_ENCODING_ABGR:
762+ return MMAL_ENCODING_ABGR_SLICE;
763+ case MMAL_ENCODING_BGRA:
764+ return MMAL_ENCODING_BGRA_SLICE;
765+ case MMAL_ENCODING_RGB16:
766+ return MMAL_ENCODING_RGB16_SLICE;
767+ case MMAL_ENCODING_RGB24:
768+ return MMAL_ENCODING_RGB24_SLICE;
769+ case MMAL_ENCODING_RGB32:
770+ return MMAL_ENCODING_RGB32_SLICE;
771+ case MMAL_ENCODING_BGR16:
772+ return MMAL_ENCODING_BGR16_SLICE;
773+ case MMAL_ENCODING_BGR24:
774+ return MMAL_ENCODING_BGR24_SLICE;
775+ case MMAL_ENCODING_BGR32:
776+ return MMAL_ENCODING_BGR32_SLICE;
777+ default:
778+ break;
779+ }
780+ return 0;
781+}
782
783- if (dec->fmt_in.i_codec == VLC_CODEC_H264) {
784- if (dec->fmt_in.i_extra > 0) {
785- status = mmal_format_extradata_alloc(sys->input->format,
786- dec->fmt_in.i_extra);
787- if (status == MMAL_SUCCESS) {
788- memcpy(sys->input->format->extradata, dec->fmt_in.p_extra,
789- dec->fmt_in.i_extra);
790- sys->input->format->extradata_size = dec->fmt_in.i_extra;
791- } else {
792- msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)",
793- sys->input->name, status, mmal_status_to_string(status));
794- }
795+#define DEBUG_SQUARES 0
796+#if DEBUG_SQUARES
797+static void draw_square(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t val)
798+{
799+ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x;
800+ unsigned int i;
801+ for (i = 0; i != h; ++i) {
802+ unsigned int j;
803+ for (j = 0; j != w; ++j) {
804+ p[j] = val;
805 }
806+ p += pic_stride;
807 }
808+}
809+#endif
810
811- status = mmal_port_format_commit(sys->input);
812- if (status != MMAL_SUCCESS) {
813- msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
814- sys->input->name, status, mmal_status_to_string(status));
815- ret = VLC_EGENERIC;
816- goto out;
817+#if 0
818+static inline void draw_line(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int len, int inc)
819+{
820+ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x;
821+ while (len-- != 0) {
822+ *p = ~0U;
823+ p += inc;
824 }
825- sys->input->buffer_size = sys->input->buffer_size_recommended;
826- sys->input->buffer_num = sys->input->buffer_num_recommended;
827+}
828
829- status = mmal_port_enable(sys->input, input_port_cb);
830- if (status != MMAL_SUCCESS) {
831- msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
832- sys->input->name, status, mmal_status_to_string(status));
833- ret = VLC_EGENERIC;
834- goto out;
835- }
836
837- sys->output = sys->component->output[0];
838- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
839+static void draw_corners(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
840+{
841+ const unsigned int len = 20;
842+ draw_line(pic_buf, pic_stride, x, y, len, 1);
843+ draw_line(pic_buf, pic_stride, x, y, len, pic_stride);
844+ draw_line(pic_buf, pic_stride, x + w - 1, y, len, -1);
845+ draw_line(pic_buf, pic_stride, x + w - 1, y, len, pic_stride);
846+ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -1);
847+ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -(int)pic_stride);
848+ draw_line(pic_buf, pic_stride, x, y + h - 1, len, 1);
849+ draw_line(pic_buf, pic_stride, x, y + h - 1, len, -(int)pic_stride);
850+}
851+#endif
852
853- if (sys->opaque) {
854- extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
855- extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
856- extra_buffers.value = NUM_EXTRA_BUFFERS;
857- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
858- if (status != MMAL_SUCCESS) {
859- msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
860- status, mmal_status_to_string(status));
861- ret = VLC_EGENERIC;
862- goto out;
863- }
864+static MMAL_RATIONAL_T
865+rationalize_sar(unsigned int num, unsigned int den)
866+{
867+ static const unsigned int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 0};
868+ const unsigned int * p = primes;
869
870- msg_Dbg(dec, "Activate zero-copy for output port");
871- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
872- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
873- 1
874- };
875+ // If either num or den is 0 then return a well formed "unknown"
876+ if (num == 0 || den == 0) {
877+ return (MMAL_RATIONAL_T){.num = 0, .den = 0};
878+ }
879
880- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
881- if (status != MMAL_SUCCESS) {
882- msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
883- sys->output->name, status, mmal_status_to_string(status));
884- goto out;
885+ while (*p != 0 && num >= *p && den >= *p) {
886+ if (num % *p != 0 || den % *p != 0)
887+ ++p;
888+ else {
889+ num /= *p;
890+ den /= *p;
891 }
892 }
893+ return (MMAL_RATIONAL_T){.num = num, .den = den};
894+}
895
896- status = mmal_port_enable(sys->output, output_port_cb);
897- if (status != MMAL_SUCCESS) {
898- msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)",
899- sys->output->name, status, mmal_status_to_string(status));
900- ret = VLC_EGENERIC;
901- goto out;
902- }
903+// Buffer either attached to pic or released
904+static picture_t * alloc_opaque_pic(decoder_t * const dec, MMAL_BUFFER_HEADER_T * const buf)
905+{
906+ decoder_sys_t *const dec_sys = dec->p_sys;
907
908- status = mmal_component_enable(sys->component);
909- if (status != MMAL_SUCCESS) {
910- msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
911- sys->component->name, status, mmal_status_to_string(status));
912- ret = VLC_EGENERIC;
913- goto out;
914+ vlc_mutex_lock(&dec_sys->pic_lock);
915+ picture_t * const pic = decoder_NewPicture(dec);
916+ vlc_mutex_unlock(&dec_sys->pic_lock);
917+
918+ if (pic == NULL)
919+ goto fail1;
920+
921+ if (buf->length == 0) {
922+ msg_Err(dec, "%s: Empty buffer", __func__);
923+ goto fail2;
924 }
925
926- sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
927+ if ((pic->context = hw_mmal_gen_context(buf, dec_sys->ppr)) == NULL)
928+ goto fail2;
929
930- if (sys->opaque) {
931- dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
932- dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
933- } else {
934- dec->fmt_out.i_codec = VLC_CODEC_I420;
935- dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
936+ buf_to_pic_copy_props(pic, buf);
937+
938+#if TRACE_ALL
939+ msg_Dbg(dec, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
940+#endif
941+
942+ return pic;
943+
944+fail2:
945+ picture_Release(pic);
946+fail1:
947+ // Recycle rather than release to avoid buffer starvation if NewPic fails
948+ hw_mmal_port_pool_ref_recycle(dec_sys->ppr, buf);
949+ return NULL;
950+}
951+
952+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
953+{
954+ decoder_t *dec = (decoder_t *)port->userdata;
955+ MMAL_STATUS_T status;
956+
957+#if TRACE_ALL
958+ msg_Dbg(dec, "<<< %s: cmd=%d, data=%p", __func__, buffer->cmd, buffer->data);
959+#endif
960+
961+ if (buffer->cmd == MMAL_EVENT_ERROR) {
962+ status = *(uint32_t *)buffer->data;
963+ dec->p_sys->err_stream = status;
964+ msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
965+ mmal_status_to_string(status));
966 }
967
968- dec->pf_decode = decode;
969- dec->pf_flush = flush_decoder;
970+ mmal_buffer_header_release(buffer);
971+}
972
973- vlc_sem_init(&sys->sem, 0);
974+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
975+{
976+ block_t * const block = (block_t *)buffer->user_data;
977
978-out:
979- if (ret != VLC_SUCCESS)
980- CloseDecoder(dec);
981+ (void)port; // Unused
982
983- return ret;
984+#if TRACE_ALL
985+ msg_Dbg((decoder_t *)port->userdata, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
986+ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
987+#endif
988+
989+ mmal_buffer_header_reset(buffer);
990+ mmal_buffer_header_release(buffer);
991+
992+ if (block != NULL)
993+ block_Release(block);
994 }
995
996-static void CloseDecoder(decoder_t *dec)
997+static void decoder_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
998 {
999- decoder_sys_t *sys = dec->p_sys;
1000- MMAL_BUFFER_HEADER_T *buffer;
1001+ decoder_t * const dec = (decoder_t *)port->userdata;
1002
1003- if (!sys)
1004+ if (buffer->cmd == 0 && buffer->length != 0)
1005+ {
1006+#if TRACE_ALL
1007+ msg_Dbg(dec, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
1008+ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
1009+#endif
1010+
1011+ picture_t *pic = alloc_opaque_pic(dec, buffer);
1012+#if TRACE_ALL
1013+ msg_Dbg(dec, "flags=%#x, video flags=%#x", buffer->flags, buffer->type->video.flags);
1014+#endif
1015+ if (pic == NULL)
1016+ msg_Err(dec, "Failed to allocate new picture");
1017+ else
1018+ decoder_QueueVideo(dec, pic);
1019+ // Buffer released or attached to pic - do not release again
1020 return;
1021+ }
1022
1023- if (sys->component && sys->component->control->is_enabled)
1024- mmal_port_disable(sys->component->control);
1025+ if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED)
1026+ {
1027+ decoder_sys_t * const sys = dec->p_sys;
1028+ MMAL_EVENT_FORMAT_CHANGED_T * const fmt = mmal_event_format_changed_get(buffer);
1029+ MMAL_ES_FORMAT_T * const format = mmal_format_alloc();
1030
1031- if (sys->input && sys->input->is_enabled)
1032- mmal_port_disable(sys->input);
1033+ if (format == NULL)
1034+ msg_Err(dec, "Failed to allocate new format");
1035+ else
1036+ {
1037+ mmal_format_full_copy(format, fmt->format);
1038+ format->encoding = MMAL_ENCODING_OPAQUE;
1039
1040- if (sys->output && sys->output->is_enabled)
1041- mmal_port_disable(sys->output);
1042+ // If no PAR in the stream - see if we've got one from the demux
1043+ if (format->es->video.par.den <= 0 || format->es->video.par.num <= 0) {
1044+ unsigned int n = dec->fmt_in.video.i_sar_num;
1045+ unsigned int d = dec->fmt_in.video.i_sar_den;
1046+
1047+ if (n == 0 || d == 0) {
1048+ // Guesswork required
1049+ const unsigned int w = format->es->video.width;
1050+ const unsigned int h = format->es->video.height;
1051+ if ((w == 704 || w == 720) && (h == 480 || h == 576)) {
1052+ // Very likely SD 4:3
1053+ n = w * 3;
1054+ d = h * 4;
1055+ }
1056+ else
1057+ {
1058+ // Otherwise guess SAR 1:1
1059+ n = 1;
1060+ d = 1;
1061+ }
1062+ }
1063
1064- if (sys->component && sys->component->is_enabled)
1065- mmal_component_disable(sys->component);
1066+ format->es->video.par = rationalize_sar(n, d);
1067+ }
1068
1069- if (sys->input_pool)
1070- mmal_pool_destroy(sys->input_pool);
1071+ if (sys->output_format != NULL)
1072+ mmal_format_free(sys->output_format);
1073
1074- if (sys->output_format)
1075- mmal_format_free(sys->output_format);
1076+ sys->output_format = format;
1077+ }
1078+ }
1079+ else if (buffer->cmd != 0) {
1080+ char buf0[5];
1081+ msg_Warn(dec, "Unexpected output cb event: %s", str_fourcc(buf0, buffer->cmd));
1082+ }
1083
1084- if (sys->output_pool)
1085- mmal_pool_destroy(sys->output_pool);
1086+ // If we get here then we were flushing (cmd == 0 && len == 0) or
1087+ // that was an EVENT - in either case we want to release the buffer
1088+ // back to its pool rather than recycle it.
1089+ mmal_buffer_header_reset(buffer);
1090+ buffer->user_data = NULL;
1091+ mmal_buffer_header_release(buffer);
1092+}
1093
1094- if (sys->component)
1095- mmal_component_release(sys->component);
1096
1097- vlc_sem_destroy(&sys->sem);
1098- free(sys);
1099
1100- bcm_host_deinit();
1101+static void fill_output_port(decoder_t *dec)
1102+{
1103+ decoder_sys_t *sys = dec->p_sys;
1104+
1105+ if (decoder_UpdateVideoFormat(dec) != 0)
1106+ {
1107+ // If we have a new format don't bother stuffing the buffer
1108+ // We should get a reset RSN
1109+#if TRACE_ALL
1110+ msg_Dbg(dec, "%s: Updated", __func__);
1111+#endif
1112+
1113+ return;
1114+ }
1115+
1116+ hw_mmal_port_pool_ref_fill(sys->ppr);
1117+ return;
1118 }
1119
1120 static int change_output_format(decoder_t *dec)
1121 {
1122 MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type;
1123- decoder_sys_t *sys = dec->p_sys;
1124+ decoder_sys_t * const sys = dec->p_sys;
1125 MMAL_STATUS_T status;
1126- int pool_size;
1127 int ret = 0;
1128
1129+#if TRACE_ALL
1130+ msg_Dbg(dec, "%s: <<<", __func__);
1131+#endif
1132+
1133 if (atomic_load(&sys->started)) {
1134 mmal_format_full_copy(sys->output->format, sys->output_format);
1135 status = mmal_port_format_commit(sys->output);
1136@@ -300,7 +476,9 @@ static int change_output_format(decoder_
1137 }
1138
1139 port_reset:
1140+#if TRACE_ALL
1141 msg_Dbg(dec, "%s: Do full port reset", __func__);
1142+#endif
1143 status = mmal_port_disable(sys->output);
1144 if (status != MMAL_SUCCESS) {
1145 msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)",
1146@@ -310,6 +488,7 @@ port_reset:
1147 }
1148
1149 mmal_format_full_copy(sys->output->format, sys->output_format);
1150+
1151 status = mmal_port_format_commit(sys->output);
1152 if (status != MMAL_SUCCESS) {
1153 msg_Err(dec, "Failed to commit output format (status=%"PRIx32" %s)",
1154@@ -318,18 +497,10 @@ port_reset:
1155 goto out;
1156 }
1157
1158- if (sys->opaque) {
1159- sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
1160- pool_size = NUM_DECODER_BUFFER_HEADERS;
1161- } else {
1162- sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended,
1163- MIN_NUM_BUFFERS_IN_TRANSIT);
1164- pool_size = sys->output->buffer_num;
1165- }
1166-
1167+ sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
1168 sys->output->buffer_size = sys->output->buffer_size_recommended;
1169
1170- status = mmal_port_enable(sys->output, output_port_cb);
1171+ status = mmal_port_enable(sys->output, decoder_output_cb);
1172 if (status != MMAL_SUCCESS) {
1173 msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)",
1174 status, mmal_status_to_string(status));
1175@@ -338,25 +509,14 @@ port_reset:
1176 }
1177
1178 if (!atomic_load(&sys->started)) {
1179- if (!sys->opaque) {
1180- sys->output_pool = mmal_port_pool_create(sys->output, pool_size, 0);
1181- msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num);
1182- }
1183-
1184 atomic_store(&sys->started, true);
1185
1186 /* we need one picture from vout for each buffer header on the output
1187 * port */
1188- dec->i_extra_picture_buffers = pool_size;
1189-
1190- /* remove what VLC core reserves as it is part of the pool_size
1191- * already */
1192- if (dec->fmt_in.i_codec == VLC_CODEC_H264)
1193- dec->i_extra_picture_buffers -= 19;
1194- else
1195- dec->i_extra_picture_buffers -= 3;
1196-
1197+ dec->i_extra_picture_buffers = 10;
1198+#if TRACE_ALL
1199 msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers);
1200+#endif
1201 }
1202
1203 apply_fmt:
1204@@ -366,8 +526,8 @@ apply_fmt:
1205 dec->fmt_out.video.i_y_offset = sys->output->format->es->video.crop.y;
1206 dec->fmt_out.video.i_visible_width = sys->output->format->es->video.crop.width;
1207 dec->fmt_out.video.i_visible_height = sys->output->format->es->video.crop.height;
1208- dec->fmt_out.video.i_sar_num = sys->output->format->es->video.par.num;
1209- dec->fmt_out.video.i_sar_den = sys->output->format->es->video.par.den;
1210+ dec->fmt_out.video.i_sar_num = sys->output_format->es->video.par.num; // SAR can be killed by commit
1211+ dec->fmt_out.video.i_sar_den = sys->output_format->es->video.par.den;
1212 dec->fmt_out.video.i_frame_rate = sys->output->format->es->video.frame_rate.num;
1213 dec->fmt_out.video.i_frame_rate_base = sys->output->format->es->video.frame_rate.den;
1214
1215@@ -382,12 +542,19 @@ apply_fmt:
1216 sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive);
1217 sys->b_top_field_first = sys->b_progressive ? true :
1218 (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst);
1219+#if TRACE_ALL
1220 msg_Dbg(dec, "Detected %s%s video (%d)",
1221 sys->b_progressive ? "progressive" : "interlaced",
1222 sys->b_progressive ? "" : (sys->b_top_field_first ? " tff" : " bff"),
1223 interlace_type.eMode);
1224+#endif
1225 }
1226
1227+ // Tell the rest of the world we have changed format
1228+ vlc_mutex_lock(&sys->pic_lock);
1229+ ret = decoder_UpdateVideoFormat(dec);
1230+ vlc_mutex_unlock(&sys->pic_lock);
1231+
1232 out:
1233 mmal_format_free(sys->output_format);
1234 sys->output_format = NULL;
1235@@ -395,144 +562,85 @@ out:
1236 return ret;
1237 }
1238
1239-static int send_output_buffer(decoder_t *dec)
1240+static MMAL_STATUS_T
1241+set_extradata_and_commit(decoder_t * const dec, decoder_sys_t * const sys)
1242 {
1243- decoder_sys_t *sys = dec->p_sys;
1244- MMAL_BUFFER_HEADER_T *buffer;
1245- picture_sys_t *p_sys;
1246- picture_t *picture = NULL;
1247 MMAL_STATUS_T status;
1248- unsigned buffer_size = 0;
1249- int ret = 0;
1250
1251- if (!sys->output->is_enabled)
1252- return VLC_EGENERIC;
1253-
1254- /* If local output pool is allocated, use it - this is only the case for
1255- * non-opaque modes */
1256- if (sys->output_pool) {
1257- buffer = mmal_queue_get(sys->output_pool->queue);
1258- if (!buffer) {
1259- msg_Warn(dec, "Failed to get new buffer");
1260- return VLC_EGENERIC;
1261- }
1262- }
1263-
1264- if (!decoder_UpdateVideoFormat(dec))
1265- picture = decoder_NewPicture(dec);
1266- if (!picture) {
1267- msg_Warn(dec, "Failed to get new picture");
1268- ret = -1;
1269- goto err;
1270- }
1271-
1272- p_sys = picture->p_sys;
1273- for (int i = 0; i < picture->i_planes; i++)
1274- buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch;
1275-
1276- if (sys->output_pool) {
1277- mmal_buffer_header_reset(buffer);
1278- buffer->alloc_size = sys->output->buffer_size;
1279- if (buffer_size < sys->output->buffer_size) {
1280- msg_Err(dec, "Retrieved picture with too small data block (%d < %d)",
1281- buffer_size, sys->output->buffer_size);
1282- ret = VLC_EGENERIC;
1283- goto err;
1284- }
1285-
1286- if (!sys->opaque)
1287- buffer->data = picture->p[0].p_pixels;
1288- } else {
1289- buffer = p_sys->buffer;
1290- if (!buffer) {
1291- msg_Warn(dec, "Picture has no buffer attached");
1292- picture_Release(picture);
1293- return VLC_EGENERIC;
1294- }
1295- buffer->data = p_sys->buffer->data;
1296- }
1297- buffer->user_data = picture;
1298- buffer->cmd = 0;
1299-
1300- status = mmal_port_send_buffer(sys->output, buffer);
1301+ status = mmal_port_format_commit(sys->input);
1302 if (status != MMAL_SUCCESS) {
1303- msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)",
1304- status, mmal_status_to_string(status));
1305- ret = -1;
1306- goto err;
1307- }
1308- atomic_fetch_add(&sys->output_in_transit, 1);
1309-
1310- return ret;
1311-
1312-err:
1313- if (picture)
1314- picture_Release(picture);
1315- if (sys->output_pool && buffer) {
1316- buffer->data = NULL;
1317- mmal_buffer_header_release(buffer);
1318+ msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
1319+ sys->input->name, status, mmal_status_to_string(status));
1320 }
1321- return ret;
1322+ return status;
1323 }
1324
1325-static void fill_output_port(decoder_t *dec)
1326+static MMAL_STATUS_T decoder_send_extradata(decoder_t * const dec, decoder_sys_t *const sys)
1327 {
1328- decoder_sys_t *sys = dec->p_sys;
1329-
1330- unsigned max_buffers_in_transit = 0;
1331- int buffers_available = 0;
1332- int buffers_to_send = 0;
1333- int i;
1334+ if (dec->fmt_in.i_codec == VLC_CODEC_H264 &&
1335+ dec->fmt_in.i_extra > 0)
1336+ {
1337+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->input_pool->queue);
1338+ MMAL_STATUS_T status;
1339+
1340+ mmal_buffer_header_reset(buf);
1341+ buf->cmd = 0;
1342+ buf->user_data = NULL;
1343+ buf->alloc_size = sys->input->buffer_size;
1344+ buf->length = dec->fmt_in.i_extra;
1345+ buf->data = dec->fmt_in.p_extra;
1346+ buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG;
1347
1348- if (sys->output_pool) {
1349- max_buffers_in_transit = __MAX(sys->output_pool->headers_num,
1350- MIN_NUM_BUFFERS_IN_TRANSIT);
1351- buffers_available = mmal_queue_length(sys->output_pool->queue);
1352- } else {
1353- max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS;
1354- buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit);
1355+ status = mmal_port_send_buffer(sys->input, buf);
1356+ if (status != MMAL_SUCCESS) {
1357+ msg_Err(dec, "Failed to send extradata buffer to input port (status=%"PRIx32" %s)",
1358+ status, mmal_status_to_string(status));
1359+ return status;
1360+ }
1361 }
1362- buffers_to_send = max_buffers_in_transit - atomic_load(&sys->output_in_transit);
1363
1364- if (buffers_to_send > buffers_available)
1365- buffers_to_send = buffers_available;
1366-
1367-#ifndef NDEBUG
1368- msg_Dbg(dec, "Send %d buffers to output port (available: %d, "
1369- "in_transit: %d, buffer_num: %d)",
1370- buffers_to_send, buffers_available,
1371- atomic_load(&sys->output_in_transit),
1372- sys->output->buffer_num);
1373-#endif
1374- for (i = 0; i < buffers_to_send; ++i)
1375- if (send_output_buffer(dec) < 0)
1376- break;
1377+ return MMAL_SUCCESS;
1378 }
1379
1380 static void flush_decoder(decoder_t *dec)
1381 {
1382- decoder_sys_t *sys = dec->p_sys;
1383- MMAL_BUFFER_HEADER_T *buffer;
1384- MMAL_STATUS_T status;
1385+ decoder_sys_t *const sys = dec->p_sys;
1386
1387- msg_Dbg(dec, "Flushing decoder ports...");
1388- mmal_port_flush(sys->output);
1389- mmal_port_flush(sys->input);
1390-
1391- while (atomic_load(&sys->output_in_transit) ||
1392- atomic_load(&sys->input_in_transit))
1393- vlc_sem_wait(&sys->sem);
1394+#if TRACE_ALL
1395+ msg_Dbg(dec, "%s: <<<", __func__);
1396+#endif
1397+
1398+ if (!sys->b_flushed) {
1399+ mmal_port_disable(sys->input);
1400+ mmal_port_disable(sys->output);
1401+ // We can leave the input disabled, but we want the output enabled
1402+ // in order to sink any buffers returning from other modules
1403+ mmal_port_enable(sys->output, decoder_output_cb);
1404+ sys->b_flushed = true;
1405+ }
1406+#if TRACE_ALL
1407+ msg_Dbg(dec, "%s: >>>", __func__);
1408+#endif
1409 }
1410
1411 static int decode(decoder_t *dec, block_t *block)
1412 {
1413 decoder_sys_t *sys = dec->p_sys;
1414 MMAL_BUFFER_HEADER_T *buffer;
1415- bool need_flush = false;
1416 uint32_t len;
1417- uint32_t flags = 0;
1418+ uint32_t flags = MMAL_BUFFER_HEADER_FLAG_FRAME_START;
1419 MMAL_STATUS_T status;
1420
1421+#if TRACE_ALL
1422+ msg_Dbg(dec, "<<< %s: %lld/%lld", __func__, block == NULL ? -1LL : block->i_dts, block == NULL ? -1LL : block->i_pts);
1423+#endif
1424+
1425+ if (sys->err_stream != MMAL_SUCCESS) {
1426+ msg_Err(dec, "MMAL error reported by ctrl");
1427+ flush_decoder(dec);
1428+ return VLCDEC_ECRITICAL; /// I think they are all fatal
1429+ }
1430+
1431 /*
1432 * Configure output port if necessary
1433 */
1434@@ -541,18 +649,50 @@ static int decode(decoder_t *dec, block_
1435 msg_Err(dec, "Failed to change output port format");
1436 }
1437
1438- if (!block)
1439- goto out;
1440+ if (block == NULL)
1441+ return VLCDEC_SUCCESS;
1442
1443 /*
1444 * Check whether full flush is required
1445 */
1446- if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
1447+ if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
1448+#if TRACE_ALL
1449+ msg_Dbg(dec, "%s: >>> Discontinuity", __func__);
1450+#endif
1451 flush_decoder(dec);
1452+ }
1453+
1454+ if (block->i_buffer == 0)
1455+ {
1456 block_Release(block);
1457 return VLCDEC_SUCCESS;
1458 }
1459
1460+ // Reenable stuff if the last thing we did was flush
1461+ if (!sys->output->is_enabled &&
1462+ (status = mmal_port_enable(sys->output, decoder_output_cb)) != MMAL_SUCCESS)
1463+ {
1464+ msg_Err(dec, "Output port enable failed");
1465+ goto fail;
1466+ }
1467+
1468+ if (!sys->input->is_enabled)
1469+ {
1470+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
1471+ goto fail;
1472+
1473+ if ((status = mmal_port_enable(sys->input, input_port_cb)) != MMAL_SUCCESS)
1474+ {
1475+ msg_Err(dec, "Input port enable failed");
1476+ goto fail;
1477+ }
1478+
1479+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
1480+ goto fail;
1481+ }
1482+
1483+ // *** We cannot get a picture to put the result in 'till we have
1484+ // reported the size & the output stages have been set up
1485 if (atomic_load(&sys->started))
1486 fill_output_port(dec);
1487
1488@@ -563,18 +703,21 @@ static int decode(decoder_t *dec, block_
1489 if (block->i_flags & BLOCK_FLAG_CORRUPTED)
1490 flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED;
1491
1492- while (block && block->i_buffer > 0) {
1493- buffer = mmal_queue_timedwait(sys->input_pool->queue, 100);
1494+ while (block != NULL)
1495+ {
1496+ buffer = mmal_queue_wait(sys->input_pool->queue);
1497 if (!buffer) {
1498 msg_Err(dec, "Failed to retrieve buffer header for input data");
1499- need_flush = true;
1500- break;
1501+ goto fail;
1502 }
1503+
1504 mmal_buffer_header_reset(buffer);
1505 buffer->cmd = 0;
1506- buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts;
1507+ buffer->pts = block->i_pts != VLC_TICK_INVALID ? block->i_pts :
1508+ block->i_dts != VLC_TICK_INVALID ? block->i_dts : MMAL_TIME_UNKNOWN;
1509 buffer->dts = block->i_dts;
1510 buffer->alloc_size = sys->input->buffer_size;
1511+ buffer->user_data = NULL;
1512
1513 len = block->i_buffer;
1514 if (len > buffer->alloc_size)
1515@@ -585,94 +728,1808 @@ static int decode(decoder_t *dec, block_
1516 block->i_buffer -= len;
1517 buffer->length = len;
1518 if (block->i_buffer == 0) {
1519+ flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
1520+ if (block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE) {
1521+ msg_Dbg(dec, "EOS sent");
1522+ flags |= MMAL_BUFFER_HEADER_FLAG_EOS;
1523+ }
1524 buffer->user_data = block;
1525 block = NULL;
1526 }
1527 buffer->flags = flags;
1528
1529+#if TRACE_ALL
1530+ msg_Dbg(dec, "%s: -- Send buffer: cmd=%d, data=%p, size=%d, len=%d, offset=%d, flags=%#x, pts=%lld, dts=%lld", __func__,\
1531+ buffer->cmd, buffer->data, buffer->alloc_size, buffer->length, buffer->offset,
1532+ buffer->flags, (long long)buffer->pts, (long long)buffer->dts);
1533+#endif
1534 status = mmal_port_send_buffer(sys->input, buffer);
1535 if (status != MMAL_SUCCESS) {
1536 msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)",
1537 status, mmal_status_to_string(status));
1538- break;
1539+ goto fail;
1540 }
1541- atomic_fetch_add(&sys->input_in_transit, 1);
1542+
1543+ // Reset flushed flag once we have sent a buf
1544+ sys->b_flushed = false;
1545+ flags &= ~MMAL_BUFFER_HEADER_FLAG_FRAME_START;
1546 }
1547+ return VLCDEC_SUCCESS;
1548
1549-out:
1550- if (need_flush)
1551- flush_decoder(dec);
1552+fail:
1553+ flush_decoder(dec);
1554+ return VLCDEC_ECRITICAL;
1555
1556- return VLCDEC_SUCCESS;
1557 }
1558
1559-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1560+
1561+static void CloseDecoder(decoder_t *dec)
1562 {
1563- decoder_t *dec = (decoder_t *)port->userdata;
1564+ decoder_sys_t *sys = dec->p_sys;
1565+
1566+#if TRACE_ALL
1567+ msg_Dbg(dec, "%s: <<<", __func__);
1568+#endif
1569+
1570+ if (!sys)
1571+ return;
1572+
1573+ if (sys->component != NULL) {
1574+ if (sys->input->is_enabled)
1575+ mmal_port_disable(sys->input);
1576+
1577+ if (sys->output->is_enabled)
1578+ mmal_port_disable(sys->output);
1579+
1580+ if (sys->component->control->is_enabled)
1581+ mmal_port_disable(sys->component->control);
1582+
1583+ if (sys->component->is_enabled)
1584+ mmal_component_disable(sys->component);
1585+
1586+ mmal_component_release(sys->component);
1587+ }
1588+
1589+ if (sys->input_pool != NULL)
1590+ mmal_pool_destroy(sys->input_pool);
1591+
1592+ if (sys->output_format != NULL)
1593+ mmal_format_free(sys->output_format);
1594+
1595+ hw_mmal_port_pool_ref_release(sys->ppr, false);
1596+
1597+ cma_vcsm_exit(sys->vcsm_init_type);
1598+
1599+ vlc_mutex_destroy(&sys->pic_lock);
1600+ free(sys);
1601+}
1602+
1603+static int OpenDecoder(decoder_t *dec)
1604+{
1605+ int ret = VLC_EGENERIC;
1606+ decoder_sys_t *sys;
1607 MMAL_STATUS_T status;
1608+ const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec);
1609+
1610+#if TRACE_ALL || 1
1611+ {
1612+ char buf1[5], buf2[5], buf2a[5];
1613+ char buf3[5], buf4[5];
1614+ MMAL_RATIONAL_T r = rationalize_sar(dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den);
1615+
1616+ msg_Dbg(dec, "%s: <<< (%s/%s)[%s] %dx%d %d/%d=%d/%d o:%#x -> (%s/%s) %dx%d %d/%d o:%#x", __func__,
1617+ str_fourcc(buf1, dec->fmt_in.i_codec),
1618+ str_fourcc(buf2, dec->fmt_in.video.i_chroma),
1619+ str_fourcc(buf2a, in_fcc),
1620+ dec->fmt_in.video.i_width, dec->fmt_in.video.i_height,
1621+ dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den,
1622+ r.num, r.den,
1623+ (int)dec->fmt_in.video.orientation,
1624+ str_fourcc(buf3, dec->fmt_out.i_codec),
1625+ str_fourcc(buf4, dec->fmt_out.video.i_chroma),
1626+ dec->fmt_out.video.i_width, dec->fmt_out.video.i_height,
1627+ dec->fmt_out.video.i_sar_num, dec->fmt_out.video.i_sar_den,
1628+ (int)dec->fmt_out.video.orientation);
1629+ }
1630+#endif
1631+
1632+ if (!is_enc_supported(&supported_decode_in_enc, in_fcc))
1633+ return VLC_EGENERIC;
1634+
1635+ sys = calloc(1, sizeof(decoder_sys_t));
1636+ if (!sys) {
1637+ ret = VLC_ENOMEM;
1638+ goto fail;
1639+ }
1640+ dec->p_sys = sys;
1641+ vlc_mutex_init(&sys->pic_lock);
1642+
1643+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
1644+ msg_Err(dec, "VCSM init failed");
1645+ goto fail;
1646+ }
1647+ msg_Info(dec, "VCSM init succeeded: %s", cma_vcsm_init_str(sys->vcsm_init_type));
1648+
1649+ sys->err_stream = MMAL_SUCCESS;
1650+
1651+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
1652+ if (status != MMAL_SUCCESS) {
1653+ msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
1654+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
1655+ goto fail;
1656+ }
1657+
1658+ sys->input = sys->component->input[0];
1659+ sys->output = sys->component->output[0];
1660+
1661+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
1662+ sys->input->format->encoding = in_fcc;
1663+
1664+ if (!set_and_test_enc_supported(&supported_decode_in_enc, sys->input, in_fcc)) {
1665+#if TRACE_ALL
1666+ char cbuf[5];
1667+ msg_Dbg(dec, "Format not supported: %s", str_fourcc(cbuf, in_fcc));
1668+#endif
1669+ goto fail;
1670+ }
1671+
1672+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
1673+ status = mmal_port_enable(sys->component->control, control_port_cb);
1674+ if (status != MMAL_SUCCESS) {
1675+ msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
1676+ sys->component->control->name, status, mmal_status_to_string(status));
1677+ goto fail;
1678+ }
1679+
1680+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
1681+ goto fail;
1682+
1683+ sys->input->buffer_size = sys->input->buffer_size_recommended;
1684+ sys->input->buffer_num = sys->input->buffer_num_recommended;
1685+
1686+ status = mmal_port_enable(sys->input, input_port_cb);
1687+ if (status != MMAL_SUCCESS) {
1688+ msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
1689+ sys->input->name, status, mmal_status_to_string(status));
1690+ goto fail;
1691+ }
1692+
1693+ // Set vanishingly unlikely shape (or at least crop)
1694+ // to ensure that we get a resolution changed event
1695+ // Small wxh are rejected (128x128 is rejected) so pick a
1696+ // plausible size.
1697+ // Crop doesn't seem to be checked for being constrained by wxh
1698+ // so we could place it outside the pic to be sure that it is
1699+ // never matched but stick with something legal in case it is ever
1700+ // actually checked
1701+ sys->output->format->es->video.height = 256;
1702+ sys->output->format->es->video.width = 256;
1703+ sys->output->format->es->video.crop.height = 4;
1704+ sys->output->format->es->video.crop.width = 2;
1705+ sys->output->format->es->video.crop.x = 66;
1706+ sys->output->format->es->video.crop.y = 88;
1707+
1708+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(dec), &sys->ppr,
1709+ sys->output, NUM_EXTRA_BUFFERS, decoder_output_cb)) != MMAL_SUCCESS)
1710+ goto fail;
1711+
1712+ status = mmal_component_enable(sys->component);
1713+ if (status != MMAL_SUCCESS) {
1714+ msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
1715+ sys->component->name, status, mmal_status_to_string(status));
1716+ goto fail;
1717+ }
1718+
1719+ if ((sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
1720+ {
1721+ msg_Err(dec, "Failed to create input pool");
1722+ goto fail;
1723+ }
1724+
1725+ sys->b_flushed = true;
1726+
1727+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
1728+ goto fail;
1729+
1730+ // Given no better ideas at this point copy input format to output
1731+ // This also copies container stuff (such as orientation) that we do not
1732+ // decode from the ES but may be important to display
1733+ video_format_Copy(&dec->fmt_out.video, &dec->fmt_in.video);
1734+ dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
1735+ dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
1736+
1737+
1738+ dec->pf_decode = decode;
1739+ dec->pf_flush = flush_decoder;
1740+
1741+#if TRACE_ALL
1742+ msg_Dbg(dec, ">>> %s: ok", __func__);
1743+#endif
1744+ return 0;
1745+
1746+fail:
1747+ CloseDecoder(dec);
1748+#if TRACE_ALL
1749+msg_Dbg(dec, ">>> %s: FAIL: ret=%d", __func__, ret);
1750+#endif
1751+ return ret;
1752+}
1753+
1754+// ----------------------------
1755+
1756+#define CONV_MAX_LATENCY 1 // In frames
1757+
1758+typedef struct pic_fifo_s {
1759+ picture_t * head;
1760+ picture_t * tail;
1761+} pic_fifo_t;
1762+
1763+static inline picture_t * pic_fifo_get(pic_fifo_t * const pf)
1764+{
1765+ picture_t * const pic = pf->head;;
1766+ if (pic != NULL) {
1767+ pf->head = pic->p_next;
1768+ pic->p_next = NULL;
1769+ }
1770+ return pic;
1771+}
1772+
1773+static inline picture_t * pic_fifo_get_all(pic_fifo_t * const pf)
1774+{
1775+ picture_t * const pic = pf->head;;
1776+ pf->head = NULL;
1777+ return pic;
1778+}
1779+
1780+static inline void pic_fifo_release_all(pic_fifo_t * const pf)
1781+{
1782+ picture_t * pic;
1783+ while ((pic = pic_fifo_get(pf)) != NULL) {
1784+ picture_Release(pic);
1785+ }
1786+}
1787+
1788+static inline void pic_fifo_init(pic_fifo_t * const pf)
1789+{
1790+ pf->head = NULL;
1791+ pf->tail = NULL; // Not strictly needed
1792+}
1793+
1794+static inline void pic_fifo_put(pic_fifo_t * const pf, picture_t * pic)
1795+{
1796+ pic->p_next = NULL;
1797+ if (pf->head == NULL)
1798+ pf->head = pic;
1799+ else
1800+ pf->tail->p_next = pic;
1801+ pf->tail = pic;
1802+}
1803+
1804+#define SUBS_MAX 3
1805+
1806+typedef enum filter_resizer_e {
1807+ FILTER_RESIZER_RESIZER,
1808+ FILTER_RESIZER_ISP,
1809+ FILTER_RESIZER_HVS
1810+} filter_resizer_t;
1811+
1812+typedef struct conv_frame_stash_s
1813+{
1814+ mtime_t pts;
1815+ MMAL_BUFFER_HEADER_T * sub_bufs[SUBS_MAX];
1816+} conv_frame_stash_t;
1817+
1818+typedef struct filter_sys_t {
1819+ filter_resizer_t resizer_type;
1820+ MMAL_COMPONENT_T *component;
1821+ MMAL_PORT_T *input;
1822+ MMAL_PORT_T *output;
1823+ MMAL_POOL_T *out_pool; // Free output buffers
1824+ MMAL_POOL_T *in_pool; // Input pool to get BH for replication
1825+
1826+ cma_buf_pool_t * cma_in_pool;
1827+ cma_buf_pool_t * cma_out_pool;
1828+
1829+ subpic_reg_stash_t subs[SUBS_MAX];
1830+
1831+ pic_fifo_t ret_pics;
1832+
1833+ unsigned int pic_n;
1834+ vlc_sem_t sem;
1835+ vlc_mutex_t lock;
1836+
1837+ MMAL_STATUS_T err_stream;
1838+
1839+ bool needs_copy_in;
1840+ bool is_cma;
1841+ bool is_sliced;
1842+ bool out_fmt_set;
1843+ const char * component_name;
1844+ MMAL_PORT_BH_CB_T in_port_cb_fn;
1845+ MMAL_PORT_BH_CB_T out_port_cb_fn;
1846+
1847+ uint64_t frame_seq;
1848+ conv_frame_stash_t stash[16];
1849+
1850+ // Slice specific tracking stuff
1851+ struct {
1852+ pic_fifo_t pics;
1853+ unsigned int line; // Lines filled
1854+ } slice;
1855+
1856+ vcsm_init_type_t vcsm_init_type;
1857+} filter_sys_t;
1858+
1859+
1860+static MMAL_STATUS_T pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic)
1861+{
1862+ unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3;
1863+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
1864+
1865+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
1866+ es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format);
1867+ es_fmt->encoding_variant = 0;
1868+
1869+ // Fill in crop etc.
1870+ hw_mmal_vlc_fmt_to_mmal_fmt(es_fmt, &pic->format);
1871+ // Override width / height with strides if appropriate
1872+ if (bpp != 0) {
1873+ v_fmt->width = pic->p[0].i_pitch / bpp;
1874+ v_fmt->height = pic->p[0].i_lines;
1875+ }
1876+ return MMAL_SUCCESS;
1877+}
1878+
1879+
1880+static MMAL_STATUS_T conv_enable_in(filter_t * const p_filter, filter_sys_t * const sys)
1881+{
1882+ MMAL_STATUS_T err = MMAL_SUCCESS;
1883+
1884+ if (!sys->input->is_enabled &&
1885+ (err = mmal_port_enable(sys->input, sys->in_port_cb_fn)) != MMAL_SUCCESS)
1886+ {
1887+ msg_Err(p_filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
1888+ sys->input->name, err, mmal_status_to_string(err));
1889+ }
1890+ return err;
1891+}
1892+
1893+static MMAL_STATUS_T conv_enable_out(filter_t * const p_filter, filter_sys_t * const sys)
1894+{
1895+ MMAL_STATUS_T err = MMAL_SUCCESS;
1896+
1897+ if (sys->is_cma)
1898+ {
1899+ if (sys->cma_out_pool == NULL &&
1900+ (sys->cma_out_pool = cma_buf_pool_new(CONVERTER_BUFFERS, CONVERTER_BUFFERS, true, "mmal_resizer")) == NULL)
1901+ {
1902+ msg_Err(p_filter, "Failed to alloc cma buf pool");
1903+ return MMAL_ENOMEM;
1904+ }
1905+ }
1906+ else
1907+ {
1908+ cma_buf_pool_deletez(&sys->cma_out_pool);
1909+ }
1910+
1911+ if (!sys->output->is_enabled &&
1912+ (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS)
1913+ {
1914+ msg_Err(p_filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
1915+ sys->output->name, err, mmal_status_to_string(err));
1916+ }
1917+ return err;
1918+}
1919+
1920+static void conv_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1921+{
1922+ filter_t * const p_filter = (filter_t *)port->userdata;
1923+
1924+#if TRACE_ALL
1925+ msg_Dbg(p_filter, "%s: <<< cmd=%d, data=%p, pic=%p", __func__, buffer->cmd, buffer->data, buffer->user_data);
1926+#endif
1927
1928 if (buffer->cmd == MMAL_EVENT_ERROR) {
1929- status = *(uint32_t *)buffer->data;
1930- msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
1931+ MMAL_STATUS_T status = *(uint32_t *)buffer->data;
1932+
1933+ p_filter->p_sys->err_stream = status;
1934+
1935+ msg_Err(p_filter, "MMAL error %"PRIx32" \"%s\"", status,
1936 mmal_status_to_string(status));
1937 }
1938
1939 mmal_buffer_header_release(buffer);
1940 }
1941
1942-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1943+static void conv_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
1944 {
1945- block_t *block = (block_t *)buffer->user_data;
1946- decoder_t *dec = (decoder_t *)port->userdata;
1947- decoder_sys_t *sys = dec->p_sys;
1948- buffer->user_data = NULL;
1949+#if TRACE_ALL
1950+ picture_context_t * ctx = buf->user_data;
1951+// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
1952+
1953+ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, ctx=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
1954+ __func__, buf->cmd, ctx, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
1955+#else
1956+ VLC_UNUSED(port);
1957+#endif
1958+
1959+ mmal_buffer_header_release(buf);
1960+
1961+#if TRACE_ALL
1962+ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
1963+#endif
1964+}
1965+
1966+static void conv_out_q_pic(filter_sys_t * const sys, picture_t * const pic)
1967+{
1968+ pic->p_next = NULL;
1969+
1970+ vlc_mutex_lock(&sys->lock);
1971+ pic_fifo_put(&sys->ret_pics, pic);
1972+ vlc_mutex_unlock(&sys->lock);
1973
1974- mmal_buffer_header_release(buffer);
1975- if (block)
1976- block_Release(block);
1977- atomic_fetch_sub(&sys->input_in_transit, 1);
1978 vlc_sem_post(&sys->sem);
1979 }
1980
1981-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
1982+static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
1983 {
1984- decoder_t *dec = (decoder_t *)port->userdata;
1985- decoder_sys_t *sys = dec->p_sys;
1986- picture_t *picture;
1987- MMAL_EVENT_FORMAT_CHANGED_T *fmt;
1988- MMAL_ES_FORMAT_T *format;
1989-
1990- if (buffer->cmd == 0) {
1991- picture = (picture_t *)buffer->user_data;
1992- if (buffer->length > 0) {
1993- picture->date = buffer->pts;
1994- picture->b_progressive = sys->b_progressive;
1995- picture->b_top_field_first = sys->b_top_field_first;
1996- decoder_QueueVideo(dec, picture);
1997- } else {
1998- picture_Release(picture);
1999- if (sys->output_pool) {
2000- buffer->user_data = NULL;
2001- buffer->alloc_size = 0;
2002- buffer->data = NULL;
2003- mmal_buffer_header_release(buffer);
2004- }
2005- }
2006- atomic_fetch_sub(&sys->output_in_transit, 1);
2007- vlc_sem_post(&sys->sem);
2008- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
2009- fmt = mmal_event_format_changed_get(buffer);
2010+ filter_t * const p_filter = (filter_t *)port->userdata;
2011+ filter_sys_t * const sys = p_filter->p_sys;
2012
2013- format = mmal_format_alloc();
2014- mmal_format_full_copy(format, fmt->format);
2015+#if TRACE_ALL
2016+ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld/%lld", __func__,
2017+ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size,
2018+ (long long)buf->pts, (long long)sys->stash[(unsigned int)(buf->pts & 0xf)].pts);
2019+#endif
2020+ if (buf->cmd == 0) {
2021+ picture_t * const pic = (picture_t *)buf->user_data;
2022
2023- if (sys->opaque)
2024- format->encoding = MMAL_ENCODING_OPAQUE;
2025+ if (pic == NULL) {
2026+ msg_Err(p_filter, "%s: Buffer has no attached picture", __func__);
2027+ }
2028+ else if (buf->data == NULL || buf->length == 0)
2029+ {
2030+#if TRACE_ALL
2031+ msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
2032+#endif
2033+ }
2034+ else
2035+ {
2036+ buf_to_pic_copy_props(pic, buf);
2037+
2038+ // Set pic data pointers from buf aux info now it has it
2039+ if (sys->is_cma) {
2040+ if (cma_pic_set_data(pic, sys->output->format, buf) != VLC_SUCCESS)
2041+ msg_Err(p_filter, "Failed to set data");
2042+ }
2043+
2044+// draw_corners(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, pic->p[0].i_visible_pitch / 4, pic->p[0].i_visible_lines);
2045+#if DEBUG_SQUARES
2046+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, 32, 32, 0xffff0000);
2047+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 32, 0, 32, 32, 0xff00ff00);
2048+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 64, 0, 32, 32, 0xff0000ff);
2049+#endif
2050+
2051+ buf->user_data = NULL; // Responsability for this pic no longer with buffer
2052+ conv_out_q_pic(sys, pic);
2053+ }
2054+ }
2055+
2056+ mmal_buffer_header_release(buf);
2057+}
2058+
2059+
2060+static void slice_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
2061+{
2062+ filter_t * const p_filter = (filter_t *)port->userdata;
2063+ filter_sys_t * const sys = p_filter->p_sys;
2064+
2065+#if TRACE_ALL
2066+ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld", __func__,
2067+ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, (long long)buf->pts);
2068+#endif
2069+
2070+ if (buf->cmd != 0)
2071+ {
2072+ mmal_buffer_header_release(buf);
2073+ return;
2074+ }
2075+
2076+ if (buf->data == NULL || buf->length == 0)
2077+ {
2078+#if TRACE_ALL
2079+ msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
2080+#endif
2081+ }
2082+ else
2083+ {
2084+ // Got slice
2085+ picture_t *pic = sys->slice.pics.head;
2086+ const unsigned int scale_lines = sys->output->format->es->video.height; // Expected lines of callback
2087+
2088+ if (pic == NULL) {
2089+ msg_Err(p_filter, "No output picture");
2090+ goto fail;
2091+ }
2092+
2093+ // Copy lines
2094+ // * single plane only - fix for I420
2095+ {
2096+ const unsigned int scale_n = __MIN(scale_lines - sys->slice.line, MMAL_SLICE_HEIGHT);
2097+ const unsigned int pic_lines = pic->p[0].i_lines;
2098+ const unsigned int copy_n = sys->slice.line + scale_n <= pic_lines ? scale_n :
2099+ sys->slice.line >= pic_lines ? 0 :
2100+ pic_lines - sys->slice.line;
2101+
2102+ const unsigned int src_stride = buf->type->video.pitch[0];
2103+ const unsigned int dst_stride = pic->p[0].i_pitch;
2104+ uint8_t *dst = pic->p[0].p_pixels + sys->slice.line * dst_stride;
2105+ const uint8_t *src = buf->data + buf->type->video.offset[0];
2106+
2107+ if (src_stride == dst_stride) {
2108+ if (copy_n != 0)
2109+ memcpy(dst, src, src_stride * copy_n);
2110+ }
2111+ else {
2112+ unsigned int i;
2113+ for (i = 0; i != copy_n; ++i) {
2114+ memcpy(dst, src, __MIN(dst_stride, src_stride));
2115+ dst += dst_stride;
2116+ src += src_stride;
2117+ }
2118+ }
2119+ sys->slice.line += scale_n;
2120+ }
2121+
2122+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) {
2123+
2124+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) {
2125+ // Stuff doesn't add up...
2126+ msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags);
2127+ goto fail;
2128+ }
2129+ else {
2130+ sys->slice.line = 0;
2131+
2132+ vlc_mutex_lock(&sys->lock);
2133+ pic_fifo_get(&sys->slice.pics); // Remove head from Q
2134+ vlc_mutex_unlock(&sys->lock);
2135+
2136+ buf_to_pic_copy_props(pic, buf);
2137+ conv_out_q_pic(sys, pic);
2138+ }
2139+ }
2140+ }
2141+
2142+ // Put back
2143+ buf->user_data = NULL; // Zap here to make sure we can't reuse later
2144+ mmal_buffer_header_reset(buf);
2145+
2146+ if (mmal_port_send_buffer(sys->output, buf) != MMAL_SUCCESS) {
2147+ mmal_buffer_header_release(buf);
2148+ }
2149+ return;
2150+
2151+fail:
2152+ sys->err_stream = MMAL_EIO;
2153+ vlc_sem_post(&sys->sem); // If we were waiting then break us out - the flush should fix sem values
2154+}
2155+
2156+
2157+static void conv_flush(filter_t * p_filter)
2158+{
2159+ filter_sys_t * const sys = p_filter->p_sys;
2160+ unsigned int i;
2161+
2162+#if TRACE_ALL
2163+ msg_Dbg(p_filter, "<<< %s", __func__);
2164+#endif
2165+
2166+ if (sys->resizer_type == FILTER_RESIZER_HVS)
2167+ {
2168+ for (i = 0; i != SUBS_MAX; ++i) {
2169+ hw_mmal_subpic_flush(VLC_OBJECT(p_filter), sys->subs + i);
2170+ }
2171+ }
2172+
2173+ if (sys->input != NULL && sys->input->is_enabled)
2174+ mmal_port_disable(sys->input);
2175+
2176+ if (sys->output != NULL && sys->output->is_enabled)
2177+ mmal_port_disable(sys->output);
2178+
2179+// cma_buf_pool_deletez(&sys->cma_out_pool);
2180+
2181+ // Free up anything we may have already lying around
2182+ // Don't need lock as the above disables should have prevented anything
2183+ // happening in the background
2184+
2185+ for (i = 0; i != 16; ++i) {
2186+ conv_frame_stash_t *const stash = sys->stash + i;
2187+ unsigned int sub_no;
2188+
2189+ stash->pts = MMAL_TIME_UNKNOWN;
2190+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2191+ if (stash->sub_bufs[sub_no] != NULL) {
2192+ mmal_buffer_header_release(stash->sub_bufs[sub_no]);
2193+ stash->sub_bufs[sub_no] = NULL;
2194+ }
2195+ }
2196+ }
2197+
2198+ pic_fifo_release_all(&sys->slice.pics);
2199+ pic_fifo_release_all(&sys->ret_pics);
2200+
2201+ // Reset sem values - easiest & most reliable way is to just kill & re-init
2202+ vlc_sem_destroy(&sys->sem);
2203+ vlc_sem_init(&sys->sem, 0);
2204+ sys->pic_n = 0;
2205+
2206+ // Reset error status
2207+ sys->err_stream = MMAL_SUCCESS;
2208+
2209+#if TRACE_ALL
2210+ msg_Dbg(p_filter, ">>> %s", __func__);
2211+#endif
2212+}
2213+
2214+static void conv_stash_fixup(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const p_pic)
2215+{
2216+ conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf);
2217+ unsigned int sub_no;
2218+ VLC_UNUSED(p_filter);
2219+
2220+ p_pic->date = stash->pts;
2221+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2222+ if (stash->sub_bufs[sub_no] != NULL) {
2223+ // **** Do stashed blend
2224+ // **** Aaargh, bother... need to rescale subs too
2225+
2226+ mmal_buffer_header_release(stash->sub_bufs[sub_no]);
2227+ stash->sub_bufs[sub_no] = NULL;
2228+ }
2229+ }
2230+}
2231+
2232+// Output buffers may contain a pic ref on error or flush
2233+// Free it
2234+static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
2235+{
2236+ VLC_UNUSED(userdata);
2237+
2238+ picture_t * const pic = header->user_data;
2239+ header->user_data = NULL;
2240+
2241+ if (pic != NULL)
2242+ picture_Release(pic);
2243+
2244+ return MMAL_FALSE;
2245+}
2246+
2247+static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const pic)
2248+{
2249+ MMAL_STATUS_T status;
2250+
2251+ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
2252+ sys->output->format->type = MMAL_ES_TYPE_VIDEO;
2253+ sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
2254+ sys->output->format->encoding_variant = 0;
2255+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->output->format, &p_filter->fmt_out.video);
2256+
2257+ if (pic != NULL)
2258+ {
2259+ // Override default format width/height if we have a pic we need to match
2260+ if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS)
2261+ {
2262+ char cbuf[5];
2263+ msg_Err(p_filter, "Bad format desc: %s, pic=%p, bits=%d", str_fourcc(cbuf, pic->format.i_chroma), pic, pic->format.i_bits_per_pixel);
2264+ return status;
2265+ }
2266+
2267+ MMAL_VIDEO_FORMAT_T *fmt = &sys->output->format->es->video;
2268+ msg_Dbg(p_filter, "%s: %dx%d [(0,0) %dx%d]", __func__, fmt->width, fmt->height, fmt->crop.width, fmt->crop.height);
2269+ }
2270+
2271+ if (sys->is_sliced) {
2272+ // Override height for slice
2273+ sys->output->format->es->video.height = MMAL_SLICE_HEIGHT;
2274+ }
2275+
2276+ mmal_log_dump_format(sys->output->format);
2277+
2278+ status = mmal_port_format_commit(sys->output);
2279+ if (status != MMAL_SUCCESS) {
2280+ msg_Err(p_filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
2281+ sys->output->name, status, mmal_status_to_string(status));
2282+ return status;
2283+ }
2284+
2285+ sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended);
2286+ sys->output->buffer_size = sys->output->buffer_size_recommended;
2287+
2288+ if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS)
2289+ return status;
2290+
2291+ return MMAL_SUCCESS;
2292+}
2293+
2294+
2295+static picture_t *conv_get_out_pics(filter_sys_t * const sys)
2296+{
2297+ picture_t * ret_pics;
2298+
2299+ vlc_sem_wait(&sys->sem);
2300+
2301+ // Return a single pending buffer
2302+ vlc_mutex_lock(&sys->lock);
2303+ ret_pics = pic_fifo_get(&sys->ret_pics);
2304+ vlc_mutex_unlock(&sys->lock);
2305+
2306+ return ret_pics;
2307+}
2308+
2309+static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic)
2310+{
2311+ filter_sys_t * const sys = p_filter->p_sys;
2312+ picture_t * ret_pics = NULL;
2313+ MMAL_STATUS_T err;
2314+ const uint64_t frame_seq = ++sys->frame_seq;
2315+ conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf);
2316+ MMAL_BUFFER_HEADER_T * out_buf = NULL;
2317+
2318+#if TRACE_ALL
2319+ {
2320+ char dbuf0[5], dbuf1[5];
2321+ msg_Dbg(p_filter, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] sar:%d/%d", __func__,
2322+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
2323+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
2324+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
2325+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
2326+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
2327+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
2328+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
2329+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
2330+ }
2331+#endif
2332+
2333+ if (sys->err_stream != MMAL_SUCCESS) {
2334+ goto stream_fail;
2335+ }
2336+
2337+ // Check pic fmt corresponds to what we have set up
2338+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
2339+ {
2340+ msg_Dbg(p_filter, "Reset input port format");
2341+
2342+ // HVS can take new formats without disable, others need it
2343+ if (sys->resizer_type != FILTER_RESIZER_HVS) {
2344+ // Extract any pending pic
2345+ if (sys->pic_n >= 2) {
2346+ ret_pics = conv_get_out_pics(sys);
2347+ // If pic_n == 1 then we return without trying to get stuff
2348+ sys->pic_n = 1;
2349+ }
2350+ if (sys->input->is_enabled) {
2351+ if ((err = mmal_port_disable(sys->input)) != MMAL_SUCCESS)
2352+ msg_Warn(p_filter, "Format update disable failed: %s", mmal_status_to_string(err));
2353+ }
2354+ }
2355+
2356+// mmal_log_dump_port(sys->input);
2357+ if ((err = mmal_port_format_commit(sys->input)) != MMAL_SUCCESS)
2358+ msg_Warn(p_filter, "Format update commit failed: %s", mmal_status_to_string(err));
2359+
2360+ // (Re)enable if required will be done later
2361+ }
2362+
2363+ if (p_pic->context == NULL) {
2364+ // Can't have stashed subpics if not one of our pics
2365+ if (!sys->needs_copy_in)
2366+ msg_Dbg(p_filter, "%s: No context", __func__);
2367+ }
2368+ else if (sys->resizer_type == FILTER_RESIZER_HVS)
2369+ {
2370+ unsigned int sub_no = 0;
2371+
2372+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2373+ int rv;
2374+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter),
2375+ hw_mmal_pic_sub_buf_get(p_pic, sub_no),
2376+ sys->subs + sub_no,
2377+ &p_pic->format,
2378+ &sys->output->format->es->video.crop,
2379+ MMAL_DISPLAY_ROT0,
2380+ frame_seq)) == 0)
2381+ break;
2382+ else if (rv < 0)
2383+ goto fail;
2384+ }
2385+ }
2386+ else
2387+ {
2388+ unsigned int sub_no = 0;
2389+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
2390+ if ((stash->sub_bufs[sub_no] = hw_mmal_pic_sub_buf_get(p_pic, sub_no)) != NULL) {
2391+ mmal_buffer_header_acquire(stash->sub_bufs[sub_no]);
2392+ }
2393+ }
2394+ }
2395+
2396+ if (!sys->out_fmt_set) {
2397+ sys->out_fmt_set = true;
2398+
2399+ if (sys->is_sliced) {
2400+ // If zc then we will do stride conversion when we copy to arm side
2401+ // so no need to worry about actual pic dimensions here
2402+ if ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS)
2403+ goto fail;
2404+
2405+ sys->out_pool = mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size);
2406+ }
2407+ else {
2408+ picture_t *pic = filter_NewPicture(p_filter);
2409+ err = conv_set_output(p_filter, sys, pic);
2410+ picture_Release(pic);
2411+ if (err != MMAL_SUCCESS)
2412+ goto fail;
2413+
2414+ sys->out_pool = mmal_pool_create(sys->output->buffer_num, 0);
2415+ }
2416+
2417+ if (sys->out_pool == NULL) {
2418+ msg_Err(p_filter, "Failed to create output pool");
2419+ goto fail;
2420+ }
2421+ }
2422+
2423+ // Reenable stuff if the last thing we did was flush
2424+ if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS ||
2425+ (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
2426+ goto fail;
2427+
2428+ // We attach pic to buf before stuffing the output port
2429+ // We could attach the pic on output for cma, but it is a lot easier to keep
2430+ // the code common.
2431+ {
2432+ picture_t * const out_pic = filter_NewPicture(p_filter);
2433+
2434+ if (out_pic == NULL)
2435+ {
2436+ msg_Err(p_filter, "Failed to alloc required filter output pic");
2437+ goto fail;
2438+ }
2439+
2440+ out_pic->format.i_sar_den = p_filter->fmt_out.video.i_sar_den;
2441+ out_pic->format.i_sar_num = p_filter->fmt_out.video.i_sar_num;
2442+
2443+ if (sys->is_sliced) {
2444+ vlc_mutex_lock(&sys->lock);
2445+ pic_fifo_put(&sys->slice.pics, out_pic);
2446+ vlc_mutex_unlock(&sys->lock);
2447+
2448+ // Poke any returned pic buffers into output
2449+ // In general this should only happen immediately after enable
2450+ while ((out_buf = mmal_queue_get(sys->out_pool->queue)) != NULL)
2451+ mmal_port_send_buffer(sys->output, out_buf);
2452+ }
2453+ else
2454+ {
2455+ // 1 in - 1 out
2456+ if ((out_buf = mmal_queue_wait(sys->out_pool->queue)) == NULL)
2457+ {
2458+ msg_Err(p_filter, "Failed to get output buffer");
2459+ picture_Release(out_pic);
2460+ goto fail;
2461+ }
2462+ mmal_buffer_header_reset(out_buf);
2463+
2464+ // Attach out_pic to the buffer & ensure it is freed when the buffer is released
2465+ // On a good send callback the pic will be extracted to avoid this
2466+ out_buf->user_data = out_pic;
2467+ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, NULL);
2468+
2469+#if 0
2470+ {
2471+ char dbuf0[5];
2472+ msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d",
2473+ str_fourcc(dbuf0, out_pic->format.i_chroma),
2474+ out_pic->format.i_width, out_pic->format.i_height,
2475+ out_pic->format.i_x_offset, out_pic->format.i_y_offset,
2476+ out_pic->format.i_visible_width, out_pic->format.i_visible_height,
2477+ out_pic->format.i_sar_num, out_pic->format.i_sar_den);
2478+ }
2479+#endif
2480+
2481+ if (sys->is_cma) {
2482+ int rv;
2483+
2484+ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size);
2485+ if (cb == NULL) {
2486+ char dbuf0[5];
2487+ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d",
2488+ str_fourcc(dbuf0, out_pic->format.i_chroma),
2489+ sys->output->buffer_size);
2490+ goto fail;
2491+ }
2492+ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable
2493+ out_buf->data = (uint8_t *)vc_h;
2494+ out_buf->alloc_size = sys->output->buffer_size;
2495+
2496+ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS)
2497+ {
2498+ char dbuf0[5];
2499+ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d",
2500+ str_fourcc(dbuf0, out_pic->format.i_chroma),
2501+ rv);
2502+ cma_buf_unref(cb);
2503+ goto fail;
2504+ }
2505+ }
2506+ else {
2507+ out_buf->data = out_pic->p[0].p_pixels;
2508+ out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines;
2509+ //**** stride ????
2510+ }
2511+
2512+#if TRACE_ALL
2513+ msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld",
2514+ p_pic, out_buf->data, out_buf->user_data, out_buf->flags,
2515+ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts);
2516+#endif
2517+
2518+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
2519+ {
2520+ msg_Err(p_filter, "Send buffer to output failed");
2521+ goto fail;
2522+ }
2523+ out_buf = NULL;
2524+ }
2525+ }
2526+
2527+
2528+ // Stuff into input
2529+ // We assume the BH is already set up with values reflecting pic date etc.
2530+ stash->pts = p_pic->date;
2531+ {
2532+ MMAL_BUFFER_HEADER_T *const pic_buf = sys->needs_copy_in ?
2533+ hw_mmal_pic_buf_copied(p_pic, sys->in_pool, sys->input, sys->cma_in_pool) :
2534+ hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
2535+
2536+ // Whether or not we extracted the pic_buf we are done with the picture
2537+ picture_Release(p_pic);
2538+ p_pic = NULL;
2539+
2540+ if (pic_buf == NULL) {
2541+ msg_Err(p_filter, "Pic has no attached buffer");
2542+ goto fail;
2543+ }
2544+
2545+ pic_buf->pts = frame_seq;
2546+
2547+#if TRACE_ALL
2548+ msg_Dbg(p_filter, "In buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d/%d, pts=%lld",
2549+ p_pic, pic_buf->data, pic_buf->user_data, pic_buf->flags,
2550+ pic_buf->length, pic_buf->alloc_size, sys->input->buffer_size, (long long)pic_buf->pts);
2551+#endif
2552+
2553+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
2554+ {
2555+ msg_Err(p_filter, "Send buffer to input failed");
2556+ mmal_buffer_header_release(pic_buf);
2557+ goto fail;
2558+ }
2559+ }
2560+
2561+ // We have a 1 pic latency for everything except the 1st pic which we
2562+ // wait for.
2563+ // This means we get a single static pic out
2564+ if (sys->pic_n++ == 1) {
2565+#if TRACE_ALL
2566+ msg_Dbg(p_filter, ">>> %s: Pic1=%p", __func__, ret_pics);
2567+#endif
2568+ return ret_pics;
2569+ }
2570+
2571+ ret_pics = conv_get_out_pics(sys);
2572+
2573+ if (sys->err_stream != MMAL_SUCCESS)
2574+ goto stream_fail;
2575+
2576+ conv_stash_fixup(p_filter, sys, ret_pics);
2577+
2578+#if TRACE_ALL
2579+ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
2580+#endif
2581+
2582+ return ret_pics;
2583+
2584+stream_fail:
2585+ msg_Err(p_filter, "MMAL error reported by callback");
2586+fail:
2587+#if TRACE_ALL
2588+ msg_Err(p_filter, ">>> %s: FAIL", __func__);
2589+#endif
2590+ if (ret_pics != NULL)
2591+ picture_Release(ret_pics);
2592+ if (out_buf != NULL)
2593+ mmal_buffer_header_release(out_buf);
2594+ if (p_pic != NULL)
2595+ picture_Release(p_pic);
2596+ conv_flush(p_filter);
2597+ return NULL;
2598+}
2599+
2600+static void CloseConverter(vlc_object_t * obj)
2601+{
2602+ filter_t * const p_filter = (filter_t *)obj;
2603+ filter_sys_t * const sys = p_filter->p_sys;
2604+ unsigned int i;
2605+
2606+#if TRACE_ALL
2607+ msg_Dbg(obj, "<<< %s", __func__);
2608+#endif
2609+
2610+ if (sys == NULL)
2611+ return;
2612+
2613+ // Disables input & output ports
2614+ conv_flush(p_filter);
2615+
2616+ cma_buf_pool_deletez(&sys->cma_in_pool);
2617+ cma_buf_pool_deletez(&sys->cma_out_pool);
2618+
2619+ if (sys->component && sys->component->control->is_enabled)
2620+ mmal_port_disable(sys->component->control);
2621+
2622+ if (sys->component && sys->component->is_enabled)
2623+ mmal_component_disable(sys->component);
2624+
2625+ if (sys->resizer_type == FILTER_RESIZER_HVS)
2626+ {
2627+ for (i = 0; i != SUBS_MAX; ++i) {
2628+ hw_mmal_subpic_close(VLC_OBJECT(p_filter), sys->subs + i);
2629+ }
2630+ }
2631+
2632+ if (sys->out_pool)
2633+ {
2634+ if (sys->is_sliced)
2635+ mmal_port_pool_destroy(sys->output, sys->out_pool);
2636+ else
2637+ mmal_pool_destroy(sys->out_pool);
2638+ }
2639+
2640+ if (sys->in_pool != NULL)
2641+ mmal_pool_destroy(sys->in_pool);
2642+
2643+ if (sys->component)
2644+ mmal_component_release(sys->component);
2645+
2646+ cma_vcsm_exit(sys->vcsm_init_type);
2647+
2648+ vlc_sem_destroy(&sys->sem);
2649+ vlc_mutex_destroy(&sys->lock);
2650+
2651+ p_filter->p_sys = NULL;
2652+ free(sys);
2653+}
2654+
2655+
2656+static inline MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt)
2657+{
2658+ if (hw_mmal_chroma_is_mmal(fmt->i_chroma))
2659+ return vlc_to_mmal_video_fourcc(fmt);
2660+
2661+ if (fmt->i_chroma == VLC_CODEC_I420 ||
2662+ fmt->i_chroma == VLC_CODEC_I420_10L)
2663+ return MMAL_ENCODING_I420;
2664+
2665+ return 0;
2666+}
2667+
2668+static inline MMAL_FOURCC_T filter_enc_out(const video_format_t * const fmt)
2669+{
2670+ const MMAL_FOURCC_T mmes = vlc_to_mmal_video_fourcc(fmt);
2671+ // Can only copy out single plane stuff currently - this could be fixed!
2672+ return hw_mmal_chroma_is_mmal(fmt->i_chroma) || mmes != MMAL_ENCODING_I420 ? mmes : 0;
2673+}
2674+
2675+
2676+static int OpenConverter(vlc_object_t * obj)
2677+{
2678+ filter_t * const p_filter = (filter_t *)obj;
2679+ int ret = VLC_EGENERIC;
2680+ filter_sys_t *sys;
2681+ MMAL_STATUS_T status;
2682+ MMAL_FOURCC_T enc_out = filter_enc_out(&p_filter->fmt_out.video);
2683+ const MMAL_FOURCC_T enc_in = filter_enc_in(&p_filter->fmt_in.video);
2684+ bool use_resizer;
2685+ bool use_isp;
2686+ int gpu_mem;
2687+
2688+ // At least in principle we should deal with any mmal format as input
2689+ if (enc_in == 0 || enc_out == 0)
2690+ return VLC_EGENERIC;
2691+
2692+ // Can't transform
2693+ if (p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation)
2694+ return VLC_EGENERIC;
2695+
2696+ use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME);
2697+ use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME);
2698+
2699+retry:
2700+ // ** Make more generic by checking supported encs
2701+ //
2702+ // Must use ISP - HVS can't do this, nor can resizer
2703+ if (enc_in == MMAL_ENCODING_YUVUV64_10) {
2704+ // If resizer selected then just give up
2705+ if (use_resizer)
2706+ return VLC_EGENERIC;
2707+ // otherwise downgrade HVS to ISP
2708+ use_isp = true;
2709+ }
2710+ // HVS can't do I420
2711+ if (enc_out == MMAL_ENCODING_I420) {
2712+ use_isp = true;
2713+ }
2714+ // Only HVS can deal with SAND30
2715+ if (enc_in == MMAL_ENCODING_YUV10_COL) {
2716+ if (use_isp || use_resizer)
2717+ return VLC_EGENERIC;
2718+ }
2719
2720- sys->output_format = format;
2721
2722- mmal_buffer_header_release(buffer);
2723+ if (use_resizer) {
2724+ // use resizer overrides use_isp
2725+ use_isp = false;
2726+ }
2727+
2728+ // Check we have a sliced version of the fourcc if we want the resizer
2729+ if (use_resizer &&
2730+ (enc_out = pic_to_slice_mmal_fourcc(enc_out)) == 0) {
2731+ return VLC_EGENERIC;
2732+ }
2733+
2734+ gpu_mem = hw_mmal_get_gpu_mem();
2735+
2736+ {
2737+ char dbuf0[5], dbuf1[5], dbuf2[5], dbuf3[5];
2738+ msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s/%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d (gpu=%d)", __func__,
2739+ use_resizer ? "resize" : use_isp ? "isp" : "hvs",
2740+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), str_fourcc(dbuf2, enc_in),
2741+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
2742+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
2743+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
2744+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
2745+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), str_fourcc(dbuf3, enc_out),
2746+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
2747+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
2748+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
2749+ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
2750+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den,
2751+ gpu_mem);
2752+ }
2753+
2754+ sys = calloc(1, sizeof(filter_sys_t));
2755+ if (!sys) {
2756+ ret = VLC_ENOMEM;
2757+ goto fail;
2758+ }
2759+ p_filter->p_sys = sys;
2760+
2761+ // Init stuff the we destroy unconditionaly in Close first
2762+ vlc_mutex_init(&sys->lock);
2763+ vlc_sem_init(&sys->sem, 0);
2764+ sys->err_stream = MMAL_SUCCESS;
2765+ pic_fifo_init(&sys->ret_pics);
2766+ pic_fifo_init(&sys->slice.pics);
2767+
2768+ sys->needs_copy_in = !hw_mmal_chroma_is_mmal(p_filter->fmt_in.video.i_chroma);
2769+ sys->in_port_cb_fn = conv_input_port_cb;
2770+
2771+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
2772+ msg_Err(p_filter, "VCSM init failed");
2773+ goto fail;
2774+ }
2775+
2776+ if (use_resizer) {
2777+ sys->resizer_type = FILTER_RESIZER_RESIZER;
2778+ sys->is_sliced = true;
2779+ sys->component_name = MMAL_COMPONENT_DEFAULT_RESIZER;
2780+ sys->out_port_cb_fn = slice_output_port_cb;
2781+ }
2782+ else if (use_isp) {
2783+ sys->resizer_type = FILTER_RESIZER_ISP;
2784+ sys->is_sliced = false; // Copy directly into filter picture
2785+ sys->component_name = MMAL_COMPONENT_ISP_RESIZER;
2786+ sys->out_port_cb_fn = conv_output_port_cb;
2787 } else {
2788- mmal_buffer_header_release(buffer);
2789+ sys->resizer_type = FILTER_RESIZER_HVS;
2790+ sys->is_sliced = false; // Copy directly into filter picture
2791+ sys->component_name = MMAL_COMPONENT_HVS;
2792+ sys->out_port_cb_fn = conv_output_port_cb;
2793+ }
2794+ sys->is_cma = is_cma_buf_pic_chroma(p_filter->fmt_out.video.i_chroma);
2795+
2796+ status = mmal_component_create(sys->component_name, &sys->component);
2797+ if (status != MMAL_SUCCESS) {
2798+ if (!use_isp && !use_resizer) {
2799+ msg_Warn(p_filter, "Failed to rcreate HVS resizer - retrying with ISP");
2800+ CloseConverter(obj);
2801+ use_isp = true;
2802+ goto retry;
2803+ }
2804+ msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
2805+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
2806+ goto fail;
2807 }
2808+ sys->output = sys->component->output[0];
2809+ sys->input = sys->component->input[0];
2810+
2811+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
2812+ status = mmal_port_enable(sys->component->control, conv_control_port_cb);
2813+ if (status != MMAL_SUCCESS) {
2814+ msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
2815+ sys->component->control->name, status, mmal_status_to_string(status));
2816+ goto fail;
2817+ }
2818+
2819+ if (sys->needs_copy_in &&
2820+ (sys->cma_in_pool = cma_buf_pool_new(2, 2, true, "conv-copy-in")) == NULL)
2821+ {
2822+ msg_Err(p_filter, "Failed to allocate input CMA pool");
2823+ goto fail;
2824+ }
2825+
2826+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
2827+ sys->input->format->type = MMAL_ES_TYPE_VIDEO;
2828+ sys->input->format->encoding = enc_in;
2829+ sys->input->format->encoding_variant = MMAL_ENCODING_I420;
2830+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &p_filter->fmt_in.video);
2831+ port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, 1);
2832+
2833+ mmal_log_dump_format(sys->input->format);
2834+
2835+ status = mmal_port_format_commit(sys->input);
2836+ if (status != MMAL_SUCCESS) {
2837+ msg_Err(p_filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
2838+ sys->input->name, status, mmal_status_to_string(status));
2839+ goto fail;
2840+ }
2841+ sys->input->buffer_size = sys->input->buffer_size_recommended;
2842+ sys->input->buffer_num = NUM_DECODER_BUFFER_HEADERS;
2843+
2844+ if ((status = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
2845+ goto fail;
2846+
2847+ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced || sys->is_cma);
2848+
2849+ status = mmal_component_enable(sys->component);
2850+ if (status != MMAL_SUCCESS) {
2851+ msg_Err(p_filter, "Failed to enable component %s (status=%"PRIx32" %s)",
2852+ sys->component->name, status, mmal_status_to_string(status));
2853+ goto fail;
2854+ }
2855+
2856+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
2857+ {
2858+ msg_Err(p_filter, "Failed to create input pool");
2859+ goto fail;
2860+ }
2861+
2862+ if (sys->resizer_type == FILTER_RESIZER_HVS)
2863+ {
2864+ unsigned int i;
2865+ for (i = 0; i != SUBS_MAX; ++i) {
2866+ if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], -1, i + 1) != MMAL_SUCCESS)
2867+ {
2868+ msg_Err(p_filter, "Failed to open subpic %d", i);
2869+ goto fail;
2870+ }
2871+ }
2872+ }
2873+
2874+ p_filter->pf_video_filter = conv_filter;
2875+ p_filter->pf_flush = conv_flush;
2876+ // video_drain NIF in filter structure
2877+
2878+#if TRACE_ALL
2879+ msg_Dbg(p_filter, ">>> %s: ok", __func__);
2880+#endif
2881+
2882+ return VLC_SUCCESS;
2883+
2884+fail:
2885+ CloseConverter(obj);
2886+
2887+ if (!use_resizer && status == MMAL_ENOMEM) {
2888+ use_resizer = true;
2889+ msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer");
2890+ goto retry;
2891+ }
2892+
2893+#if TRACE_ALL
2894+ msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret);
2895+#endif
2896+ return ret;
2897+}
2898+
2899+#if OPT_TO_FROM_ZC
2900+//----------------------------------------------------------------------------
2901+//
2902+// Simple copy in to ZC
2903+
2904+typedef struct to_zc_sys_s {
2905+ vcsm_init_type_t vcsm_init_type;
2906+ cma_buf_pool_t * cma_out_pool;
2907+} to_zc_sys_t;
2908+
2909+
2910+static size_t buf_alloc_size(const vlc_fourcc_t i_chroma, const unsigned int width, const unsigned int height)
2911+{
2912+ const unsigned int pels = width * height;
2913+
2914+ switch (i_chroma)
2915+ {
2916+ case VLC_CODEC_MMAL_ZC_RGB32:
2917+ return pels * 4;
2918+ case VLC_CODEC_MMAL_ZC_I420:
2919+ return pels * 3 / 2;
2920+ default:
2921+ break;
2922+ }
2923+ return 0;
2924+}
2925+
2926+
2927+static picture_t *
2928+to_zc_filter(filter_t *p_filter, picture_t *in_pic)
2929+{
2930+ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys;
2931+#if TRACE_ALL
2932+ msg_Dbg(p_filter, "<<< %s", __func__);
2933+#endif
2934+
2935+ assert(p_filter->fmt_out.video.i_chroma == VLC_CODEC_MMAL_ZC_I420);
2936+
2937+ picture_t * const out_pic = filter_NewPicture(p_filter);
2938+ if (out_pic == NULL)
2939+ goto fail0;
2940+
2941+ MMAL_ES_SPECIFIC_FORMAT_T mm_vfmt = {.video={0}};
2942+ MMAL_ES_FORMAT_T mm_esfmt = {
2943+ .encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video),
2944+ .es = &mm_vfmt};
2945+
2946+ hw_mmal_vlc_fmt_to_mmal_fmt(&mm_esfmt, &p_filter->fmt_out.video);
2947+
2948+ const size_t buf_alloc = buf_alloc_size(p_filter->fmt_out.video.i_chroma,
2949+ mm_vfmt.video.width, mm_vfmt.video.height);
2950+ if (buf_alloc == 0)
2951+ goto fail1;
2952+ cma_buf_t *const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, buf_alloc);
2953+ if (cb == NULL)
2954+ goto fail1;
2955+
2956+ if (cma_buf_pic_attach(cb, out_pic) != VLC_SUCCESS)
2957+ goto fail2;
2958+ cma_pic_set_data(out_pic, &mm_esfmt, NULL);
2959+
2960+ hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), NULL, &mm_esfmt, in_pic);
2961+
2962+ // Copy pic properties
2963+ out_pic->date = in_pic->date;
2964+ out_pic->b_force = in_pic->b_force;
2965+ out_pic->b_progressive = in_pic->b_progressive;
2966+ out_pic->b_top_field_first = in_pic->b_top_field_first;
2967+ out_pic->i_nb_fields = in_pic->i_nb_fields;
2968+
2969+ picture_Release(in_pic);
2970+
2971+ return out_pic;
2972+
2973+fail2:
2974+ cma_buf_unref(cb);
2975+fail1:
2976+ picture_Release(out_pic);
2977+fail0:
2978+ picture_Release(in_pic);
2979+ return NULL;
2980+}
2981+
2982+static void to_zc_flush(filter_t * p_filter)
2983+{
2984+ VLC_UNUSED(p_filter);
2985 }
2986+
2987+static void CloseConverterToZc(vlc_object_t * obj)
2988+{
2989+ filter_t * const p_filter = (filter_t *)obj;
2990+ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys;
2991+
2992+ if (sys == NULL)
2993+ return;
2994+
2995+ p_filter->p_sys = NULL;
2996+
2997+ cma_buf_pool_deletez(&sys->cma_out_pool);
2998+ cma_vcsm_exit(sys->vcsm_init_type);
2999+
3000+ free(sys);
3001+}
3002+
3003+static bool to_zc_validate_fmt(const video_format_t * const f_in, const video_format_t * const f_out)
3004+{
3005+ if (!((f_in->i_chroma == VLC_CODEC_I420 || f_in->i_chroma == VLC_CODEC_I420_10L) &&
3006+ f_out->i_chroma == VLC_CODEC_MMAL_ZC_I420))
3007+ {
3008+ return false;
3009+ }
3010+ if (f_in->i_height != f_out->i_height ||
3011+ f_in->i_width != f_out->i_width)
3012+ {
3013+ return false;
3014+ }
3015+
3016+ return true;
3017+}
3018+
3019+static int OpenConverterToZc(vlc_object_t * obj)
3020+{
3021+ int ret = VLC_EGENERIC;
3022+ filter_t * const p_filter = (filter_t *)obj;
3023+
3024+ if (!to_zc_validate_fmt(&p_filter->fmt_in.video, &p_filter->fmt_out.video))
3025+ goto fail;
3026+
3027+ {
3028+ char dbuf0[5], dbuf1[5];
3029+ msg_Dbg(p_filter, "%s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d", __func__,
3030+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma),
3031+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
3032+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
3033+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
3034+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
3035+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma),
3036+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
3037+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
3038+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
3039+ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
3040+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
3041+ }
3042+
3043+ to_zc_sys_t * const sys = calloc(1, sizeof(*sys));
3044+ if (!sys) {
3045+ ret = VLC_ENOMEM;
3046+ goto fail;
3047+ }
3048+ p_filter->p_sys = (filter_sys_t *)sys;
3049+
3050+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
3051+ msg_Err(p_filter, "VCSM init failed");
3052+ goto fail;
3053+ }
3054+
3055+ if ((sys->cma_out_pool = cma_buf_pool_new(5, 5, true, "conv-to-zc")) == NULL)
3056+ {
3057+ msg_Err(p_filter, "Failed to allocate input CMA pool");
3058+ goto fail;
3059+ }
3060+
3061+ p_filter->pf_video_filter = to_zc_filter;
3062+ p_filter->pf_flush = to_zc_flush;
3063+ return VLC_SUCCESS;
3064+
3065+fail:
3066+ CloseConverterToZc(obj);
3067+ return ret;
3068+}
3069+
3070+//----------------------------------------------------------------------------
3071+//
3072+// Simple "copy" from ZC
3073+
3074+static void CloseConverterFromZc(vlc_object_t * obj)
3075+{
3076+ VLC_UNUSED(obj);
3077+}
3078+
3079+static int OpenConverterFromZc(vlc_object_t * obj)
3080+{
3081+ return VLC_EGENERIC;
3082+}
3083+#endif
3084+//----------------------------------------------------------------------------
3085+
3086+typedef struct blend_sys_s {
3087+ vzc_pool_ctl_t * vzc;
3088+ const picture_t * last_dst; // Not a ref, just a hint that we have a new pic
3089+ vcsm_init_type_t vcsm_init_type;
3090+} blend_sys_t;
3091+
3092+static void FilterBlendMmal(filter_t *p_filter,
3093+ picture_t *dst, const picture_t * src,
3094+ int x_offset, int y_offset, int alpha)
3095+{
3096+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
3097+#if TRACE_ALL
3098+ msg_Dbg(p_filter, "%s (%d,%d:%d) pic=%p, pts=%lld, force=%d", __func__, x_offset, y_offset, alpha, src, src->date, src->b_force);
3099+#endif
3100+ // If nothing to do then do nothing
3101+ if (alpha == 0 ||
3102+ src->format.i_visible_height == 0 ||
3103+ src->format.i_visible_width == 0)
3104+ {
3105+ return;
3106+ }
3107+
3108+ if (dst->context == NULL)
3109+ msg_Err(p_filter, "MMAL pic missing context");
3110+ else
3111+ {
3112+ // cast away src const so we can ref it
3113+ MMAL_BUFFER_HEADER_T *buf = hw_mmal_vzc_buf_from_pic(sys->vzc, (picture_t *)src,
3114+ vis_mmal_rect(&dst->format),
3115+ x_offset, y_offset,
3116+ alpha,
3117+ dst != sys->last_dst || !hw_mmal_pic_has_sub_bufs(dst));
3118+ if (buf == NULL) {
3119+ msg_Err(p_filter, "Failed to allocate vzc buffer for subpic");
3120+ return;
3121+ }
3122+
3123+ hw_mmal_pic_sub_buf_add(dst, buf);
3124+
3125+ sys->last_dst = dst;
3126+ }
3127+}
3128+
3129+static void FlushBlendMmal(filter_t * p_filter)
3130+{
3131+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
3132+ sys->last_dst = NULL;
3133+ hw_mmal_vzc_pool_flush(sys->vzc);
3134+}
3135+
3136+static void CloseBlendMmal(vlc_object_t *object)
3137+{
3138+ filter_t * const p_filter = (filter_t *)object;
3139+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
3140+
3141+ if (sys != NULL) {
3142+ p_filter->p_sys = NULL;
3143+
3144+ hw_mmal_vzc_pool_release(sys->vzc);
3145+ cma_vcsm_exit(sys->vcsm_init_type);
3146+ free(sys);
3147+ }
3148+}
3149+
3150+static int OpenBlendMmal(vlc_object_t *object)
3151+{
3152+ filter_t * const p_filter = (filter_t *)object;
3153+ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
3154+
3155+ if (!hw_mmal_chroma_is_mmal(vfcc_dst) ||
3156+ !hw_mmal_vzc_subpic_fmt_valid(&p_filter->fmt_in.video))
3157+ {
3158+ return VLC_EGENERIC;
3159+ }
3160+
3161+ {
3162+ char dbuf0[5], dbuf1[5];
3163+ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %dx%d]->%s,%dx%d [(%d,%d) %dx%d]", __func__,
3164+ "blend",
3165+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
3166+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
3167+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
3168+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
3169+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
3170+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
3171+ }
3172+
3173+ {
3174+ blend_sys_t * const sys = calloc(1, sizeof (*sys));
3175+ if (sys == NULL)
3176+ return VLC_ENOMEM;
3177+
3178+ p_filter->p_sys = (filter_sys_t *)sys;
3179+
3180+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
3181+ msg_Err(p_filter, "VCSM init failed");
3182+ goto fail;
3183+ }
3184+
3185+ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
3186+ goto fail;
3187+ }
3188+
3189+ p_filter->pf_video_blend = FilterBlendMmal;
3190+ p_filter->pf_flush = FlushBlendMmal;
3191+
3192+ return VLC_SUCCESS;
3193+
3194+fail:
3195+ CloseBlendMmal(VLC_OBJECT(p_filter));
3196+ return VLC_ENOMEM;
3197+}
3198+
3199+// ---------------------------------------------------------------------------
3200+
3201+static void FilterBlendNeon(filter_t *p_filter,
3202+ picture_t *dst_pic, const picture_t * src_pic,
3203+ int x_offset, int y_offset, int alpha)
3204+{
3205+ const uint8_t * s_data;
3206+ uint8_t * d_data;
3207+ int width = src_pic->format.i_visible_width;
3208+ int height = src_pic->format.i_visible_height;
3209+ blend_neon_fn *const blend_fn = (blend_neon_fn * )p_filter->p_sys;
3210+
3211+#if TRACE_ALL
3212+ msg_Dbg(p_filter, "%s (%d,%d:%d) pic=%p, pts=%lld, force=%d", __func__, x_offset, y_offset, alpha, src_pic, src_pic->date, src_pic->b_force);
3213+#endif
3214+
3215+ if (alpha == 0 ||
3216+ src_pic->format.i_visible_height == 0 ||
3217+ src_pic->format.i_visible_width == 0)
3218+ {
3219+ return;
3220+ }
3221+
3222+ x_offset += dst_pic->format.i_x_offset;
3223+ y_offset += dst_pic->format.i_y_offset;
3224+
3225+ // Deal with R/B overrun
3226+ if (x_offset + width >= (int)(dst_pic->format.i_x_offset + dst_pic->format.i_visible_width))
3227+ width = dst_pic->format.i_x_offset + dst_pic->format.i_visible_width - x_offset;
3228+ if (y_offset + height >= (int)(dst_pic->format.i_y_offset + dst_pic->format.i_visible_height))
3229+ height = dst_pic->format.i_y_offset + dst_pic->format.i_visible_height - y_offset;
3230+
3231+ if (width <= 0 || height <= 0) {
3232+ return;
3233+ }
3234+
3235+ // *** L/U overrun
3236+
3237+ s_data = src_pic->p[0].p_pixels +
3238+ src_pic->p[0].i_pixel_pitch * src_pic->format.i_x_offset +
3239+ src_pic->p[0].i_pitch * src_pic->format.i_y_offset;
3240+ d_data = dst_pic->p[0].p_pixels +
3241+ dst_pic->p[0].i_pixel_pitch * x_offset +
3242+ dst_pic->p[0].i_pitch * y_offset;
3243+
3244+
3245+ do {
3246+ blend_fn(d_data, s_data, alpha, width);
3247+ s_data += src_pic->p[0].i_pitch;
3248+ d_data += dst_pic->p[0].i_pitch;
3249+ } while (--height > 0);
3250+}
3251+
3252+static void CloseBlendNeon(vlc_object_t *object)
3253+{
3254+ VLC_UNUSED(object);
3255+}
3256+
3257+static int OpenBlendNeon(vlc_object_t *object)
3258+{
3259+ filter_t * const p_filter = (filter_t *)object;
3260+ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
3261+ MMAL_FOURCC_T mfcc_src = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video);
3262+ MMAL_FOURCC_T mfcc_dst = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
3263+ blend_neon_fn * blend_fn = (blend_neon_fn *)0;
3264+
3265+ // Non-alpha RGB only for dest
3266+ if (vfcc_dst != VLC_CODEC_RGB32)
3267+ return VLC_EGENERIC;
3268+
3269+ // Check we have appropriate blend fn (mmal doesn't have a non-alpha RGB32)
3270+ switch (mfcc_src) {
3271+ case MMAL_ENCODING_RGBA:
3272+ if (mfcc_dst == MMAL_ENCODING_RGBA)
3273+ blend_fn = blend_rgbx_rgba_neon;
3274+ else if (mfcc_dst == MMAL_ENCODING_BGRA)
3275+ blend_fn = blend_bgrx_rgba_neon;
3276+ break;
3277+
3278+ case MMAL_ENCODING_BGRA:
3279+ if (mfcc_dst == MMAL_ENCODING_BGRA)
3280+ blend_fn = blend_rgbx_rgba_neon;
3281+ else if (mfcc_dst == MMAL_ENCODING_RGBA)
3282+ blend_fn = blend_bgrx_rgba_neon;
3283+ break;
3284+
3285+ default:
3286+ break;
3287+ }
3288+
3289+ if (blend_fn == (blend_neon_fn *)0)
3290+ {
3291+ return VLC_EGENERIC;
3292+ }
3293+
3294+ p_filter->p_sys = (void *)blend_fn;
3295+ p_filter->pf_video_blend = FilterBlendNeon;
3296+
3297+ {
3298+ char dbuf0[5], dbuf1[5];
3299+ char dbuf0a[5], dbuf1a[5];
3300+ msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %dx%d]->%s/%s,%dx%d [(%d,%d) %dx%d]", __func__,
3301+ "blend",
3302+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma),
3303+ str_fourcc(dbuf0a, mfcc_src),
3304+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
3305+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
3306+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
3307+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma),
3308+ str_fourcc(dbuf1a, mfcc_dst),
3309+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
3310+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
3311+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
3312+ }
3313+
3314+ return VLC_SUCCESS;
3315+}
3316+
3317+vlc_module_begin()
3318+ set_category( CAT_INPUT )
3319+ set_subcategory( SUBCAT_INPUT_VCODEC )
3320+ set_shortname(N_("MMAL decoder"))
3321+ set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
3322+ set_capability("video decoder", 90)
3323+ add_shortcut("mmal_decoder")
3324+ add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
3325+ set_callbacks(OpenDecoder, CloseDecoder)
3326+
3327+ add_submodule()
3328+ set_category( CAT_VIDEO )
3329+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3330+ set_shortname(N_("MMAL resizer"))
3331+ set_description(N_("MMAL resizing conversion filter"))
3332+ add_shortcut("mmal_converter")
3333+ set_capability( "video converter", 900 )
3334+ add_bool(MMAL_RESIZE_NAME, /* default */ false, MMAL_RESIZE_TEXT, MMAL_RESIZE_LONGTEXT, /* advanced option */ false)
3335+ add_bool(MMAL_ISP_NAME, /* default */ false, MMAL_ISP_TEXT, MMAL_ISP_LONGTEXT, /* advanced option */ false)
3336+ set_callbacks(OpenConverter, CloseConverter)
3337+
3338+#if OPT_TO_FROM_ZC
3339+ add_submodule()
3340+ set_category( CAT_VIDEO )
3341+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3342+ set_shortname(N_("MMAL to ZC"))
3343+ set_description(N_("MMAL conversion to ZC filter"))
3344+ add_shortcut("mmal_to_zc")
3345+ set_capability( "video converter", 901 )
3346+ set_callbacks(OpenConverterToZc, CloseConverterToZc)
3347+
3348+ add_submodule()
3349+ set_category( CAT_VIDEO )
3350+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3351+ set_shortname(N_("MMAL from ZC"))
3352+ set_description(N_("MMAL conversion from ZC filter"))
3353+ add_shortcut("mmal_from_zc")
3354+ set_capability( "video converter", 902 )
3355+ set_callbacks(OpenConverterFromZc, CloseConverterFromZc)
3356+#endif
3357+
3358+ add_submodule()
3359+ set_category( CAT_VIDEO )
3360+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3361+ set_description(N_("Video pictures blending for MMAL"))
3362+ add_shortcut("mmal_blend")
3363+ set_capability("video blending", 120)
3364+ set_callbacks(OpenBlendMmal, CloseBlendMmal)
3365+
3366+ add_submodule()
3367+ set_category( CAT_VIDEO )
3368+ set_subcategory( SUBCAT_VIDEO_VFILTER )
3369+ set_description(N_("Video pictures blending for neon"))
3370+ add_shortcut("neon_blend")
3371+ set_capability("video blending", 110)
3372+ set_callbacks(OpenBlendNeon, CloseBlendNeon)
3373+
3374+vlc_module_end()
3375+
3376+
3377--- /dev/null
3378+++ b/modules/hw/mmal/converter_mmal.c
3379@@ -0,0 +1,479 @@
3380+#ifdef HAVE_CONFIG_H
3381+# include "config.h"
3382+#endif
3383+
3384+#include <unistd.h>
3385+#include <fcntl.h>
3386+#include <sys/ioctl.h>
3387+#include <sys/mman.h>
3388+
3389+#include <interface/vcsm/user-vcsm.h>
3390+
3391+#include <vlc_common.h>
3392+#include <vlc_picture.h>
3393+
3394+#include <libdrm/drm_fourcc.h>
3395+#include <EGL/egl.h>
3396+#include <EGL/eglext.h>
3397+#include <GLES2/gl2.h>
3398+#include <GLES2/gl2ext.h>
3399+
3400+#include "mmal_cma.h"
3401+
3402+#include "../../video_output/opengl/converter.h"
3403+
3404+#include "mmal_picture.h"
3405+
3406+#include <assert.h>
3407+
3408+#define TRACE_ALL 0
3409+
3410+typedef struct mmal_gl_converter_s
3411+{
3412+ EGLint drm_fourcc;
3413+ vcsm_init_type_t vcsm_init_type;
3414+ cma_buf_t * last_cb;
3415+
3416+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
3417+} mmal_gl_converter_t;
3418+
3419+
3420+static EGLint vlc_to_gl_fourcc(const video_format_t * const fmt)
3421+{
3422+ // Converting to mmal selects the right RGB32 varient
3423+ switch(vlc_to_mmal_video_fourcc(fmt))
3424+ {
3425+ case MMAL_ENCODING_I420:
3426+ return MMAL_FOURCC('Y','U','1','2');
3427+ case MMAL_ENCODING_YV12:
3428+ return MMAL_FOURCC('Y','V','1','2');
3429+ case MMAL_ENCODING_I422:
3430+ return MMAL_FOURCC('Y','U','1','6');
3431+// case MMAL_ENCODING_YUVUV128: // Doesn't actually work yet
3432+ case MMAL_ENCODING_NV12:
3433+ return MMAL_FOURCC('N','V','1','2');
3434+ case MMAL_ENCODING_NV21:
3435+ return MMAL_FOURCC('N','V','2','1');
3436+ case MMAL_ENCODING_RGB16:
3437+ return MMAL_FOURCC('R','G','1','6');
3438+ case MMAL_ENCODING_RGB24:
3439+ return MMAL_FOURCC('B','G','2','4');
3440+ case MMAL_ENCODING_BGR24:
3441+ return MMAL_FOURCC('R','G','2','4');
3442+ case MMAL_ENCODING_BGR32:
3443+ case MMAL_ENCODING_BGRA:
3444+ return MMAL_FOURCC('X','R','2','4');
3445+ case MMAL_ENCODING_RGB32:
3446+ case MMAL_ENCODING_RGBA:
3447+ return MMAL_FOURCC('X','B','2','4');
3448+ default:
3449+ break;
3450+ }
3451+ return 0;
3452+}
3453+
3454+typedef struct tex_context_s {
3455+ picture_context_t cmn;
3456+ GLuint texture;
3457+
3458+ PFNGLDELETETEXTURESPROC DeleteTextures; // Copy fn pointer so we don't need tc on delete
3459+} tex_context_t;
3460+
3461+static void tex_context_delete(tex_context_t * const tex)
3462+{
3463+ tex->DeleteTextures(1, &tex->texture);
3464+ free(tex);
3465+}
3466+
3467+static void tex_context_destroy(picture_context_t * pic_ctx)
3468+{
3469+ tex_context_delete((tex_context_t *)pic_ctx);
3470+}
3471+
3472+static picture_context_t * tex_context_copy(picture_context_t * pic_ctx)
3473+{
3474+ return pic_ctx;
3475+}
3476+
3477+static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic, cma_buf_t * const cb)
3478+{
3479+ mmal_gl_converter_t * const sys = tc->priv;
3480+ tex_context_t * tex = (tex_context_t *)cma_buf_context2(cb);
3481+ if (tex != NULL)
3482+ return tex;
3483+
3484+ if ((tex = malloc(sizeof(*tex))) == NULL)
3485+ return NULL;
3486+
3487+ *tex = (tex_context_t){
3488+ .cmn = {
3489+ .destroy = tex_context_destroy,
3490+ .copy = tex_context_copy
3491+ },
3492+ .texture = 0,
3493+ .DeleteTextures = tc->vt->DeleteTextures
3494+ };
3495+
3496+ {
3497+ EGLint attribs[30];
3498+ EGLint * a = attribs;
3499+ const int fd = cma_buf_fd(cb);
3500+ uint8_t * base_addr = cma_buf_addr(cb);
3501+
3502+ if (pic->i_planes >= 4 || pic->i_planes <= 0)
3503+ {
3504+ msg_Err(tc, "%s: Bad planes: %d", __func__, pic->i_planes);
3505+ goto fail;
3506+ }
3507+
3508+ *a++ = EGL_WIDTH;
3509+ *a++ = pic->format.i_visible_width;
3510+ *a++ = EGL_HEIGHT;
3511+ *a++ = pic->format.i_visible_height;
3512+ *a++ = EGL_LINUX_DRM_FOURCC_EXT;
3513+ *a++ = sys->drm_fourcc;
3514+
3515+ if (pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8)
3516+ {
3517+ // Sand is its own very special bunny :-(
3518+ static const EGLint attnames[] = {
3519+ EGL_DMA_BUF_PLANE0_FD_EXT,
3520+ EGL_DMA_BUF_PLANE0_OFFSET_EXT,
3521+ EGL_DMA_BUF_PLANE0_PITCH_EXT,
3522+ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT,
3523+ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT,
3524+ EGL_DMA_BUF_PLANE1_FD_EXT,
3525+ EGL_DMA_BUF_PLANE1_OFFSET_EXT,
3526+ EGL_DMA_BUF_PLANE1_PITCH_EXT,
3527+ EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT,
3528+ EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT
3529+ };
3530+
3531+ const EGLint * n = attnames;
3532+
3533+ for (int i = 0; i < pic->i_planes; ++i)
3534+ {
3535+ const uint64_t mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(pic->p[i].i_pitch >> 7);
3536+
3537+ *a++ = *n++;
3538+ *a++ = fd;
3539+ *a++ = *n++;
3540+ *a++ = pic->p[i].p_pixels - base_addr;
3541+ *a++ = *n++;
3542+ *a++ = pic->format.i_width;
3543+ *a++ = *n++;
3544+ *a++ = (EGLint)(mod >> 32);
3545+ *a++ = *n++;
3546+ *a++ = (EGLint)(mod & 0xffffffff);
3547+ }
3548+ }
3549+ else
3550+ {
3551+ static const EGLint attnames[] = {
3552+ EGL_DMA_BUF_PLANE0_FD_EXT,
3553+ EGL_DMA_BUF_PLANE0_OFFSET_EXT,
3554+ EGL_DMA_BUF_PLANE0_PITCH_EXT,
3555+ EGL_DMA_BUF_PLANE1_FD_EXT,
3556+ EGL_DMA_BUF_PLANE1_OFFSET_EXT,
3557+ EGL_DMA_BUF_PLANE1_PITCH_EXT,
3558+ EGL_DMA_BUF_PLANE2_FD_EXT,
3559+ EGL_DMA_BUF_PLANE2_OFFSET_EXT,
3560+ EGL_DMA_BUF_PLANE2_PITCH_EXT,
3561+ EGL_DMA_BUF_PLANE3_FD_EXT,
3562+ EGL_DMA_BUF_PLANE3_OFFSET_EXT,
3563+ EGL_DMA_BUF_PLANE3_PITCH_EXT
3564+ };
3565+
3566+ const EGLint * n = attnames;
3567+
3568+ for (int i = 0; i < pic->i_planes; ++i)
3569+ {
3570+ *a++ = *n++;
3571+ *a++ = fd;
3572+ *a++ = *n++;
3573+ *a++ = pic->p[i].p_pixels - base_addr;
3574+ *a++ = *n++;
3575+ *a++ = pic->p[i].i_pitch;
3576+ }
3577+ }
3578+
3579+ *a = EGL_NONE;
3580+
3581+ const EGLImage image = tc->gl->egl.createImageKHR(tc->gl, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
3582+ if (!image) {
3583+ msg_Err(tc, "Failed to import fd %d: Err=%#x", fd, tc->vt->GetError());
3584+ goto fail;
3585+ }
3586+
3587+ // ** ?? tc->tex_target
3588+ tc->vt->GenTextures(1, &tex->texture);
3589+ tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture);
3590+ tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3591+ tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3592+ sys->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
3593+
3594+ tc->gl->egl.destroyImageKHR(tc->gl, image);
3595+ }
3596+
3597+ if (cma_buf_add_context2(cb, &tex->cmn) != VLC_SUCCESS)
3598+ {
3599+ msg_Err(tc, "%s: add_context2 failed", __func__);
3600+ goto fail;
3601+ }
3602+ return tex;
3603+
3604+fail:
3605+ tex_context_delete(tex);
3606+ return NULL;
3607+}
3608+
3609+
3610+static int
3611+tc_mmal_update(const opengl_tex_converter_t *tc, GLuint *textures,
3612+ const GLsizei *tex_width, const GLsizei *tex_height,
3613+ picture_t *pic, const size_t *plane_offset)
3614+{
3615+ mmal_gl_converter_t * const sys = tc->priv;
3616+#if TRACE_ALL
3617+ {
3618+ char cbuf[5];
3619+ msg_Dbg(tc, "%s: %s %d*%dx%d : %d*%dx%d", __func__,
3620+ str_fourcc(cbuf, pic->format.i_chroma),
3621+ tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines);
3622+ }
3623+#endif
3624+ VLC_UNUSED(tex_width);
3625+ VLC_UNUSED(tex_height);
3626+ VLC_UNUSED(plane_offset);
3627+
3628+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma))
3629+ {
3630+ char cbuf[5];
3631+ msg_Err(tc, "Pic with unexpected chroma: %s", str_fourcc(cbuf, pic->format.i_chroma));
3632+ return VLC_EGENERIC;
3633+ }
3634+
3635+ cma_buf_t * const cb = cma_buf_pic_get(pic);
3636+ if (cb == NULL)
3637+ {
3638+ msg_Err(tc, "Pic missing cma buf");
3639+ return VLC_EGENERIC;
3640+ }
3641+
3642+ tex_context_t * const tex = get_tex_context(tc, pic, cb);
3643+ if (tex == NULL)
3644+ return VLC_EGENERIC;
3645+
3646+// tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture);
3647+
3648+ cma_buf_unref(sys->last_cb);
3649+ sys->last_cb = cma_buf_ref(cb);
3650+
3651+ textures[0] = tex->texture;
3652+ return VLC_SUCCESS;
3653+}
3654+
3655+static int
3656+tc_mmal_fetch_locations(opengl_tex_converter_t *tc, GLuint program)
3657+{
3658+ tc->uloc.Texture[0] = tc->vt->GetUniformLocation(program, "Texture0");
3659+ return tc->uloc.Texture[0] != -1 ? VLC_SUCCESS : VLC_EGENERIC;
3660+}
3661+
3662+static void
3663+tc_mmal_prepare_shader(const opengl_tex_converter_t *tc,
3664+ const GLsizei *tex_width, const GLsizei *tex_height,
3665+ float alpha)
3666+{
3667+ (void) tex_width; (void) tex_height; (void) alpha;
3668+ VLC_UNUSED(tc);
3669+// tc->vt->Uniform1i(tc->uloc.Texture[0], 0);
3670+}
3671+
3672+static GLuint
3673+tc_fragment_shader_init(opengl_tex_converter_t * const tc, const GLenum tex_target,
3674+ const vlc_fourcc_t chroma, const video_color_space_t yuv_space)
3675+{
3676+ VLC_UNUSED(yuv_space);
3677+
3678+ tc->tex_count = 1;
3679+ tc->tex_target = tex_target;
3680+ tc->texs[0] = (struct opengl_tex_cfg) {
3681+ { 1, 1 }, { 1, 1 }, GL_RGB, chroma, GL_UNSIGNED_SHORT //** ??
3682+ };
3683+
3684+ tc->pf_fetch_locations = tc_mmal_fetch_locations;
3685+ tc->pf_prepare_shader = tc_mmal_prepare_shader;
3686+
3687+
3688+ const char fs[] =
3689+ "#extension GL_OES_EGL_image_external : enable\n"
3690+ "precision mediump float;\n"
3691+ "uniform samplerExternalOES Texture0;\n"
3692+ "varying vec2 TexCoord0;\n"
3693+ "void main() {\n"
3694+ " gl_FragColor = texture2D(Texture0, TexCoord0);\n"
3695+ "}\n";
3696+
3697+
3698+ const char *code = fs;
3699+
3700+ GLuint fragment_shader = tc->vt->CreateShader(GL_FRAGMENT_SHADER);
3701+ tc->vt->ShaderSource(fragment_shader, 1, &code, NULL);
3702+ tc->vt->CompileShader(fragment_shader);
3703+ return fragment_shader;
3704+}
3705+
3706+
3707+static void
3708+CloseGLConverter(vlc_object_t *obj)
3709+{
3710+ opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj;
3711+ mmal_gl_converter_t * const sys = tc->priv;
3712+
3713+ if (sys == NULL)
3714+ return;
3715+
3716+ cma_buf_unref(sys->last_cb);
3717+ cma_vcsm_exit(sys->vcsm_init_type);
3718+ free(sys);
3719+}
3720+
3721+
3722+// Pick a chroma that we can convert to
3723+// Prefer I420 as smallest
3724+static vlc_fourcc_t chroma_in_out(const vlc_fourcc_t chroma_in)
3725+{
3726+ switch (chroma_in)
3727+ {
3728+ case VLC_CODEC_MMAL_OPAQUE:
3729+ case VLC_CODEC_MMAL_ZC_I420:
3730+ case VLC_CODEC_MMAL_ZC_SAND8:
3731+ case VLC_CODEC_MMAL_ZC_SAND10: // ISP only
3732+ return VLC_CODEC_MMAL_ZC_I420;
3733+ case VLC_CODEC_MMAL_ZC_SAND30: // HVS only
3734+ case VLC_CODEC_MMAL_ZC_RGB32:
3735+ return VLC_CODEC_MMAL_ZC_RGB32; // HVS can't generate YUV of any sort
3736+ default:
3737+ break;
3738+ }
3739+ return 0;
3740+}
3741+
3742+
3743+static int
3744+OpenGLConverter(vlc_object_t *obj)
3745+{
3746+ opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj;
3747+ int rv = VLC_EGENERIC;
3748+ const EGLint eglfmt = vlc_to_gl_fourcc(&tc->fmt);
3749+ const vlc_fourcc_t chroma_out = chroma_in_out(tc->fmt.i_chroma);
3750+
3751+ // Do we know what to do with this?
3752+ if (chroma_out == 0)
3753+ return rv;
3754+
3755+ {
3756+ char dbuf0[5], dbuf1[5], dbuf2[5];
3757+ msg_Dbg(tc, "<<< %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__,
3758+ str_fourcc(dbuf0, tc->fmt.i_chroma),
3759+ str_fourcc(dbuf1, eglfmt),
3760+ tc->fmt.i_width, tc->fmt.i_height,
3761+ tc->fmt.i_x_offset, tc->fmt.i_y_offset,
3762+ tc->fmt.i_visible_width, tc->fmt.i_visible_height,
3763+ tc->fmt.i_sar_num, tc->fmt.i_sar_den,
3764+ str_fourcc(dbuf2, chroma_out));
3765+ }
3766+
3767+ if (tc->gl->ext != VLC_GL_EXT_EGL ||
3768+ !tc->gl->egl.createImageKHR || !tc->gl->egl.destroyImageKHR)
3769+ {
3770+ // Missing an important callback
3771+ msg_Dbg(tc, "Missing EGL xxxImageKHR calls");
3772+ return rv;
3773+ }
3774+
3775+ if ((tc->priv = calloc(1, sizeof(mmal_gl_converter_t))) == NULL)
3776+ {
3777+ msg_Err(tc, "priv alloc failure");
3778+ rv = VLC_ENOMEM;
3779+ goto fail;
3780+ }
3781+ mmal_gl_converter_t * const sys = tc->priv;
3782+
3783+ sys->drm_fourcc = eglfmt;
3784+
3785+ if ((sys->vcsm_init_type = cma_vcsm_init()) != VCSM_INIT_CMA) {
3786+ msg_Dbg(tc, "VCSM init failed");
3787+ goto fail;
3788+ }
3789+
3790+ if ((sys->glEGLImageTargetTexture2DOES = vlc_gl_GetProcAddress(tc->gl, "glEGLImageTargetTexture2DOES")) == NULL)
3791+ {
3792+ msg_Err(tc, "Failed to bind GL fns");
3793+ goto fail;
3794+ }
3795+
3796+ if ((tc->fshader = tc_fragment_shader_init(tc, GL_TEXTURE_EXTERNAL_OES,
3797+ eglfmt == 0 ? VLC_CODEC_RGB32 : tc->fmt.i_chroma,
3798+ eglfmt == 0 ? COLOR_SPACE_SRGB : tc->fmt.space)) == 0)
3799+ {
3800+ msg_Err(tc, "Failed to make shader");
3801+ goto fail;
3802+ }
3803+
3804+ if (eglfmt == 0)
3805+ {
3806+ tc->fmt.i_chroma = chroma_out;
3807+ tc->fmt.i_bits_per_pixel = 8;
3808+ if (tc->fmt.i_chroma == VLC_CODEC_MMAL_ZC_RGB32)
3809+ {
3810+ tc->fmt.i_rmask = 0xff0000;
3811+ tc->fmt.i_gmask = 0xff00;
3812+ tc->fmt.i_bmask = 0xff;
3813+ tc->fmt.space = COLOR_SPACE_SRGB;
3814+ }
3815+ else
3816+ {
3817+ tc->fmt.i_rmask = 0;
3818+ tc->fmt.i_gmask = 0;
3819+ tc->fmt.i_bmask = 0;
3820+ tc->fmt.space = COLOR_SPACE_UNDEF;
3821+ }
3822+ sys->drm_fourcc = vlc_to_gl_fourcc(&tc->fmt);
3823+ }
3824+
3825+ tc->handle_texs_gen = true; // We manage the texs
3826+ tc->pf_update = tc_mmal_update;
3827+
3828+#if TRACE_ALL
3829+ {
3830+ char dbuf0[5], dbuf1[5], dbuf2[5];
3831+ msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__,
3832+ str_fourcc(dbuf0, tc->fmt.i_chroma),
3833+ str_fourcc(dbuf1, sys->drm_fourcc),
3834+ tc->fmt.i_width, tc->fmt.i_height,
3835+ tc->fmt.i_x_offset, tc->fmt.i_y_offset,
3836+ tc->fmt.i_visible_width, tc->fmt.i_visible_height,
3837+ tc->fmt.i_sar_num, tc->fmt.i_sar_den,
3838+ str_fourcc(dbuf2, chroma_out));
3839+ }
3840+#endif
3841+
3842+ return VLC_SUCCESS;
3843+
3844+fail:
3845+ CloseGLConverter(obj);
3846+ return rv;
3847+}
3848+
3849+vlc_module_begin ()
3850+ set_description("MMAL OpenGL surface converter")
3851+ set_shortname (N_("MMALGLConverter"))
3852+ set_capability("glconv", 900)
3853+ set_callbacks(OpenGLConverter, CloseGLConverter)
3854+ set_category(CAT_VIDEO)
3855+ set_subcategory(SUBCAT_VIDEO_VOUT)
3856+ add_shortcut("mmal_gl_converter")
3857+vlc_module_end ()
3858+
3859--- a/modules/hw/mmal/deinterlace.c
3860+++ b/modules/hw/mmal/deinterlace.c
3861@@ -26,11 +26,12 @@
3862 #include "config.h"
3863 #endif
3864
3865-#include <vlc_picture_pool.h>
3866+#include <stdatomic.h>
3867+
3868 #include <vlc_common.h>
3869+#include <vlc_picture_pool.h>
3870 #include <vlc_plugin.h>
3871 #include <vlc_filter.h>
3872-#include <vlc_atomic.h>
3873
3874 #include "mmal_picture.h"
3875
3876@@ -39,468 +40,814 @@
3877 #include <interface/mmal/util/mmal_util.h>
3878 #include <interface/mmal/util/mmal_default_components.h>
3879
3880-#define MIN_NUM_BUFFERS_IN_TRANSIT 2
3881+#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu"
3882+#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.")
3883+#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.")
3884
3885-#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu"
3886-#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.")
3887-#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.")
3888+#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv"
3889+#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace")
3890+#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace")
3891
3892-static int Open(filter_t *filter);
3893-static void Close(filter_t *filter);
3894+#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast"
3895+#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace")
3896+#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace")
3897
3898-vlc_module_begin()
3899- set_shortname(N_("MMAL deinterlace"))
3900- set_description(N_("MMAL-based deinterlace filter plugin"))
3901- set_capability("video filter", 0)
3902- set_category(CAT_VIDEO)
3903- set_subcategory(SUBCAT_VIDEO_VFILTER)
3904- set_callbacks(Open, Close)
3905- add_shortcut("deinterlace")
3906- add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT,
3907- MMAL_DEINTERLACE_QPU_LONGTEXT, true);
3908-vlc_module_end()
3909+#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none"
3910+#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace")
3911+#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\
3912+ "This is the default for > SD if < 96M gpu-mem")
3913+
3914+#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate"
3915+#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate")
3916+#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input")
3917+
3918+#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate"
3919+#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate")
3920+#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input")
3921
3922-struct filter_sys_t {
3923+
3924+typedef struct filter_sys_t
3925+{
3926 MMAL_COMPONENT_T *component;
3927 MMAL_PORT_T *input;
3928 MMAL_PORT_T *output;
3929+ MMAL_POOL_T *in_pool;
3930+
3931+ MMAL_QUEUE_T * out_q;
3932+
3933+ // Bind this lot somehow into ppr????
3934+ bool is_cma;
3935+ cma_buf_pool_t * cma_out_pool;
3936+ MMAL_POOL_T * out_pool;
3937+
3938+ hw_mmal_port_pool_ref_t *out_ppr;
3939+
3940+ bool half_rate;
3941+ bool use_qpu;
3942+ bool use_fast;
3943+ bool use_passthrough;
3944+ unsigned int seq_in; // Seq of next frame to submit (1-15) [Init=1]
3945+ unsigned int seq_out; // Seq of last frame received (1-15) [Init=15]
3946
3947- MMAL_QUEUE_T *filtered_pictures;
3948- vlc_sem_t sem;
3949+ vcsm_init_type_t vcsm_init_type;
3950
3951- atomic_bool started;
3952+} filter_sys_t;
3953
3954- /* statistics */
3955- int output_in_transit;
3956- int input_in_transit;
3957-};
3958-
3959-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
3960-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
3961-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
3962-static picture_t *deinterlace(filter_t *filter, picture_t *picture);
3963-static void flush(filter_t *filter);
3964
3965 #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
3966
3967-static int Open(filter_t *filter)
3968+#define TRACE_ALL 0
3969+
3970+
3971+
3972+// Buffer attached to pic on success, is still valid on failure
3973+static picture_t * di_alloc_opaque(filter_t * const p_filter, MMAL_BUFFER_HEADER_T * const buf)
3974 {
3975- int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
3976- (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base /
3977- filter->fmt_in.video.i_frame_rate : 0;
3978- bool use_qpu = var_InheritBool(filter, MMAL_DEINTERLACE_QPU);
3979+ filter_sys_t *const filter_sys = p_filter->p_sys;
3980+ picture_t * const pic = filter_NewPicture(p_filter);
3981
3982- MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
3983- { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
3984- MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
3985- 4,
3986- { 3, frame_duration, 0, use_qpu }
3987- };
3988+ if (pic == NULL)
3989+ goto fail1;
3990
3991- int ret = VLC_SUCCESS;
3992- MMAL_STATUS_T status;
3993- filter_sys_t *sys;
3994+ if (buf->length == 0) {
3995+ msg_Err(p_filter, "%s: Empty buffer", __func__);
3996+ goto fail2;
3997+ }
3998
3999- msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!",
4000- frame_duration, use_qpu ? "used" : "unused");
4001+ if ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL)
4002+ goto fail2;
4003
4004- if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
4005- return VLC_EGENERIC;
4006+ buf_to_pic_copy_props(pic, buf);
4007
4008- if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
4009- return VLC_EGENERIC;
4010+#if TRACE_ALL
4011+ msg_Dbg(p_filter, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
4012+#endif
4013
4014- sys = calloc(1, sizeof(filter_sys_t));
4015- if (!sys)
4016- return VLC_ENOMEM;
4017- filter->p_sys = sys;
4018+ return pic;
4019
4020- bcm_host_init();
4021+fail2:
4022+ picture_Release(pic);
4023+fail1:
4024+// mmal_buffer_header_release(buf);
4025+ return NULL;
4026+}
4027
4028- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
4029- if (status != MMAL_SUCCESS) {
4030- msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
4031- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4032- ret = VLC_EGENERIC;
4033- goto out;
4034- }
4035+static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4036+{
4037+#if TRACE_ALL
4038+ pic_ctx_mmal_t * ctx = buffer->user_data;
4039+// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
4040+
4041+ msg_Dbg((filter_t *)port->userdata, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buffer->cmd, ctx, buffer,
4042+ buffer->flags, (long long)buffer->pts);
4043+#else
4044+ VLC_UNUSED(port);
4045+#endif
4046
4047- status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
4048- if (status != MMAL_SUCCESS) {
4049- msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
4050- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4051- ret = VLC_EGENERIC;
4052- goto out;
4053- }
4054+ mmal_buffer_header_release(buffer);
4055
4056- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4057- status = mmal_port_enable(sys->component->control, control_port_cb);
4058- if (status != MMAL_SUCCESS) {
4059- msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
4060- sys->component->control->name, status, mmal_status_to_string(status));
4061- ret = VLC_EGENERIC;
4062- goto out;
4063+#if TRACE_ALL
4064+ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
4065+#endif
4066+}
4067+
4068+static void di_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
4069+{
4070+ if (buf->cmd == 0 && buf->length != 0)
4071+ {
4072+ // The filter structure etc. should always exist if we have contents
4073+ // but might not on later flushes as we shut down
4074+ filter_t * const p_filter = (filter_t *)port->userdata;
4075+ filter_sys_t * const sys = p_filter->p_sys;
4076+
4077+#if TRACE_ALL
4078+ msg_Dbg(p_filter, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts);
4079+#endif
4080+ mmal_queue_put(sys->out_q, buf);
4081+#if TRACE_ALL
4082+ msg_Dbg(p_filter, ">>> %s: out Q len=%d", __func__, mmal_queue_length(sys->out_q));
4083+#endif
4084+ return;
4085 }
4086
4087- sys->input = sys->component->input[0];
4088- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4089- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
4090- sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
4091- sys->input->format->es->video.width = filter->fmt_in.video.i_width;
4092- sys->input->format->es->video.height = filter->fmt_in.video.i_height;
4093- sys->input->format->es->video.crop.x = 0;
4094- sys->input->format->es->video.crop.y = 0;
4095- sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width;
4096- sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height;
4097- sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num;
4098- sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den;
4099+ mmal_buffer_header_reset(buf); // User data stays intact so release will kill pic
4100+ mmal_buffer_header_release(buf);
4101+}
4102
4103- es_format_Copy(&filter->fmt_out, &filter->fmt_in);
4104- filter->fmt_out.video.i_frame_rate *= 2;
4105
4106- status = mmal_port_format_commit(sys->input);
4107- if (status != MMAL_SUCCESS) {
4108- msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
4109- sys->input->name, status, mmal_status_to_string(status));
4110- ret = VLC_EGENERIC;
4111- goto out;
4112- }
4113- sys->input->buffer_size = sys->input->buffer_size_recommended;
4114- sys->input->buffer_num = sys->input->buffer_num_recommended;
4115
4116- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
4117- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
4118- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
4119- 1
4120- };
4121+static MMAL_STATUS_T fill_output_from_q(filter_t * const p_filter, filter_sys_t * const sys, MMAL_QUEUE_T * const q)
4122+{
4123+ MMAL_BUFFER_HEADER_T * out_buf;
4124
4125- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
4126- if (status != MMAL_SUCCESS) {
4127- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
4128- sys->input->name, status, mmal_status_to_string(status));
4129- goto out;
4130+ while ((out_buf = mmal_queue_get(q)) != NULL)
4131+ {
4132+ MMAL_STATUS_T err;
4133+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
4134+ {
4135+ msg_Err(p_filter, "Send buffer to output failed");
4136+ mmal_queue_put_back(q, out_buf);
4137+ return err;
4138 }
4139 }
4140+ return MMAL_SUCCESS;
4141+}
4142
4143- status = mmal_port_enable(sys->input, input_port_cb);
4144- if (status != MMAL_SUCCESS) {
4145- msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
4146- sys->input->name, status, mmal_status_to_string(status));
4147- ret = VLC_EGENERIC;
4148- goto out;
4149- }
4150+// Output buffers may contain a pic ref on error or flush
4151+// Free it
4152+static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
4153+{
4154+ VLC_UNUSED(userdata);
4155
4156- sys->output = sys->component->output[0];
4157- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4158- mmal_format_full_copy(sys->output->format, sys->input->format);
4159+ cma_buf_t * const cb = header->user_data;
4160+ header->user_data = NULL;
4161+ cma_buf_unref(cb); // Copes fine with NULL
4162
4163- status = mmal_port_format_commit(sys->output);
4164- if (status != MMAL_SUCCESS) {
4165- msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
4166- sys->input->name, status, mmal_status_to_string(status));
4167- ret = VLC_EGENERIC;
4168- goto out;
4169+ return MMAL_FALSE;
4170+}
4171+
4172+static inline unsigned int seq_inc(unsigned int x)
4173+{
4174+ return x + 1 >= 16 ? 1 : x + 1;
4175+}
4176+
4177+static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq)
4178+{
4179+ return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq);
4180+}
4181+
4182+static picture_t *deinterlace(filter_t * p_filter, picture_t * p_pic)
4183+{
4184+ filter_sys_t * const sys = p_filter->p_sys;
4185+ picture_t *ret_pics = NULL;
4186+ MMAL_STATUS_T err;
4187+ MMAL_BUFFER_HEADER_T * out_buf = NULL;
4188+
4189+#if TRACE_ALL
4190+ msg_Dbg(p_filter, "<<< %s", __func__);
4191+#endif
4192+
4193+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
4194+ {
4195+ // ****** Breaks on opaque (at least)
4196+
4197+ if (sys->input->is_enabled)
4198+ mmal_port_disable(sys->input);
4199+#if 0
4200+ if (sys->output->is_enabled)
4201+ mmal_port_disable(sys->output);
4202+
4203+ mmal_format_full_copy(sys->output->format, sys->input->format);
4204+ mmal_port_format_commit(sys->output);
4205+ sys->output->buffer_num = 30;
4206+ sys->output->buffer_size = sys->input->buffer_size_recommended;
4207+ mmal_port_enable(sys->output, di_output_port_cb);
4208+#endif
4209+ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
4210+ msg_Err(p_filter, "Failed to update pic format");
4211+ sys->input->buffer_num = 30;
4212+ sys->input->buffer_size = sys->input->buffer_size_recommended;
4213+ mmal_log_dump_format(sys->input->format);
4214+ }
4215+
4216+ // Reenable stuff if the last thing we did was flush
4217+ // Output should always be enabled
4218+ if (!sys->input->is_enabled &&
4219+ (err = mmal_port_enable(sys->input, di_input_port_cb)) != MMAL_SUCCESS)
4220+ {
4221+ msg_Err(p_filter, "Input port reenable failed");
4222+ goto fail;
4223+ }
4224+
4225+ if (!sys->is_cma)
4226+ {
4227+ // Fill output from anything that has turned up in pool Q
4228+ if (hw_mmal_port_pool_ref_fill(sys->out_ppr) != MMAL_SUCCESS)
4229+ {
4230+ msg_Err(p_filter, "Out port fill fail");
4231+ goto fail;
4232+ }
4233 }
4234+ else
4235+ {
4236+ // We are expecting one in - one out so simply wedge a new bufer
4237+ // into the output port. Flow control will happen on cma alloc.
4238+
4239+ if ((out_buf = mmal_queue_get(sys->out_pool->queue)) == NULL)
4240+ {
4241+ // Should never happen
4242+ msg_Err(p_filter, "Failed to get output buffer");
4243+ goto fail;
4244+ }
4245+ mmal_buffer_header_reset(out_buf);
4246
4247- sys->output->buffer_num = 3;
4248+ // Attach cma_buf to the buffer & ensure it is freed when the buffer is released
4249+ // On a good send callback the pic will be extracted to avoid this
4250+ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, p_filter);
4251+
4252+ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size);
4253+ if ((out_buf->user_data = cb) == NULL) // Check & attach cb to buf
4254+ {
4255+ char dbuf0[5];
4256+ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d",
4257+ str_fourcc(dbuf0, p_pic->format.i_chroma),
4258+ sys->output->buffer_size);
4259+ goto fail;
4260+ }
4261+ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable
4262+ out_buf->data = (uint8_t *)vc_h;
4263+ out_buf->alloc_size = sys->output->buffer_size;
4264+
4265+#if TRACE_ALL
4266+ msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld",
4267+ p_pic, out_buf->data, out_buf->user_data, out_buf->flags,
4268+ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts);
4269+#endif
4270
4271- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
4272- MMAL_PARAMETER_UINT32_T extra_buffers = {
4273- { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) },
4274- 5
4275- };
4276- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
4277- if (status != MMAL_SUCCESS) {
4278- msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
4279- status, mmal_status_to_string(status));
4280- goto out;
4281+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
4282+ {
4283+ msg_Err(p_filter, "Send buffer to output failed");
4284+ goto fail;
4285 }
4286+ out_buf = NULL;
4287+ }
4288
4289- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
4290- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
4291- 1
4292- };
4293+ // Stuff into input
4294+ // We assume the BH is already set up with values reflecting pic date etc.
4295+ {
4296+ MMAL_BUFFER_HEADER_T * const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
4297+
4298+ if (pic_buf == NULL)
4299+ {
4300+ msg_Err(p_filter, "Pic has not attached buffer");
4301+ goto fail;
4302+ }
4303
4304- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
4305- if (status != MMAL_SUCCESS) {
4306- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
4307- sys->output->name, status, mmal_status_to_string(status));
4308- goto out;
4309+ picture_Release(p_pic);
4310+
4311+ // Add a sequence to the flags so we can track what we have actually
4312+ // deinterlaced
4313+ pic_buf->flags = (pic_buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0));
4314+ sys->seq_in = seq_inc(sys->seq_in);
4315+
4316+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
4317+ {
4318+ msg_Err(p_filter, "Send buffer to input failed");
4319+ mmal_buffer_header_release(pic_buf);
4320+ goto fail;
4321 }
4322 }
4323
4324- status = mmal_port_enable(sys->output, output_port_cb);
4325- if (status != MMAL_SUCCESS) {
4326- msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
4327- sys->output->name, status, mmal_status_to_string(status));
4328- ret = VLC_EGENERIC;
4329- goto out;
4330+ // Return anything that is in the out Q
4331+ {
4332+ picture_t ** pp_pic = &ret_pics;
4333+
4334+ // Advanced di has a 3 frame latency, so if the seq delta is greater
4335+ // than that then we are expecting at least two frames of output. Wait
4336+ // for one of those.
4337+ // seq_in is seq of the next frame we are going to submit (1-15, no 0)
4338+ // seq_out is last frame we removed from Q
4339+ // So after 4 frames sent (1st time we want to wait), 0 rx seq_in=5, seq_out=15, delta=5
4340+
4341+ while ((out_buf = (seq_delta(sys->seq_in, sys->seq_out) >= 5 ? mmal_queue_timedwait(sys->out_q, 1000) : mmal_queue_get(sys->out_q))) != NULL)
4342+ {
4343+ const unsigned int seq_out = (out_buf->flags / MMAL_BUFFER_HEADER_FLAG_USER0) & 0xf;
4344+ int rv;
4345+
4346+ picture_t * out_pic;
4347+
4348+ if (sys->is_cma)
4349+ {
4350+ // Alloc pic
4351+ if ((out_pic = filter_NewPicture(p_filter)) == NULL)
4352+ {
4353+ // Can't alloc pic - just stop extraction
4354+ mmal_queue_put_back(sys->out_q, out_buf);
4355+ out_buf = NULL;
4356+ msg_Warn(p_filter, "Failed to alloc new filter output pic");
4357+ break;
4358+ }
4359+
4360+ // Extract cma_buf from buf & attach to pic
4361+ cma_buf_t * const cb = (cma_buf_t *)out_buf->user_data;
4362+ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS)
4363+ {
4364+ char dbuf0[5];
4365+ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d",
4366+ str_fourcc(dbuf0, out_pic->format.i_chroma),
4367+ rv);
4368+ // cb still attached to buffer and will be freed with it
4369+ goto fail;
4370+ }
4371+ out_buf->user_data = NULL;
4372+
4373+ buf_to_pic_copy_props(out_pic, out_buf);
4374+
4375+ // Set pic data pointers from buf aux info now it has it
4376+ if ((rv = cma_pic_set_data(out_pic, sys->output->format, out_buf)) != VLC_SUCCESS)
4377+ {
4378+ char dbuf0[5];
4379+ msg_Err(p_filter, "Failed to set data: fmt=%s, rv=%d",
4380+ str_fourcc(dbuf0, sys->output->format->encoding),
4381+ rv);
4382+ }
4383+
4384+ out_buf->user_data = NULL; // Responsability for this pic no longer with buffer
4385+ mmal_buffer_header_release(out_buf);
4386+ }
4387+ else
4388+ {
4389+ out_pic = di_alloc_opaque(p_filter, out_buf);
4390+
4391+ if (out_pic == NULL) {
4392+ msg_Warn(p_filter, "Failed to alloc new filter output pic");
4393+ mmal_queue_put_back(sys->out_q, out_buf); // Wedge buf back into Q in the hope we can alloc a pic later
4394+ out_buf = NULL;
4395+ break;
4396+ }
4397+ }
4398+ out_buf = NULL; // Now attached to pic or recycled
4399+
4400+#if TRACE_ALL
4401+ msg_Dbg(p_filter, "-- %s: Q pic=%p: seq_in=%d, seq_out=%d, delta=%d", __func__, out_pic, sys->seq_in, seq_out, seq_delta(sys->seq_in, seq_out));
4402+#endif
4403+
4404+ *pp_pic = out_pic;
4405+ pp_pic = &out_pic->p_next;
4406+
4407+ // Ignore 0 seqs
4408+ // Don't think these should actually happen
4409+ if (seq_out != 0)
4410+ sys->seq_out = seq_out;
4411+ }
4412+
4413+ // Crash on lockup
4414+ assert(ret_pics != NULL || seq_delta(sys->seq_in, sys->seq_out) < 5);
4415 }
4416
4417- status = mmal_component_enable(sys->component);
4418- if (status != MMAL_SUCCESS) {
4419- msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
4420- sys->component->name, status, mmal_status_to_string(status));
4421- ret = VLC_EGENERIC;
4422- goto out;
4423+#if TRACE_ALL
4424+ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
4425+#endif
4426+
4427+ return ret_pics;
4428+
4429+fail:
4430+ if (out_buf != NULL)
4431+ mmal_buffer_header_release(out_buf);
4432+ picture_Release(p_pic);
4433+ return NULL;
4434+}
4435+
4436+static void di_flush(filter_t *p_filter)
4437+{
4438+ filter_sys_t * const sys = p_filter->p_sys;
4439+
4440+#if TRACE_ALL
4441+ msg_Dbg(p_filter, "<<< %s", __func__);
4442+#endif
4443+
4444+ if (sys->input != NULL && sys->input->is_enabled)
4445+ mmal_port_disable(sys->input);
4446+
4447+ if (sys->output != NULL && sys->output->is_enabled)
4448+ {
4449+ if (sys->is_cma)
4450+ {
4451+ MMAL_BUFFER_HEADER_T * buf;
4452+ mmal_port_disable(sys->output);
4453+ while ((buf = mmal_queue_get(sys->out_q)) != NULL)
4454+ mmal_buffer_header_release(buf);
4455+ }
4456+ else
4457+ {
4458+ // Wedge anything we've got into the output port as that will free the underlying buffers
4459+ fill_output_from_q(p_filter, sys, sys->out_q);
4460+
4461+ mmal_port_disable(sys->output);
4462+
4463+ // If that dumped anything real into the out_q then have another go
4464+ if (mmal_queue_length(sys->out_q) != 0)
4465+ {
4466+ mmal_port_enable(sys->output, di_output_port_cb);
4467+ fill_output_from_q(p_filter, sys, sys->out_q);
4468+ mmal_port_disable(sys->output);
4469+ // Out q should now be empty & should remain so until the input is reenabled
4470+ }
4471+ }
4472+ mmal_port_enable(sys->output, di_output_port_cb);
4473+
4474+ // Leaving the input disabled is fine - but we want to leave the output enabled
4475+ // so we can retrieve buffers that are still bound to pictures
4476 }
4477
4478- sys->filtered_pictures = mmal_queue_create();
4479+ sys->seq_in = 1;
4480+ sys->seq_out = 15;
4481
4482- filter->pf_video_filter = deinterlace;
4483- filter->pf_flush = flush;
4484+#if TRACE_ALL
4485+ msg_Dbg(p_filter, ">>> %s", __func__);
4486+#endif
4487+}
4488
4489- vlc_sem_init(&sys->sem, 0);
4490
4491-out:
4492- if (ret != VLC_SUCCESS)
4493- Close(filter);
4494+static void pass_flush(filter_t *p_filter)
4495+{
4496+ // Nothing to do
4497+ VLC_UNUSED(p_filter);
4498+}
4499
4500- return ret;
4501+static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic)
4502+{
4503+ VLC_UNUSED(p_filter);
4504+
4505+ p_pic->b_progressive = true;
4506+ return p_pic;
4507 }
4508
4509-static void Close(filter_t *filter)
4510+
4511+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4512 {
4513- filter_sys_t *sys = filter->p_sys;
4514- MMAL_BUFFER_HEADER_T *buffer;
4515+ filter_t *filter = (filter_t *)port->userdata;
4516+ MMAL_STATUS_T status;
4517
4518- if (!sys)
4519+ if (buffer->cmd == MMAL_EVENT_ERROR) {
4520+ status = *(uint32_t *)buffer->data;
4521+ msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
4522+ mmal_status_to_string(status));
4523+ }
4524+
4525+ mmal_buffer_header_reset(buffer);
4526+ mmal_buffer_header_release(buffer);
4527+}
4528+
4529+static void CloseMmalDeinterlace(filter_t *filter)
4530+{
4531+ filter_sys_t * const sys = filter->p_sys;
4532+
4533+#if TRACE_ALL
4534+ msg_Dbg(filter, "<<< %s", __func__);
4535+#endif
4536+
4537+ if (sys == NULL)
4538 return;
4539
4540- if (sys->component && sys->component->control->is_enabled)
4541- mmal_port_disable(sys->component->control);
4542+ if (sys->use_passthrough)
4543+ {
4544+ free(sys);
4545+ return;
4546+ }
4547
4548- if (sys->input && sys->input->is_enabled)
4549- mmal_port_disable(sys->input);
4550+ di_flush(filter);
4551
4552- if (sys->output && sys->output->is_enabled)
4553- mmal_port_disable(sys->output);
4554+ if (sys->component && sys->component->control->is_enabled)
4555+ mmal_port_disable(sys->component->control);
4556
4557 if (sys->component && sys->component->is_enabled)
4558 mmal_component_disable(sys->component);
4559
4560- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
4561- picture_t *pic = (picture_t *)buffer->user_data;
4562- picture_Release(pic);
4563+ if (sys->in_pool != NULL)
4564+ mmal_pool_destroy(sys->in_pool);
4565+
4566+ hw_mmal_port_pool_ref_release(sys->out_ppr, false);
4567+ // Once we exit filter & sys are invalid so mark as such
4568+ if (sys->output != NULL)
4569+ sys->output->userdata = NULL;
4570+
4571+ if (sys->is_cma)
4572+ {
4573+ if (sys->output && sys->output->is_enabled)
4574+ mmal_port_disable(sys->output);
4575+
4576+ cma_buf_pool_deletez(&sys->cma_out_pool);
4577+
4578+ if (sys->out_pool != NULL)
4579+ mmal_pool_destroy(sys->out_pool);
4580 }
4581
4582- if (sys->filtered_pictures)
4583- mmal_queue_destroy(sys->filtered_pictures);
4584+ if (sys->out_q != NULL)
4585+ mmal_queue_destroy(sys->out_q);
4586
4587 if (sys->component)
4588 mmal_component_release(sys->component);
4589
4590- vlc_sem_destroy(&sys->sem);
4591+ cma_vcsm_exit(sys->vcsm_init_type);
4592+
4593 free(sys);
4594+}
4595+
4596
4597- bcm_host_deinit();
4598+static bool is_fmt_valid_in(const vlc_fourcc_t fmt)
4599+{
4600+ return fmt == VLC_CODEC_MMAL_OPAQUE ||
4601+ fmt == VLC_CODEC_MMAL_ZC_I420 ||
4602+ fmt == VLC_CODEC_MMAL_ZC_SAND8;
4603 }
4604
4605-static int send_output_buffer(filter_t *filter)
4606+static int OpenMmalDeinterlace(filter_t *filter)
4607 {
4608- filter_sys_t *sys = filter->p_sys;
4609- MMAL_BUFFER_HEADER_T *buffer;
4610+ int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
4611+ CLOCK_FREQ * filter->fmt_in.video.i_frame_rate_base /
4612+ filter->fmt_in.video.i_frame_rate : 0;
4613+
4614+ int ret = VLC_EGENERIC;
4615 MMAL_STATUS_T status;
4616- picture_t *picture;
4617- int ret = 0;
4618+ filter_sys_t *sys;
4619+
4620+ msg_Dbg(filter, "<<< %s", __func__);
4621+
4622+ if (!is_fmt_valid_in(filter->fmt_in.video.i_chroma) ||
4623+ filter->fmt_out.video.i_chroma != filter->fmt_in.video.i_chroma)
4624+ return VLC_EGENERIC;
4625
4626- if (!sys->output->is_enabled) {
4627- ret = VLC_EGENERIC;
4628- goto out;
4629+ sys = calloc(1, sizeof(filter_sys_t));
4630+ if (!sys)
4631+ return VLC_ENOMEM;
4632+ filter->p_sys = sys;
4633+
4634+ sys->seq_in = 1;
4635+ sys->seq_out = 15;
4636+ sys->is_cma = is_cma_buf_pic_chroma(filter->fmt_out.video.i_chroma);
4637+
4638+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
4639+ msg_Err(filter, "VCSM init failed");
4640+ goto fail;
4641+ }
4642+
4643+ if (rpi_is_model_pi4())
4644+ {
4645+ sys->half_rate = true;
4646+ sys->use_qpu = false;
4647+ sys->use_fast = true;
4648+ }
4649+ else
4650+ {
4651+ sys->half_rate = false;
4652+ sys->use_qpu = true;
4653+ sys->use_fast = false;
4654+ }
4655+ sys->use_passthrough = false;
4656+
4657+ if (filter->fmt_in.video.i_width * filter->fmt_in.video.i_height > 768 * 576)
4658+ {
4659+ // We get stressed if we have to try too hard - so make life easier
4660+ sys->half_rate = true;
4661+ // Also check we actually have enough memory to do this
4662+ // Memory always comes from GPU if Opaque
4663+ // Assume we have plenty of memory if it comes from CMA
4664+ if ((!sys->is_cma || sys->vcsm_init_type == VCSM_INIT_LEGACY) &&
4665+ hw_mmal_get_gpu_mem() < (96 << 20))
4666+ {
4667+ sys->use_passthrough = true;
4668+ msg_Warn(filter, "Deinterlace bypassed due to lack of GPU memory");
4669+ }
4670 }
4671
4672- picture = filter_NewPicture(filter);
4673- if (!picture) {
4674- msg_Warn(filter, "Failed to get new picture");
4675- ret = -1;
4676- goto out;
4677+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU))
4678+ sys->use_qpu = false;
4679+ if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV))
4680+ {
4681+ sys->use_fast = false;
4682+ sys->use_passthrough = false;
4683+ }
4684+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FAST))
4685+ {
4686+ sys->use_fast = true;
4687+ sys->use_passthrough = false;
4688+ }
4689+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NONE))
4690+ sys->use_passthrough = true;
4691+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FULL_RATE))
4692+ sys->half_rate = false;
4693+ if (var_InheritBool(filter, MMAL_DEINTERLACE_HALF_RATE))
4694+ sys->half_rate = true;
4695+
4696+ if (sys->use_passthrough)
4697+ {
4698+ filter->pf_video_filter = pass_deinterlace;
4699+ filter->pf_flush = pass_flush;
4700+ // Don't need VCSM - get rid of it now
4701+ cma_vcsm_exit(sys->vcsm_init_type);
4702+ sys->vcsm_init_type = VCSM_INIT_NONE;
4703+ return 0;
4704+ }
4705+
4706+ {
4707+ char dbuf0[5], dbuf1[5];
4708+ msg_Dbg(filter, "%s: %s,%dx%d [(%d,%d) %d/%d] -> %s,%dx%d [(%d,%d) %dx%d]: %s %s %s", __func__,
4709+ str_fourcc(dbuf0, filter->fmt_in.video.i_chroma),
4710+ filter->fmt_in.video.i_width, filter->fmt_in.video.i_height,
4711+ filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset,
4712+ filter->fmt_in.video.i_visible_width, filter->fmt_in.video.i_visible_height,
4713+ str_fourcc(dbuf1, filter->fmt_out.video.i_chroma),
4714+ filter->fmt_out.video.i_width, filter->fmt_out.video.i_height,
4715+ filter->fmt_out.video.i_x_offset, filter->fmt_out.video.i_y_offset,
4716+ filter->fmt_out.video.i_visible_width, filter->fmt_out.video.i_visible_height,
4717+ sys->use_qpu ? "QPU" : "VPU",
4718+ sys->use_fast ? "FAST" : "ADV",
4719+ sys->use_passthrough ? "PASS" : sys->half_rate ? "HALF" : "FULL");
4720 }
4721- picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate;
4722- picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base;
4723
4724- buffer = picture->p_sys->buffer;
4725- buffer->user_data = picture;
4726- buffer->cmd = 0;
4727+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
4728+ if (status != MMAL_SUCCESS) {
4729+ msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
4730+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4731+ goto fail;
4732+ }
4733
4734- mmal_picture_lock(picture);
4735+ {
4736+ const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
4737+ { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
4738+ sys->use_fast ?
4739+ MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST :
4740+ MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
4741+ 4,
4742+ { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu }
4743+ };
4744
4745- status = mmal_port_send_buffer(sys->output, buffer);
4746+ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
4747+ if (status != MMAL_SUCCESS) {
4748+ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
4749+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
4750+ goto fail;
4751+ }
4752+ }
4753+
4754+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4755+ status = mmal_port_enable(sys->component->control, control_port_cb);
4756 if (status != MMAL_SUCCESS) {
4757- msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)",
4758- status, mmal_status_to_string(status));
4759- mmal_buffer_header_release(buffer);
4760- picture_Release(picture);
4761- ret = -1;
4762- } else {
4763- atomic_fetch_add(&sys->output_in_transit, 1);
4764- vlc_sem_post(&sys->sem);
4765+ msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
4766+ sys->component->control->name, status, mmal_status_to_string(status));
4767+ goto fail;
4768 }
4769
4770-out:
4771- return ret;
4772-}
4773+ sys->input = sys->component->input[0];
4774+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4775+ sys->input->format->encoding = vlc_to_mmal_video_fourcc(&filter->fmt_in.video);
4776+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &filter->fmt_in.video);
4777
4778-static void fill_output_port(filter_t *filter)
4779-{
4780- filter_sys_t *sys = filter->p_sys;
4781- /* allow at least 2 buffers in transit */
4782- unsigned max_buffers_in_transit = __MAX(2, MIN_NUM_BUFFERS_IN_TRANSIT);
4783- int buffers_available = sys->output->buffer_num -
4784- atomic_load(&sys->output_in_transit) -
4785- mmal_queue_length(sys->filtered_pictures);
4786- int buffers_to_send = max_buffers_in_transit - sys->output_in_transit;
4787- int i;
4788+ es_format_Copy(&filter->fmt_out, &filter->fmt_in);
4789+ if (!sys->half_rate)
4790+ filter->fmt_out.video.i_frame_rate *= 2;
4791
4792- if (buffers_to_send > buffers_available)
4793- buffers_to_send = buffers_available;
4794+ status = mmal_port_format_commit(sys->input);
4795+ if (status != MMAL_SUCCESS) {
4796+ msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
4797+ sys->input->name, status, mmal_status_to_string(status));
4798+ goto fail;
4799+ }
4800+ sys->input->buffer_size = sys->input->buffer_size_recommended;
4801+ sys->input->buffer_num = 30;
4802+// sys->input->buffer_num = sys->input->buffer_num_recommended;
4803
4804-#ifndef NDEBUG
4805- msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
4806- buffers_to_send, buffers_available, sys->output_in_transit,
4807- sys->output->buffer_num);
4808-#endif
4809- for (i = 0; i < buffers_to_send; ++i) {
4810- if (send_output_buffer(filter) < 0)
4811- break;
4812+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
4813+ {
4814+ msg_Err(filter, "Failed to create input pool");
4815+ goto fail;
4816 }
4817-}
4818
4819-static picture_t *deinterlace(filter_t *filter, picture_t *picture)
4820-{
4821- filter_sys_t *sys = filter->p_sys;
4822- MMAL_BUFFER_HEADER_T *buffer;
4823- picture_t *out_picture = NULL;
4824- picture_t *ret = NULL;
4825- MMAL_STATUS_T status;
4826- unsigned i = 0;
4827+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
4828+ if (status != MMAL_SUCCESS) {
4829+ msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
4830+ sys->input->name, status, mmal_status_to_string(status));
4831+ goto fail;
4832+ }
4833
4834- fill_output_port(filter);
4835+ status = mmal_port_enable(sys->input, di_input_port_cb);
4836+ if (status != MMAL_SUCCESS) {
4837+ msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
4838+ sys->input->name, status, mmal_status_to_string(status));
4839+ goto fail;
4840+ }
4841
4842- buffer = picture->p_sys->buffer;
4843- buffer->user_data = picture;
4844- buffer->pts = picture->date;
4845- buffer->cmd = 0;
4846
4847- if (!picture->p_sys->displayed) {
4848- status = mmal_port_send_buffer(sys->input, buffer);
4849- if (status != MMAL_SUCCESS) {
4850- msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)",
4851- status, mmal_status_to_string(status));
4852- picture_Release(picture);
4853- } else {
4854- picture->p_sys->displayed = true;
4855- atomic_fetch_add(&sys->input_in_transit, 1);
4856- vlc_sem_post(&sys->sem);
4857- }
4858- } else {
4859- picture_Release(picture);
4860- }
4861-
4862- /*
4863- * Send output buffers
4864- */
4865- while(atomic_load(&sys->started) && i < 2) {
4866- if (buffer = mmal_queue_timedwait(sys->filtered_pictures, 2000)) {
4867- i++;
4868- if (!out_picture) {
4869- out_picture = (picture_t *)buffer->user_data;
4870- ret = out_picture;
4871- } else {
4872- out_picture->p_next = (picture_t *)buffer->user_data;
4873- out_picture = out_picture->p_next;
4874- }
4875- out_picture->date = buffer->pts;
4876- } else {
4877- msg_Dbg(filter, "Failed waiting for filtered picture");
4878- break;
4879- }
4880+ if ((sys->out_q = mmal_queue_create()) == NULL)
4881+ {
4882+ msg_Err(filter, "Failed to create out Q");
4883+ goto fail;
4884 }
4885- if (out_picture)
4886- out_picture->p_next = NULL;
4887
4888- return ret;
4889-}
4890-
4891-static void flush(filter_t *filter)
4892-{
4893- filter_sys_t *sys = filter->p_sys;
4894- MMAL_BUFFER_HEADER_T *buffer;
4895+ sys->output = sys->component->output[0];
4896+ mmal_format_full_copy(sys->output->format, sys->input->format);
4897
4898- msg_Dbg(filter, "flush deinterlace filter");
4899+ if (!sys->is_cma)
4900+ {
4901+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS)
4902+ goto fail;
4903+ }
4904+ else
4905+ {
4906+ // CMA stuff
4907+ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
4908+
4909+ if ((sys->cma_out_pool = cma_buf_pool_new(8, 8, true, "deinterlace")) == NULL)
4910+ {
4911+ msg_Err(filter, "Failed to alloc cma buf pool");
4912+ goto fail;
4913+ }
4914
4915- msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)",
4916- sys->input_in_transit, sys->output_in_transit);
4917- mmal_port_flush(sys->output);
4918- mmal_port_flush(sys->input);
4919-
4920- msg_Dbg(filter, "flush: wait for all buffers to be returned");
4921- while (atomic_load(&sys->input_in_transit) ||
4922- atomic_load(&sys->output_in_transit))
4923- vlc_sem_wait(&sys->sem);
4924-
4925- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
4926- picture_t *pic = (picture_t *)buffer->user_data;
4927- msg_Dbg(filter, "flush: release already filtered pic %p",
4928- (void *)pic);
4929- picture_Release(pic);
4930- }
4931- atomic_store(&sys->started, false);
4932- msg_Dbg(filter, "flush: done");
4933-}
4934+ // Rate control done by CMA in flight logic, so have "inexhaustable" pool here
4935+ if ((sys->out_pool = mmal_pool_create(30, 0)) == NULL)
4936+ {
4937+ msg_Err(filter, "Failed to alloc out pool");
4938+ goto fail;
4939+ }
4940
4941-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4942-{
4943- filter_t *filter = (filter_t *)port->userdata;
4944- MMAL_STATUS_T status;
4945+ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, true);
4946
4947- if (buffer->cmd == MMAL_EVENT_ERROR) {
4948- status = *(uint32_t *)buffer->data;
4949- msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
4950- mmal_status_to_string(status));
4951- }
4952+ if ((status = mmal_port_format_commit(sys->output)) != MMAL_SUCCESS)
4953+ {
4954+ msg_Err(filter, "Output port format commit failed");
4955+ goto fail;
4956+ }
4957
4958- mmal_buffer_header_release(buffer);
4959-}
4960+ sys->output->buffer_num = 30;
4961+ sys->output->buffer_size = sys->output->buffer_size_recommended;
4962
4963-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
4964-{
4965- picture_t *picture = (picture_t *)buffer->user_data;
4966- filter_t *filter = (filter_t *)port->userdata;
4967- filter_sys_t *sys = filter->p_sys;
4968+ // CB just drops all bufs into out_q
4969+ if ((status = mmal_port_enable(sys->output, di_output_port_cb)) != MMAL_SUCCESS)
4970+ {
4971+ msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
4972+ sys->output->name, status, mmal_status_to_string(status));
4973+ goto fail;
4974+ }
4975+ }
4976
4977- if (picture) {
4978- picture_Release(picture);
4979- } else {
4980- msg_Warn(filter, "Got buffer without picture on input port - OOOPS");
4981- mmal_buffer_header_release(buffer);
4982+ status = mmal_component_enable(sys->component);
4983+ if (status != MMAL_SUCCESS) {
4984+ msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
4985+ sys->component->name, status, mmal_status_to_string(status));
4986+ goto fail;
4987 }
4988
4989- atomic_fetch_sub(&sys->input_in_transit, 1);
4990- vlc_sem_post(&sys->sem);
4991+ filter->pf_video_filter = deinterlace;
4992+ filter->pf_flush = di_flush;
4993+ return 0;
4994+
4995+fail:
4996+ CloseMmalDeinterlace(filter);
4997+ return ret;
4998 }
4999
5000-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
5001-{
5002- filter_t *filter = (filter_t *)port->userdata;
5003- filter_sys_t *sys = filter->p_sys;
5004- picture_t *picture;
5005+vlc_module_begin()
5006+ set_shortname(N_("MMAL deinterlace"))
5007+ set_description(N_("MMAL-based deinterlace filter plugin"))
5008+ set_capability("video filter", 900)
5009+ set_category(CAT_VIDEO)
5010+ set_subcategory(SUBCAT_VIDEO_VFILTER)
5011+ set_callbacks(OpenMmalDeinterlace, CloseMmalDeinterlace)
5012+ add_shortcut("deinterlace")
5013+ add_bool(MMAL_DEINTERLACE_NO_QPU, false, MMAL_DEINTERLACE_NO_QPU_TEXT,
5014+ MMAL_DEINTERLACE_NO_QPU_LONGTEXT, true);
5015+ add_bool(MMAL_DEINTERLACE_ADV, false, MMAL_DEINTERLACE_ADV_TEXT,
5016+ MMAL_DEINTERLACE_ADV_LONGTEXT, true);
5017+ add_bool(MMAL_DEINTERLACE_FAST, false, MMAL_DEINTERLACE_FAST_TEXT,
5018+ MMAL_DEINTERLACE_FAST_LONGTEXT, true);
5019+ add_bool(MMAL_DEINTERLACE_NONE, false, MMAL_DEINTERLACE_NONE_TEXT,
5020+ MMAL_DEINTERLACE_NONE_LONGTEXT, true);
5021+ add_bool(MMAL_DEINTERLACE_HALF_RATE, false, MMAL_DEINTERLACE_HALF_RATE_TEXT,
5022+ MMAL_DEINTERLACE_HALF_RATE_LONGTEXT, true);
5023+ add_bool(MMAL_DEINTERLACE_FULL_RATE, false, MMAL_DEINTERLACE_FULL_RATE_TEXT,
5024+ MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true);
5025+
5026+vlc_module_end()
5027+
5028
5029- if (buffer->cmd == 0) {
5030- if (buffer->length > 0) {
5031- atomic_store(&sys->started, true);
5032- mmal_queue_put(sys->filtered_pictures, buffer);
5033- picture = (picture_t *)buffer->user_data;
5034- } else {
5035- picture = (picture_t *)buffer->user_data;
5036- picture_Release(picture);
5037- }
5038-
5039- atomic_fetch_sub(&sys->output_in_transit, 1);
5040- vlc_sem_post(&sys->sem);
5041- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
5042- msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled");
5043- mmal_buffer_header_release(buffer);
5044- } else {
5045- mmal_buffer_header_release(buffer);
5046- }
5047-}
5048--- /dev/null
5049+++ b/modules/hw/mmal/mmal_avcodec.c
5050@@ -0,0 +1,2175 @@
5051+/*****************************************************************************
5052+ * video.c: video decoder using the libavcodec library
5053+ *****************************************************************************
5054+ * Copyright (C) 1999-2001 VLC authors and VideoLAN
5055+ * $Id$
5056+ *
5057+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>
5058+ * Gildas Bazin <gbazin@videolan.org>
5059+ *
5060+ * This program is free software; you can redistribute it and/or modify it
5061+ * under the terms of the GNU Lesser General Public License as published by
5062+ * the Free Software Foundation; either version 2.1 of the License, or
5063+ * (at your option) any later version.
5064+ *
5065+ * This program is distributed in the hope that it will be useful,
5066+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
5067+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5068+ * GNU Lesser General Public License for more details.
5069+ *
5070+ * You should have received a copy of the GNU Lesser General Public License
5071+ * along with this program; if not, write to the Free Software Foundation,
5072+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
5073+ *****************************************************************************/
5074+
5075+/*****************************************************************************
5076+ * Preamble
5077+ *****************************************************************************/
5078+#include "config.h"
5079+
5080+#include <vlc_common.h>
5081+#include <vlc_codec.h>
5082+#include <vlc_avcodec.h>
5083+#include <vlc_cpu.h>
5084+#include <vlc_atomic.h>
5085+#include <assert.h>
5086+
5087+#include <libavcodec/avcodec.h>
5088+#include <libavutil/mem.h>
5089+#include <libavutil/pixdesc.h>
5090+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
5091+#include <libavutil/mastering_display_metadata.h>
5092+#endif
5093+
5094+//#include "avcodec.h"
5095+//#include "va.h"
5096+
5097+#include <vlc_plugin.h>
5098+#include <libavutil/rpi_sand_fns.h>
5099+#include <libavcodec/rpi_zc.h>
5100+#include "../../codec/cc.h"
5101+#include "../../codec/avcodec/avcommon.h" // ??? Beware over inclusion
5102+#include "mmal_cma.h"
5103+#include "mmal_picture.h"
5104+
5105+#define TRACE_ALL 0
5106+
5107+#define BUFFERS_IN_FLIGHT 5 // Default max value for in flight buffers
5108+#define BUFFERS_IN_FLIGHT_UHD 3 // Fewer if very big
5109+
5110+#define MMAL_AVCODEC_BUFFERS "mmal-avcodec-buffers"
5111+#define MMAL_AVCODEC_BUFFERS_TEXT N_("In flight buffer count before blocking.")
5112+#define MMAL_AVCODEC_BUFFERS_LONGTEXT N_("In flight buffer count before blocking. " \
5113+"Beware that incautious changing of this can lead to lockup. " \
5114+"Zero will disable the module.")
5115+
5116+
5117+// Fwd declarations required due to wanting to avoid reworking the original
5118+// code too much
5119+static void MmalAvcodecCloseDecoder( vlc_object_t *obj );
5120+
5121+
5122+/*****************************************************************************
5123+ * decoder_sys_t : decoder descriptor
5124+ *****************************************************************************/
5125+struct decoder_sys_t
5126+{
5127+ AVCodecContext *p_context;
5128+ const AVCodec *p_codec;
5129+
5130+ /* Video decoder specific part */
5131+ date_t pts;
5132+
5133+ /* Closed captions for decoders */
5134+ cc_data_t cc;
5135+
5136+ /* for frame skipping algo */
5137+ bool b_hurry_up;
5138+ bool b_show_corrupted;
5139+ bool b_from_preroll;
5140+ enum AVDiscard i_skip_frame;
5141+
5142+ /* how many decoded frames are late */
5143+ int i_late_frames;
5144+ mtime_t i_late_frames_start;
5145+ mtime_t i_last_late_delay;
5146+
5147+ /* for direct rendering */
5148+ bool b_direct_rendering;
5149+ atomic_bool b_dr_failure;
5150+
5151+ /* Hack to force display of still pictures */
5152+ bool b_first_frame;
5153+
5154+
5155+ /* */
5156+ bool palette_sent;
5157+
5158+ /* VA API */
5159+// vlc_va_t *p_va;
5160+ enum PixelFormat pix_fmt;
5161+ int profile;
5162+ int level;
5163+
5164+ vlc_sem_t sem_mt;
5165+
5166+ // Rpi vars
5167+ cma_buf_pool_t * cma_pool;
5168+ bool pool_alloc_1;
5169+ vcsm_init_type_t vcsm_init_type;
5170+ int cma_in_flight_max;
5171+ // Debug
5172+ decoder_t * p_dec;
5173+};
5174+
5175+
5176+static vlc_fourcc_t
5177+ZcFindVlcChroma(const int i_ffmpeg_chroma)
5178+{
5179+ switch (i_ffmpeg_chroma)
5180+ {
5181+ // This is all we claim to deal with
5182+ // In theory RGB should be doable within our current framework
5183+ case AV_PIX_FMT_YUV420P:
5184+ return VLC_CODEC_MMAL_ZC_I420;
5185+ case AV_PIX_FMT_SAND128:
5186+ case AV_PIX_FMT_RPI4_8:
5187+ return VLC_CODEC_MMAL_ZC_SAND8;
5188+ case AV_PIX_FMT_SAND64_10:
5189+ return VLC_CODEC_MMAL_ZC_SAND10;
5190+ case AV_PIX_FMT_RPI4_10:
5191+ return VLC_CODEC_MMAL_ZC_SAND30;
5192+ default:
5193+ break;
5194+ }
5195+ return 0;
5196+}
5197+
5198+// Pix Fmt conv for MMal
5199+// video_fromat from ffmpeg pic_fmt
5200+static int
5201+ZcGetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma )
5202+{
5203+ fmt->i_rmask = 0;
5204+ fmt->i_gmask = 0;
5205+ fmt->i_bmask = 0;
5206+ fmt->i_chroma = ZcFindVlcChroma(i_ffmpeg_chroma);
5207+
5208+ return fmt->i_chroma == 0 ? -1 : 0;
5209+}
5210+
5211+
5212+// Format chooser is way simpler than vlc
5213+static enum PixelFormat
5214+ZcGetFormat(AVCodecContext *p_context, const enum PixelFormat *pi_fmt)
5215+{
5216+ enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt);
5217+ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++)
5218+ {
5219+ if (ZcFindVlcChroma(pi_fmt[i]) != 0)
5220+ return pi_fmt[i];
5221+ }
5222+ return swfmt;
5223+}
5224+
5225+
5226+static void cma_avbuf_pool_free(void * v)
5227+{
5228+ cma_buf_unref(v);
5229+}
5230+
5231+static unsigned int zc_buf_vcsm_handle(void * v)
5232+{
5233+ return cma_buf_vcsm_handle(v);
5234+}
5235+
5236+static unsigned int zc_buf_vc_handle(void * v)
5237+{
5238+ return cma_buf_vc_handle(v);
5239+}
5240+
5241+static void * zc_buf_map_arm(void * v)
5242+{
5243+ return cma_buf_addr(v);
5244+}
5245+
5246+static unsigned int zc_buf_map_vc(void * v)
5247+{
5248+ return cma_buf_vc_addr(v);
5249+}
5250+
5251+
5252+
5253+static const av_rpi_zc_buf_fn_tab_t zc_buf_fn_tab = {
5254+ .free = cma_avbuf_pool_free,
5255+
5256+ .vcsm_handle = zc_buf_vcsm_handle,
5257+ .vc_handle = zc_buf_vc_handle,
5258+ .map_arm = zc_buf_map_arm,
5259+ .map_vc = zc_buf_map_vc
5260+};
5261+
5262+
5263+static AVBufferRef *
5264+zc_alloc_buf(void * v, size_t size, const AVRpiZcFrameGeometry * geo)
5265+{
5266+ decoder_t * const dec = v;
5267+ decoder_sys_t * const sys = dec->p_sys;
5268+
5269+ VLC_UNUSED(geo);
5270+
5271+ assert(sys != NULL);
5272+
5273+ const unsigned int dec_pool_req = av_rpi_zc_get_decoder_pool_size(sys->p_context->opaque);
5274+ if (dec_pool_req != 0)
5275+ {
5276+ cma_buf_pool_resize(sys->cma_pool, dec_pool_req + sys->cma_in_flight_max, sys->cma_in_flight_max);
5277+
5278+ if (!sys->pool_alloc_1)
5279+ {
5280+ sys->pool_alloc_1 = true;
5281+ msg_Dbg(dec, "Pool size: (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size);
5282+ if (cma_buf_pool_fill(sys->cma_pool, size) != 0)
5283+ msg_Warn(dec, "Failed to preallocate decoder pool (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size);
5284+ }
5285+ }
5286+
5287+ void * const cmabuf = cma_buf_pool_alloc_buf(sys->cma_pool, size);
5288+
5289+ if (cmabuf == NULL)
5290+ {
5291+ msg_Err(dec, "CMA buf pool alloc buf failed");
5292+ return NULL;
5293+ }
5294+
5295+ AVBufferRef *const avbuf = av_rpi_zc_buf(cma_buf_size(cmabuf), 0, cmabuf, &zc_buf_fn_tab);
5296+
5297+ if (avbuf == NULL)
5298+ {
5299+ msg_Err(dec, "av_rpi_zc_buf failed");
5300+ cma_buf_unref(cmabuf);
5301+ return NULL;
5302+ }
5303+
5304+ return avbuf;
5305+}
5306+
5307+static void
5308+zc_free_pool(void * v)
5309+{
5310+ decoder_t * const dec = v;
5311+ cma_buf_pool_delete(dec->p_sys->cma_pool);
5312+}
5313+
5314+
5315+static const uint8_t shift_01[] = {0,1,1,1};
5316+static const uint8_t pb_1[] = {1,1,1,1};
5317+static const uint8_t pb_12[] = {1,2,2,2};
5318+static const uint8_t pb_24[] = {2,4,4,4};
5319+static const uint8_t pb_4[] = {4,4,4,4};
5320+
5321+static int set_pic_from_frame(picture_t * const pic, const AVFrame * const frame)
5322+{
5323+ const uint8_t * hs = shift_01;
5324+ const uint8_t * ws = shift_01;
5325+ const uint8_t * pb = pb_1;
5326+
5327+ switch (pic->format.i_chroma)
5328+ {
5329+ case VLC_CODEC_MMAL_ZC_RGB32:
5330+ pic->i_planes = 1;
5331+ pb = pb_4;
5332+ break;
5333+ case VLC_CODEC_MMAL_ZC_I420:
5334+ pic->i_planes = 3;
5335+ break;
5336+ case VLC_CODEC_MMAL_ZC_SAND8:
5337+ pic->i_planes = 2;
5338+ pb = pb_12;
5339+ break;
5340+ case VLC_CODEC_MMAL_ZC_SAND10:
5341+ case VLC_CODEC_MMAL_ZC_SAND30: // Lies: SAND30 is "special"
5342+ pic->i_planes = 2;
5343+ pb = pb_24;
5344+ break;
5345+ default:
5346+ return VLC_EGENERIC;
5347+ }
5348+
5349+ const cma_buf_t * const cb = cma_buf_pic_get(pic);
5350+ uint8_t * const data = cma_buf_addr(cb);
5351+ if (data == NULL) {
5352+ return VLC_ENOMEM;
5353+ }
5354+
5355+ uint8_t * frame_end = frame->data[0] + cma_buf_size(cb);
5356+ for (int i = 0; i != pic->i_planes; ++i) {
5357+ // Calculate lines from gap between planes
5358+ // This will give us an accurate "height" for later use by MMAL
5359+ const int lines = ((i + 1 == pic->i_planes ? frame_end : frame->data[i + 1]) -
5360+ frame->data[i]) / frame->linesize[i];
5361+ pic->p[i] = (plane_t){
5362+ .p_pixels = data + (frame->data[i] - frame->data[0]),
5363+ .i_lines = lines,
5364+ .i_pitch = frame->linesize[i],
5365+ .i_pixel_pitch = pb[i],
5366+ .i_visible_lines = av_frame_cropped_height(frame) >> hs[i],
5367+ .i_visible_pitch = av_frame_cropped_width(frame) >> ws[i]
5368+ };
5369+ }
5370+ return 0;
5371+}
5372+
5373+
5374+//============================================================================
5375+//
5376+// Nicked from avcodec/fourcc.c
5377+//
5378+// * Really we should probably use that directly
5379+
5380+/*
5381+ * Video Codecs
5382+ */
5383+
5384+struct vlc_avcodec_fourcc
5385+{
5386+ vlc_fourcc_t i_fourcc;
5387+ unsigned i_codec;
5388+};
5389+
5390+
5391+static const struct vlc_avcodec_fourcc video_codecs[] =
5392+{
5393+ { VLC_CODEC_MP1V, AV_CODEC_ID_MPEG1VIDEO },
5394+ { VLC_CODEC_MP2V, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */
5395+ { VLC_CODEC_MPGV, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */
5396+ /* AV_CODEC_ID_MPEG2VIDEO_XVMC */
5397+ { VLC_CODEC_H261, AV_CODEC_ID_H261 },
5398+ { VLC_CODEC_H263, AV_CODEC_ID_H263 },
5399+ { VLC_CODEC_RV10, AV_CODEC_ID_RV10 },
5400+ { VLC_CODEC_RV13, AV_CODEC_ID_RV10 },
5401+ { VLC_CODEC_RV20, AV_CODEC_ID_RV20 },
5402+ { VLC_CODEC_MJPG, AV_CODEC_ID_MJPEG },
5403+ { VLC_CODEC_MJPGB, AV_CODEC_ID_MJPEGB },
5404+ { VLC_CODEC_LJPG, AV_CODEC_ID_LJPEG },
5405+ { VLC_CODEC_SP5X, AV_CODEC_ID_SP5X },
5406+ { VLC_CODEC_JPEGLS, AV_CODEC_ID_JPEGLS },
5407+ { VLC_CODEC_MP4V, AV_CODEC_ID_MPEG4 },
5408+ /* AV_CODEC_ID_RAWVIDEO */
5409+ { VLC_CODEC_DIV1, AV_CODEC_ID_MSMPEG4V1 },
5410+ { VLC_CODEC_DIV2, AV_CODEC_ID_MSMPEG4V2 },
5411+ { VLC_CODEC_DIV3, AV_CODEC_ID_MSMPEG4V3 },
5412+ { VLC_CODEC_WMV1, AV_CODEC_ID_WMV1 },
5413+ { VLC_CODEC_WMV2, AV_CODEC_ID_WMV2 },
5414+ { VLC_CODEC_H263P, AV_CODEC_ID_H263P },
5415+ { VLC_CODEC_H263I, AV_CODEC_ID_H263I },
5416+ { VLC_CODEC_FLV1, AV_CODEC_ID_FLV1 },
5417+ { VLC_CODEC_SVQ1, AV_CODEC_ID_SVQ1 },
5418+ { VLC_CODEC_SVQ3, AV_CODEC_ID_SVQ3 },
5419+ { VLC_CODEC_DV, AV_CODEC_ID_DVVIDEO },
5420+ { VLC_CODEC_HUFFYUV, AV_CODEC_ID_HUFFYUV },
5421+ { VLC_CODEC_CYUV, AV_CODEC_ID_CYUV },
5422+ { VLC_CODEC_H264, AV_CODEC_ID_H264 },
5423+ { VLC_CODEC_INDEO3, AV_CODEC_ID_INDEO3 },
5424+ { VLC_CODEC_VP3, AV_CODEC_ID_VP3 },
5425+ { VLC_CODEC_THEORA, AV_CODEC_ID_THEORA },
5426+#if ( !defined( WORDS_BIGENDIAN ) )
5427+ /* Asus Video (Another thing that doesn't work on PPC) */
5428+ { VLC_CODEC_ASV1, AV_CODEC_ID_ASV1 },
5429+ { VLC_CODEC_ASV2, AV_CODEC_ID_ASV2 },
5430+#endif
5431+ { VLC_CODEC_FFV1, AV_CODEC_ID_FFV1 },
5432+ { VLC_CODEC_4XM, AV_CODEC_ID_4XM },
5433+ { VLC_CODEC_VCR1, AV_CODEC_ID_VCR1 },
5434+ { VLC_CODEC_CLJR, AV_CODEC_ID_CLJR },
5435+ { VLC_CODEC_MDEC, AV_CODEC_ID_MDEC },
5436+ { VLC_CODEC_ROQ, AV_CODEC_ID_ROQ },
5437+ { VLC_CODEC_INTERPLAY, AV_CODEC_ID_INTERPLAY_VIDEO },
5438+ { VLC_CODEC_XAN_WC3, AV_CODEC_ID_XAN_WC3 },
5439+ { VLC_CODEC_XAN_WC4, AV_CODEC_ID_XAN_WC4 },
5440+ { VLC_CODEC_RPZA, AV_CODEC_ID_RPZA },
5441+ { VLC_CODEC_CINEPAK, AV_CODEC_ID_CINEPAK },
5442+ { VLC_CODEC_WS_VQA, AV_CODEC_ID_WS_VQA },
5443+ { VLC_CODEC_MSRLE, AV_CODEC_ID_MSRLE },
5444+ { VLC_CODEC_MSVIDEO1, AV_CODEC_ID_MSVIDEO1 },
5445+ { VLC_CODEC_IDCIN, AV_CODEC_ID_IDCIN },
5446+ { VLC_CODEC_8BPS, AV_CODEC_ID_8BPS },
5447+ { VLC_CODEC_SMC, AV_CODEC_ID_SMC },
5448+ { VLC_CODEC_FLIC, AV_CODEC_ID_FLIC },
5449+ { VLC_CODEC_TRUEMOTION1, AV_CODEC_ID_TRUEMOTION1 },
5450+ { VLC_CODEC_VMDVIDEO, AV_CODEC_ID_VMDVIDEO },
5451+ { VLC_CODEC_LCL_MSZH, AV_CODEC_ID_MSZH },
5452+ { VLC_CODEC_LCL_ZLIB, AV_CODEC_ID_ZLIB },
5453+ { VLC_CODEC_QTRLE, AV_CODEC_ID_QTRLE },
5454+ { VLC_CODEC_TSCC, AV_CODEC_ID_TSCC },
5455+ { VLC_CODEC_ULTI, AV_CODEC_ID_ULTI },
5456+ { VLC_CODEC_QDRAW, AV_CODEC_ID_QDRAW },
5457+ { VLC_CODEC_VIXL, AV_CODEC_ID_VIXL },
5458+ { VLC_CODEC_QPEG, AV_CODEC_ID_QPEG },
5459+ { VLC_CODEC_PNG, AV_CODEC_ID_PNG },
5460+ { VLC_CODEC_PPM, AV_CODEC_ID_PPM },
5461+ /* AV_CODEC_ID_PBM */
5462+ { VLC_CODEC_PGM, AV_CODEC_ID_PGM },
5463+ { VLC_CODEC_PGMYUV, AV_CODEC_ID_PGMYUV },
5464+ { VLC_CODEC_PAM, AV_CODEC_ID_PAM },
5465+ { VLC_CODEC_FFVHUFF, AV_CODEC_ID_FFVHUFF },
5466+ { VLC_CODEC_RV30, AV_CODEC_ID_RV30 },
5467+ { VLC_CODEC_RV40, AV_CODEC_ID_RV40 },
5468+ { VLC_CODEC_VC1, AV_CODEC_ID_VC1 },
5469+ { VLC_CODEC_WMVA, AV_CODEC_ID_VC1 },
5470+ { VLC_CODEC_WMV3, AV_CODEC_ID_WMV3 },
5471+ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3 },
5472+ { VLC_CODEC_LOCO, AV_CODEC_ID_LOCO },
5473+ { VLC_CODEC_WNV1, AV_CODEC_ID_WNV1 },
5474+ { VLC_CODEC_AASC, AV_CODEC_ID_AASC },
5475+ { VLC_CODEC_INDEO2, AV_CODEC_ID_INDEO2 },
5476+ { VLC_CODEC_FRAPS, AV_CODEC_ID_FRAPS },
5477+ { VLC_CODEC_TRUEMOTION2, AV_CODEC_ID_TRUEMOTION2 },
5478+ { VLC_CODEC_BMP, AV_CODEC_ID_BMP },
5479+ { VLC_CODEC_CSCD, AV_CODEC_ID_CSCD },
5480+ { VLC_CODEC_MMVIDEO, AV_CODEC_ID_MMVIDEO },
5481+ { VLC_CODEC_ZMBV, AV_CODEC_ID_ZMBV },
5482+ { VLC_CODEC_AVS, AV_CODEC_ID_AVS },
5483+ { VLC_CODEC_SMACKVIDEO, AV_CODEC_ID_SMACKVIDEO },
5484+ { VLC_CODEC_NUV, AV_CODEC_ID_NUV },
5485+ { VLC_CODEC_KMVC, AV_CODEC_ID_KMVC },
5486+ { VLC_CODEC_FLASHSV, AV_CODEC_ID_FLASHSV },
5487+ { VLC_CODEC_CAVS, AV_CODEC_ID_CAVS },
5488+ { VLC_CODEC_JPEG2000, AV_CODEC_ID_JPEG2000 },
5489+ { VLC_CODEC_VMNC, AV_CODEC_ID_VMNC },
5490+ { VLC_CODEC_VP5, AV_CODEC_ID_VP5 },
5491+ { VLC_CODEC_VP6, AV_CODEC_ID_VP6 },
5492+ { VLC_CODEC_VP6F, AV_CODEC_ID_VP6F },
5493+ { VLC_CODEC_TARGA, AV_CODEC_ID_TARGA },
5494+ { VLC_CODEC_DSICINVIDEO, AV_CODEC_ID_DSICINVIDEO },
5495+ { VLC_CODEC_TIERTEXSEQVIDEO, AV_CODEC_ID_TIERTEXSEQVIDEO },
5496+ { VLC_CODEC_TIFF, AV_CODEC_ID_TIFF },
5497+ { VLC_CODEC_GIF, AV_CODEC_ID_GIF },
5498+ { VLC_CODEC_DXA, AV_CODEC_ID_DXA },
5499+ { VLC_CODEC_DNXHD, AV_CODEC_ID_DNXHD },
5500+ { VLC_CODEC_THP, AV_CODEC_ID_THP },
5501+ { VLC_CODEC_SGI, AV_CODEC_ID_SGI },
5502+ { VLC_CODEC_C93, AV_CODEC_ID_C93 },
5503+ { VLC_CODEC_BETHSOFTVID, AV_CODEC_ID_BETHSOFTVID },
5504+ /* AV_CODEC_ID_PTX */
5505+ { VLC_CODEC_TXD, AV_CODEC_ID_TXD },
5506+ { VLC_CODEC_VP6A, AV_CODEC_ID_VP6A },
5507+ { VLC_CODEC_AMV, AV_CODEC_ID_AMV },
5508+ { VLC_CODEC_VB, AV_CODEC_ID_VB },
5509+ { VLC_CODEC_PCX, AV_CODEC_ID_PCX },
5510+ /* AV_CODEC_ID_SUNRAST */
5511+ { VLC_CODEC_INDEO4, AV_CODEC_ID_INDEO4 },
5512+ { VLC_CODEC_INDEO5, AV_CODEC_ID_INDEO5 },
5513+ { VLC_CODEC_MIMIC, AV_CODEC_ID_MIMIC },
5514+ { VLC_CODEC_RL2, AV_CODEC_ID_RL2 },
5515+ { VLC_CODEC_ESCAPE124, AV_CODEC_ID_ESCAPE124 },
5516+ { VLC_CODEC_DIRAC, AV_CODEC_ID_DIRAC },
5517+ { VLC_CODEC_BFI, AV_CODEC_ID_BFI },
5518+ { VLC_CODEC_CMV, AV_CODEC_ID_CMV },
5519+ { VLC_CODEC_MOTIONPIXELS, AV_CODEC_ID_MOTIONPIXELS },
5520+ { VLC_CODEC_TGV, AV_CODEC_ID_TGV },
5521+ { VLC_CODEC_TGQ, AV_CODEC_ID_TGQ },
5522+ { VLC_CODEC_TQI, AV_CODEC_ID_TQI },
5523+ { VLC_CODEC_AURA, AV_CODEC_ID_AURA },
5524+ /* AV_CODEC_ID_AURA2 */
5525+ /* AV_CODEC_ID_V210X */
5526+ { VLC_CODEC_TMV, AV_CODEC_ID_TMV },
5527+ { VLC_CODEC_V210, AV_CODEC_ID_V210 },
5528+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 54, 50, 100 ) && LIBAVCODEC_VERSION_MICRO >= 100
5529+ { VLC_CODEC_VUYA, AV_CODEC_ID_AYUV },
5530+#endif
5531+ /* AV_CODEC_ID_DPX */
5532+ { VLC_CODEC_MAD, AV_CODEC_ID_MAD },
5533+ { VLC_CODEC_FRWU, AV_CODEC_ID_FRWU },
5534+ { VLC_CODEC_FLASHSV2, AV_CODEC_ID_FLASHSV2 },
5535+ /* AV_CODEC_ID_CDGRAPHICS */
5536+ /* AV_CODEC_ID_R210 */
5537+ { VLC_CODEC_ANM, AV_CODEC_ID_ANM },
5538+ { VLC_CODEC_BINKVIDEO, AV_CODEC_ID_BINKVIDEO },
5539+ /* AV_CODEC_ID_IFF_ILBM */
5540+ /* AV_CODEC_ID_IFF_BYTERUN1 */
5541+ { VLC_CODEC_KGV1, AV_CODEC_ID_KGV1 },
5542+ { VLC_CODEC_YOP, AV_CODEC_ID_YOP },
5543+ { VLC_CODEC_VP8, AV_CODEC_ID_VP8 },
5544+ /* AV_CODEC_ID_PICTOR */
5545+ /* AV_CODEC_ID_ANSI */
5546+ /* AV_CODEC_ID_A64_MULTI */
5547+ /* AV_CODEC_ID_A64_MULTI5 */
5548+ /* AV_CODEC_ID_R10K */
5549+ { VLC_CODEC_MXPEG, AV_CODEC_ID_MXPEG },
5550+ { VLC_CODEC_LAGARITH, AV_CODEC_ID_LAGARITH },
5551+ { VLC_CODEC_PRORES, AV_CODEC_ID_PRORES },
5552+ { VLC_CODEC_JV, AV_CODEC_ID_JV },
5553+ { VLC_CODEC_DFA, AV_CODEC_ID_DFA },
5554+ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3IMAGE },
5555+ { VLC_CODEC_WMVP2, AV_CODEC_ID_VC1IMAGE },
5556+ { VLC_CODEC_UTVIDEO, AV_CODEC_ID_UTVIDEO },
5557+ { VLC_CODEC_BMVVIDEO, AV_CODEC_ID_BMV_VIDEO },
5558+ { VLC_CODEC_VBLE, AV_CODEC_ID_VBLE },
5559+ { VLC_CODEC_DXTORY, AV_CODEC_ID_DXTORY },
5560+ /* AV_CODEC_ID_V410 */
5561+ /* AV_CODEC_ID_XWD */
5562+ { VLC_CODEC_CDXL, AV_CODEC_ID_CDXL },
5563+ /* AV_CODEC_ID_XBM */
5564+ /* AV_CODEC_ID_ZEROCODEC */
5565+ { VLC_CODEC_MSS1, AV_CODEC_ID_MSS1 },
5566+ { VLC_CODEC_MSA1, AV_CODEC_ID_MSA1 },
5567+ { VLC_CODEC_TSC2, AV_CODEC_ID_TSCC2 },
5568+ { VLC_CODEC_MTS2, AV_CODEC_ID_MTS2 },
5569+ { VLC_CODEC_CLLC, AV_CODEC_ID_CLLC },
5570+ { VLC_CODEC_MSS2, AV_CODEC_ID_MSS2 },
5571+ { VLC_CODEC_VP9, AV_CODEC_ID_VP9 },
5572+#if LIBAVCODEC_VERSION_CHECK( 57, 26, 0, 83, 101 )
5573+ { VLC_CODEC_AV1, AV_CODEC_ID_AV1 },
5574+#endif
5575+ { VLC_CODEC_ICOD, AV_CODEC_ID_AIC },
5576+ /* AV_CODEC_ID_ESCAPE130 */
5577+ { VLC_CODEC_G2M4, AV_CODEC_ID_G2M },
5578+ { VLC_CODEC_G2M2, AV_CODEC_ID_G2M },
5579+ { VLC_CODEC_G2M3, AV_CODEC_ID_G2M },
5580+ /* AV_CODEC_ID_WEBP */
5581+ { VLC_CODEC_HNM4_VIDEO, AV_CODEC_ID_HNM4_VIDEO },
5582+ { VLC_CODEC_HEVC, AV_CODEC_ID_HEVC },
5583+
5584+ { VLC_CODEC_FIC , AV_CODEC_ID_FIC },
5585+ /* AV_CODEC_ID_ALIAS_PIX */
5586+ /* AV_CODEC_ID_BRENDER_PIX */
5587+ /* AV_CODEC_ID_PAF_VIDEO */
5588+ /* AV_CODEC_ID_EXR */
5589+
5590+ { VLC_CODEC_VP7 , AV_CODEC_ID_VP7 },
5591+ /* AV_CODEC_ID_SANM */
5592+ /* AV_CODEC_ID_SGIRLE */
5593+ /* AV_CODEC_ID_MVC1 */
5594+ /* AV_CODEC_ID_MVC2 */
5595+ { VLC_CODEC_HQX, AV_CODEC_ID_HQX },
5596+
5597+ { VLC_CODEC_TDSC, AV_CODEC_ID_TDSC },
5598+
5599+ { VLC_CODEC_HQ_HQA, AV_CODEC_ID_HQ_HQA },
5600+
5601+ { VLC_CODEC_HAP, AV_CODEC_ID_HAP },
5602+ /* AV_CODEC_ID_DDS */
5603+
5604+ { VLC_CODEC_DXV, AV_CODEC_ID_DXV },
5605+
5606+ /* ffmpeg only: AV_CODEC_ID_BRENDER_PIX */
5607+ /* ffmpeg only: AV_CODEC_ID_Y41P */
5608+ /* ffmpeg only: AV_CODEC_ID_EXR */
5609+ /* ffmpeg only: AV_CODEC_ID_AVRP */
5610+ /* ffmpeg only: AV_CODEC_ID_012V */
5611+ /* ffmpeg only: AV_CODEC_ID_AVUI */
5612+ /* ffmpeg only: AV_CODEC_ID_TARGA_Y216 */
5613+ /* ffmpeg only: AV_CODEC_ID_V308 */
5614+ /* ffmpeg only: AV_CODEC_ID_V408 */
5615+ /* ffmpeg only: AV_CODEC_ID_YUV4 */
5616+ /* ffmpeg only: AV_CODEC_ID_SANM */
5617+ /* ffmpeg only: AV_CODEC_ID_PAF_VIDEO */
5618+ /* ffmpeg only: AV_CODEC_ID_AVRN */
5619+ /* ffmpeg only: AV_CODEC_ID_CPIA */
5620+ /* ffmpeg only: AV_CODEC_ID_XFACE */
5621+ /* ffmpeg only: AV_CODEC_ID_SGIRLE */
5622+ /* ffmpeg only: AV_CODEC_ID_MVC1 */
5623+ /* ffmpeg only: AV_CODEC_ID_MVC2 */
5624+ /* ffmpeg only: AV_CODEC_ID_SNOW */
5625+ /* ffmpeg only: AV_CODEC_ID_SMVJPEG */
5626+
5627+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 24, 102 )
5628+ { VLC_CODEC_CINEFORM, AV_CODEC_ID_CFHD },
5629+#endif
5630+
5631+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 70, 100 )
5632+ { VLC_CODEC_PIXLET, AV_CODEC_ID_PIXLET },
5633+#endif
5634+
5635+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 101 )
5636+ { VLC_CODEC_SPEEDHQ, AV_CODEC_ID_SPEEDHQ },
5637+#endif
5638+
5639+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 79, 100 )
5640+ { VLC_CODEC_FMVC, AV_CODEC_ID_FMVC },
5641+#endif
5642+};
5643+
5644+// *** Really we should probably use GetFfmpegCodec with a pre-kludge for the bits we care about
5645+static bool
5646+ZcGetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc,
5647+ unsigned *pi_ffmpeg_codec, const char **ppsz_name )
5648+{
5649+ const struct vlc_avcodec_fourcc *base;
5650+ size_t count;
5651+
5652+ base = video_codecs;
5653+ count = ARRAY_SIZE(video_codecs);
5654+ i_fourcc = vlc_fourcc_GetCodec( cat, i_fourcc );
5655+
5656+ for( size_t i = 0; i < count; i++ )
5657+ {
5658+ if( base[i].i_fourcc == i_fourcc )
5659+ {
5660+ if( pi_ffmpeg_codec != NULL )
5661+ *pi_ffmpeg_codec = base[i].i_codec;
5662+ if( ppsz_name )
5663+ *ppsz_name = vlc_fourcc_GetDescription( cat, i_fourcc );
5664+ return true;
5665+ }
5666+ }
5667+ return false;
5668+}
5669+
5670+
5671+
5672+//============================================================================
5673+// Derived from codec/avcodec/avcodec.c
5674+
5675+static AVCodecContext *
5676+ZcFfmpeg_AllocContext( decoder_t *p_dec,
5677+ const AVCodec **restrict codecp )
5678+{
5679+ unsigned i_codec_id;
5680+ const char *psz_namecodec;
5681+ const AVCodec *p_codec = NULL;
5682+
5683+ /* *** determine codec type *** */
5684+ if( !ZcGetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec,
5685+ &i_codec_id, &psz_namecodec ) )
5686+ return NULL;
5687+
5688+ msg_Dbg( p_dec, "using %s %s", AVPROVIDER(LIBAVCODEC), LIBAVCODEC_IDENT );
5689+
5690+ /* Initialization must be done before avcodec_find_decoder() */
5691+ vlc_init_avcodec(VLC_OBJECT(p_dec));
5692+
5693+ /* *** ask ffmpeg for a decoder *** */
5694+ char *psz_decoder = var_InheritString( p_dec, "avcodec-codec" );
5695+ if( psz_decoder != NULL )
5696+ {
5697+ p_codec = avcodec_find_decoder_by_name( psz_decoder );
5698+ if( !p_codec )
5699+ msg_Err( p_dec, "Decoder `%s' not found", psz_decoder );
5700+ else if( p_codec->id != i_codec_id )
5701+ {
5702+ msg_Err( p_dec, "Decoder `%s' can't handle %4.4s",
5703+ psz_decoder, (char*)&p_dec->fmt_in.i_codec );
5704+ p_codec = NULL;
5705+ }
5706+ free( psz_decoder );
5707+ }
5708+ if( !p_codec )
5709+// p_codec = avcodec_find_decoder( i_codec_id );
5710+ {
5711+ if( p_dec->fmt_in.i_codec != VLC_CODEC_HEVC )
5712+ p_codec = avcodec_find_decoder(i_codec_id);
5713+ else
5714+ {
5715+ psz_namecodec = rpi_is_model_pi4() ? "hevc" : "hevc_rpi";
5716+ msg_Info(p_dec, "Looking for HEVC decoder '%s'", psz_namecodec);
5717+ p_codec = avcodec_find_decoder_by_name(psz_namecodec);
5718+ }
5719+ }
5720+
5721+ if( !p_codec )
5722+ {
5723+ msg_Dbg( p_dec, "codec not found (%s)", psz_namecodec );
5724+ return NULL;
5725+ }
5726+
5727+ *codecp = p_codec;
5728+
5729+ /* *** get a p_context *** */
5730+ AVCodecContext *avctx = avcodec_alloc_context3(p_codec);
5731+ if( unlikely(avctx == NULL) )
5732+ return NULL;
5733+
5734+ avctx->debug = var_InheritInteger( p_dec, "avcodec-debug" );
5735+ avctx->opaque = p_dec;
5736+ return avctx;
5737+}
5738+
5739+/*****************************************************************************
5740+ * ffmpeg_OpenCodec:
5741+ *****************************************************************************/
5742+
5743+static int
5744+ZcFfmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx,
5745+ const AVCodec *codec )
5746+{
5747+ char *psz_opts = var_InheritString( p_dec, "avcodec-options" );
5748+ AVDictionary *options = NULL;
5749+ int ret;
5750+
5751+ if (psz_opts) {
5752+ vlc_av_get_options(psz_opts, &options);
5753+ free(psz_opts);
5754+ }
5755+
5756+ if (av_rpi_zc_init2(ctx, p_dec, zc_alloc_buf, zc_free_pool) != 0)
5757+ {
5758+ msg_Err(p_dec, "Failed to init AV ZC");
5759+ return VLC_EGENERIC;
5760+ }
5761+
5762+ vlc_avcodec_lock();
5763+ ret = avcodec_open2( ctx, codec, options ? &options : NULL );
5764+ vlc_avcodec_unlock();
5765+
5766+ AVDictionaryEntry *t = NULL;
5767+ while ((t = av_dict_get(options, "", t, AV_DICT_IGNORE_SUFFIX))) {
5768+ msg_Err( p_dec, "Unknown option \"%s\"", t->key );
5769+ }
5770+ av_dict_free(&options);
5771+
5772+ if( ret < 0 )
5773+ {
5774+ msg_Err( p_dec, "cannot start codec (%s)", codec->name );
5775+ return VLC_EGENERIC;
5776+ }
5777+
5778+ msg_Dbg( p_dec, "codec (%s) started", codec->name );
5779+ return VLC_SUCCESS;
5780+}
5781+
5782+//============================================================================
5783+// Derived from 3.0.7.1 codec/avcodec/video.c
5784+
5785+static inline void wait_mt(decoder_sys_t *sys)
5786+{
5787+#if 1
5788+ // As we only ever update the output in our main thread this lock is
5789+ // redundant
5790+ VLC_UNUSED(sys);
5791+#else
5792+ vlc_sem_wait(&sys->sem_mt);
5793+#endif
5794+}
5795+
5796+static inline void post_mt(decoder_sys_t *sys)
5797+{
5798+#if 1
5799+ // As we only ever update the output in our main thread this lock is
5800+ // redundant
5801+ VLC_UNUSED(sys);
5802+#else
5803+ vlc_sem_post(&sys->sem_mt);
5804+#endif
5805+}
5806+
5807+/*****************************************************************************
5808+ * Local prototypes
5809+ *****************************************************************************/
5810+static void ffmpeg_InitCodec ( decoder_t * );
5811+static int DecodeVideo( decoder_t *, block_t * );
5812+static void Flush( decoder_t * );
5813+
5814+static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc )
5815+{
5816+ uint8_t *p = (uint8_t*)&fcc;
5817+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
5818+}
5819+
5820+/*****************************************************************************
5821+ * Local Functions
5822+ *****************************************************************************/
5823+
5824+/**
5825+ * Sets the decoder output format.
5826+ */
5827+static int lavc_GetVideoFormat(decoder_t *dec, video_format_t *restrict fmt,
5828+ AVCodecContext *ctx, enum AVPixelFormat pix_fmt,
5829+ enum AVPixelFormat sw_pix_fmt)
5830+{
5831+ int width = ctx->coded_width;
5832+ int height = ctx->coded_height;
5833+
5834+ video_format_Init(fmt, 0);
5835+
5836+#if 1
5837+ VLC_UNUSED(sw_pix_fmt);
5838+ if ((fmt->i_chroma = ZcFindVlcChroma(pix_fmt)) == 0)
5839+ return -1;
5840+#else
5841+ if (pix_fmt == sw_pix_fmt)
5842+ { /* software decoding */
5843+ int aligns[AV_NUM_DATA_POINTERS];
5844+
5845+ if (GetVlcChroma(fmt, pix_fmt))
5846+ return -1;
5847+
5848+ /* The libavcodec palette can only be fetched when the first output
5849+ * frame is decoded. Assume that the current chroma is RGB32 while we
5850+ * are waiting for a valid palette. Indeed, fmt_out.video.p_palette
5851+ * doesn't trigger a new vout request, but a new chroma yes. */
5852+ if (pix_fmt == AV_PIX_FMT_PAL8 && !dec->fmt_out.video.p_palette)
5853+ fmt->i_chroma = VLC_CODEC_RGB32;
5854+
5855+ avcodec_align_dimensions2(ctx, &width, &height, aligns);
5856+ }
5857+ else /* hardware decoding */
5858+ fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt);
5859+#endif
5860+
5861+ if( width == 0 || height == 0 || width > 8192 || height > 8192 ||
5862+ width < ctx->width || height < ctx->height )
5863+ {
5864+ msg_Err(dec, "Invalid frame size %dx%d vsz %dx%d",
5865+ width, height, ctx->width, ctx->height );
5866+ return -1; /* invalid display size */
5867+ }
5868+
5869+ fmt->i_width = width;
5870+ fmt->i_height = height;
5871+ fmt->i_visible_width = ctx->width;
5872+ fmt->i_visible_height = ctx->height;
5873+
5874+ /* If an aspect-ratio was specified in the input format then force it */
5875+ if (dec->fmt_in.video.i_sar_num > 0 && dec->fmt_in.video.i_sar_den > 0)
5876+ {
5877+ fmt->i_sar_num = dec->fmt_in.video.i_sar_num;
5878+ fmt->i_sar_den = dec->fmt_in.video.i_sar_den;
5879+ }
5880+ else
5881+ {
5882+ fmt->i_sar_num = ctx->sample_aspect_ratio.num;
5883+ fmt->i_sar_den = ctx->sample_aspect_ratio.den;
5884+
5885+ if (fmt->i_sar_num == 0 || fmt->i_sar_den == 0)
5886+ fmt->i_sar_num = fmt->i_sar_den = 1;
5887+ }
5888+
5889+ if (dec->fmt_in.video.i_frame_rate > 0
5890+ && dec->fmt_in.video.i_frame_rate_base > 0)
5891+ {
5892+ fmt->i_frame_rate = dec->fmt_in.video.i_frame_rate;
5893+ fmt->i_frame_rate_base = dec->fmt_in.video.i_frame_rate_base;
5894+ }
5895+ else if (ctx->framerate.num > 0 && ctx->framerate.den > 0)
5896+ {
5897+ fmt->i_frame_rate = ctx->framerate.num;
5898+ fmt->i_frame_rate_base = ctx->framerate.den;
5899+# if LIBAVCODEC_VERSION_MICRO < 100
5900+ // for some reason libav don't thinkg framerate presents actually same thing as in ffmpeg
5901+ fmt->i_frame_rate_base *= __MAX(ctx->ticks_per_frame, 1);
5902+# endif
5903+ }
5904+ else if (ctx->time_base.num > 0 && ctx->time_base.den > 0)
5905+ {
5906+ fmt->i_frame_rate = ctx->time_base.den;
5907+ fmt->i_frame_rate_base = ctx->time_base.num
5908+ * __MAX(ctx->ticks_per_frame, 1);
5909+ }
5910+
5911+ /* FIXME we should only set the known values and let the core decide
5912+ * later of fallbacks, but we can't do that with a boolean */
5913+ switch ( ctx->color_range )
5914+ {
5915+ case AVCOL_RANGE_JPEG:
5916+ fmt->b_color_range_full = true;
5917+ break;
5918+ case AVCOL_RANGE_UNSPECIFIED:
5919+ fmt->b_color_range_full = !vlc_fourcc_IsYUV( fmt->i_chroma );
5920+ break;
5921+ case AVCOL_RANGE_MPEG:
5922+ default:
5923+ fmt->b_color_range_full = false;
5924+ break;
5925+ }
5926+
5927+ switch( ctx->colorspace )
5928+ {
5929+ case AVCOL_SPC_BT709:
5930+ fmt->space = COLOR_SPACE_BT709;
5931+ break;
5932+ case AVCOL_SPC_SMPTE170M:
5933+ case AVCOL_SPC_BT470BG:
5934+ fmt->space = COLOR_SPACE_BT601;
5935+ break;
5936+ case AVCOL_SPC_BT2020_NCL:
5937+ case AVCOL_SPC_BT2020_CL:
5938+ fmt->space = COLOR_SPACE_BT2020;
5939+ break;
5940+ default:
5941+ break;
5942+ }
5943+
5944+ switch( ctx->color_trc )
5945+ {
5946+ case AVCOL_TRC_LINEAR:
5947+ fmt->transfer = TRANSFER_FUNC_LINEAR;
5948+ break;
5949+ case AVCOL_TRC_GAMMA22:
5950+ fmt->transfer = TRANSFER_FUNC_SRGB;
5951+ break;
5952+ case AVCOL_TRC_BT709:
5953+ fmt->transfer = TRANSFER_FUNC_BT709;
5954+ break;
5955+ case AVCOL_TRC_SMPTE170M:
5956+ case AVCOL_TRC_BT2020_10:
5957+ case AVCOL_TRC_BT2020_12:
5958+ fmt->transfer = TRANSFER_FUNC_BT2020;
5959+ break;
5960+#if LIBAVUTIL_VERSION_CHECK( 55, 14, 0, 31, 100)
5961+ case AVCOL_TRC_ARIB_STD_B67:
5962+ fmt->transfer = TRANSFER_FUNC_ARIB_B67;
5963+ break;
5964+#endif
5965+#if LIBAVUTIL_VERSION_CHECK( 55, 17, 0, 37, 100)
5966+ case AVCOL_TRC_SMPTE2084:
5967+ fmt->transfer = TRANSFER_FUNC_SMPTE_ST2084;
5968+ break;
5969+ case AVCOL_TRC_SMPTE240M:
5970+ fmt->transfer = TRANSFER_FUNC_SMPTE_240;
5971+ break;
5972+ case AVCOL_TRC_GAMMA28:
5973+ fmt->transfer = TRANSFER_FUNC_BT470_BG;
5974+ break;
5975+#endif
5976+ default:
5977+ break;
5978+ }
5979+
5980+ switch( ctx->color_primaries )
5981+ {
5982+ case AVCOL_PRI_BT709:
5983+ fmt->primaries = COLOR_PRIMARIES_BT709;
5984+ break;
5985+ case AVCOL_PRI_BT470BG:
5986+ fmt->primaries = COLOR_PRIMARIES_BT601_625;
5987+ break;
5988+ case AVCOL_PRI_SMPTE170M:
5989+ case AVCOL_PRI_SMPTE240M:
5990+ fmt->primaries = COLOR_PRIMARIES_BT601_525;
5991+ break;
5992+ case AVCOL_PRI_BT2020:
5993+ fmt->primaries = COLOR_PRIMARIES_BT2020;
5994+ break;
5995+ default:
5996+ break;
5997+ }
5998+
5999+ switch( ctx->chroma_sample_location )
6000+ {
6001+ case AVCHROMA_LOC_LEFT:
6002+ fmt->chroma_location = CHROMA_LOCATION_LEFT;
6003+ break;
6004+ case AVCHROMA_LOC_CENTER:
6005+ fmt->chroma_location = CHROMA_LOCATION_CENTER;
6006+ break;
6007+ case AVCHROMA_LOC_TOPLEFT:
6008+ fmt->chroma_location = CHROMA_LOCATION_TOP_LEFT;
6009+ break;
6010+ default:
6011+ break;
6012+ }
6013+
6014+ return 0;
6015+}
6016+
6017+static int lavc_UpdateVideoFormat(decoder_t *dec, AVCodecContext *ctx,
6018+ enum AVPixelFormat fmt,
6019+ enum AVPixelFormat swfmt)
6020+{
6021+ video_format_t fmt_out;
6022+ int val;
6023+#if TRACE_ALL
6024+ msg_Dbg(dec, "<<< %s", __func__);
6025+#endif
6026+ val = lavc_GetVideoFormat(dec, &fmt_out, ctx, fmt, swfmt);
6027+ if (val)
6028+ {
6029+ msg_Dbg(dec, "Failed to get format");
6030+ return val;
6031+ }
6032+
6033+ /* always have date in fields/ticks units */
6034+ if(dec->p_sys->pts.i_divider_num)
6035+ date_Change(&dec->p_sys->pts, fmt_out.i_frame_rate *
6036+ __MAX(ctx->ticks_per_frame, 1),
6037+ fmt_out.i_frame_rate_base);
6038+ else
6039+ date_Init(&dec->p_sys->pts, fmt_out.i_frame_rate *
6040+ __MAX(ctx->ticks_per_frame, 1),
6041+ fmt_out.i_frame_rate_base);
6042+
6043+ fmt_out.p_palette = dec-> fmt_out.video.p_palette;
6044+ dec->fmt_out.video.p_palette = NULL;
6045+
6046+ es_format_Change(&dec->fmt_out, VIDEO_ES, fmt_out.i_chroma);
6047+ dec->fmt_out.video = fmt_out;
6048+ dec->fmt_out.video.orientation = dec->fmt_in.video.orientation;
6049+ dec->fmt_out.video.projection_mode = dec->fmt_in.video.projection_mode;
6050+ dec->fmt_out.video.multiview_mode = dec->fmt_in.video.multiview_mode;
6051+ dec->fmt_out.video.pose = dec->fmt_in.video.pose;
6052+ if ( dec->fmt_in.video.mastering.max_luminance )
6053+ dec->fmt_out.video.mastering = dec->fmt_in.video.mastering;
6054+ dec->fmt_out.video.lighting = dec->fmt_in.video.lighting;
6055+
6056+ val = decoder_UpdateVideoFormat(dec);
6057+#if TRACE_ALL
6058+ msg_Dbg(dec, ">>> %s: rv=%d", __func__, val);
6059+#endif
6060+ return val;
6061+}
6062+
6063+static int OpenVideoCodec( decoder_t *p_dec )
6064+{
6065+ decoder_sys_t *p_sys = p_dec->p_sys;
6066+ AVCodecContext *ctx = p_sys->p_context;
6067+ const AVCodec *codec = p_sys->p_codec;
6068+ int ret;
6069+
6070+ if( ctx->extradata_size <= 0 )
6071+ {
6072+ if( codec->id == AV_CODEC_ID_VC1 ||
6073+ codec->id == AV_CODEC_ID_THEORA )
6074+ {
6075+ msg_Warn( p_dec, "waiting for extra data for codec %s",
6076+ codec->name );
6077+ return 1;
6078+ }
6079+ }
6080+
6081+ ctx->width = p_dec->fmt_in.video.i_visible_width;
6082+ ctx->height = p_dec->fmt_in.video.i_visible_height;
6083+
6084+ ctx->coded_width = p_dec->fmt_in.video.i_width;
6085+ ctx->coded_height = p_dec->fmt_in.video.i_height;
6086+
6087+ ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel;
6088+ p_sys->pix_fmt = AV_PIX_FMT_NONE;
6089+ p_sys->profile = -1;
6090+ p_sys->level = -1;
6091+ cc_Init( &p_sys->cc );
6092+
6093+ set_video_color_settings( &p_dec->fmt_in.video, ctx );
6094+ if( p_dec->fmt_in.video.i_frame_rate_base &&
6095+ p_dec->fmt_in.video.i_frame_rate &&
6096+ (double) p_dec->fmt_in.video.i_frame_rate /
6097+ p_dec->fmt_in.video.i_frame_rate_base < 6 )
6098+ {
6099+ ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
6100+ }
6101+
6102+ post_mt( p_sys );
6103+ ret = ZcFfmpeg_OpenCodec( p_dec, ctx, codec );
6104+ wait_mt( p_sys );
6105+ if( ret < 0 )
6106+ return ret;
6107+
6108+ switch( ctx->active_thread_type )
6109+ {
6110+ case FF_THREAD_FRAME:
6111+ msg_Dbg( p_dec, "using frame thread mode with %d threads",
6112+ ctx->thread_count );
6113+ break;
6114+ case FF_THREAD_SLICE:
6115+ msg_Dbg( p_dec, "using slice thread mode with %d threads",
6116+ ctx->thread_count );
6117+ break;
6118+ case 0:
6119+ if( ctx->thread_count > 1 )
6120+ msg_Warn( p_dec, "failed to enable threaded decoding" );
6121+ break;
6122+ default:
6123+ msg_Warn( p_dec, "using unknown thread mode with %d threads",
6124+ ctx->thread_count );
6125+ break;
6126+ }
6127+ return 0;
6128+}
6129+
6130+/*****************************************************************************
6131+ * InitVideo: initialize the video decoder
6132+ *****************************************************************************
6133+ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet
6134+ * opened (done after the first decoded frame).
6135+ *****************************************************************************/
6136+static int MmalAvcodecOpenDecoder( vlc_object_t *obj )
6137+{
6138+ decoder_t *p_dec = (decoder_t *)obj;
6139+ const AVCodec *p_codec;
6140+
6141+ int extra_buffers = var_InheritInteger(p_dec, MMAL_AVCODEC_BUFFERS);
6142+
6143+ if (extra_buffers < 0)
6144+ {
6145+ extra_buffers = p_dec->fmt_in.video.i_height * p_dec->fmt_in.video.i_width >= 1920 * 1088 ?
6146+ BUFFERS_IN_FLIGHT_UHD : BUFFERS_IN_FLIGHT;
6147+ }
6148+
6149+ if (extra_buffers <= 0)
6150+ {
6151+ msg_Dbg(p_dec, "%s: extra_buffers=%d - cannot use module", __func__, extra_buffers);
6152+ return VLC_EGENERIC;
6153+ }
6154+
6155+ const vcsm_init_type_t vcsm_type = cma_vcsm_init();
6156+ const int vcsm_size =
6157+ vcsm_type == VCSM_INIT_LEGACY ? hw_mmal_get_gpu_mem() : 512 << 20;
6158+
6159+#if 1
6160+ {
6161+ char buf1[5], buf2[5], buf2a[5];
6162+ char buf3[5], buf4[5];
6163+ uint32_t in_fcc = 0;
6164+ msg_Dbg(p_dec, "%s: <<< (%s/%s)[%s] %dx%d -> (%s/%s) %dx%d [%s/%d] xb:%d", __func__,
6165+ str_fourcc(buf1, p_dec->fmt_in.i_codec),
6166+ str_fourcc(buf2, p_dec->fmt_in.video.i_chroma),
6167+ str_fourcc(buf2a, in_fcc),
6168+ p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height,
6169+ str_fourcc(buf3, p_dec->fmt_out.i_codec),
6170+ str_fourcc(buf4, p_dec->fmt_out.video.i_chroma),
6171+ p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height,
6172+ cma_vcsm_init_str(vcsm_type), vcsm_size, extra_buffers);
6173+ }
6174+#endif
6175+
6176+ if( vcsm_type == VCSM_INIT_NONE )
6177+ return VLC_EGENERIC;
6178+#if 1
6179+ if( (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC &&
6180+ (vcsm_type == VCSM_INIT_CMA || vcsm_size < (96 << 20))) ||
6181+ (p_dec->fmt_in.i_codec == VLC_CODEC_HEVC &&
6182+ vcsm_size < (128 << 20)))
6183+ {
6184+ cma_vcsm_exit(vcsm_type);
6185+ return VLC_EGENERIC;
6186+ }
6187+#endif
6188+
6189+ AVCodecContext *p_context = ZcFfmpeg_AllocContext( p_dec, &p_codec );
6190+ if( p_context == NULL )
6191+ {
6192+ cma_vcsm_exit(vcsm_type);
6193+ return VLC_EGENERIC;
6194+ }
6195+
6196+ int i_val;
6197+
6198+ /* Allocate the memory needed to store the decoder's structure */
6199+ decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) );
6200+ if( unlikely(p_sys == NULL) )
6201+ {
6202+ avcodec_free_context( &p_context );
6203+ cma_vcsm_exit(vcsm_type);
6204+ return VLC_ENOMEM;
6205+ }
6206+
6207+ p_dec->p_sys = p_sys;
6208+ p_sys->p_context = p_context;
6209+ p_sys->p_codec = p_codec;
6210+ p_sys->p_dec = p_dec;
6211+// p_sys->p_va = NULL;
6212+ p_sys->cma_in_flight_max = extra_buffers;
6213+ p_sys->vcsm_init_type = vcsm_type;
6214+ vlc_sem_init( &p_sys->sem_mt, 0 );
6215+
6216+ /* ***** Fill p_context with init values ***** */
6217+ p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ?
6218+ p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec );
6219+
6220+ /* ***** Get configuration of ffmpeg plugin ***** */
6221+ p_context->workaround_bugs =
6222+ var_InheritInteger( p_dec, "avcodec-workaround-bugs" );
6223+ p_context->err_recognition =
6224+ var_InheritInteger( p_dec, "avcodec-error-resilience" );
6225+
6226+ if( var_CreateGetBool( p_dec, "grayscale" ) )
6227+ p_context->flags |= AV_CODEC_FLAG_GRAY;
6228+
6229+ /* ***** Output always the frames ***** */
6230+ p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
6231+
6232+ i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" );
6233+ if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL;
6234+ else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY;
6235+ else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR;
6236+ else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF;
6237+ else p_context->skip_loop_filter = AVDISCARD_DEFAULT;
6238+
6239+ if( var_CreateGetBool( p_dec, "avcodec-fast" ) )
6240+ p_context->flags2 |= AV_CODEC_FLAG2_FAST;
6241+
6242+ /* ***** libavcodec frame skipping ***** */
6243+ p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" );
6244+ p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" );
6245+
6246+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" );
6247+ if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL;
6248+ else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY;
6249+ else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR;
6250+ else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF;
6251+ else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE;
6252+ else p_sys->i_skip_frame = AVDISCARD_DEFAULT;
6253+ p_context->skip_frame = p_sys->i_skip_frame;
6254+
6255+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" );
6256+ if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL;
6257+ else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY;
6258+ else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR;
6259+ else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF;
6260+ else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE;
6261+ else p_context->skip_idct = AVDISCARD_DEFAULT;
6262+
6263+ /* ***** libavcodec direct rendering ***** */
6264+ p_sys->b_direct_rendering = false;
6265+ atomic_init(&p_sys->b_dr_failure, false);
6266+ if( var_CreateGetBool( p_dec, "avcodec-dr" ) &&
6267+ (p_codec->capabilities & AV_CODEC_CAP_DR1) &&
6268+ /* No idea why ... but this fixes flickering on some TSCC streams */
6269+ p_sys->p_codec->id != AV_CODEC_ID_TSCC &&
6270+ p_sys->p_codec->id != AV_CODEC_ID_CSCD &&
6271+ p_sys->p_codec->id != AV_CODEC_ID_CINEPAK )
6272+ {
6273+ /* Some codecs set pix_fmt only after the 1st frame has been decoded,
6274+ * so we need to do another check in ffmpeg_GetFrameBuf() */
6275+ p_sys->b_direct_rendering = true;
6276+ }
6277+
6278+ p_context->get_format = ZcGetFormat;
6279+#if 0
6280+ p_context->get_format = ffmpeg_GetFormat;
6281+ /* Always use our get_buffer wrapper so we can calculate the
6282+ * PTS correctly */
6283+ p_context->get_buffer2 = lavc_GetFrame;
6284+ p_context->opaque = p_dec;
6285+#endif
6286+
6287+ int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" );
6288+ if( i_thread_count <= 0 )
6289+#if 1
6290+ {
6291+ // Pick 5 threads for everything on Pi except for HEVC where the h/w
6292+ // really limits the useful size to 3
6293+ i_thread_count = p_codec->id == AV_CODEC_ID_HEVC ? 3 : 5;
6294+ }
6295+#else
6296+ {
6297+ i_thread_count = vlc_GetCPUCount();
6298+ if( i_thread_count > 1 )
6299+ i_thread_count++;
6300+
6301+ //FIXME: take in count the decoding time
6302+#if VLC_WINSTORE_APP
6303+ i_thread_count = __MIN( i_thread_count, 6 );
6304+#else
6305+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 );
6306+#endif
6307+ }
6308+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 );
6309+#endif
6310+ msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count );
6311+ p_context->thread_count = i_thread_count;
6312+ p_context->thread_safe_callbacks = true;
6313+
6314+ switch( p_codec->id )
6315+ {
6316+ case AV_CODEC_ID_MPEG4:
6317+ case AV_CODEC_ID_H263:
6318+ p_context->thread_type = 0;
6319+ break;
6320+ case AV_CODEC_ID_MPEG1VIDEO:
6321+ case AV_CODEC_ID_MPEG2VIDEO:
6322+ p_context->thread_type &= ~FF_THREAD_SLICE;
6323+ /* fall through */
6324+# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0))
6325+ case AV_CODEC_ID_H264:
6326+ case AV_CODEC_ID_VC1:
6327+ case AV_CODEC_ID_WMV3:
6328+ p_context->thread_type &= ~FF_THREAD_FRAME;
6329+# endif
6330+ default:
6331+ break;
6332+ }
6333+
6334+ if( p_context->thread_type & FF_THREAD_FRAME )
6335+ p_dec->i_extra_picture_buffers = 2 * p_context->thread_count;
6336+
6337+ /* ***** misc init ***** */
6338+ date_Init(&p_sys->pts, 1, 30001);
6339+ date_Set(&p_sys->pts, VLC_TS_INVALID);
6340+ p_sys->b_first_frame = true;
6341+ p_sys->i_late_frames = 0;
6342+ p_sys->b_from_preroll = false;
6343+
6344+ /* Set output properties */
6345+ if( ZcGetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS )
6346+ {
6347+ /* we are doomed. but not really, because most codecs set their pix_fmt later on */
6348+// p_dec->fmt_out.i_codec = VLC_CODEC_I420;
6349+ p_dec->fmt_out.i_codec = VLC_CODEC_MMAL_ZC_I420;
6350+ }
6351+ p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma;
6352+
6353+ p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation;
6354+
6355+ if( p_dec->fmt_in.video.p_palette ) {
6356+ p_sys->palette_sent = false;
6357+ p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) );
6358+ if( p_dec->fmt_out.video.p_palette )
6359+ *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette;
6360+ } else
6361+ p_sys->palette_sent = true;
6362+
6363+ if ((p_sys->cma_pool = cma_buf_pool_new(p_sys->cma_in_flight_max, p_sys->cma_in_flight_max, false, "mmal_avcodec")) == NULL)
6364+ {
6365+ msg_Err(p_dec, "CMA pool alloc failure");
6366+ goto fail;
6367+ }
6368+
6369+ /* ***** init this codec with special data ***** */
6370+ ffmpeg_InitCodec( p_dec );
6371+
6372+ /* ***** Open the codec ***** */
6373+ if( OpenVideoCodec( p_dec ) < 0 )
6374+ {
6375+ vlc_sem_destroy( &p_sys->sem_mt );
6376+ free( p_sys );
6377+ avcodec_free_context( &p_context );
6378+ return VLC_EGENERIC;
6379+ }
6380+
6381+ p_dec->pf_decode = DecodeVideo;
6382+ p_dec->pf_flush = Flush;
6383+
6384+ /* XXX: Writing input format makes little sense. */
6385+ if( p_context->profile != FF_PROFILE_UNKNOWN )
6386+ p_dec->fmt_in.i_profile = p_context->profile;
6387+ if( p_context->level != FF_LEVEL_UNKNOWN )
6388+ p_dec->fmt_in.i_level = p_context->level;
6389+
6390+#if 1
6391+ // Most of the time we have nothing useful by way of a format here
6392+ // wait till we've decoded something
6393+#else
6394+ // Update output format
6395+ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
6396+ p_context->pix_fmt) != 0)
6397+ {
6398+ msg_Err(p_dec, "Unable to update format: pix_fmt=%d", p_context->pix_fmt);
6399+// goto fail;
6400+ }
6401+#endif
6402+
6403+#if TRACE_ALL
6404+ msg_Dbg(p_dec, "<<< %s: OK", __func__);
6405+#endif
6406+ return VLC_SUCCESS;
6407+
6408+fail:
6409+ MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec));
6410+
6411+#if TRACE_ALL
6412+ msg_Dbg(p_dec, "<<< %s: FAIL", __func__);
6413+#endif
6414+
6415+ return VLC_EGENERIC;
6416+}
6417+
6418+/*****************************************************************************
6419+ * Flush:
6420+ *****************************************************************************/
6421+static void Flush( decoder_t *p_dec )
6422+{
6423+ decoder_sys_t *p_sys = p_dec->p_sys;
6424+ AVCodecContext *p_context = p_sys->p_context;
6425+
6426+#if TRACE_ALL
6427+ msg_Dbg(p_dec, "<<< %s", __func__);
6428+#endif
6429+
6430+ date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */
6431+ p_sys->i_late_frames = 0;
6432+ cc_Flush( &p_sys->cc );
6433+
6434+ /* Abort pictures in order to unblock all avcodec workers threads waiting
6435+ * for a picture. This will avoid a deadlock between avcodec_flush_buffers
6436+ * and workers threads */
6437+// It would probably be good to use AbortPicture but that often deadlocks on close
6438+// and given that we wait for pics in the main thread it should be unneeded (whereas
6439+// cma is alloced in the depths of ffmpeg on its own threads)
6440+// decoder_AbortPictures( p_dec, true );
6441+ cma_buf_pool_cancel(p_sys->cma_pool);
6442+
6443+ post_mt( p_sys );
6444+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
6445+ if( avcodec_is_open( p_context ) )
6446+ avcodec_flush_buffers( p_context );
6447+ wait_mt( p_sys );
6448+
6449+ /* Reset cancel state to false */
6450+ cma_buf_pool_uncancel(p_sys->cma_pool);
6451+// decoder_AbortPictures( p_dec, false );
6452+
6453+#if TRACE_ALL
6454+ msg_Dbg(p_dec, ">>> %s", __func__);
6455+#endif
6456+
6457+}
6458+
6459+static bool check_block_validity( decoder_sys_t *p_sys, block_t *block )
6460+{
6461+ if( !block)
6462+ return true;
6463+
6464+ if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )
6465+ {
6466+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
6467+ cc_Flush( &p_sys->cc );
6468+
6469+ p_sys->i_late_frames = 0;
6470+ if( block->i_flags & BLOCK_FLAG_CORRUPTED )
6471+ {
6472+ block_Release( block );
6473+ return false;
6474+ }
6475+ }
6476+ return true;
6477+}
6478+
6479+static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time)
6480+{
6481+ if( !block )
6482+ return false;
6483+ if( block->i_flags & BLOCK_FLAG_PREROLL )
6484+ {
6485+ /* Do not care about late frames when prerolling
6486+ * TODO avoid decoding of non reference frame
6487+ * (ie all B except for H264 where it depends only on nal_ref_idc) */
6488+ p_sys->i_late_frames = 0;
6489+ p_sys->b_from_preroll = true;
6490+ p_sys->i_last_late_delay = INT64_MAX;
6491+ }
6492+
6493+ if( p_sys->i_late_frames <= 0 )
6494+ return false;
6495+
6496+ if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ))
6497+ {
6498+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
6499+ block_Release( block );
6500+ p_sys->i_late_frames--;
6501+ return true;
6502+ }
6503+ return false;
6504+}
6505+
6506+static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture )
6507+{
6508+ if( p_sys->i_late_frames <= 4)
6509+ return false;
6510+
6511+ *b_need_output_picture = false;
6512+ if( p_sys->i_late_frames < 12 )
6513+ {
6514+ p_context->skip_frame =
6515+ (p_sys->i_skip_frame <= AVDISCARD_NONREF) ?
6516+ AVDISCARD_NONREF : p_sys->i_skip_frame;
6517+ }
6518+ else
6519+ {
6520+ /* picture too late, won't decode
6521+ * but break picture until a new I, and for mpeg4 ...*/
6522+ p_sys->i_late_frames--; /* needed else it will never be decrease */
6523+ return true;
6524+ }
6525+ return false;
6526+}
6527+
6528+static mtime_t interpolate_next_pts( decoder_t *p_dec, AVFrame *frame )
6529+{
6530+ decoder_sys_t *p_sys = p_dec->p_sys;
6531+ AVCodecContext *p_context = p_sys->p_context;
6532+
6533+ if( date_Get( &p_sys->pts ) == VLC_TS_INVALID ||
6534+ p_sys->pts.i_divider_num == 0 )
6535+ return VLC_TS_INVALID;
6536+
6537+ int i_tick = p_context->ticks_per_frame;
6538+ if( i_tick <= 0 )
6539+ i_tick = 1;
6540+
6541+ /* interpolate the next PTS */
6542+ return date_Increment( &p_sys->pts, i_tick + frame->repeat_pict );
6543+}
6544+
6545+static void update_late_frame_count( decoder_t *p_dec, block_t *p_block,
6546+ mtime_t current_time, mtime_t i_pts,
6547+ mtime_t i_next_pts )
6548+{
6549+ decoder_sys_t *p_sys = p_dec->p_sys;
6550+ /* Update frame late count (except when doing preroll) */
6551+ mtime_t i_display_date = VLC_TS_INVALID;
6552+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
6553+ i_display_date = decoder_GetDisplayDate( p_dec, i_pts );
6554+
6555+ mtime_t i_threshold = i_next_pts != VLC_TS_INVALID ? (i_next_pts - i_pts) / 2 : 20000;
6556+
6557+ if( i_display_date > VLC_TS_INVALID && i_display_date + i_threshold <= current_time )
6558+ {
6559+ /* Out of preroll, consider only late frames on rising delay */
6560+ if( p_sys->b_from_preroll )
6561+ {
6562+ if( p_sys->i_last_late_delay > current_time - i_display_date )
6563+ {
6564+ p_sys->i_last_late_delay = current_time - i_display_date;
6565+ return;
6566+ }
6567+ p_sys->b_from_preroll = false;
6568+ }
6569+
6570+ p_sys->i_late_frames++;
6571+ if( p_sys->i_late_frames == 1 )
6572+ p_sys->i_late_frames_start = current_time;
6573+
6574+ }
6575+ else
6576+ {
6577+ p_sys->i_late_frames = 0;
6578+ }
6579+}
6580+
6581+
6582+static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic )
6583+{
6584+ decoder_sys_t *p_sys = p_dec->p_sys;
6585+ bool format_changed = false;
6586+
6587+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
6588+#define FROM_AVRAT(default_factor, avrat) \
6589+(uint64_t)(default_factor) * (avrat).num / (avrat).den
6590+ const AVFrameSideData *metadata =
6591+ av_frame_get_side_data( frame,
6592+ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA );
6593+ if ( metadata )
6594+ {
6595+ const AVMasteringDisplayMetadata *hdr_meta =
6596+ (const AVMasteringDisplayMetadata *) metadata->data;
6597+ if ( hdr_meta->has_luminance )
6598+ {
6599+#define ST2086_LUMA_FACTOR 10000
6600+ p_pic->format.mastering.max_luminance =
6601+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance);
6602+ p_pic->format.mastering.min_luminance =
6603+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance);
6604+ }
6605+ if ( hdr_meta->has_primaries )
6606+ {
6607+#define ST2086_RED 2
6608+#define ST2086_GREEN 0
6609+#define ST2086_BLUE 1
6610+#define LAV_RED 0
6611+#define LAV_GREEN 1
6612+#define LAV_BLUE 2
6613+#define ST2086_PRIM_FACTOR 50000
6614+ p_pic->format.mastering.primaries[ST2086_RED*2 + 0] =
6615+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]);
6616+ p_pic->format.mastering.primaries[ST2086_RED*2 + 1] =
6617+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]);
6618+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] =
6619+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]);
6620+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] =
6621+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]);
6622+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] =
6623+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]);
6624+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] =
6625+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]);
6626+ p_pic->format.mastering.white_point[0] =
6627+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]);
6628+ p_pic->format.mastering.white_point[1] =
6629+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]);
6630+ }
6631+
6632+ if ( memcmp( &p_dec->fmt_out.video.mastering,
6633+ &p_pic->format.mastering,
6634+ sizeof(p_pic->format.mastering) ) )
6635+ {
6636+ p_dec->fmt_out.video.mastering = p_pic->format.mastering;
6637+ format_changed = true;
6638+ }
6639+#undef FROM_AVRAT
6640+ }
6641+#endif
6642+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) )
6643+ const AVFrameSideData *metadata_lt =
6644+ av_frame_get_side_data( frame,
6645+ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL );
6646+ if ( metadata_lt )
6647+ {
6648+ const AVContentLightMetadata *light_meta =
6649+ (const AVContentLightMetadata *) metadata_lt->data;
6650+ p_pic->format.lighting.MaxCLL = light_meta->MaxCLL;
6651+ p_pic->format.lighting.MaxFALL = light_meta->MaxFALL;
6652+ if ( memcmp( &p_dec->fmt_out.video.lighting,
6653+ &p_pic->format.lighting,
6654+ sizeof(p_pic->format.lighting) ) )
6655+ {
6656+ p_dec->fmt_out.video.lighting = p_pic->format.lighting;
6657+ format_changed = true;
6658+ }
6659+ }
6660+#endif
6661+
6662+ if (format_changed && decoder_UpdateVideoFormat( p_dec ))
6663+ return -1;
6664+
6665+ const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC );
6666+ if( p_avcc )
6667+ {
6668+ cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size );
6669+ if( p_sys->cc.b_reorder || p_sys->cc.i_data )
6670+ {
6671+ block_t *p_cc = block_Alloc( p_sys->cc.i_data );
6672+ if( p_cc )
6673+ {
6674+ memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
6675+ if( p_sys->cc.b_reorder )
6676+ p_cc->i_dts = p_cc->i_pts = p_pic->date;
6677+ else
6678+ p_cc->i_pts = p_cc->i_dts;
6679+ decoder_cc_desc_t desc;
6680+ desc.i_608_channels = p_sys->cc.i_608channels;
6681+ desc.i_708_channels = p_sys->cc.i_708channels;
6682+ desc.i_reorder_depth = 4;
6683+ decoder_QueueCc( p_dec, p_cc, &desc );
6684+ }
6685+ cc_Flush( &p_sys->cc );
6686+ }
6687+ }
6688+ return 0;
6689+}
6690+
6691+/*****************************************************************************
6692+ * DecodeBlock: Called to decode one or more frames
6693+ *****************************************************************************/
6694+
6695+static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error )
6696+{
6697+ decoder_sys_t *p_sys = p_dec->p_sys;
6698+ AVCodecContext *p_context = p_sys->p_context;
6699+ /* Boolean if we assume that we should get valid pic as result */
6700+ bool b_need_output_picture = true;
6701+
6702+ /* Boolean for END_OF_SEQUENCE */
6703+ bool eos_spotted = false;
6704+
6705+#if TRACE_ALL
6706+ msg_Dbg(p_dec, "<<< %s: (buf_size=%d)", __func__, pp_block == NULL || *pp_block == NULL ? 0 : (*pp_block)->i_buffer);
6707+#endif
6708+
6709+ block_t *p_block;
6710+ mtime_t current_time;
6711+ picture_t *p_pic = NULL;
6712+ AVFrame *frame = NULL;
6713+
6714+ // By default we are OK
6715+ *error = false;
6716+
6717+ if( !p_context->extradata_size && p_dec->fmt_in.i_extra )
6718+ {
6719+ ffmpeg_InitCodec( p_dec );
6720+ if( !avcodec_is_open( p_context ) )
6721+ OpenVideoCodec( p_dec );
6722+ }
6723+
6724+ p_block = pp_block ? *pp_block : NULL;
6725+ if(!p_block && !(p_sys->p_codec->capabilities & AV_CODEC_CAP_DELAY) )
6726+ return NULL;
6727+
6728+ if( !avcodec_is_open( p_context ) )
6729+ {
6730+ if( p_block )
6731+ block_Release( p_block );
6732+ return NULL;
6733+ }
6734+
6735+ if( !check_block_validity( p_sys, p_block ) )
6736+ return NULL;
6737+
6738+ current_time = mdate();
6739+ if( p_dec->b_frame_drop_allowed && check_block_being_late( p_sys, p_block, current_time) )
6740+ {
6741+ msg_Err( p_dec, "more than 5 seconds of late video -> "
6742+ "dropping frame (computer too slow ?)" );
6743+ return NULL;
6744+ }
6745+
6746+
6747+ /* A good idea could be to decode all I pictures and see for the other */
6748+
6749+ /* Defaults that if we aren't in prerolling, we want output picture
6750+ same for if we are flushing (p_block==NULL) */
6751+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
6752+ b_need_output_picture = true;
6753+ else
6754+ b_need_output_picture = false;
6755+
6756+ /* Change skip_frame config only if hurry_up is enabled */
6757+ if( p_sys->b_hurry_up )
6758+ {
6759+ p_context->skip_frame = p_sys->i_skip_frame;
6760+
6761+ /* Check also if we should/can drop the block and move to next block
6762+ as trying to catchup the speed*/
6763+ if( p_dec->b_frame_drop_allowed &&
6764+ check_frame_should_be_dropped( p_sys, p_context, &b_need_output_picture ) )
6765+ {
6766+ if( p_block )
6767+ block_Release( p_block );
6768+ msg_Warn( p_dec, "More than 11 late frames, dropping frame" );
6769+ return NULL;
6770+ }
6771+ }
6772+ if( !b_need_output_picture )
6773+ {
6774+ p_context->skip_frame = __MAX( p_context->skip_frame,
6775+ AVDISCARD_NONREF );
6776+ }
6777+
6778+ /*
6779+ * Do the actual decoding now */
6780+
6781+ /* Don't forget that libavcodec requires a little more bytes
6782+ * that the real frame size */
6783+ if( p_block && p_block->i_buffer > 0 )
6784+ {
6785+ eos_spotted = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0;
6786+
6787+ p_block = block_Realloc( p_block, 0,
6788+ p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE );
6789+ if( !p_block )
6790+ return NULL;
6791+ p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
6792+ *pp_block = p_block;
6793+ memset( p_block->p_buffer + p_block->i_buffer, 0,
6794+ FF_INPUT_BUFFER_PADDING_SIZE );
6795+ }
6796+
6797+ while( !p_block || p_block->i_buffer > 0 || eos_spotted )
6798+ {
6799+ int i_used;
6800+ AVPacket pkt;
6801+
6802+ post_mt( p_sys );
6803+
6804+ av_init_packet( &pkt );
6805+ if( p_block && p_block->i_buffer > 0 )
6806+ {
6807+ pkt.data = p_block->p_buffer;
6808+ pkt.size = p_block->i_buffer;
6809+ pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE;
6810+ pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE;
6811+ }
6812+ else
6813+ {
6814+ /* Return delayed frames if codec has CODEC_CAP_DELAY */
6815+ pkt.data = NULL;
6816+ pkt.size = 0;
6817+ }
6818+
6819+ if( !p_sys->palette_sent )
6820+ {
6821+ uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
6822+ if (pal) {
6823+ memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE);
6824+ p_sys->palette_sent = true;
6825+ }
6826+ }
6827+
6828+ /* Make sure we don't reuse the same timestamps twice */
6829+ if( p_block )
6830+ {
6831+ p_block->i_pts =
6832+ p_block->i_dts = VLC_TS_INVALID;
6833+ }
6834+
6835+ int ret = avcodec_send_packet(p_context, &pkt);
6836+ if( ret != 0 && ret != AVERROR(EAGAIN) )
6837+ {
6838+ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
6839+ {
6840+ msg_Err(p_dec, "avcodec_send_packet critical error");
6841+ *error = true;
6842+ }
6843+ av_packet_unref( &pkt );
6844+ break;
6845+ }
6846+ i_used = ret != AVERROR(EAGAIN) ? pkt.size : 0;
6847+ av_packet_unref( &pkt );
6848+
6849+ frame = av_frame_alloc();
6850+ if (unlikely(frame == NULL))
6851+ {
6852+ *error = true;
6853+ break;
6854+ }
6855+
6856+ ret = avcodec_receive_frame(p_context, frame);
6857+ if( ret != 0 && ret != AVERROR(EAGAIN) )
6858+ {
6859+ msg_Dbg(p_dec, "No receive");
6860+ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
6861+ {
6862+ msg_Err(p_dec, "avcodec_receive_frame critical error");
6863+ *error = true;
6864+ }
6865+ av_frame_free(&frame);
6866+ /* After draining, we need to reset decoder with a flush */
6867+ if( ret == AVERROR_EOF )
6868+ avcodec_flush_buffers( p_sys->p_context );
6869+ break;
6870+ }
6871+ bool not_received_frame = ret;
6872+
6873+ wait_mt( p_sys );
6874+
6875+ if( eos_spotted )
6876+ p_sys->b_first_frame = true;
6877+
6878+ if( p_block )
6879+ {
6880+ if( p_block->i_buffer <= 0 )
6881+ eos_spotted = false;
6882+
6883+ /* Consumed bytes */
6884+ p_block->p_buffer += i_used;
6885+ p_block->i_buffer -= i_used;
6886+ }
6887+
6888+ /* Nothing to display */
6889+ if( not_received_frame )
6890+ {
6891+// msg_Dbg(p_dec, "No rx: used=%d", i_used);
6892+ av_frame_free(&frame);
6893+ if( i_used == 0 ) break;
6894+ continue;
6895+ }
6896+
6897+ /* Compute the PTS */
6898+#ifdef FF_API_PKT_PTS
6899+ mtime_t i_pts = frame->pts;
6900+#else
6901+ mtime_t i_pts = frame->pkt_pts;
6902+#endif
6903+ if (i_pts == AV_NOPTS_VALUE )
6904+ i_pts = frame->pkt_dts;
6905+
6906+ if( i_pts == AV_NOPTS_VALUE )
6907+ i_pts = date_Get( &p_sys->pts );
6908+
6909+ /* Interpolate the next PTS */
6910+ if( i_pts > VLC_TS_INVALID )
6911+ date_Set( &p_sys->pts, i_pts );
6912+
6913+ const mtime_t i_next_pts = interpolate_next_pts(p_dec, frame);
6914+
6915+ update_late_frame_count( p_dec, p_block, current_time, i_pts, i_next_pts);
6916+
6917+ if( !b_need_output_picture ||
6918+// ( !p_sys->p_va && !frame->linesize[0] ) ||
6919+ ( !frame->linesize[0] ) ||
6920+ ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) &&
6921+ !p_sys->b_show_corrupted ) )
6922+ {
6923+ av_frame_free(&frame);
6924+// msg_Dbg(p_dec, "Bad frame");
6925+ continue;
6926+ }
6927+
6928+ if( p_context->pix_fmt == AV_PIX_FMT_PAL8
6929+ && !p_dec->fmt_out.video.p_palette )
6930+ {
6931+ /* See AV_PIX_FMT_PAL8 comment in avc_GetVideoFormat(): update the
6932+ * fmt_out palette and change the fmt_out chroma to request a new
6933+ * vout */
6934+ assert( p_dec->fmt_out.video.i_chroma != VLC_CODEC_RGBP );
6935+
6936+ video_palette_t *p_palette;
6937+ p_palette = p_dec->fmt_out.video.p_palette
6938+ = malloc( sizeof(video_palette_t) );
6939+ if( !p_palette )
6940+ {
6941+ *error = true;
6942+ av_frame_free(&frame);
6943+ break;
6944+ }
6945+ static_assert( sizeof(p_palette->palette) == AVPALETTE_SIZE,
6946+ "Palette size mismatch between vlc and libavutil" );
6947+ assert( frame->data[1] != NULL );
6948+ memcpy( p_palette->palette, frame->data[1], AVPALETTE_SIZE );
6949+ p_palette->i_entries = AVPALETTE_COUNT;
6950+ p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBP;
6951+ if( decoder_UpdateVideoFormat( p_dec ) )
6952+ {
6953+ av_frame_free(&frame);
6954+ continue;
6955+ }
6956+ }
6957+
6958+#if 1
6959+ {
6960+ cma_buf_t * const cb = av_rpi_zc_buf_v(frame->buf[0]);
6961+
6962+ if (cb == NULL)
6963+ {
6964+ msg_Err(p_dec, "Frame has no attached CMA buffer");
6965+ goto fail;
6966+ }
6967+
6968+ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
6969+ p_context->pix_fmt) != 0)
6970+ {
6971+ msg_Err(p_dec, "Failed to update format");
6972+ goto fail;
6973+ }
6974+
6975+ if ((p_pic = decoder_NewPicture(p_dec)) == NULL)
6976+ {
6977+ msg_Err(p_dec, "Failed to allocate pic");
6978+ goto fail;
6979+ }
6980+
6981+ if (cma_buf_pic_attach(cma_buf_ref(cb), p_pic) != 0)
6982+ {
6983+ cma_buf_unref(cb); // Undo the in_flight
6984+ char dbuf0[5];
6985+ msg_Err(p_dec, "Failed to attach bufs to pic: fmt=%s", str_fourcc(dbuf0, p_pic->format.i_chroma));
6986+ goto fail;
6987+ }
6988+
6989+ // ****** Set planes etc.
6990+ set_pic_from_frame(p_pic, frame);
6991+ }
6992+#else
6993+ picture_t *p_pic = frame->opaque;
6994+ if( p_pic == NULL )
6995+ { /* When direct rendering is not used, get_format() and get_buffer()
6996+ * might not be called. The output video format must be set here
6997+ * then picture buffer can be allocated. */
6998+ if (p_sys->p_va == NULL
6999+ && lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
7000+ p_context->pix_fmt) == 0)
7001+ p_pic = decoder_NewPicture(p_dec);
7002+
7003+ if( !p_pic )
7004+ {
7005+ av_frame_free(&frame);
7006+ break;
7007+ }
7008+
7009+ /* Fill picture_t from AVFrame */
7010+ if( lavc_CopyPicture( p_dec, p_pic, frame ) != VLC_SUCCESS )
7011+ {
7012+ av_frame_free(&frame);
7013+ picture_Release( p_pic );
7014+ break;
7015+ }
7016+ }
7017+ else
7018+ {
7019+ /* Some codecs can return the same frame multiple times. By the
7020+ * time that the same frame is returned a second time, it will be
7021+ * too late to clone the underlying picture. So clone proactively.
7022+ * A single picture CANNOT be queued multiple times.
7023+ */
7024+ p_pic = picture_Clone( p_pic );
7025+ if( unlikely(p_pic == NULL) )
7026+ {
7027+ av_frame_free(&frame);
7028+ break;
7029+ }
7030+ }
7031+#endif
7032+
7033+ if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den )
7034+ {
7035+ /* Fetch again the aspect ratio in case it changed */
7036+ p_dec->fmt_out.video.i_sar_num
7037+ = p_context->sample_aspect_ratio.num;
7038+ p_dec->fmt_out.video.i_sar_den
7039+ = p_context->sample_aspect_ratio.den;
7040+
7041+ if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den )
7042+ {
7043+ p_dec->fmt_out.video.i_sar_num = 1;
7044+ p_dec->fmt_out.video.i_sar_den = 1;
7045+ }
7046+ }
7047+
7048+ p_pic->date = i_pts;
7049+ /* Hack to force display of still pictures */
7050+ p_pic->b_force = p_sys->b_first_frame;
7051+ p_pic->i_nb_fields = 2 + frame->repeat_pict;
7052+ p_pic->b_progressive = !frame->interlaced_frame;
7053+ p_pic->b_top_field_first = frame->top_field_first;
7054+
7055+ if (DecodeSidedata(p_dec, frame, p_pic))
7056+ i_pts = VLC_TS_INVALID;
7057+
7058+ av_frame_free(&frame);
7059+
7060+ /* Send decoded frame to vout */
7061+ if (i_pts > VLC_TS_INVALID)
7062+ {
7063+ p_sys->b_first_frame = false;
7064+#if TRACE_ALL
7065+ msg_Dbg(p_dec, ">>> %s: Got pic", __func__);
7066+#endif
7067+ return p_pic;
7068+ }
7069+ else
7070+ picture_Release( p_pic );
7071+ }
7072+
7073+ if( p_block )
7074+ block_Release( p_block );
7075+
7076+#if TRACE_ALL
7077+ msg_Dbg(p_dec, ">>> %s: NULL", __func__);
7078+#endif
7079+ return NULL;
7080+
7081+fail:
7082+#if TRACE_ALL
7083+ msg_Dbg(p_dec, ">>> %s: FAIL", __func__);
7084+#endif
7085+ av_frame_free(&frame);
7086+ if (p_pic != NULL)
7087+ picture_Release(p_pic);
7088+ if (p_block != NULL)
7089+ block_Release(p_block);
7090+ *error = true;
7091+ return NULL;
7092+}
7093+
7094+static int DecodeVideo( decoder_t *p_dec, block_t *p_block )
7095+{
7096+ block_t **pp_block = p_block ? &p_block : NULL;
7097+ picture_t *p_pic;
7098+ bool error = false;
7099+ while( ( p_pic = DecodeBlock( p_dec, pp_block, &error ) ) != NULL )
7100+ decoder_QueueVideo( p_dec, p_pic );
7101+ return VLCDEC_SUCCESS;
7102+// Easiest to just ignore all errors - returning a real error seems to
7103+// kill output forever
7104+// return error ? VLCDEC_ECRITICAL : VLCDEC_SUCCESS;
7105+}
7106+
7107+/*****************************************************************************
7108+ * EndVideo: decoder destruction
7109+ *****************************************************************************
7110+ * This function is called when the thread ends after a successful
7111+ * initialization.
7112+ *****************************************************************************/
7113+static void MmalAvcodecCloseDecoder( vlc_object_t *obj )
7114+{
7115+ decoder_t *p_dec = (decoder_t *)obj;
7116+ decoder_sys_t *p_sys = p_dec->p_sys;
7117+ AVCodecContext *ctx = p_sys->p_context;
7118+// void *hwaccel_context;
7119+
7120+ msg_Dbg(obj, "<<< %s", __func__);
7121+
7122+ post_mt( p_sys );
7123+
7124+ cma_buf_pool_cancel(p_sys->cma_pool); // Abort any pending frame allocs
7125+
7126+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
7127+ if( avcodec_is_open( ctx ) )
7128+ avcodec_flush_buffers( ctx );
7129+
7130+ av_rpi_zc_uninit2(ctx);
7131+
7132+ wait_mt( p_sys );
7133+
7134+ cc_Flush( &p_sys->cc );
7135+
7136+// hwaccel_context = ctx->hwaccel_context;
7137+ avcodec_free_context( &ctx );
7138+
7139+// if( p_sys->p_va )
7140+// vlc_va_Delete( p_sys->p_va, &hwaccel_context );
7141+
7142+ cma_vcsm_exit(p_sys->vcsm_init_type);
7143+
7144+ vlc_sem_destroy( &p_sys->sem_mt );
7145+ free( p_sys );
7146+}
7147+
7148+/*****************************************************************************
7149+ * ffmpeg_InitCodec: setup codec extra initialization data for ffmpeg
7150+ *****************************************************************************/
7151+static void ffmpeg_InitCodec( decoder_t *p_dec )
7152+{
7153+ decoder_sys_t *p_sys = p_dec->p_sys;
7154+ size_t i_size = p_dec->fmt_in.i_extra;
7155+
7156+ if( !i_size ) return;
7157+
7158+ if( p_sys->p_codec->id == AV_CODEC_ID_SVQ3 )
7159+ {
7160+ uint8_t *p;
7161+
7162+ p_sys->p_context->extradata_size = i_size + 12;
7163+ p = p_sys->p_context->extradata =
7164+ av_malloc( p_sys->p_context->extradata_size +
7165+ FF_INPUT_BUFFER_PADDING_SIZE );
7166+ if( !p )
7167+ return;
7168+
7169+ memcpy( &p[0], "SVQ3", 4 );
7170+ memset( &p[4], 0, 8 );
7171+ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size );
7172+
7173+ /* Now remove all atoms before the SMI one */
7174+ if( p_sys->p_context->extradata_size > 0x5a &&
7175+ strncmp( (char*)&p[0x56], "SMI ", 4 ) )
7176+ {
7177+ uint8_t *psz = &p[0x52];
7178+
7179+ while( psz < &p[p_sys->p_context->extradata_size - 8] )
7180+ {
7181+ uint_fast32_t atom_size = GetDWBE( psz );
7182+ if( atom_size <= 1 )
7183+ {
7184+ /* FIXME handle 1 as long size */
7185+ break;
7186+ }
7187+ if( !strncmp( (char*)&psz[4], "SMI ", 4 ) )
7188+ {
7189+ memmove( &p[0x52], psz,
7190+ &p[p_sys->p_context->extradata_size] - psz );
7191+ break;
7192+ }
7193+
7194+ psz += atom_size;
7195+ }
7196+ }
7197+ }
7198+ else
7199+ {
7200+ p_sys->p_context->extradata_size = i_size;
7201+ p_sys->p_context->extradata =
7202+ av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );
7203+ if( p_sys->p_context->extradata )
7204+ {
7205+ memcpy( p_sys->p_context->extradata,
7206+ p_dec->fmt_in.p_extra, i_size );
7207+ memset( p_sys->p_context->extradata + i_size,
7208+ 0, FF_INPUT_BUFFER_PADDING_SIZE );
7209+ }
7210+ }
7211+}
7212+
7213+
7214+vlc_module_begin()
7215+ set_category( CAT_INPUT )
7216+ set_subcategory( SUBCAT_INPUT_VCODEC )
7217+ set_shortname(N_("MMAL avcodec"))
7218+ set_description(N_("MMAL buffered avcodec "))
7219+ set_capability("video decoder", 80)
7220+ add_shortcut("mmal_avcodec")
7221+ add_integer(MMAL_AVCODEC_BUFFERS, -1, MMAL_AVCODEC_BUFFERS_TEXT,
7222+ MMAL_AVCODEC_BUFFERS_LONGTEXT, true)
7223+ set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder)
7224+vlc_module_end()
7225+
7226--- /dev/null
7227+++ b/modules/hw/mmal/mmal_cma.c
7228@@ -0,0 +1,668 @@
7229+#ifdef HAVE_CONFIG_H
7230+# include "config.h"
7231+#endif
7232+
7233+#include <stdatomic.h>
7234+#include <unistd.h>
7235+#include <fcntl.h>
7236+#include <sys/ioctl.h>
7237+#include <sys/mman.h>
7238+
7239+#include <interface/vcsm/user-vcsm.h>
7240+
7241+#include <vlc_common.h>
7242+#include <vlc_picture.h>
7243+
7244+#include "mmal_cma.h"
7245+#include "mmal_picture.h"
7246+
7247+#include <assert.h>
7248+
7249+#define TRACE_ALL 0
7250+
7251+//-----------------------------------------------------------------------------
7252+//
7253+// Generic pool functions
7254+// Knows nothing about pool entries
7255+
7256+typedef void * cma_pool_alloc_fn(void * v, size_t size);
7257+typedef void cma_pool_free_fn(void * v, void * el, size_t size);
7258+
7259+#if TRACE_ALL
7260+static atomic_int pool_seq;
7261+#endif
7262+
7263+// Pool structure
7264+// Ref count is held by pool owner and pool els that have been got
7265+// Els in the pool do not count towards its ref count
7266+struct cma_pool_fixed_s
7267+{
7268+ atomic_int ref_count;
7269+
7270+ vlc_mutex_t lock;
7271+ unsigned int n_in;
7272+ unsigned int n_out;
7273+ unsigned int pool_size;
7274+ int flight_size;
7275+ size_t el_size;
7276+ void ** pool;
7277+
7278+ bool cancel;
7279+ int in_flight;
7280+ vlc_cond_t flight_cond;
7281+
7282+ void * alloc_v;
7283+ cma_pool_alloc_fn * el_alloc_fn;
7284+ cma_pool_free_fn * el_free_fn;
7285+ cma_pool_on_delete_fn * on_delete_fn;
7286+
7287+ const char * name;
7288+#if TRACE_ALL
7289+ int seq;
7290+#endif
7291+};
7292+
7293+static inline unsigned int inc_mod(const unsigned int n, const unsigned int m)
7294+{
7295+ return n + 1 >= m ? 0 : n + 1;
7296+}
7297+
7298+static void free_pool(const cma_pool_fixed_t * const p, void ** const pool,
7299+ const unsigned int pool_size, const size_t el_size)
7300+{
7301+ if (pool == NULL)
7302+ return;
7303+
7304+ for (unsigned int n = 0; n != pool_size; ++n)
7305+ if (pool[n] != NULL)
7306+ p->el_free_fn(p->alloc_v, pool[n], el_size);
7307+ free(pool);
7308+}
7309+
7310+// Just kill this - no checks
7311+static void cma_pool_fixed_delete(cma_pool_fixed_t * const p)
7312+{
7313+ cma_pool_on_delete_fn *const on_delete_fn = p->on_delete_fn;
7314+ void *const v = p->alloc_v;
7315+
7316+ free_pool(p, p->pool, p->pool_size, p->el_size);
7317+
7318+ if (p->name != NULL)
7319+ free((void *)p->name); // Discard const
7320+
7321+ vlc_cond_destroy(&p->flight_cond);
7322+ vlc_mutex_destroy(&p->lock);
7323+ free(p);
7324+
7325+ // Inform our container that we are dead (if it cares)
7326+ if (on_delete_fn)
7327+ on_delete_fn(v);
7328+}
7329+
7330+static void cma_pool_fixed_unref(cma_pool_fixed_t * const p)
7331+{
7332+ if (atomic_fetch_sub(&p->ref_count, 1) <= 1)
7333+ cma_pool_fixed_delete(p);
7334+}
7335+
7336+static void cma_pool_fixed_ref(cma_pool_fixed_t * const p)
7337+{
7338+ atomic_fetch_add(&p->ref_count, 1);
7339+}
7340+
7341+static void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p)
7342+{
7343+ vlc_mutex_lock(&p->lock);
7344+ ++p->in_flight;
7345+ vlc_mutex_unlock(&p->lock);
7346+}
7347+
7348+static void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p)
7349+{
7350+ vlc_mutex_lock(&p->lock);
7351+ if (--p->in_flight == 0)
7352+ vlc_cond_signal(&p->flight_cond);
7353+ vlc_mutex_unlock(&p->lock);
7354+}
7355+
7356+static void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool inc_flight, const bool no_pool)
7357+{
7358+ void * v = NULL;
7359+
7360+ vlc_mutex_lock(&p->lock);
7361+
7362+ for (;;)
7363+ {
7364+ if (req_el_size != p->el_size)
7365+ {
7366+ void ** const deadpool = p->pool;
7367+ const size_t dead_size = p->el_size;
7368+ const unsigned int dead_n = p->pool_size;
7369+
7370+ p->pool = NULL;
7371+ p->n_in = 0;
7372+ p->n_out = 0;
7373+ p->el_size = req_el_size;
7374+
7375+ if (deadpool != NULL)
7376+ {
7377+ vlc_mutex_unlock(&p->lock);
7378+ // Do the free old op outside the mutex in case the free is slow
7379+ free_pool(p, deadpool, dead_n, dead_size);
7380+ vlc_mutex_lock(&p->lock);
7381+ continue;
7382+ }
7383+ }
7384+
7385+ // Late abort if flush or cancel so we can still kill the pool
7386+ if (req_el_size == 0 || p->cancel)
7387+ {
7388+ vlc_mutex_unlock(&p->lock);
7389+ return NULL;
7390+ }
7391+
7392+ if (p->pool != NULL && !no_pool)
7393+ {
7394+ v = p->pool[p->n_in];
7395+ if (v != NULL)
7396+ {
7397+ p->pool[p->n_in] = NULL;
7398+ p->n_in = inc_mod(p->n_in, p->pool_size);
7399+ break;
7400+ }
7401+ }
7402+
7403+ if (p->in_flight <= 0)
7404+ break;
7405+
7406+ vlc_cond_wait(&p->flight_cond, &p->lock);
7407+ }
7408+
7409+ if (inc_flight)
7410+ ++p->in_flight;
7411+
7412+ vlc_mutex_unlock(&p->lock);
7413+
7414+ if (v == NULL && req_el_size != 0)
7415+ v = p->el_alloc_fn(p->alloc_v, req_el_size);
7416+
7417+ // Tag ref
7418+ if (v != NULL)
7419+ cma_pool_fixed_ref(p);
7420+ // Remove flight if we set it and error
7421+ else if (inc_flight)
7422+ cma_pool_fixed_dec_in_flight(p);
7423+
7424+ return v;
7425+}
7426+
7427+static void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight)
7428+{
7429+ vlc_mutex_lock(&p->lock);
7430+
7431+ if (el_size == p->el_size && (p->pool == NULL || p->pool[p->n_out] == NULL))
7432+ {
7433+ if (p->pool == NULL)
7434+ p->pool = calloc(p->pool_size, sizeof(void*));
7435+
7436+ p->pool[p->n_out] = v;
7437+ p->n_out = inc_mod(p->n_out, p->pool_size);
7438+ v = NULL;
7439+ }
7440+
7441+ if (was_in_flight)
7442+ --p->in_flight;
7443+
7444+ vlc_mutex_unlock(&p->lock);
7445+
7446+ vlc_cond_signal(&p->flight_cond);
7447+
7448+ if (v != NULL)
7449+ p->el_free_fn(p->alloc_v, v, el_size);
7450+
7451+ cma_pool_fixed_unref(p);
7452+}
7453+
7454+static int cma_pool_fixed_resize(cma_pool_fixed_t * const p,
7455+ const unsigned int new_pool_size, const int new_flight_size)
7456+{
7457+ void ** dead_pool = NULL;
7458+ size_t dead_size = 0;
7459+ unsigned int dead_n = 0;
7460+
7461+ // This makes this non-reentrant but saves us a lot of time in the normal
7462+ // "nothing happens" case
7463+ if (p->pool_size == new_pool_size && p->flight_size == new_flight_size)
7464+ return 0;
7465+
7466+ vlc_mutex_lock(&p->lock);
7467+
7468+ if (p->pool != NULL && new_pool_size != p->pool_size)
7469+ {
7470+ void ** const new_pool = calloc(new_pool_size, sizeof(void*));
7471+ unsigned int d, s;
7472+ dead_pool = p->pool;
7473+ dead_size = p->el_size;
7474+ dead_n = p->pool_size;
7475+
7476+ if (new_pool == NULL)
7477+ {
7478+ vlc_mutex_unlock(&p->lock);
7479+ return -1;
7480+ }
7481+
7482+ for (d = 0, s = p->n_in; d != new_pool_size && (new_pool[d] = dead_pool[s]) != NULL; ++d, s = inc_mod(s, dead_n))
7483+ dead_pool[s] = NULL;
7484+
7485+ p->n_out = 0;
7486+ p->n_in = (d != new_pool_size) ? d : 0;
7487+ p->pool = new_pool;
7488+ }
7489+
7490+ p->pool_size = new_pool_size;
7491+ if (new_flight_size > p->flight_size)
7492+ vlc_cond_broadcast(&p->flight_cond); // Lock still active so nothing happens till we release it
7493+ p->in_flight += p->flight_size - new_flight_size;
7494+ p->flight_size = new_flight_size;
7495+
7496+ vlc_mutex_unlock(&p->lock);
7497+
7498+ free_pool(p, dead_pool, dead_n, dead_size);
7499+ return 0;
7500+}
7501+
7502+static int cma_pool_fixed_fill(cma_pool_fixed_t * const p, const size_t el_size)
7503+{
7504+ for (;;)
7505+ {
7506+ vlc_mutex_lock(&p->lock);
7507+ bool done = el_size == p->el_size && p->pool != NULL && p->pool[p->n_out] != NULL;
7508+ vlc_mutex_unlock(&p->lock);
7509+ if (done)
7510+ break;
7511+ void * buf = cma_pool_fixed_get(p, el_size, false, true);
7512+ if (buf == NULL)
7513+ return -ENOMEM;
7514+ cma_pool_fixed_put(p, buf, el_size, false);
7515+ }
7516+ return 0;
7517+}
7518+
7519+static void cma_pool_fixed_cancel(cma_pool_fixed_t * const p)
7520+{
7521+ vlc_mutex_lock(&p->lock);
7522+ p->cancel = true;
7523+ vlc_cond_broadcast(&p->flight_cond);
7524+ vlc_mutex_unlock(&p->lock);
7525+}
7526+
7527+static void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p)
7528+{
7529+ vlc_mutex_lock(&p->lock);
7530+ p->cancel = false;
7531+ vlc_mutex_unlock(&p->lock);
7532+}
7533+
7534+
7535+// Purge pool & unref
7536+static void cma_pool_fixed_kill(cma_pool_fixed_t * const p)
7537+{
7538+ if (p == NULL)
7539+ return;
7540+
7541+ // This flush is not strictly needed but it reclaims what memory we can reclaim asap
7542+ cma_pool_fixed_get(p, 0, false, false);
7543+ cma_pool_fixed_unref(p);
7544+}
7545+
7546+// Create a new pool
7547+static cma_pool_fixed_t*
7548+cma_pool_fixed_new(const unsigned int pool_size,
7549+ const int flight_size,
7550+ void * const alloc_v,
7551+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn,
7552+ cma_pool_on_delete_fn * const on_delete_fn,
7553+ const char * const name)
7554+{
7555+ cma_pool_fixed_t* const p = calloc(1, sizeof(cma_pool_fixed_t));
7556+ if (p == NULL)
7557+ return NULL;
7558+
7559+ atomic_store(&p->ref_count, 1);
7560+ vlc_mutex_init(&p->lock);
7561+ vlc_cond_init(&p->flight_cond);
7562+
7563+ p->pool_size = pool_size;
7564+ p->flight_size = flight_size;
7565+ p->in_flight = -flight_size;
7566+
7567+ p->alloc_v = alloc_v;
7568+ p->el_alloc_fn = alloc_fn;
7569+ p->el_free_fn = free_fn;
7570+ p->on_delete_fn = on_delete_fn;
7571+ p->name = name == NULL ? NULL : strdup(name);
7572+#if TRACE_ALL
7573+ p->seq = atomic_fetch_add(&pool_seq, 1);
7574+#endif
7575+
7576+ return p;
7577+}
7578+
7579+// ---------------------------------------------------------------------------
7580+//
7581+// CMA buffer functions - uses cma_pool_fixed for pooling
7582+
7583+struct cma_buf_pool_s {
7584+ cma_pool_fixed_t * pool;
7585+ vcsm_init_type_t init_type;
7586+
7587+ bool all_in_flight;
7588+#if TRACE_ALL
7589+ size_t alloc_n;
7590+ size_t alloc_size;
7591+#endif
7592+};
7593+
7594+typedef struct cma_buf_s {
7595+ atomic_int ref_count;
7596+ cma_buf_pool_t * cbp;
7597+ bool in_flight;
7598+ size_t size;
7599+ unsigned int vcsm_h; // VCSM handle from initial alloc
7600+ unsigned int vc_h; // VC handle for ZC mmal buffers
7601+ unsigned int vc_addr; // VC addr - unused by us but wanted by FFmpeg
7602+ int fd; // dmabuf handle for GL
7603+ void * mmap; // ARM mapped address
7604+ picture_context_t *ctx2;
7605+} cma_buf_t;
7606+
7607+static void cma_pool_delete(cma_buf_t * const cb)
7608+{
7609+ assert(atomic_load(&cb->ref_count) == 0);
7610+#if TRACE_ALL
7611+ cb->cbp->alloc_size -= cb->size;
7612+ --cb->cbp->alloc_n;
7613+ fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cb->cbp->pool->seq, cb->cbp->pool->name, cb->cbp->alloc_n, cb->cbp->alloc_size);
7614+#endif
7615+
7616+ if (cb->ctx2 != NULL)
7617+ cb->ctx2->destroy(cb->ctx2);
7618+
7619+ if (cb->mmap != MAP_FAILED)
7620+ {
7621+ if (cb->cbp->init_type == VCSM_INIT_CMA)
7622+ munmap(cb->mmap, cb->size);
7623+ else
7624+ vcsm_unlock_hdl(cb->vcsm_h);
7625+ }
7626+ if (cb->fd != -1)
7627+ close(cb->fd);
7628+ if (cb->vcsm_h != 0)
7629+ vcsm_free(cb->vcsm_h);
7630+ free(cb);
7631+}
7632+
7633+static void cma_pool_free_cb(void * v, void * el, size_t size)
7634+{
7635+ VLC_UNUSED(v);
7636+ VLC_UNUSED(size);
7637+
7638+ cma_pool_delete(el);
7639+}
7640+
7641+static void * cma_pool_alloc_cb(void * v, size_t size)
7642+{
7643+ cma_buf_pool_t * const cbp = v;
7644+
7645+ cma_buf_t * const cb = malloc(sizeof(cma_buf_t));
7646+ if (cb == NULL)
7647+ return NULL;
7648+
7649+ *cb = (cma_buf_t){
7650+ .ref_count = ATOMIC_VAR_INIT(0),
7651+ .cbp = cbp,
7652+ .in_flight = 0,
7653+ .size = size,
7654+ .vcsm_h = 0,
7655+ .vc_h = 0,
7656+ .fd = -1,
7657+ .mmap = MAP_FAILED,
7658+ .ctx2 = NULL
7659+ };
7660+#if TRACE_ALL
7661+ cb->cbp->alloc_size += cb->size;
7662+ ++cb->cbp->alloc_n;
7663+ fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cbp->pool->seq, cbp->pool->name, cbp->alloc_n, cbp->alloc_size);
7664+#endif
7665+
7666+ // 0x80 is magic value to force full ARM-side mapping - otherwise
7667+ // cache requests can cause kernel crashes
7668+ if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST | 0x80, "VLC frame")) == 0)
7669+ {
7670+#if TRACE_ALL
7671+ fprintf(stderr, "vcsm_malloc_cache fail\n");
7672+#endif
7673+ goto fail;
7674+ }
7675+
7676+ if ((cb->vc_h = vcsm_vc_hdl_from_hdl(cb->vcsm_h)) == 0)
7677+ {
7678+#if TRACE_ALL
7679+ fprintf(stderr, "vcsm_vc_hdl_from_hdl fail\n");
7680+#endif
7681+ goto fail;
7682+ }
7683+
7684+ if (cbp->init_type == VCSM_INIT_CMA)
7685+ {
7686+ if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1)
7687+ {
7688+#if TRACE_ALL
7689+ fprintf(stderr, "vcsm_export_dmabuf fail\n");
7690+#endif
7691+ goto fail;
7692+ }
7693+
7694+ if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED)
7695+ goto fail;
7696+ }
7697+ else
7698+ {
7699+ void * arm_addr;
7700+ if ((arm_addr = vcsm_lock(cb->vcsm_h)) == NULL)
7701+ {
7702+#if TRACE_ALL
7703+ fprintf(stderr, "vcsm_lock fail\n");
7704+#endif
7705+ goto fail;
7706+ }
7707+ cb->mmap = arm_addr;
7708+ }
7709+
7710+ cb->vc_addr = vcsm_vc_addr_from_hdl(cb->vcsm_h);
7711+
7712+ return cb;
7713+
7714+fail:
7715+ cma_pool_delete(cb);
7716+ return NULL;
7717+}
7718+
7719+// Pool has died - safe now to exit vcsm
7720+static void cma_buf_pool_on_delete_cb(void * v)
7721+{
7722+ cma_buf_pool_t * const cbp = v;
7723+
7724+ cma_vcsm_exit(cbp->init_type);
7725+ free(cbp);
7726+}
7727+
7728+void cma_buf_pool_cancel(cma_buf_pool_t * const cbp)
7729+{
7730+ if (cbp == NULL || cbp->pool == NULL)
7731+ return;
7732+
7733+ cma_pool_fixed_cancel(cbp->pool);
7734+}
7735+
7736+void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp)
7737+{
7738+ if (cbp == NULL || cbp->pool == NULL)
7739+ return;
7740+
7741+ cma_pool_fixed_uncancel(cbp->pool);
7742+}
7743+
7744+// User finished with pool
7745+void cma_buf_pool_delete(cma_buf_pool_t * const cbp)
7746+{
7747+ if (cbp == NULL)
7748+ return;
7749+
7750+ if (cbp->pool != NULL)
7751+ {
7752+ // We will call cma_buf_pool_on_delete_cb when the pool finally dies
7753+ // (might be now) which will free up our env.
7754+ cma_pool_fixed_kill(cbp->pool);
7755+ }
7756+ else
7757+ {
7758+ // Had no pool for some reason (error) but must still finish cleanup
7759+ cma_buf_pool_on_delete_cb(cbp);
7760+ }
7761+}
7762+
7763+int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size)
7764+{
7765+ return cma_pool_fixed_fill(cbp->pool, el_size);
7766+}
7767+
7768+int cma_buf_pool_resize(cma_buf_pool_t * const cbp,
7769+ const unsigned int new_pool_size, const int new_flight_size)
7770+{
7771+ return cma_pool_fixed_resize(cbp->pool, new_pool_size, new_flight_size);
7772+}
7773+
7774+cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size, const bool all_in_flight, const char * const name)
7775+{
7776+ vcsm_init_type_t const init_type = cma_vcsm_init();
7777+ if (init_type == VCSM_INIT_NONE)
7778+ return NULL;
7779+
7780+ cma_buf_pool_t * const cbp = calloc(1, sizeof(cma_buf_pool_t));
7781+ if (cbp == NULL)
7782+ return NULL;
7783+
7784+ cbp->init_type = init_type;
7785+ cbp->all_in_flight = all_in_flight;
7786+
7787+ if ((cbp->pool = cma_pool_fixed_new(pool_size, flight_size, cbp, cma_pool_alloc_cb, cma_pool_free_cb, cma_buf_pool_on_delete_cb, name)) == NULL)
7788+ goto fail;
7789+ return cbp;
7790+
7791+fail:
7792+ cma_buf_pool_delete(cbp);
7793+ return NULL;
7794+}
7795+
7796+
7797+void cma_buf_in_flight(cma_buf_t * const cb)
7798+{
7799+ if (!cb->cbp->all_in_flight)
7800+ {
7801+ assert(!cb->in_flight);
7802+ cb->in_flight = true;
7803+ cma_pool_fixed_inc_in_flight(cb->cbp->pool);
7804+ }
7805+}
7806+
7807+void cma_buf_end_flight(cma_buf_t * const cb)
7808+{
7809+ if (cb != NULL && !cb->cbp->all_in_flight && cb->in_flight)
7810+ {
7811+ cb->in_flight = false;
7812+ cma_pool_fixed_dec_in_flight(cb->cbp->pool);
7813+ }
7814+}
7815+
7816+
7817+// Return vcsm handle
7818+unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb)
7819+{
7820+ return cb->vcsm_h;
7821+}
7822+
7823+size_t cma_buf_size(const cma_buf_t * const cb)
7824+{
7825+ return cb->size;
7826+}
7827+
7828+int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2)
7829+{
7830+ if (cb->ctx2 != NULL)
7831+ return VLC_EGENERIC;
7832+
7833+ cb->ctx2 = ctx2;
7834+ return VLC_SUCCESS;
7835+}
7836+
7837+unsigned int cma_buf_vc_handle(const cma_buf_t *const cb)
7838+{
7839+ return cb->vc_h;
7840+}
7841+
7842+int cma_buf_fd(const cma_buf_t *const cb)
7843+{
7844+ return cb->fd;
7845+}
7846+
7847+void * cma_buf_addr(const cma_buf_t *const cb)
7848+{
7849+ return cb->mmap;
7850+}
7851+
7852+unsigned int cma_buf_vc_addr(const cma_buf_t *const cb)
7853+{
7854+ return cb->vc_addr;
7855+}
7856+
7857+
7858+picture_context_t * cma_buf_context2(const cma_buf_t *const cb)
7859+{
7860+ return cb->ctx2;
7861+}
7862+
7863+
7864+void cma_buf_unref(cma_buf_t * const cb)
7865+{
7866+ if (cb == NULL)
7867+ return;
7868+ if (atomic_fetch_sub(&cb->ref_count, 1) <= 1)
7869+ {
7870+ const bool was_in_flight = cb->in_flight;
7871+ cb->in_flight = false;
7872+ cma_pool_fixed_put(cb->cbp->pool, cb, cb->size, was_in_flight);
7873+ }
7874+}
7875+
7876+cma_buf_t * cma_buf_ref(cma_buf_t * const cb)
7877+{
7878+ if (cb == NULL)
7879+ return NULL;
7880+ atomic_fetch_add(&cb->ref_count, 1);
7881+ return cb;
7882+}
7883+
7884+cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const cbp, const size_t size)
7885+{
7886+ cma_buf_t *const cb = cma_pool_fixed_get(cbp->pool, size, cbp->all_in_flight, false);
7887+
7888+ if (cb == NULL)
7889+ return NULL;
7890+
7891+ cb->in_flight = cbp->all_in_flight;
7892+ // When 1st allocated or retrieved from the pool the block will have a
7893+ // ref count of 0 so ref here
7894+ return cma_buf_ref(cb);
7895+}
7896+
7897--- /dev/null
7898+++ b/modules/hw/mmal/mmal_cma.h
7899@@ -0,0 +1,71 @@
7900+#ifndef VLC_MMAL_MMAL_CMA_H_
7901+#define VLC_MMAL_MMAL_CMA_H_
7902+
7903+
7904+struct cma_pool_fixed_s;
7905+typedef struct cma_pool_fixed_s cma_pool_fixed_t;
7906+
7907+typedef void * cma_pool_alloc_fn(void * v, size_t size);
7908+typedef void cma_pool_free_fn(void * v, void * el, size_t size);
7909+typedef void cma_pool_on_delete_fn(void * v);
7910+
7911+#if 0
7912+void cma_pool_fixed_unref(cma_pool_fixed_t * const p);
7913+void cma_pool_fixed_ref(cma_pool_fixed_t * const p);
7914+void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool in_flight);
7915+void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight);
7916+void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p);
7917+void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p);
7918+void cma_pool_fixed_cancel(cma_pool_fixed_t * const p);
7919+void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p);
7920+void cma_pool_fixed_kill(cma_pool_fixed_t * const p);
7921+int cma_pool_fixed_resize(cma_pool_fixed_t * const p,
7922+ const unsigned int new_pool_size, const int new_flight_size);
7923+cma_pool_fixed_t * cma_pool_fixed_new(const unsigned int pool_size,
7924+ const int flight_size,
7925+ void * const alloc_v,
7926+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn,
7927+ cma_pool_on_delete_fn * const on_delete_fn,
7928+ const char * const name);
7929+#endif
7930+
7931+struct cma_buf_s;
7932+typedef struct cma_buf_s cma_buf_t;
7933+
7934+void cma_buf_in_flight(cma_buf_t * const cb);
7935+void cma_buf_end_flight(cma_buf_t * const cb);
7936+unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb);
7937+size_t cma_buf_size(const cma_buf_t * const cb);
7938+int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2);
7939+unsigned int cma_buf_vc_handle(const cma_buf_t *const cb);
7940+int cma_buf_fd(const cma_buf_t *const cb);
7941+void * cma_buf_addr(const cma_buf_t *const cb);
7942+unsigned int cma_buf_vc_addr(const cma_buf_t *const cb);
7943+picture_context_t * cma_buf_context2(const cma_buf_t *const cb);
7944+
7945+void cma_buf_unref(cma_buf_t * const cb);
7946+cma_buf_t * cma_buf_ref(cma_buf_t * const cb);
7947+
7948+struct cma_buf_pool_s;
7949+typedef struct cma_buf_pool_s cma_buf_pool_t;
7950+
7951+cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const p, const size_t size);
7952+void cma_buf_pool_cancel(cma_buf_pool_t * const cbp);
7953+void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp);
7954+void cma_buf_pool_delete(cma_buf_pool_t * const p);
7955+int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size);
7956+int cma_buf_pool_resize(cma_buf_pool_t * const cbp,
7957+ const unsigned int new_pool_size, const int new_flight_size);
7958+cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size,
7959+ const bool all_in_flight, const char * const name);
7960+
7961+static inline void cma_buf_pool_deletez(cma_buf_pool_t ** const pp)
7962+{
7963+ cma_buf_pool_t * const p = *pp;
7964+ if (p != NULL) {
7965+ *pp = NULL;
7966+ cma_buf_pool_delete(p);
7967+ }
7968+}
7969+
7970+#endif // VLC_MMAL_MMAL_CMA_H_
7971--- /dev/null
7972+++ b/modules/hw/mmal/mmal_gl.h
7973@@ -0,0 +1,45 @@
7974+// Trim this include list!
7975+
7976+#include <libdrm/drm.h>
7977+#include <libdrm/drm_mode.h>
7978+#include <libdrm/drm_fourcc.h>
7979+//#include <xf86drm.h>
7980+//#include <xf86drmMode.h>
7981+#include <X11/Xlib.h>
7982+#include <X11/Xutil.h>
7983+#include <X11/Xlib-xcb.h>
7984+#include <epoxy/gl.h>
7985+#include <epoxy/egl.h>
7986+#include <xcb/xcb.h>
7987+#include <xcb/dri3.h>
7988+
7989+struct mmal_gl_converter_s;
7990+
7991+typedef struct cma_buf_s {
7992+ struct mmal_gl_converter_s * sys;
7993+
7994+ size_t size;
7995+ __u32 h_dumb;
7996+ int fd;
7997+ unsigned int h_vcsm;
7998+ void * mapped_addr;
7999+ GLuint texture;
8000+} cma_buf_t;
8001+
8002+typedef struct cma_pic_sys_s {
8003+ cma_buf_t * cmabuf;
8004+} cma_pic_sys_t;
8005+
8006+static inline unsigned int
8007+hw_mmal_h_vcsm(const picture_t * const pic)
8008+{
8009+ const cma_pic_sys_t *const pic_sys = (cma_pic_sys_t *)pic->p_sys;
8010+
8011+ if (pic->format.i_chroma != VLC_CODEC_MMAL_GL_RGB32 ||
8012+ pic_sys == NULL || pic_sys->cmabuf == NULL) {
8013+ return 0;
8014+ }
8015+
8016+ return pic_sys->cmabuf->h_vcsm;
8017+}
8018+
8019--- /dev/null
8020+++ b/modules/hw/mmal/mmal_piccpy_neon.S
8021@@ -0,0 +1,105 @@
8022+// Copy pix
8023+
8024+ .syntax unified
8025+ .arm
8026+// .thumb
8027+ .text
8028+ .align 16
8029+ .arch armv7-a
8030+ .fpu neon-vfpv4
8031+
8032+
8033+.macro function name
8034+ .global \name
8035+#ifdef __ELF__
8036+ .type \name, %function
8037+#endif
8038+\name:
8039+.endm
8040+
8041+
8042+.macro piccpy_to_8, bit_depth
8043+ subs r2, #128
8044+ vpush {q4-q7}
8045+ blt 2f
8046+1:
8047+ vldm r1!, {q0-q7}
8048+ subs r2, #128
8049+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8050+ vqrshrn.u16 d1, q1, #\bit_depth - 8
8051+ vqrshrn.u16 d2, q2, #\bit_depth - 8
8052+ vqrshrn.u16 d3, q3, #\bit_depth - 8
8053+ vldm r1!, {q8-q15}
8054+ vqrshrn.u16 d4, q4, #\bit_depth - 8
8055+ vqrshrn.u16 d5, q5, #\bit_depth - 8
8056+ vqrshrn.u16 d6, q6, #\bit_depth - 8
8057+ vqrshrn.u16 d7, q7, #\bit_depth - 8
8058+ vqrshrn.u16 d8, q8, #\bit_depth - 8
8059+ vqrshrn.u16 d9, q9, #\bit_depth - 8
8060+ vqrshrn.u16 d10, q10, #\bit_depth - 8
8061+ vqrshrn.u16 d11, q11, #\bit_depth - 8
8062+ vqrshrn.u16 d12, q12, #\bit_depth - 8
8063+ vqrshrn.u16 d13, q13, #\bit_depth - 8
8064+ vqrshrn.u16 d14, q14, #\bit_depth - 8
8065+ vqrshrn.u16 d15, q15, #\bit_depth - 8
8066+ vstm r0!, {q0-q7}
8067+ bge 1b
8068+2:
8069+ adds r2, #64
8070+ blt 1f
8071+
8072+ vldm r1!, {q0-q7}
8073+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8074+ vqrshrn.u16 d1, q1, #\bit_depth - 8
8075+ vqrshrn.u16 d2, q2, #\bit_depth - 8
8076+ vqrshrn.u16 d3, q3, #\bit_depth - 8
8077+ vqrshrn.u16 d4, q4, #\bit_depth - 8
8078+ vqrshrn.u16 d5, q5, #\bit_depth - 8
8079+ vqrshrn.u16 d6, q6, #\bit_depth - 8
8080+ vqrshrn.u16 d7, q7, #\bit_depth - 8
8081+ vstm r0!, {q0-q3}
8082+1:
8083+ adds r2, #32
8084+ blt 1f
8085+
8086+ vldm r1!, {q0-q3}
8087+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8088+ vqrshrn.u16 d1, q1, #\bit_depth - 8
8089+ vqrshrn.u16 d2, q2, #\bit_depth - 8
8090+ vqrshrn.u16 d3, q3, #\bit_depth - 8
8091+ vstm r0!, {q0-q1}
8092+1:
8093+ adds r2, #16
8094+ blt 1f
8095+
8096+ vldm r1!, {q0-q1}
8097+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8098+ vqrshrn.u16 d1, q1, #\bit_depth - 8
8099+ vstm r0!, {q0}
8100+1:
8101+ adds r2, #8
8102+ blt 1f
8103+
8104+ vldm r1!, {q0}
8105+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8106+ vstr d0, [r0]
8107+ add r0, #8
8108+1:
8109+ adds r2, #4
8110+ blt 1f
8111+
8112+ vldr d0, [r1]
8113+ vqrshrn.u16 d0, q0, #\bit_depth - 8
8114+ vstr s0, [r0]
8115+1:
8116+ vpop {q4-q7}
8117+ bx lr
8118+.endm
8119+
8120+
8121+@ [r0] Dest
8122+@ [r1] Src
8123+@ r2 Pels
8124+function mmal_piccpy_10_to_8_neon
8125+ piccpy_to_8 10
8126+
8127--- a/modules/hw/mmal/mmal_picture.c
8128+++ b/modules/hw/mmal/mmal_picture.c
8129@@ -21,25 +21,1542 @@
8130 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
8131 *****************************************************************************/
8132
8133+// We would really like to use vlc_thread.h but the detach thread stuff can't be
8134+// used here :-(
8135+#include <pthread.h>
8136+
8137+#include <stdatomic.h>
8138+#include <unistd.h>
8139+#include <fcntl.h>
8140+
8141 #include <vlc_common.h>
8142+#include <vlc_cpu.h>
8143 #include <vlc_picture.h>
8144+
8145+#pragma GCC diagnostic push
8146+#pragma GCC diagnostic ignored "-Wbad-function-cast"
8147+#include <bcm_host.h>
8148+#pragma GCC diagnostic pop
8149 #include <interface/mmal/mmal.h>
8150+#include <interface/mmal/util/mmal_util.h>
8151+#include <interface/mmal/util/mmal_default_components.h>
8152+#include <interface/vmcs_host/vcgencmd.h>
8153+#include <interface/vcsm/user-vcsm.h>
8154
8155+#include "mmal_cma.h"
8156 #include "mmal_picture.h"
8157+#include "transform_ops.h"
8158+
8159+#define TRACE_TRANSFORMS 0
8160+
8161+#define UINT64_SIZE(s) (((s) + sizeof(uint64_t) - 1)/sizeof(uint64_t))
8162+
8163+static inline char safe_char(const unsigned int c0)
8164+{
8165+ const unsigned int c = c0 & 0xff;
8166+ return c > ' ' && c < 0x7f ? c : '.';
8167+}
8168+
8169+const char * str_fourcc(char * const buf, const unsigned int fcc)
8170+{
8171+ if (fcc == 0)
8172+ return "----";
8173+ buf[0] = safe_char(fcc >> 0);
8174+ buf[1] = safe_char(fcc >> 8);
8175+ buf[2] = safe_char(fcc >> 16);
8176+ buf[3] = safe_char(fcc >> 24);
8177+ buf[4] = 0;
8178+ return buf;
8179+}
8180+
8181+// WB + Inv
8182+static inline void flush_range(void * const start, const size_t len)
8183+{
8184+ uint64_t buf[UINT64_SIZE(sizeof(struct vcsm_user_clean_invalid2_s) + sizeof(struct vcsm_user_clean_invalid2_block_s))];
8185+ struct vcsm_user_clean_invalid2_s * const b = (struct vcsm_user_clean_invalid2_s *)buf;
8186+
8187+ *b = (struct vcsm_user_clean_invalid2_s){
8188+ .op_count = 1
8189+ };
8190+
8191+ b->s[0] = (struct vcsm_user_clean_invalid2_block_s){
8192+ .invalidate_mode = 3, // wb + invalidate
8193+ .block_count = 1,
8194+ .start_address = start, // Rely on clean inv to fix up align & size boundries
8195+ .block_size = len,
8196+ .inter_block_stride = 0
8197+ };
8198+
8199+ vcsm_clean_invalid2(b);
8200+}
8201+
8202+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs)
8203+{
8204+ switch (vlc_cs)
8205+ {
8206+ case COLOR_SPACE_BT601:
8207+ return MMAL_COLOR_SPACE_ITUR_BT601;
8208+ case COLOR_SPACE_BT709:
8209+ return MMAL_COLOR_SPACE_ITUR_BT709;
8210+ default:
8211+ break;
8212+ }
8213+ return MMAL_COLOR_SPACE_UNKNOWN;
8214+}
8215+
8216+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc)
8217+{
8218+ switch (vf_vlc->i_chroma) {
8219+ case VLC_CODEC_MMAL_ZC_RGB32:
8220+ case VLC_CODEC_RGB32:
8221+ {
8222+ // VLC RGB32 aka RV32 means we have to look at the mask values
8223+ const uint32_t r = vf_vlc->i_rmask;
8224+ const uint32_t g = vf_vlc->i_gmask;
8225+ const uint32_t b = vf_vlc->i_bmask;
8226+ if (r == 0xff0000 && g == 0xff00 && b == 0xff)
8227+ return MMAL_ENCODING_BGRA;
8228+ if (r == 0xff && g == 0xff00 && b == 0xff0000)
8229+ return MMAL_ENCODING_RGBA;
8230+ if (r == 0xff000000 && g == 0xff0000 && b == 0xff00)
8231+ return MMAL_ENCODING_ABGR;
8232+ if (r == 0xff00 && g == 0xff0000 && b == 0xff000000)
8233+ return MMAL_ENCODING_ARGB;
8234+ break;
8235+ }
8236+ case VLC_CODEC_RGB16:
8237+ {
8238+ // VLC RGB16 aka RV16 means we have to look at the mask values
8239+ const uint32_t r = vf_vlc->i_rmask;
8240+ const uint32_t g = vf_vlc->i_gmask;
8241+ const uint32_t b = vf_vlc->i_bmask;
8242+ if (r == 0xf800 && g == 0x7e0 && b == 0x1f)
8243+ return MMAL_ENCODING_RGB16;
8244+ break;
8245+ }
8246+ case VLC_CODEC_I420:
8247+ case VLC_CODEC_MMAL_ZC_I420:
8248+ return MMAL_ENCODING_I420;
8249+ case VLC_CODEC_RGBA:
8250+ return MMAL_ENCODING_RGBA;
8251+ case VLC_CODEC_BGRA:
8252+ return MMAL_ENCODING_BGRA;
8253+ case VLC_CODEC_ARGB:
8254+ return MMAL_ENCODING_ARGB;
8255+ // VLC_CODEC_ABGR does not exist in VLC
8256+ case VLC_CODEC_MMAL_OPAQUE:
8257+ return MMAL_ENCODING_OPAQUE;
8258+ case VLC_CODEC_MMAL_ZC_SAND8:
8259+ return MMAL_ENCODING_YUVUV128;
8260+ case VLC_CODEC_MMAL_ZC_SAND10:
8261+ return MMAL_ENCODING_YUVUV64_10;
8262+ case VLC_CODEC_MMAL_ZC_SAND30:
8263+ return MMAL_ENCODING_YUV10_COL;
8264+ default:
8265+ break;
8266+ }
8267+ return 0;
8268+}
8269+
8270+static void vlc_fmt_to_video_format(MMAL_VIDEO_FORMAT_T *const vf_mmal, const video_frame_format_t * const vf_vlc)
8271+{
8272+ const unsigned int wmask = (vf_vlc->i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
8273+ vf_vlc->i_chroma == VLC_CODEC_I420) ? 31 : 15;
8274+
8275+ vf_mmal->width = (vf_vlc->i_width + wmask) & ~wmask;
8276+ vf_mmal->height = (vf_vlc->i_height + 15) & ~15;
8277+ vf_mmal->crop.x = vf_vlc->i_x_offset;
8278+ vf_mmal->crop.y = vf_vlc->i_y_offset;
8279+ vf_mmal->crop.width = vf_vlc->i_visible_width;
8280+ vf_mmal->crop.height = vf_vlc->i_visible_height;
8281+ if (vf_vlc->i_sar_num == 0 || vf_vlc->i_sar_den == 0) {
8282+ vf_mmal->par.num = 1;
8283+ vf_mmal->par.den = 1;
8284+ } else {
8285+ vf_mmal->par.num = vf_vlc->i_sar_num;
8286+ vf_mmal->par.den = vf_vlc->i_sar_den;
8287+ }
8288+ vf_mmal->frame_rate.num = vf_vlc->i_frame_rate;
8289+ vf_mmal->frame_rate.den = vf_vlc->i_frame_rate_base;
8290+ vf_mmal->color_space = vlc_to_mmal_color_space(vf_vlc->space);
8291+}
8292+
8293+
8294+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc)
8295+{
8296+ vlc_fmt_to_video_format(&es_fmt->es->video, vf_vlc);
8297+}
8298+
8299+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic)
8300+{
8301+ MMAL_VIDEO_FORMAT_T vf_new_ss;
8302+ MMAL_VIDEO_FORMAT_T *const vf_old = &es_fmt->es->video;
8303+ MMAL_VIDEO_FORMAT_T *const vf_new = &vf_new_ss;
8304+
8305+ vlc_fmt_to_video_format(vf_new, &pic->format);
8306+
8307+ // If we have a format that might have come from ffmpeg then rework for
8308+ // a better guess as to layout. All sand stuff is "special" with regards to
8309+ // width/height vs real layout so leave as is if that
8310+ if ((pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
8311+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) &&
8312+ pic->p[0].i_pixel_pitch != 0)
8313+ {
8314+ // Now overwrite width/height with a better guess as to actual layout info
8315+ vf_new->height = pic->p[0].i_lines;
8316+ vf_new->width = pic->p[0].i_pitch / pic->p[0].i_pixel_pitch;
8317+ }
8318+
8319+ if (
8320+ vf_new->width != vf_old->width ||
8321+ vf_new->height != vf_old->height ||
8322+ vf_new->crop.x != vf_old->crop.x ||
8323+ vf_new->crop.y != vf_old->crop.y ||
8324+ vf_new->crop.width != vf_old->crop.width ||
8325+ vf_new->crop.height != vf_old->crop.height ||
8326+ vf_new->par.num != vf_old->par.num ||
8327+ vf_new->par.den != vf_old->par.den ||
8328+ // Frame rate ignored
8329+ vf_new->color_space != vf_old->color_space)
8330+ {
8331+#if 0
8332+ char dbuf0[5], dbuf1[5];
8333+ printf("%dx%d (%d,%d %dx%d) par:%d/%d %s -> %dx%d (%d,%d %dx%d) par:%d/%d %s\n",
8334+ vf_old->width ,
8335+ vf_old->height ,
8336+ vf_old->crop.x ,
8337+ vf_old->crop.y ,
8338+ vf_old->crop.width ,
8339+ vf_old->crop.height ,
8340+ vf_old->par.num ,
8341+ vf_old->par.den ,
8342+ str_fourcc(dbuf0, vf_old->color_space) ,
8343+ vf_new->width ,
8344+ vf_new->height ,
8345+ vf_new->crop.x ,
8346+ vf_new->crop.y ,
8347+ vf_new->crop.width ,
8348+ vf_new->crop.height ,
8349+ vf_new->par.num ,
8350+ vf_new->par.den ,
8351+ str_fourcc(dbuf1, vf_new->color_space) );
8352+#endif
8353+ *vf_old = *vf_new;
8354+ return true;
8355+ }
8356+ return false;
8357+}
8358+
8359+
8360+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
8361+ const unsigned int headers, const uint32_t payload_size)
8362+{
8363+ hw_mmal_port_pool_ref_t * ppr = calloc(1, sizeof(hw_mmal_port_pool_ref_t));
8364+ if (ppr == NULL)
8365+ return NULL;
8366+
8367+ if ((ppr->pool = mmal_port_pool_create(port, headers, payload_size)) == NULL)
8368+ goto fail;
8369+
8370+ ppr->port = port;
8371+ atomic_store(&ppr->refs, 1);
8372+ return ppr;
8373+
8374+fail:
8375+ free(ppr);
8376+ return NULL;
8377+}
8378+
8379+static void do_detached(void *(*fn)(void *), void * v)
8380+{
8381+ pthread_t dothread;
8382+ pthread_create(&dothread, NULL, fn, v);
8383+ pthread_detach(dothread);
8384+}
8385+
8386+// Destroy a ppr - aranged s.t. it has the correct prototype for a pthread
8387+static void * kill_ppr(void * v)
8388+{
8389+ hw_mmal_port_pool_ref_t * const ppr = v;
8390+ if (ppr->port->is_enabled)
8391+ mmal_port_disable(ppr->port); // Avoid annoyed messages from MMAL when we kill the pool
8392+ mmal_port_pool_destroy(ppr->port, ppr->pool);
8393+ free(ppr);
8394+ return NULL;
8395+}
8396+
8397+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb)
8398+{
8399+ if (ppr == NULL)
8400+ return;
8401+ if (atomic_fetch_sub(&ppr->refs, 1) != 1)
8402+ return;
8403+ if (in_cb)
8404+ do_detached(kill_ppr, ppr);
8405+ else
8406+ kill_ppr(ppr);
8407+}
8408+
8409+// Put buffer in port if possible - if not then release to pool
8410+// Returns true if sent, false if recycled
8411+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf)
8412+{
8413+ mmal_buffer_header_reset(buf);
8414+ buf->user_data = NULL;
8415+
8416+ if (mmal_port_send_buffer(ppr->port, buf) == MMAL_SUCCESS)
8417+ return true;
8418+ mmal_buffer_header_release(buf);
8419+ return false;
8420+}
8421+
8422+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr)
8423+{
8424+ MMAL_BUFFER_HEADER_T * buf;
8425+ MMAL_STATUS_T err = MMAL_SUCCESS;
8426+
8427+ while ((buf = mmal_queue_get(ppr->pool->queue)) != NULL) {
8428+ if ((err = mmal_port_send_buffer(ppr->port, buf)) != MMAL_SUCCESS)
8429+ {
8430+ mmal_queue_put_back(ppr->pool->queue, buf);
8431+ break;
8432+ }
8433+ }
8434+ return err;
8435+}
8436+
8437+
8438+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
8439+ hw_mmal_port_pool_ref_t ** pppr,
8440+ MMAL_PORT_T * const port,
8441+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback)
8442+{
8443+ MMAL_STATUS_T status;
8444+
8445+ port->userdata = (struct MMAL_PORT_USERDATA_T *)obj;
8446+
8447+ status = port_parameter_set_uint32(port, MMAL_PARAMETER_EXTRA_BUFFERS, extra_buffers);
8448+ if (status != MMAL_SUCCESS) {
8449+ msg_Err(obj, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
8450+ status, mmal_status_to_string(status));
8451+ return status;
8452+ }
8453+
8454+ status = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, 1);
8455+ if (status != MMAL_SUCCESS) {
8456+ msg_Err(obj, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
8457+ port->name, status, mmal_status_to_string(status));
8458+ return status;
8459+ }
8460+
8461+ port->format->encoding = MMAL_ENCODING_OPAQUE;
8462+ port->format->encoding_variant = 0;
8463+ if ((status = mmal_port_format_commit(port)) != MMAL_SUCCESS)
8464+ {
8465+ msg_Err(obj, "Failed to commit format on port %s (status=%"PRIx32" %s)",
8466+ port->name, status, mmal_status_to_string(status));
8467+ return status;
8468+ }
8469+
8470+ port->buffer_num = 30;
8471+ port->buffer_size = port->buffer_size_recommended;
8472+
8473+ if ((*pppr = hw_mmal_port_pool_ref_create(port, port->buffer_num, port->buffer_size)) == NULL) {
8474+ msg_Err(obj, "Failed to create output pool");
8475+ return status;
8476+ }
8477+
8478+ status = mmal_port_enable(port, callback);
8479+ if (status != MMAL_SUCCESS) {
8480+ hw_mmal_port_pool_ref_release(*pppr, false);
8481+ *pppr = NULL;
8482+ msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)",
8483+ port->name, status, mmal_status_to_string(status));
8484+ return status;
8485+ }
8486+
8487+ return MMAL_SUCCESS;
8488+}
8489+
8490+
8491+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn)
8492+{
8493+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
8494+ unsigned int i;
8495+
8496+ for (i = 0; i != ctx->buf_count; ++i) {
8497+ if (ctx->bufs[i] != NULL)
8498+ mmal_buffer_header_release(ctx->bufs[i]);
8499+ }
8500+
8501+ cma_buf_end_flight(ctx->cb);
8502+ cma_buf_unref(ctx->cb);
8503+
8504+ free(ctx);
8505+}
8506+
8507+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn)
8508+{
8509+ const pic_ctx_mmal_t * const src_ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
8510+ pic_ctx_mmal_t * const dst_ctx = calloc(1, sizeof(*dst_ctx));
8511+ unsigned int i;
8512+
8513+ if (dst_ctx == NULL)
8514+ return NULL;
8515+
8516+ // Copy
8517+ dst_ctx->cmn = src_ctx->cmn;
8518+
8519+ dst_ctx->cb = cma_buf_ref(src_ctx->cb);
8520+
8521+ dst_ctx->buf_count = src_ctx->buf_count;
8522+ for (i = 0; i != src_ctx->buf_count; ++i) {
8523+ dst_ctx->bufs[i] = src_ctx->bufs[i];
8524+ if (dst_ctx->bufs[i] != NULL)
8525+ mmal_buffer_header_acquire(dst_ctx->bufs[i]);
8526+ }
8527+
8528+ return &dst_ctx->cmn;
8529+}
8530+
8531+static MMAL_BOOL_T
8532+buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata)
8533+{
8534+ hw_mmal_port_pool_ref_t * const ppr = userdata;
8535+
8536+ // Kill the callback - otherwise we will go in circles!
8537+ mmal_buffer_header_pre_release_cb_set(buf, (MMAL_BH_PRE_RELEASE_CB_T)0, NULL);
8538+ mmal_buffer_header_acquire(buf); // Ref it again
8539+
8540+ // As we have re-acquired the buffer we need a full release
8541+ // (not continue) to zap the ref count back to zero
8542+ // This is "safe" 'cos we have already reset the cb
8543+ hw_mmal_port_pool_ref_recycle(ppr, buf);
8544+ hw_mmal_port_pool_ref_release(ppr, true); // Assume in callback
8545+
8546+ return MMAL_TRUE;
8547+}
8548+
8549+// Buffer belongs to context on successful return from this fn
8550+// is still valid on failure
8551+picture_context_t *
8552+hw_mmal_gen_context(MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr)
8553+{
8554+ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
8555+
8556+ if (ctx == NULL)
8557+ return NULL;
8558+
8559+ // If we have an associated ppr then ref & set appropriate callbacks
8560+ if (ppr != NULL) {
8561+ hw_mmal_port_pool_ref_acquire(ppr);
8562+ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr);
8563+ buf->user_data = NULL;
8564+ }
8565+
8566+ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
8567+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
8568+
8569+ ctx->buf_count = 1;
8570+ ctx->bufs[0] = buf;
8571+
8572+ return &ctx->cmn;
8573+}
8574+
8575+// n is els
8576+// * Make NEON!
8577+typedef void piccpy_fn(void * dest, const void * src, size_t n);
8578+
8579+extern piccpy_fn mmal_piccpy_10_to_8_neon;
8580+
8581+static void piccpy_10_to_8_c(void * dest, const void * src, size_t n)
8582+{
8583+ uint8_t * d = dest;
8584+ const uint16_t * s = src;
8585+ while (n-- != 0)
8586+ *d++ = *s++ >> 2;
8587+}
8588+
8589+// Do a stride converting copy - if the strides are the same and line_len is
8590+// close then do a single block copy - we don't expect to have to preserve
8591+// pixels in the output frame
8592+static void mem_copy_2d(uint8_t * d_ptr, const size_t d_stride,
8593+ const uint8_t * s_ptr, const size_t s_stride,
8594+ size_t lines, const size_t line_len)
8595+{
8596+ if (s_stride == d_stride && d_stride < line_len + 32)
8597+ {
8598+ memcpy(d_ptr, s_ptr, d_stride * lines);
8599+ }
8600+ else
8601+ {
8602+ while (lines-- != 0) {
8603+ memcpy(d_ptr, s_ptr, line_len);
8604+ d_ptr += d_stride;
8605+ s_ptr += s_stride;
8606+ }
8607+ }
8608+}
8609+
8610+// line_len in D units
8611+static void mem_copy_2d_10_to_8(uint8_t * d_ptr, const size_t d_stride,
8612+ const uint8_t * s_ptr, const size_t s_stride,
8613+ size_t lines, const size_t line_len)
8614+{
8615+ piccpy_fn * const docpy = vlc_CPU_ARM_NEON() ? mmal_piccpy_10_to_8_neon : piccpy_10_to_8_c;
8616+ if (s_stride == d_stride * 2 && d_stride < line_len + 32)
8617+ {
8618+ docpy(d_ptr, s_ptr, d_stride * lines);
8619+ }
8620+ else
8621+ {
8622+ while (lines-- != 0) {
8623+ docpy(d_ptr, s_ptr, line_len);
8624+ d_ptr += d_stride;
8625+ s_ptr += s_stride;
8626+ }
8627+ }
8628+}
8629+
8630+
8631+int hw_mmal_copy_pic_to_buf(void * const buf_data,
8632+ uint32_t * const pLength,
8633+ const MMAL_ES_FORMAT_T * const fmt,
8634+ const picture_t * const pic)
8635+{
8636+ const MMAL_VIDEO_FORMAT_T *const video = &fmt->es->video;
8637+ uint8_t * const dest = buf_data;
8638+ size_t length = 0;
8639+
8640+ //**** Worry about x/y_offsets
8641+
8642+ assert(fmt->encoding == MMAL_ENCODING_I420);
8643+
8644+ switch (pic->format.i_chroma) {
8645+ case VLC_CODEC_I420:
8646+ {
8647+ const size_t y_size = video->width * video->height;
8648+ mem_copy_2d(dest, video->width,
8649+ pic->p[0].p_pixels, pic->p[0].i_pitch,
8650+ video->crop.height,
8651+ video->crop.width);
8652+
8653+ mem_copy_2d(dest + y_size, video->width / 2,
8654+ pic->p[1].p_pixels, pic->p[1].i_pitch,
8655+ video->crop.height / 2,
8656+ video->crop.width / 2);
8657+
8658+ mem_copy_2d(dest + y_size + y_size / 4, video->width / 2,
8659+ pic->p[2].p_pixels, pic->p[2].i_pitch,
8660+ video->crop.height / 2,
8661+ video->crop.width / 2);
8662+
8663+ // And make sure it is actually in memory
8664+ length = y_size + y_size / 2;
8665+ break;
8666+ }
8667+
8668+ case VLC_CODEC_I420_10L:
8669+ {
8670+ const size_t y_size = video->width * video->height;
8671+ mem_copy_2d_10_to_8(dest, video->width,
8672+ pic->p[0].p_pixels, pic->p[0].i_pitch,
8673+ video->crop.height,
8674+ video->crop.width);
8675+
8676+ mem_copy_2d_10_to_8(dest + y_size, video->width / 2,
8677+ pic->p[1].p_pixels, pic->p[1].i_pitch,
8678+ video->crop.height / 2,
8679+ video->crop.width / 2);
8680+
8681+ mem_copy_2d_10_to_8(dest + y_size + y_size / 4, video->width / 2,
8682+ pic->p[2].p_pixels, pic->p[2].i_pitch,
8683+ video->crop.height / 2,
8684+ video->crop.width / 2);
8685+
8686+ // And make sure it is actually in memory
8687+ length = y_size + y_size / 2;
8688+ break;
8689+ }
8690+
8691+ default:
8692+ if (pLength != NULL)
8693+ *pLength = 0;
8694+ return VLC_EBADVAR;
8695+ }
8696+
8697+ if (cma_vcsm_type() == VCSM_INIT_LEGACY) { // ** CMA is currently always uncached
8698+ flush_range(dest, length);
8699+ }
8700+
8701+ if (pLength != NULL)
8702+ *pLength = (uint32_t)length;
8703+
8704+ return VLC_SUCCESS;
8705+}
8706+
8707+
8708+static MMAL_BOOL_T rep_buf_free_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
8709+{
8710+ cma_buf_t * const cb = userdata;
8711+ VLC_UNUSED(header);
8712+
8713+ cma_buf_unref(cb);
8714+ return MMAL_FALSE;
8715+}
8716+
8717+static int cma_buf_buf_attach(MMAL_BUFFER_HEADER_T * const buf, cma_buf_t * const cb)
8718+{
8719+ // Just a CMA buffer - fill in new buffer
8720+ const uintptr_t vc_h = cma_buf_vc_handle(cb);
8721+ if (vc_h == 0)
8722+ return VLC_EGENERIC;
8723+
8724+ mmal_buffer_header_reset(buf);
8725+ buf->data = (uint8_t *)vc_h;
8726+ buf->alloc_size = cma_buf_size(cb);
8727+ buf->length = buf->alloc_size;
8728+ // Ensure cb remains valid for the duration of this buffer
8729+ mmal_buffer_header_pre_release_cb_set(buf, rep_buf_free_cb, cma_buf_ref(cb));
8730+ return VLC_SUCCESS;
8731+}
8732+
8733+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic,
8734+ MMAL_POOL_T * const rep_pool,
8735+ MMAL_PORT_T * const port,
8736+ cma_buf_pool_t * const cbp)
8737+{
8738+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(rep_pool->queue);
8739+ if (buf == NULL)
8740+ goto fail0;
8741+
8742+ cma_buf_t * const cb = cma_buf_pool_alloc_buf(cbp, port->buffer_size);
8743+ if (cb == NULL)
8744+ goto fail1;
8745+
8746+ if (cma_buf_buf_attach(buf, cb) != VLC_SUCCESS)
8747+ goto fail2;
8748+
8749+ pic_to_buf_copy_props(buf, pic);
8750+
8751+ if (hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), &buf->length, port->format, pic) != VLC_SUCCESS)
8752+ goto fail2;
8753+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
8754+
8755+ cma_buf_unref(cb);
8756+ return buf;
8757+
8758+fail2:
8759+ cma_buf_unref(cb);
8760+fail1:
8761+ mmal_buffer_header_release(buf);
8762+fail0:
8763+ return NULL;
8764+}
8765+
8766+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool)
8767+{
8768+ pic_ctx_mmal_t *const ctx = (pic_ctx_mmal_t *)pic->context;
8769+ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
8770+
8771+ if (rep_buf == NULL)
8772+ return NULL;
8773+
8774+ if (ctx->bufs[0] != NULL)
8775+ {
8776+ // Existing buffer - replicate it
8777+ if (mmal_buffer_header_replicate(rep_buf, ctx->bufs[0]) != MMAL_SUCCESS)
8778+ goto fail;
8779+ }
8780+ else if (ctx->cb != NULL)
8781+ {
8782+ // Just a CMA buffer - fill in new buffer
8783+ if (cma_buf_buf_attach(rep_buf, ctx->cb) != 0)
8784+ goto fail;
8785+ }
8786+ else
8787+ goto fail;
8788+
8789+ pic_to_buf_copy_props(rep_buf, pic);
8790+ return rep_buf;
8791+
8792+fail:
8793+ mmal_buffer_header_release(rep_buf);
8794+ return NULL;
8795+}
8796+
8797+
8798+
8799+
8800+int hw_mmal_get_gpu_mem(void) {
8801+ static int stashed_val = -2;
8802+ VCHI_INSTANCE_T vchi_instance;
8803+ VCHI_CONNECTION_T *vchi_connection = NULL;
8804+ char rbuf[1024] = { 0 };
8805+
8806+ if (stashed_val >= -1)
8807+ return stashed_val;
8808+
8809+ if (vchi_initialise(&vchi_instance) != 0)
8810+ goto fail0;
8811+
8812+ //create a vchi connection
8813+ if (vchi_connect(NULL, 0, vchi_instance) != 0)
8814+ goto fail0;
8815+
8816+ vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1);
8817+
8818+ //send the gencmd for the argument
8819+ if (vc_gencmd_send("get_mem gpu") != 0)
8820+ goto fail;
8821+
8822+ if (vc_gencmd_read_response(rbuf, sizeof(rbuf) - 1) != 0)
8823+ goto fail;
8824+
8825+ if (strncmp(rbuf, "gpu=", 4) != 0)
8826+ goto fail;
8827+
8828+ char *p;
8829+ unsigned long m = strtoul(rbuf + 4, &p, 10);
8830+
8831+ if (p[0] != 'M' || p[1] != '\0')
8832+ stashed_val = -1;
8833+ else
8834+ stashed_val = (int)m << 20;
8835+
8836+ vc_gencmd_stop();
8837+
8838+ //close the vchi connection
8839+ vchi_disconnect(vchi_instance);
8840+
8841+ return stashed_val;
8842+
8843+fail:
8844+ vc_gencmd_stop();
8845+ vchi_disconnect(vchi_instance);
8846+fail0:
8847+ stashed_val = -1;
8848+ return -1;
8849+};
8850+
8851+// ===========================================================================
8852+
8853+typedef struct pool_ent_s
8854+{
8855+ struct pool_ent_s * next;
8856+ struct pool_ent_s * prev;
8857+
8858+ atomic_int ref_count;
8859+ unsigned int seq;
8860+
8861+ size_t size;
8862+
8863+ int vcsm_hdl;
8864+ int vc_hdl;
8865+ void * buf;
8866+
8867+ unsigned int width;
8868+ unsigned int height;
8869+ MMAL_FOURCC_T enc_type;
8870+
8871+ picture_t * pic;
8872+} pool_ent_t;
8873+
8874+
8875+typedef struct ent_list_hdr_s
8876+{
8877+ pool_ent_t * ents;
8878+ pool_ent_t * tail;
8879+ unsigned int n;
8880+} ent_list_hdr_t;
8881+
8882+#define ENT_LIST_HDR_INIT (ent_list_hdr_t){ \
8883+ .ents = NULL, \
8884+ .tail = NULL, \
8885+ .n = 0 \
8886+}
8887+
8888+struct vzc_pool_ctl_s
8889+{
8890+ atomic_int ref_count;
8891+
8892+ ent_list_hdr_t ent_pool;
8893+ ent_list_hdr_t ents_cur;
8894+ ent_list_hdr_t ents_prev;
8895+
8896+ unsigned int max_n;
8897+ unsigned int seq;
8898+
8899+ vlc_mutex_t lock;
8900+
8901+ MMAL_POOL_T * buf_pool;
8902+
8903+ vcsm_init_type_t vcsm_init_type;
8904+};
8905+
8906+typedef struct vzc_subbuf_ent_s
8907+{
8908+ pool_ent_t * ent;
8909+ MMAL_RECT_T pic_rect;
8910+ MMAL_RECT_T orig_dest_rect;
8911+ MMAL_DISPLAYREGION_T dreg;
8912+} vzc_subbuf_ent_t;
8913+
8914+
8915+static pool_ent_t * ent_extract(ent_list_hdr_t * const elh, pool_ent_t * const ent)
8916+{
8917+// printf("List %p [%d]: Ext %p\n", elh, elh->n, ent);
8918+
8919+ if (ent == NULL)
8920+ return NULL;
8921+
8922+ if (ent->next == NULL)
8923+ elh->tail = ent->prev;
8924+ else
8925+ ent->next->prev = ent->prev;
8926+
8927+ if (ent->prev == NULL)
8928+ elh->ents = ent->next;
8929+ else
8930+ ent->prev->next = ent->next;
8931+
8932+ ent->prev = ent->next = NULL;
8933+
8934+ --elh->n;
8935+
8936+ return ent; // For convienience
8937+}
8938+
8939+static inline pool_ent_t * ent_extract_tail(ent_list_hdr_t * const elh)
8940+{
8941+ return ent_extract(elh, elh->tail);
8942+}
8943+
8944+static void ent_add_head(ent_list_hdr_t * const elh, pool_ent_t * const ent)
8945+{
8946+// printf("List %p [%d]: Add %p\n", elh, elh->n, ent);
8947+
8948+ if ((ent->next = elh->ents) == NULL)
8949+ elh->tail = ent;
8950+ else
8951+ ent->next->prev = ent;
8952+
8953+ ent->prev = NULL;
8954+ elh->ents = ent;
8955+ ++elh->n;
8956+}
8957+
8958+static void ent_free(pool_ent_t * const ent)
8959+{
8960+// printf("Free ent: %p\n", ent);
8961+ if (ent != NULL) {
8962+ // If we still have a ref to a pic - kill it now
8963+ if (ent->pic != NULL)
8964+ picture_Release(ent->pic);
8965+
8966+ // Free contents
8967+ vcsm_unlock_hdl(ent->vcsm_hdl);
8968+
8969+ vcsm_free(ent->vcsm_hdl);
8970+
8971+ free(ent);
8972+ }
8973+}
8974+
8975+static void ent_free_list(ent_list_hdr_t * const elh)
8976+{
8977+ pool_ent_t * ent = elh->ents;
8978+
8979+// printf("Free list: %p [%d]\n", elh, elh->n);
8980+
8981+ *elh = ENT_LIST_HDR_INIT;
8982+
8983+ while (ent != NULL) {
8984+ pool_ent_t * const t = ent;
8985+ ent = t->next;
8986+ ent_free(t);
8987+ }
8988+}
8989+
8990+static void ent_list_move(ent_list_hdr_t * const dst, ent_list_hdr_t * const src)
8991+{
8992+// printf("Move %p->%p\n", src, dst);
8993+
8994+ *dst = *src;
8995+ *src = ENT_LIST_HDR_INIT;
8996+}
8997+
8998+// Scans "backwards" as that should give us the fastest match if we are
8999+// presented with pics in the same order each time
9000+static pool_ent_t * ent_list_extract_pic_ent(ent_list_hdr_t * const elh, picture_t * const pic)
9001+{
9002+ pool_ent_t *ent = elh->tail;
9003+
9004+// printf("Find list: %p [%d]; pic:%p\n", elh, elh->n, pic);
9005+
9006+ while (ent != NULL) {
9007+// printf("Check ent: %p, pic:%p\n", ent, ent->pic);
9008+
9009+ if (ent->pic == pic)
9010+ return ent_extract(elh, ent);
9011+ ent = ent->prev;
9012+ }
9013+ return NULL;
9014+}
9015+
9016+#define POOL_ENT_ALLOC_BLOCK 0x10000
9017+
9018+static pool_ent_t * pool_ent_alloc_new(size_t req_size)
9019+{
9020+ pool_ent_t * ent = calloc(1, sizeof(*ent));
9021+ const size_t alloc_size = (req_size + POOL_ENT_ALLOC_BLOCK - 1) & ~(POOL_ENT_ALLOC_BLOCK - 1);
9022+
9023+ if (ent == NULL)
9024+ return NULL;
9025+
9026+ ent->next = ent->prev = NULL;
9027+
9028+ // Alloc from vcsm
9029+ if ((ent->vcsm_hdl = vcsm_malloc_cache(alloc_size, VCSM_CACHE_TYPE_HOST, (char *)"vlc-subpic")) == -1)
9030+ goto fail1;
9031+ if ((ent->vc_hdl = vcsm_vc_hdl_from_hdl(ent->vcsm_hdl)) == 0)
9032+ goto fail2;
9033+ if ((ent->buf = vcsm_lock(ent->vcsm_hdl)) == NULL)
9034+ goto fail2;
9035+
9036+ ent->size = alloc_size;
9037+ return ent;
9038+
9039+fail2:
9040+ vcsm_free(ent->vcsm_hdl);
9041+fail1:
9042+ free(ent);
9043+ return NULL;
9044+}
9045+
9046+static inline pool_ent_t * pool_ent_ref(pool_ent_t * const ent)
9047+{
9048+// int n = atomic_fetch_add(&ent->ref_count, 1) + 1;
9049+// printf("Ref: %p: %d\n", ent, n);
9050+ atomic_fetch_add(&ent->ref_count, 1);
9051+ return ent;
9052+}
9053+
9054+static void pool_recycle(vzc_pool_ctl_t * const pc, pool_ent_t * const ent)
9055+{
9056+ pool_ent_t * xs = NULL;
9057+ int n;
9058+
9059+ if (ent == NULL)
9060+ return;
9061+
9062+ n = atomic_fetch_sub(&ent->ref_count, 1) - 1;
9063+
9064+// printf("%s: Pool: %p: Ent: %p: %d\n", __func__, &pc->ent_pool, ent, n);
9065+
9066+ if (n != 0)
9067+ return;
9068+
9069+ if (ent->pic != NULL) {
9070+ picture_Release(ent->pic);
9071+ ent->pic = NULL;
9072+ }
9073+
9074+ vlc_mutex_lock(&pc->lock);
9075+
9076+ // If we have a full pool then extract the LRU and free it
9077+ // Free done outside mutex
9078+ if (pc->ent_pool.n >= pc->max_n)
9079+ xs = ent_extract_tail(&pc->ent_pool);
9080+
9081+ ent_add_head(&pc->ent_pool, ent);
9082+
9083+ vlc_mutex_unlock(&pc->lock);
9084+
9085+ ent_free(xs);
9086+}
9087+
9088+// * This could be made more efficient, but this is easy
9089+static void pool_recycle_list(vzc_pool_ctl_t * const pc, ent_list_hdr_t * const elh)
9090+{
9091+ pool_ent_t * ent;
9092+ while ((ent = ent_extract_tail(elh)) != NULL) {
9093+ pool_recycle(pc, ent);
9094+ }
9095+}
9096+
9097+static pool_ent_t * pool_best_fit(vzc_pool_ctl_t * const pc, size_t req_size)
9098+{
9099+ pool_ent_t * best = NULL;
9100+
9101+ vlc_mutex_lock(&pc->lock);
9102+
9103+ {
9104+ pool_ent_t * ent = pc->ent_pool.ents;
9105+
9106+ // Simple scan
9107+ while (ent != NULL) {
9108+ if (ent->size >= req_size && ent->size <= req_size * 2 + POOL_ENT_ALLOC_BLOCK &&
9109+ (best == NULL || best->size > ent->size))
9110+ best = ent;
9111+ ent = ent->next;
9112+ }
9113+
9114+ // extract best from chain if we've found it
9115+ ent_extract(&pc->ent_pool, best);
9116+ }
9117+
9118+ vlc_mutex_unlock(&pc->lock);
9119+
9120+ if (best == NULL)
9121+ best = pool_ent_alloc_new(req_size);
9122+
9123+ if ((best->seq = ++pc->seq) == 0)
9124+ best->seq = ++pc->seq; // Never allow to be zero
9125+
9126+ atomic_store(&best->ref_count, 1);
9127+ return best;
9128+}
9129+
9130+
9131+const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[] = { VLC_CODEC_RGBA, VLC_CODEC_BGRA, VLC_CODEC_ARGB, 0 };
9132+
9133+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH)
9134+{
9135+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
9136+ *pW = ent->width;
9137+ *pH = ent->height;
9138+}
9139+
9140+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt)
9141+{
9142+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
9143+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
9144+
9145+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
9146+ es_fmt->encoding = ent->enc_type;
9147+ es_fmt->encoding_variant = 0;
9148+
9149+ v_fmt->width = ent->width;
9150+ v_fmt->height = ent->height;
9151+ v_fmt->crop.x = 0;
9152+ v_fmt->crop.y = 0;
9153+ v_fmt->crop.width = ent->width;
9154+ v_fmt->crop.height = ent->height;
9155+
9156+ return true;
9157+}
9158+
9159+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
9160+ uint32_t * const pWidth, uint32_t * const pHeight)
9161+{
9162+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
9163+ *pWidth = ent->width;
9164+ *pHeight = ent->height;
9165+}
9166+
9167+
9168+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf)
9169+{
9170+ vzc_subbuf_ent_t * sb = buf->user_data;
9171+ return &sb->dreg;
9172+}
9173+
9174+static inline int rescale_x(int x, int mul, int div)
9175+{
9176+ return div == 0 ? x * mul : (x * mul + div/2) / div;
9177+}
9178+
9179+static void rescale_rect(MMAL_RECT_T * const d, const MMAL_RECT_T * const s, const MMAL_RECT_T * mul_rect, const MMAL_RECT_T * div_rect)
9180+{
9181+ d->x = rescale_x(s->x - div_rect->x, mul_rect->width, div_rect->width) + mul_rect->x;
9182+ d->y = rescale_x(s->y - div_rect->y, mul_rect->height, div_rect->height) + mul_rect->y;
9183+ d->width = rescale_x(s->width, mul_rect->width, div_rect->width);
9184+ d->height = rescale_x(s->height, mul_rect->height, div_rect->height);
9185+#if TRACE_TRANSFORMS
9186+ fprintf(stderr, "(%d,%d %dx%d) * (%d,%d %dx%d) / (%d,%d %dx%d) -> (%d,%d %dx%d)\n",
9187+ s->x, s->y, s->width, s->height,
9188+ mul_rect->x, mul_rect->y, mul_rect->width, mul_rect->height,
9189+ div_rect->x, div_rect->y, div_rect->width, div_rect->height,
9190+ d->x, d->y, d->width, d->height);
9191+#endif
9192+}
9193+
9194+static MMAL_RECT_T
9195+rect_untransform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t)
9196+{
9197+#if TRACE_TRANSFORMS
9198+ fprintf(stderr, "t=%d, s=%d,%d:%dx%d, c=%d,%d:%dx%d -> ", (int)t,
9199+ s.x,s.y,s.width,s.height,
9200+ c.x,c.y,c.width,c.height);
9201+#endif
9202+ if (is_transform_hflip(t))
9203+ s = rect_hflip(s, c);
9204+ if (is_transform_vflip(t) != 0)
9205+ s = rect_vflip(s, c);
9206+ if (is_transform_transpose(t) != 0)
9207+ s = rect_transpose(s);
9208+#if TRACE_TRANSFORMS
9209+ fprintf(stderr, "s=%d,%d:%dx%d\n",
9210+ s.x,s.y,s.width,s.height);
9211+#endif
9212+ return s;
9213+}
9214+
9215+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect, const MMAL_DISPLAYTRANSFORM_T scale_transform)
9216+{
9217+ vzc_subbuf_ent_t * sb = buf->user_data;
9218+ if (scale_rect == NULL) {
9219+ sb->dreg.dest_rect = sb->orig_dest_rect;
9220+ sb->dreg.transform = MMAL_DISPLAY_ROT0;
9221+ }
9222+ else
9223+ {
9224+ // The scale rect has been transposed if we have a transposing
9225+ // transform - untranspose so we are the same way up as the source
9226+ const MMAL_RECT_T c = (scale_transform & 4) == 0 ? *scale_rect : rect_transpose(*scale_rect);
9227+ rescale_rect(&sb->dreg.dest_rect, &sb->orig_dest_rect,
9228+ &c, &sb->pic_rect);
9229+ sb->dreg.dest_rect = rect_untransform(sb->dreg.dest_rect, c, scale_transform);
9230+ sb->dreg.transform = scale_transform;
9231+ }
9232+}
9233+
9234+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf)
9235+{
9236+ vzc_subbuf_ent_t * sb = buf->user_data;
9237+ return sb->ent->seq;
9238+}
9239+
9240+
9241+// The intent with the ents_cur & ents_last stuff is to remember the buffers
9242+// we used on the last frame and reuse them on the current one if they are the
9243+// same. Unfortunately detection of "is_first" is only a heuristic (there are
9244+// no rules governing the order in which things are blended) so we must deal
9245+// (fairly) gracefully with it never (or always) being set.
9246+
9247+// dst_fmt gives the number space in which the destination pixels are specified
9248+
9249+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc,
9250+ picture_t * const pic,
9251+ const MMAL_RECT_T dst_pic_rect,
9252+ const int x_offset, const int y_offset,
9253+ const unsigned int alpha,
9254+ const bool is_first)
9255+{
9256+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(pc->buf_pool->queue);
9257+ vzc_subbuf_ent_t * sb;
9258+
9259+ if (buf == NULL)
9260+ return NULL;
9261+
9262+ if ((sb = calloc(1, sizeof(*sb))) == NULL)
9263+ goto fail1;
9264+
9265+ // If first or we've had a lot of stuff move everything to the last list
9266+ // (we could deal more gracefully with the "too many" case but it shouldn't
9267+ // really happen)
9268+ if (is_first || pc->ents_cur.n >= CTX_BUFS_MAX) {
9269+ pool_recycle_list(pc, &pc->ents_prev);
9270+ ent_list_move(&pc->ents_prev, &pc->ents_cur);
9271+ }
9272+
9273+ sb->dreg.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
9274+ sb->dreg.hdr.size = sizeof(sb->dreg);
9275+ buf->user_data = sb;
9276+
9277+ {
9278+ // ?? Round start offset as well as length
9279+ const video_format_t *const fmt = &pic->format;
9280+
9281+ const unsigned int bpp = (fmt->i_bits_per_pixel + 7) >> 3;
9282+ const unsigned int xl = (fmt->i_x_offset & ~15);
9283+ const unsigned int xr = (fmt->i_x_offset + fmt->i_visible_width + 15) & ~15;
9284+ const size_t dst_stride = (xr - xl) * bpp;
9285+ const size_t dst_lines = ((fmt->i_visible_height + 15) & ~15);
9286+ const size_t dst_size = dst_stride * dst_lines;
9287+
9288+ pool_ent_t * ent = ent_list_extract_pic_ent(&pc->ents_prev, pic);
9289+ bool needs_copy = false;
9290+
9291+ // If we didn't find ent in last then look in cur in case is_first
9292+ // isn't working
9293+ if (ent == NULL)
9294+ ent = ent_list_extract_pic_ent(&pc->ents_cur, pic);
9295+
9296+// printf("ent_found: %p\n", ent);
9297
9298-int mmal_picture_lock(picture_t *picture)
9299+ if (ent == NULL)
9300+ {
9301+ // Need a new ent
9302+ needs_copy = true;
9303+
9304+ if ((ent = pool_best_fit(pc, dst_size)) == NULL)
9305+ goto fail2;
9306+ if ((ent->enc_type = vlc_to_mmal_video_fourcc(&pic->format)) == 0)
9307+ goto fail2;
9308+
9309+ ent->pic = picture_Hold(pic);
9310+ }
9311+
9312+ ent_add_head(&pc->ents_cur, ent);
9313+
9314+ sb->ent = pool_ent_ref(ent);
9315+ hw_mmal_vzc_pool_ref(pc);
9316+
9317+ // Copy data
9318+ buf->next = NULL;
9319+ buf->cmd = 0;
9320+ buf->data = (uint8_t *)(ent->vc_hdl);
9321+ buf->alloc_size = buf->length = dst_size;
9322+ buf->offset = 0;
9323+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
9324+ buf->pts = buf->dts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
9325+ buf->type->video = (MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T){
9326+ .planes = 1,
9327+ .pitch = { dst_stride }
9328+ };
9329+
9330+ // Remember offsets
9331+ sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT |
9332+ MMAL_DISPLAY_SET_DEST_RECT |
9333+ MMAL_DISPLAY_SET_FULLSCREEN |
9334+ MMAL_DISPLAY_SET_TRANSFORM |
9335+ MMAL_DISPLAY_SET_ALPHA;
9336+
9337+ sb->dreg.fullscreen = 0;
9338+
9339+ // Will be set later - zero now to avoid any confusion
9340+ sb->dreg.transform = MMAL_DISPLAY_ROT0;
9341+ sb->dreg.dest_rect = (MMAL_RECT_T){0, 0, 0, 0};
9342+
9343+ sb->dreg.alpha = (uint32_t)(alpha & 0xff) | MMAL_DISPLAY_ALPHA_FLAGS_MIX;
9344+
9345+// printf("+++ bpp:%d, vis:%dx%d wxh:%dx%d, d:%dx%d\n", bpp, fmt->i_visible_width, fmt->i_visible_height, fmt->i_width, fmt->i_height, dst_stride, dst_lines);
9346+
9347+ sb->dreg.src_rect = (MMAL_RECT_T){
9348+ .x = (fmt->i_x_offset - xl),
9349+ .y = 0,
9350+ .width = fmt->i_visible_width,
9351+ .height = fmt->i_visible_height
9352+ };
9353+
9354+ sb->pic_rect = dst_pic_rect;
9355+
9356+ sb->orig_dest_rect = (MMAL_RECT_T){
9357+ .x = x_offset,
9358+ .y = y_offset,
9359+ .width = fmt->i_visible_width,
9360+ .height = fmt->i_visible_height
9361+ };
9362+
9363+ if (needs_copy)
9364+ {
9365+ ent->width = dst_stride / bpp;
9366+ ent->height = dst_lines;
9367+
9368+ // 2D copy
9369+ {
9370+ uint8_t *d = ent->buf;
9371+ const uint8_t *s = pic->p[0].p_pixels + xl * bpp + fmt->i_y_offset * pic->p[0].i_pitch;
9372+
9373+ mem_copy_2d(d, dst_stride, s, pic->p[0].i_pitch, fmt->i_visible_height, dst_stride);
9374+
9375+ // And make sure it is actually in memory
9376+ if (pc->vcsm_init_type != VCSM_INIT_CMA) { // ** CMA is currently always uncached
9377+ flush_range(ent->buf, dst_stride * fmt->i_visible_height);
9378+ }
9379+ }
9380+ }
9381+ }
9382+
9383+ return buf;
9384+
9385+fail2:
9386+ free(sb);
9387+fail1:
9388+ mmal_buffer_header_release(buf);
9389+ return NULL;
9390+}
9391+
9392+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc)
9393+{
9394+ pool_recycle_list(pc, &pc->ents_prev);
9395+ pool_recycle_list(pc, &pc->ents_cur);
9396+}
9397+
9398+static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc)
9399+{
9400+
9401+// printf("<<< %s\n", __func__);
9402+
9403+ hw_mmal_vzc_pool_flush(pc);
9404+
9405+ ent_free_list(&pc->ent_pool);
9406+
9407+ if (pc->buf_pool != NULL)
9408+ mmal_pool_destroy(pc->buf_pool);
9409+
9410+ vlc_mutex_destroy(&pc->lock);
9411+
9412+ cma_vcsm_exit(pc->vcsm_init_type);
9413+
9414+// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash
9415+ free (pc);
9416+
9417+ // printf(">>> %s\n", __func__);
9418+}
9419+
9420+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc)
9421+{
9422+ int n;
9423+
9424+ if (pc == NULL)
9425+ return;
9426+
9427+ n = atomic_fetch_sub(&pc->ref_count, 1) - 1;
9428+
9429+ if (n != 0)
9430+ return;
9431+
9432+ hw_mmal_vzc_pool_delete(pc);
9433+}
9434+
9435+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc)
9436+{
9437+ atomic_fetch_add(&pc->ref_count, 1);
9438+}
9439+
9440+static MMAL_BOOL_T vcz_pool_release_cb(MMAL_POOL_T * buf_pool, MMAL_BUFFER_HEADER_T *buf, void *userdata)
9441+{
9442+ vzc_pool_ctl_t * const pc = userdata;
9443+ vzc_subbuf_ent_t * const sb = buf->user_data;
9444+
9445+ VLC_UNUSED(buf_pool);
9446+
9447+// printf("<<< %s\n", __func__);
9448+
9449+ if (sb != NULL) {
9450+ buf->user_data = NULL;
9451+ pool_recycle(pc, sb->ent);
9452+ hw_mmal_vzc_pool_release(pc);
9453+ free(sb);
9454+ }
9455+
9456+// printf(">>> %s\n", __func__);
9457+
9458+ return MMAL_TRUE;
9459+}
9460+
9461+vzc_pool_ctl_t * hw_mmal_vzc_pool_new()
9462+{
9463+ vzc_pool_ctl_t * const pc = calloc(1, sizeof(*pc));
9464+
9465+ if (pc == NULL)
9466+ return NULL;
9467+
9468+ if ((pc->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
9469+ {
9470+ free(pc);
9471+ return NULL;
9472+ }
9473+
9474+ pc->max_n = 8;
9475+ vlc_mutex_init(&pc->lock); // Must init before potential destruction
9476+
9477+ if ((pc->buf_pool = mmal_pool_create(64, 0)) == NULL)
9478+ {
9479+ hw_mmal_vzc_pool_delete(pc);
9480+ return NULL;
9481+ }
9482+
9483+ atomic_store(&pc->ref_count, 1);
9484+
9485+ mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc);
9486+
9487+ return pc;
9488+}
9489+
9490+//----------------------------------------------------------------------------
9491+
9492+
9493+static const uint8_t shift_00[] = {0,0,0,0};
9494+static const uint8_t shift_01[] = {0,1,1,1};
9495+
9496+int cma_pic_set_data(picture_t * const pic,
9497+ const MMAL_ES_FORMAT_T * const mm_esfmt,
9498+ const MMAL_BUFFER_HEADER_T * const buf)
9499 {
9500- picture_sys_t *pic_sys = picture->p_sys;
9501- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
9502+ const MMAL_VIDEO_FORMAT_T * const mm_fmt = &mm_esfmt->es->video;
9503+ const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = (buf == NULL) ? NULL : &buf->type->video;
9504+ cma_buf_t *const cb = cma_buf_pic_get(pic);
9505+ unsigned int planes = 1;
9506+
9507+ uint8_t * const data = cma_buf_addr(cb);
9508+ if (data == NULL) {
9509+ return VLC_ENOMEM;
9510+ }
9511+
9512+ const uint8_t * ws = shift_00;
9513+ const uint8_t * hs = shift_00;
9514+ int pb = 1;
9515+
9516+ switch (mm_esfmt->encoding)
9517+ {
9518+ case MMAL_ENCODING_ARGB:
9519+ case MMAL_ENCODING_ABGR:
9520+ case MMAL_ENCODING_RGBA:
9521+ case MMAL_ENCODING_BGRA:
9522+ case MMAL_ENCODING_RGB32:
9523+ case MMAL_ENCODING_BGR32:
9524+ pb = 4;
9525+ break;
9526+ case MMAL_ENCODING_RGB16:
9527+ pb = 2;
9528+ break;
9529
9530- int offset = 0;
9531- picture->p[0].p_pixels = buffer->data;
9532- for (int i = 1; i < picture->i_planes; i++) {
9533- offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines;
9534- picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset;
9535+ case MMAL_ENCODING_I420:
9536+ ws = shift_01;
9537+ hs = shift_01;
9538+ planes = 3;
9539+ break;
9540+
9541+ case MMAL_ENCODING_YUVUV128:
9542+ hs = shift_01;
9543+ planes = 2;
9544+ break;
9545+
9546+ default:
9547+// msg_Err(p_filter, "%s: Unexpected format", __func__);
9548+ return VLC_EGENERIC;
9549 }
9550
9551- pic_sys->displayed = false;
9552+ // Fix up SAR if unset
9553+ if (pic->format.i_sar_den == 0 || pic->format.i_sar_num == 0) {
9554+ pic->format.i_sar_den = mm_fmt->par.den;
9555+ pic->format.i_sar_num = mm_fmt->par.num;
9556+ }
9557
9558+ pic->i_planes = planes;
9559+ unsigned int offset = 0;
9560+ for (unsigned int i = 0; i != planes; ++i) {
9561+ pic->p[i] = (plane_t){
9562+ .p_pixels = data + (buf_vid != NULL ? buf_vid->offset[i] : offset),
9563+ .i_lines = mm_fmt->height >> hs[i],
9564+ .i_pitch = buf_vid != NULL ? buf_vid->pitch[i] : mm_fmt->width * pb,
9565+ .i_pixel_pitch = pb,
9566+ .i_visible_lines = mm_fmt->crop.height >> hs[i],
9567+ .i_visible_pitch = mm_fmt->crop.width >> ws[i]
9568+ };
9569+ offset += pic->p[i].i_pitch * pic->p[i].i_lines;
9570+ }
9571 return VLC_SUCCESS;
9572 }
9573+
9574+int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic)
9575+{
9576+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma))
9577+ return VLC_EGENERIC;
9578+ if (pic->context != NULL)
9579+ return VLC_EBADVAR;
9580+
9581+ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
9582+
9583+ if (ctx == NULL)
9584+ return VLC_ENOMEM;
9585+
9586+ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
9587+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
9588+ ctx->buf_count = 1; // cb takes the place of the 1st buf
9589+ ctx->cb = cb;
9590+
9591+ cma_buf_in_flight(cb);
9592+
9593+ pic->context = &ctx->cmn;
9594+ return VLC_SUCCESS;
9595+}
9596+
9597+cma_buf_t * cma_buf_pic_get(picture_t * const pic)
9598+{
9599+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9600+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? 0 : ctx->cb;
9601+}
9602+
9603+
9604+//----------------------------------------------------------------------------
9605+
9606+/* Returns the type of the Pi being used
9607+*/
9608+bool rpi_is_model_pi4(void) {
9609+ return bcm_host_is_model_pi4();
9610+}
9611+
9612+// Preferred mode - none->cma on Pi4 otherwise legacy
9613+static volatile vcsm_init_type_t last_vcsm_type = VCSM_INIT_NONE;
9614+
9615+vcsm_init_type_t cma_vcsm_type(void)
9616+{
9617+ return last_vcsm_type;
9618+}
9619+
9620+vcsm_init_type_t cma_vcsm_init(void)
9621+{
9622+ vcsm_init_type_t rv = VCSM_INIT_NONE;
9623+ // We don't bother locking - taking a copy here should be good enough
9624+ vcsm_init_type_t try_type = last_vcsm_type;
9625+
9626+ if (try_type == VCSM_INIT_NONE) {
9627+ if (bcm_host_is_fkms_active())
9628+ try_type = VCSM_INIT_CMA;
9629+ else
9630+ try_type = VCSM_INIT_LEGACY;
9631+ }
9632+
9633+ if (try_type == VCSM_INIT_CMA) {
9634+ if (vcsm_init_ex(1, -1) == 0)
9635+ rv = VCSM_INIT_CMA;
9636+ else if (vcsm_init_ex(0, -1) == 0)
9637+ rv = VCSM_INIT_LEGACY;
9638+ }
9639+ else
9640+ {
9641+ if (vcsm_init_ex(0, -1) == 0)
9642+ rv = VCSM_INIT_LEGACY;
9643+ else if (vcsm_init_ex(1, -1) == 0)
9644+ rv = VCSM_INIT_CMA;
9645+ }
9646+
9647+ // Just in case this affects vcsm init do after that
9648+ if (rv != VCSM_INIT_NONE)
9649+ bcm_host_init();
9650+
9651+ last_vcsm_type = rv;
9652+ return rv;
9653+}
9654+
9655+void cma_vcsm_exit(const vcsm_init_type_t init_mode)
9656+{
9657+ if (init_mode != VCSM_INIT_NONE)
9658+ {
9659+ vcsm_exit();
9660+ bcm_host_deinit(); // Does nothing but add in case it ever does
9661+ }
9662+}
9663+
9664+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode)
9665+{
9666+ switch (init_mode)
9667+ {
9668+ case VCSM_INIT_CMA:
9669+ return "CMA";
9670+ case VCSM_INIT_LEGACY:
9671+ return "Legacy";
9672+ case VCSM_INIT_NONE:
9673+ return "none";
9674+ default:
9675+ break;
9676+ }
9677+ return "???";
9678+}
9679+
9680+
9681--- a/modules/hw/mmal/mmal_picture.h
9682+++ b/modules/hw/mmal/mmal_picture.h
9683@@ -24,19 +24,298 @@
9684 #ifndef VLC_MMAL_MMAL_PICTURE_H_
9685 #define VLC_MMAL_MMAL_PICTURE_H_
9686
9687+#include <stdatomic.h>
9688+
9689 #include <vlc_common.h>
9690 #include <interface/mmal/mmal.h>
9691
9692+#include "mmal_cma.h"
9693+
9694 /* Think twice before changing this. Incorrect values cause havoc. */
9695 #define NUM_ACTUAL_OPAQUE_BUFFERS 30
9696
9697-struct picture_sys_t {
9698- vlc_object_t *owner;
9699+#ifndef VLC_TICK_INVALID
9700+#define VLC_TICK_INVALID VLC_TS_INVALID
9701+#define VLC_VER_3 1
9702+#else
9703+#define VLC_VER_3 0
9704+#endif
9705+
9706+typedef struct mmal_port_pool_ref_s
9707+{
9708+ atomic_uint refs;
9709+ MMAL_POOL_T * pool;
9710+ MMAL_PORT_T * port;
9711+} hw_mmal_port_pool_ref_t;
9712+
9713+typedef struct pic_ctx_subpic_s {
9714+ picture_t * subpic;
9715+ int x, y;
9716+ int alpha;
9717+} pic_ctx_subpic_t;
9718+
9719+
9720+#define CTX_BUFS_MAX 4
9721+typedef struct pic_ctx_mmal_s {
9722+ picture_context_t cmn; // PARENT: Common els at start
9723+
9724+ cma_buf_t * cb;
9725+
9726+ unsigned int buf_count;
9727+ MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX];
9728+
9729+} pic_ctx_mmal_t;
9730+
9731+const char * str_fourcc(char * const buf, const unsigned int fcc);
9732+
9733+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc);
9734+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs);
9735+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc);
9736+// Returns true if fmt_changed
9737+// frame_rate ignored for compare, but is set if something else is updated
9738+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic);
9739+
9740+// Copy pic contents into an existing buffer
9741+int hw_mmal_copy_pic_to_buf(void * const buf_data, uint32_t * const pLength,
9742+ const MMAL_ES_FORMAT_T * const fmt, const picture_t * const pic);
9743+
9744+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
9745+ const unsigned int headers, const uint32_t payload_size);
9746+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb);
9747+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf);
9748+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr);
9749+static inline void hw_mmal_port_pool_ref_acquire(hw_mmal_port_pool_ref_t * const ppr)
9750+{
9751+ atomic_fetch_add(&ppr->refs, 1);
9752+}
9753+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
9754+ hw_mmal_port_pool_ref_t ** pppr,
9755+ MMAL_PORT_T * const port,
9756+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback);
9757+
9758+static inline int hw_mmal_pic_has_sub_bufs(picture_t * const pic)
9759+{
9760+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9761+ return ctx->buf_count > 1;
9762+}
9763+
9764+static inline void hw_mmal_pic_sub_buf_add(picture_t * const pic, MMAL_BUFFER_HEADER_T * const sub)
9765+{
9766+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9767+
9768+ if (ctx->buf_count >= CTX_BUFS_MAX) {
9769+ mmal_buffer_header_release(sub);
9770+ return;
9771+ }
9772+
9773+ ctx->bufs[ctx->buf_count++] = sub;
9774+}
9775+
9776+static inline MMAL_BUFFER_HEADER_T * hw_mmal_pic_sub_buf_get(picture_t * const pic, const unsigned int n)
9777+{
9778+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
9779+
9780+ return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1];
9781+}
9782+
9783+static inline bool hw_mmal_chroma_is_mmal(const vlc_fourcc_t chroma)
9784+{
9785+ return
9786+ chroma == VLC_CODEC_MMAL_OPAQUE ||
9787+ chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
9788+ chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
9789+ chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
9790+ chroma == VLC_CODEC_MMAL_ZC_I420 ||
9791+ chroma == VLC_CODEC_MMAL_ZC_RGB32;
9792+}
9793+
9794+static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic)
9795+{
9796+ return hw_mmal_chroma_is_mmal(pic->format.i_chroma);
9797+}
9798+
9799+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn);
9800+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn);
9801+picture_context_t * hw_mmal_gen_context(
9802+ MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr);
9803+
9804+int hw_mmal_get_gpu_mem(void);
9805+
9806+
9807+static inline MMAL_STATUS_T port_parameter_set_uint32(MMAL_PORT_T * port, uint32_t id, uint32_t val)
9808+{
9809+ const MMAL_PARAMETER_UINT32_T param = {
9810+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_UINT32_T)},
9811+ .value = val
9812+ };
9813+ return mmal_port_parameter_set(port, &param.hdr);
9814+}
9815+
9816+static inline MMAL_STATUS_T port_parameter_set_bool(MMAL_PORT_T * const port, const uint32_t id, const bool val)
9817+{
9818+ const MMAL_PARAMETER_BOOLEAN_T param = {
9819+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_BOOLEAN_T)},
9820+ .enable = val
9821+ };
9822+ return mmal_port_parameter_set(port, &param.hdr);
9823+}
9824+
9825+static inline MMAL_STATUS_T port_send_replicated(MMAL_PORT_T * const port, MMAL_POOL_T * const rep_pool,
9826+ MMAL_BUFFER_HEADER_T * const src_buf,
9827+ const uint64_t seq)
9828+{
9829+ MMAL_STATUS_T err;
9830+ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
9831+
9832+ if (rep_buf == NULL)
9833+ return MMAL_ENOSPC;
9834+
9835+ if ((err = mmal_buffer_header_replicate(rep_buf, src_buf)) != MMAL_SUCCESS)
9836+ return err;
9837+
9838+ rep_buf->pts = seq;
9839+
9840+ if ((err = mmal_port_send_buffer(port, rep_buf)) != MMAL_SUCCESS)
9841+ {
9842+ mmal_buffer_header_release(rep_buf);
9843+ return err;
9844+ }
9845+
9846+ return MMAL_SUCCESS;
9847+}
9848+
9849+
9850+static inline void pic_to_buf_copy_props(MMAL_BUFFER_HEADER_T * const buf, const picture_t * const pic)
9851+{
9852+ if (!pic->b_progressive)
9853+ {
9854+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9855+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9856+ }
9857+ else
9858+ {
9859+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9860+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
9861+ }
9862+ if (pic->b_top_field_first)
9863+ {
9864+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9865+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9866+ }
9867+ else
9868+ {
9869+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9870+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
9871+ }
9872+ buf->pts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
9873+ buf->dts = buf->pts;
9874+}
9875+
9876+static inline void buf_to_pic_copy_props(picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf)
9877+{
9878+ // Contrary to docn the interlace & tff flags turn up in the header flags rather than the
9879+ // video specific flags (which appear to be currently unused).
9880+ pic->b_progressive = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED) == 0;
9881+ pic->b_top_field_first = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST) != 0;
9882+
9883+ pic->date = buf->pts != MMAL_TIME_UNKNOWN ? buf->pts :
9884+ buf->dts != MMAL_TIME_UNKNOWN ? buf->dts :
9885+ VLC_TICK_INVALID;
9886+}
9887+
9888+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic,
9889+ MMAL_POOL_T * const rep_pool,
9890+ MMAL_PORT_T * const port,
9891+ cma_buf_pool_t * const cbp);
9892+
9893+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool);
9894+
9895+struct vzc_pool_ctl_s;
9896+typedef struct vzc_pool_ctl_s vzc_pool_ctl_t;
9897+
9898+// At the moment we cope with any mono-planar RGBA thing
9899+// We could cope with many other things but they currently don't occur
9900+extern const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[];
9901+static inline bool hw_mmal_vzc_subpic_fmt_valid(const video_frame_format_t * const vf_vlc)
9902+{
9903+ const vlc_fourcc_t vfcc_src = vf_vlc->i_chroma;
9904+ for (const vlc_fourcc_t * p = hw_mmal_vzc_subpicture_chromas; *p != 0; ++p)
9905+ if (*p == vfcc_src)
9906+ return true;
9907+
9908+ return false;
9909+}
9910+
9911+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt);
9912+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf);
9913+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect, const MMAL_DISPLAYTRANSFORM_T scale_transform);
9914+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH);
9915+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf);
9916+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic,
9917+ const MMAL_RECT_T dst_pic_rect,
9918+ const int x_offset, const int y_offset,
9919+ const unsigned int alpha, const bool is_first);
9920+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
9921+ uint32_t * const pWidth, uint32_t * const pHeight);
9922+
9923+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc);
9924+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc);
9925+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc);
9926+vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void);
9927+
9928+
9929+static inline MMAL_RECT_T vis_mmal_rect(const video_format_t * const fmt)
9930+{
9931+ return (MMAL_RECT_T){
9932+ .x = fmt->i_x_offset,
9933+ .y = fmt->i_y_offset,
9934+ .width = fmt->i_visible_width,
9935+ .height = fmt->i_visible_height
9936+ };
9937+}
9938+
9939+int cma_pic_set_data(picture_t * const pic,
9940+ const MMAL_ES_FORMAT_T * const mm_esfmt,
9941+ const MMAL_BUFFER_HEADER_T * const buf);
9942+
9943+// Attaches cma buf to pic
9944+// Marks in_flight if not all_in_flight anyway
9945+int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic);
9946+// Returns a pointer to the cma_buf attached to the pic
9947+// Just a pointer - doesn't add a ref
9948+cma_buf_t * cma_buf_pic_get(picture_t * const pic);
9949+
9950+static inline bool is_cma_buf_pic_chroma(const uint32_t chroma)
9951+{
9952+ return chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
9953+ chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
9954+ chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
9955+ chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
9956+ chroma == VLC_CODEC_MMAL_ZC_I420;
9957+}
9958+
9959+
9960+int rpi_get_model_type(void);
9961+bool rpi_is_model_pi4(void);
9962+bool rpi_is_fkms_active(void);
9963+
9964+typedef enum vcsm_init_type_e {
9965+ VCSM_INIT_NONE = 0,
9966+ VCSM_INIT_LEGACY,
9967+ VCSM_INIT_CMA
9968+} vcsm_init_type_t;
9969+
9970+vcsm_init_type_t cma_vcsm_init(void);
9971+void cma_vcsm_exit(const vcsm_init_type_t init_mode);
9972+vcsm_init_type_t cma_vcsm_type(void);
9973+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode);
9974+
9975
9976- MMAL_BUFFER_HEADER_T *buffer;
9977- bool displayed;
9978-};
9979+#define VOUT_DISPLAY_CHANGE_MMAL_BASE 1024
9980+#define VOUT_DISPLAY_CHANGE_MMAL_HIDE (VOUT_DISPLAY_CHANGE_MMAL_BASE + 0)
9981
9982-int mmal_picture_lock(picture_t *picture);
9983+#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize"
9984+#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp"
9985+#define MMAL_COMPONENT_HVS "vc.ril.hvs"
9986
9987 #endif
9988--- /dev/null
9989+++ b/modules/hw/mmal/rpi_prof.h
9990@@ -0,0 +1,110 @@
9991+#ifndef RPI_PROFILE_H
9992+#define RPI_PROFILE_H
9993+
9994+#include <stdint.h>
9995+#include <inttypes.h>
9996+
9997+#ifndef RPI_PROFILE
9998+#define RPI_PROFILE 0
9999+#endif
10000+
10001+#if RPI_PROFILE
10002+
10003+#include "v7_pmu.h"
10004+
10005+#ifdef RPI_PROC_ALLOC
10006+#define X volatile
10007+#define Z =0
10008+#else
10009+#define X extern volatile
10010+#define Z
10011+#endif
10012+
10013+X uint64_t av_rpi_prof0_cycles Z;
10014+X unsigned int av_rpi_prof0_cnt Z;
10015+#define RPI_prof0_MAX_DURATION 100000
10016+
10017+X uint64_t av_rpi_prof1_cycles Z;
10018+X unsigned int av_rpi_prof1_cnt Z;
10019+#define RPI_prof1_MAX_DURATION 100000
10020+
10021+X uint64_t av_rpi_prof2_cycles Z;
10022+X unsigned int av_rpi_prof2_cnt Z;
10023+#define RPI_prof2_MAX_DURATION 10000
10024+
10025+X uint64_t av_rpi_prof_n_cycles[128];
10026+X unsigned int av_rpi_prof_n_cnt[128];
10027+#define RPI_prof_n_MAX_DURATION 10000
10028+
10029+
10030+#undef X
10031+#undef Z
10032+
10033+#define PROFILE_INIT()\
10034+do {\
10035+ enable_pmu();\
10036+ enable_ccnt();\
10037+} while (0)
10038+
10039+#define PROFILE_START()\
10040+do {\
10041+ volatile uint32_t perf_1 = read_ccnt();\
10042+ volatile uint32_t perf_2
10043+
10044+
10045+#define PROFILE_ACC(x)\
10046+ perf_2 = read_ccnt();\
10047+ {\
10048+ const uint32_t duration = perf_2 - perf_1;\
10049+ if (duration < RPI_##x##_MAX_DURATION)\
10050+ {\
10051+ av_rpi_##x##_cycles += duration;\
10052+ av_rpi_##x##_cnt += 1;\
10053+ }\
10054+ }\
10055+} while(0)
10056+
10057+
10058+#define PROFILE_ACC_N(n)\
10059+ if ((n) >= 0) {\
10060+ perf_2 = read_ccnt();\
10061+ {\
10062+ const uint32_t duration = perf_2 - perf_1;\
10063+ if (duration < RPI_prof_n_MAX_DURATION)\
10064+ {\
10065+ av_rpi_prof_n_cycles[n] += duration;\
10066+ av_rpi_prof_n_cnt[n] += 1;\
10067+ }\
10068+ }\
10069+ }\
10070+} while(0)
10071+
10072+#define PROFILE_PRINTF(x)\
10073+ printf("%-20s cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", #x, av_rpi_##x##_cycles, av_rpi_##x##_cnt,\
10074+ av_rpi_##x##_cnt == 0 ? (uint64_t)0 : av_rpi_##x##_cycles / (uint64_t)av_rpi_##x##_cnt)
10075+
10076+#define PROFILE_PRINTF_N(n)\
10077+ printf("prof[%d] cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", (n), av_rpi_prof_n_cycles[n], av_rpi_prof_n_cnt[n],\
10078+ av_rpi_prof_n_cnt[n] == 0 ? (uint64_t)0 : av_rpi_prof_n_cycles[n] / (uint64_t)av_rpi_prof_n_cnt[n])
10079+
10080+#define PROFILE_CLEAR_N(n) \
10081+do {\
10082+ av_rpi_prof_n_cycles[n] = 0;\
10083+ av_rpi_prof_n_cnt[n] = 0;\
10084+} while(0)
10085+
10086+#else
10087+
10088+// No profile
10089+#define PROFILE_INIT()
10090+#define PROFILE_START()
10091+#define PROFILE_ACC(x)
10092+#define PROFILE_ACC_N(x)
10093+#define PROFILE_PRINTF(x)
10094+#define PROFILE_PRINTF_N(x)
10095+#define PROFILE_CLEAR_N(n)
10096+
10097+#endif
10098+
10099+#endif
10100+
10101--- /dev/null
10102+++ b/modules/hw/mmal/subpic.c
10103@@ -0,0 +1,257 @@
10104+/*****************************************************************************
10105+ * mmal.c: MMAL-based decoder plugin for Raspberry Pi
10106+ *****************************************************************************
10107+ * Authors: jc@kynesim.co.uk
10108+ *
10109+ * This program is free software; you can redistribute it and/or modify it
10110+ * under the terms of the GNU Lesser General Public License as published by
10111+ * the Free Software Foundation; either version 2.1 of the License, or
10112+ * (at your option) any later version.
10113+ *
10114+ * This program is distributed in the hope that it will be useful,
10115+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10116+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10117+ * GNU Lesser General Public License for more details.
10118+ *
10119+ * You should have received a copy of the GNU Lesser General Public License
10120+ * along with this program; if not, write to the Free Software Foundation,
10121+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
10122+ *****************************************************************************/
10123+
10124+#ifdef HAVE_CONFIG_H
10125+#include "config.h"
10126+#endif
10127+
10128+#include <stdatomic.h>
10129+
10130+#include <vlc_common.h>
10131+#include <vlc_plugin.h>
10132+#include <vlc_codec.h>
10133+#include <vlc_filter.h>
10134+#include <vlc_threads.h>
10135+
10136+#include <bcm_host.h>
10137+#include <interface/mmal/mmal.h>
10138+#include <interface/mmal/util/mmal_util.h>
10139+#include <interface/mmal/util/mmal_default_components.h>
10140+
10141+#include "mmal_picture.h"
10142+#include "subpic.h"
10143+
10144+
10145+#define TRACE_ALL 0
10146+
10147+static inline bool cmp_rect(const MMAL_RECT_T * const a, const MMAL_RECT_T * const b)
10148+{
10149+ return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height;
10150+}
10151+
10152+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const sub)
10153+{
10154+ VLC_UNUSED(p_filter);
10155+ if (sub->port != NULL && sub->port->is_enabled)
10156+ mmal_port_disable(sub->port);
10157+ sub->seq = 0;
10158+}
10159+
10160+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe)
10161+{
10162+ hw_mmal_subpic_flush(p_filter, spe);
10163+
10164+ if (spe->pool != NULL)
10165+ mmal_pool_destroy(spe->pool);
10166+
10167+ // Zap to avoid any accidental reuse
10168+ *spe = (subpic_reg_stash_t){NULL};
10169+}
10170+
10171+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port,
10172+ const int display_id, const unsigned int layer)
10173+{
10174+ MMAL_STATUS_T err;
10175+
10176+ // Start by zapping all to zero
10177+ *spe = (subpic_reg_stash_t){NULL};
10178+
10179+ if ((err = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
10180+ {
10181+ msg_Err(p_filter, "Failed to set sub port zero copy");
10182+ return err;
10183+ }
10184+
10185+ if ((spe->pool = mmal_pool_create(30, 0)) == NULL)
10186+ {
10187+ msg_Err(p_filter, "Failed to create sub pool");
10188+ return MMAL_ENOMEM;
10189+ }
10190+
10191+ port->userdata = (void *)p_filter;
10192+ spe->port = port;
10193+ spe->display_id = display_id;
10194+ spe->layer = layer;
10195+
10196+ return MMAL_SUCCESS;
10197+}
10198+
10199+static void conv_subpic_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
10200+{
10201+#if TRACE_ALL
10202+ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, user=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
10203+ __func__, buf->cmd, buf->user_data, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
10204+#else
10205+ VLC_UNUSED(port);
10206+#endif
10207+
10208+ mmal_buffer_header_release(buf); // Will extract & release pic in pool callback
10209+}
10210+
10211+static int
10212+subpic_send_empty(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, const uint64_t pts)
10213+{
10214+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(spe->pool->queue);
10215+ MMAL_STATUS_T err;
10216+
10217+ if (buf == NULL) {
10218+ msg_Err(p_filter, "Buffer get for subpic failed");
10219+ return -1;
10220+ }
10221+#if TRACE_ALL
10222+ msg_Dbg(p_filter, "Remove pic for sub %d", spe->seq);
10223+#endif
10224+ buf->cmd = 0;
10225+ buf->data = NULL;
10226+ buf->alloc_size = 0;
10227+ buf->offset = 0;
10228+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
10229+ buf->pts = pts;
10230+ buf->dts = MMAL_TIME_UNKNOWN;
10231+ buf->user_data = NULL;
10232+
10233+ if ((err = mmal_port_send_buffer(spe->port, buf)) != MMAL_SUCCESS)
10234+ {
10235+ msg_Err(p_filter, "Send buffer to subput failed");
10236+ mmal_buffer_header_release(buf);
10237+ return -1;
10238+ }
10239+ return 0;
10240+}
10241+
10242+// < 0 Error
10243+// 0 Done & stop
10244+// 1 Done & continue
10245+
10246+int hw_mmal_subpic_update(vlc_object_t * const p_filter,
10247+ MMAL_BUFFER_HEADER_T * const sub_buf,
10248+ subpic_reg_stash_t * const spe,
10249+ const video_format_t * const fmt,
10250+ const MMAL_RECT_T * const scale_out,
10251+ const MMAL_DISPLAYTRANSFORM_T transform_out,
10252+ const uint64_t pts)
10253+{
10254+ MMAL_STATUS_T err;
10255+
10256+ if (sub_buf == NULL)
10257+ {
10258+ if (spe->port->is_enabled && spe->seq != 0)
10259+ {
10260+ subpic_send_empty(p_filter, spe, pts);
10261+ spe->seq = 0;
10262+ }
10263+ }
10264+ else
10265+ {
10266+ const unsigned int seq = hw_mmal_vzc_buf_seq(sub_buf);
10267+ bool needs_update = (spe->seq != seq);
10268+
10269+ hw_mmal_vzc_buf_scale_dest_rect(sub_buf, scale_out, transform_out);
10270+
10271+ if (hw_mmal_vzc_buf_set_format(sub_buf, spe->port->format))
10272+ {
10273+ MMAL_DISPLAYREGION_T * const dreg = hw_mmal_vzc_buf_region(sub_buf);
10274+ MMAL_VIDEO_FORMAT_T *const v_fmt = &spe->port->format->es->video;
10275+
10276+ v_fmt->frame_rate.den = fmt->i_frame_rate_base;
10277+ v_fmt->frame_rate.num = fmt->i_frame_rate;
10278+ v_fmt->par.den = fmt->i_sar_den;
10279+ v_fmt->par.num = fmt->i_sar_num;
10280+ v_fmt->color_space = MMAL_COLOR_SPACE_UNKNOWN;
10281+
10282+ if (needs_update || dreg->alpha != spe->alpha || !cmp_rect(&dreg->dest_rect, &spe->dest_rect)) {
10283+
10284+ spe->alpha = dreg->alpha;
10285+ spe->dest_rect = dreg->dest_rect;
10286+ needs_update = true;
10287+
10288+ if (spe->display_id >= 0)
10289+ {
10290+ dreg->display_num = spe->display_id;
10291+ dreg->set |= MMAL_DISPLAY_SET_NUM;
10292+ }
10293+ dreg->layer = spe->layer;
10294+ dreg->set |= MMAL_DISPLAY_SET_LAYER;
10295+
10296+#if TRACE_ALL
10297+ msg_Dbg(p_filter, "%s: Update region: Set=%x, dest=%dx%d @ (%d,%d), src=%dx%d @ (%d,%d), layer=%d, alpha=%#x",
10298+ __func__, dreg->set,
10299+ dreg->dest_rect.width, dreg->dest_rect.height, dreg->dest_rect.x, dreg->dest_rect.y,
10300+ dreg->src_rect.width, dreg->src_rect.height, dreg->src_rect.x, dreg->src_rect.y,
10301+ dreg->layer, dreg->alpha);
10302+#endif
10303+
10304+ // If now completely offscreen just flush this & return
10305+ // We only do -ve as (a) that is easy and (b) it seems to be
10306+ // something that can confuse mmal
10307+ if (dreg->dest_rect.y + dreg->dest_rect.height <= 0 ||
10308+ dreg->dest_rect.x + dreg->dest_rect.width <= 0)
10309+ {
10310+ if (spe->port->is_enabled)
10311+ subpic_send_empty(p_filter, spe, pts);
10312+ spe->seq = seq;
10313+ return 1;
10314+ }
10315+
10316+ if ((err = mmal_port_parameter_set(spe->port, &dreg->hdr)) != MMAL_SUCCESS)
10317+ {
10318+ msg_Err(p_filter, "Set display region on subput failed");
10319+ return -1;
10320+ }
10321+
10322+ if ((err = mmal_port_format_commit(spe->port)) != MMAL_SUCCESS)
10323+ {
10324+ msg_Dbg(p_filter, "%s: Subpic commit fail: %d", __func__, err);
10325+ return -1;
10326+ }
10327+ }
10328+ }
10329+
10330+ if (!spe->port->is_enabled)
10331+ {
10332+ spe->port->buffer_num = 30;
10333+ spe->port->buffer_size = spe->port->buffer_size_recommended; // Not used but shuts up the error checking
10334+
10335+ if ((err = mmal_port_enable(spe->port, conv_subpic_cb)) != MMAL_SUCCESS)
10336+ {
10337+ msg_Dbg(p_filter, "%s: Subpic enable fail: %d", __func__, err);
10338+ return -1;
10339+ }
10340+ }
10341+
10342+ if (needs_update)
10343+ {
10344+#if TRACE_ALL
10345+ msg_Dbg(p_filter, "Update pic for sub %d", spe->seq);
10346+#endif
10347+ if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS)
10348+ {
10349+ msg_Err(p_filter, "Send buffer to subput failed");
10350+ return -1;
10351+ }
10352+
10353+ spe->seq = seq;
10354+ }
10355+ }
10356+ return 1;
10357+}
10358+
10359+
10360+
10361--- /dev/null
10362+++ b/modules/hw/mmal/subpic.h
10363@@ -0,0 +1,33 @@
10364+#ifndef VLC_HW_MMAL_SUBPIC_H_
10365+#define VLC_HW_MMAL_SUBPIC_H_
10366+
10367+typedef struct subpic_reg_stash_s
10368+{
10369+ MMAL_PORT_T * port;
10370+ MMAL_POOL_T * pool;
10371+ int display_id; // -1 => do not set
10372+ unsigned int layer;
10373+ // Shadow vars so we can tell if stuff has changed
10374+ MMAL_RECT_T dest_rect;
10375+ unsigned int alpha;
10376+ unsigned int seq;
10377+} subpic_reg_stash_t;
10378+
10379+int hw_mmal_subpic_update(vlc_object_t * const p_filter,
10380+ MMAL_BUFFER_HEADER_T * const sub_buf,
10381+ subpic_reg_stash_t * const spe,
10382+ const video_format_t * const fmt,
10383+ const MMAL_RECT_T * const scale_out,
10384+ const MMAL_DISPLAYTRANSFORM_T transform_out,
10385+ const uint64_t pts);
10386+
10387+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
10388+
10389+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
10390+
10391+// If display id is -1 it will be unset
10392+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port,
10393+ const int display_id, const unsigned int layer);
10394+
10395+#endif
10396+
10397--- /dev/null
10398+++ b/modules/hw/mmal/transform_ops.h
10399@@ -0,0 +1,99 @@
10400+#ifndef VLC_MMAL_TRANSFORM_OPS_H
10401+#define VLC_MMAL_TRANSFORM_OPS_H
10402+
10403+#include <vlc_common.h>
10404+#include <vlc_picture.h>
10405+#include <interface/mmal/mmal.h>
10406+
10407+
10408+// These are enums with the same order so simply coerce
10409+static inline MMAL_DISPLAYTRANSFORM_T vlc_to_mmal_transform(const video_orientation_t orientation){
10410+ return (MMAL_DISPLAYTRANSFORM_T)orientation;
10411+}
10412+
10413+// MMAL headers comment these (getting 2 a bit wrong) but do not give
10414+// defines
10415+#define XFORM_H_SHIFT 0 // Hflip
10416+#define XFORM_V_SHIFT 1 // Vflip
10417+#define XFORM_T_SHIFT 2 // Transpose
10418+#define XFORM_H_BIT (1 << XFORM_H_SHIFT)
10419+#define XFORM_V_BIT (1 << XFORM_V_SHIFT)
10420+#define XFORM_T_BIT (1 << XFORM_T_SHIFT)
10421+
10422+static inline bool
10423+is_transform_transpose(const MMAL_DISPLAYTRANSFORM_T t)
10424+{
10425+ return ((unsigned int)t & XFORM_T_BIT) != 0;
10426+}
10427+
10428+static inline bool
10429+is_transform_hflip(const MMAL_DISPLAYTRANSFORM_T t)
10430+{
10431+ return ((unsigned int)t & XFORM_H_BIT) != 0;
10432+}
10433+
10434+static inline bool
10435+is_transform_vflip(const MMAL_DISPLAYTRANSFORM_T t)
10436+{
10437+ return ((unsigned int)t & XFORM_V_BIT) != 0;
10438+}
10439+
10440+static inline MMAL_DISPLAYTRANSFORM_T
10441+swap_transform_hv(const MMAL_DISPLAYTRANSFORM_T x)
10442+{
10443+ return (((x >> XFORM_H_SHIFT) & 1) << XFORM_V_SHIFT) |
10444+ (((x >> XFORM_V_SHIFT) & 1) << XFORM_H_SHIFT) |
10445+ (x & XFORM_T_BIT);
10446+}
10447+
10448+static inline MMAL_DISPLAYTRANSFORM_T
10449+transform_inverse(const MMAL_DISPLAYTRANSFORM_T x)
10450+{
10451+ return is_transform_transpose(x) ? swap_transform_hv(x) : x;
10452+}
10453+
10454+// Transform generated by A then B
10455+// All ops are self inverse so can simply be XORed on their own
10456+// H & V flips after a transpose need to be swapped
10457+static inline MMAL_DISPLAYTRANSFORM_T
10458+combine_transform(const MMAL_DISPLAYTRANSFORM_T a, const MMAL_DISPLAYTRANSFORM_T b)
10459+{
10460+ return a ^ (is_transform_transpose(a) ? swap_transform_hv(b) : b);
10461+}
10462+
10463+static inline MMAL_RECT_T
10464+rect_transpose(const MMAL_RECT_T s)
10465+{
10466+ return (MMAL_RECT_T){
10467+ .x = s.y,
10468+ .y = s.x,
10469+ .width = s.height,
10470+ .height = s.width
10471+ };
10472+}
10473+
10474+// hflip s in c
10475+static inline MMAL_RECT_T rect_hflip(const MMAL_RECT_T s, const MMAL_RECT_T c)
10476+{
10477+ return (MMAL_RECT_T){
10478+ .x = c.x + (c.x + c.width) - (s.x + s.width),
10479+ .y = s.y,
10480+ .width = s.width,
10481+ .height = s.height
10482+ };
10483+}
10484+
10485+// vflip s in c
10486+static inline MMAL_RECT_T rect_vflip(const MMAL_RECT_T s, const MMAL_RECT_T c)
10487+{
10488+ return (MMAL_RECT_T){
10489+ .x = s.x,
10490+ .y = (c.y + c.height) - (s.y - c.y) - s.height,
10491+ .width = s.width,
10492+ .height = s.height
10493+ };
10494+}
10495+
10496+
10497+#endif
10498+
10499--- /dev/null
10500+++ b/modules/hw/mmal/v7_pmu.S
10501@@ -0,0 +1,263 @@
10502+/*------------------------------------------------------------
10503+Performance Monitor Block
10504+------------------------------------------------------------*/
10505+ .arm @ Make sure we are in ARM mode.
10506+ .text
10507+ .align 2
10508+ .global getPMN @ export this function for the linker
10509+
10510+/* Returns the number of progammable counters uint32_t getPMN(void) */
10511+
10512+getPMN:
10513+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC Register */
10514+ MOV r0, r0, LSR #11 /* Shift N field down to bit 0 */
10515+ AND r0, r0, #0x1F /* Mask to leave just the 5 N bits */
10516+ BX lr
10517+
10518+
10519+
10520+ .global pmn_config @ export this function for the linker
10521+ /* Sets the event for a programmable counter to record */
10522+ /* void pmn_config(unsigned counter, uint32_t event) */
10523+ /* counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1 */
10524+ /* event = r1 = The event code */
10525+pmn_config:
10526+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */
10527+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */
10528+ MCR p15, 0, r1, c9, c13, 1 /* Write EVTSELx Register */
10529+ BX lr
10530+
10531+
10532+
10533+ .global ccnt_divider @ export this function for the linker
10534+ /* Enables/disables the divider (1/64) on CCNT */
10535+ /* void ccnt_divider(int divider) */
10536+ /* divider = r0 = If 0 disable divider, else enable dvider */
10537+ccnt_divider:
10538+ MRC p15, 0, r1, c9, c12, 0 /* Read PMNC */
10539+
10540+ CMP r0, #0x0 /* IF (r0 == 0) */
10541+ BICEQ r1, r1, #0x08 /* THEN: Clear the D bit (disables the */
10542+ ORRNE r1, r1, #0x08 /* ELSE: Set the D bit (enables the di */
10543+
10544+ MCR p15, 0, r1, c9, c12, 0 /* Write PMNC */
10545+ BX lr
10546+
10547+
10548+ /* --------------------------------------------------------------- */
10549+ /* Enable/Disable */
10550+ /* --------------------------------------------------------------- */
10551+
10552+ .global enable_pmu @ export this function for the linker
10553+ /* Global PMU enable */
10554+ /* void enable_pmu(void) */
10555+enable_pmu:
10556+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
10557+ ORR r0, r0, #0x01 /* Set E bit */
10558+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
10559+ BX lr
10560+
10561+
10562+
10563+ .global disable_pmu @ export this function for the linker
10564+ /* Global PMU disable */
10565+ /* void disable_pmu(void) */
10566+disable_pmu:
10567+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
10568+ BIC r0, r0, #0x01 /* Clear E bit */
10569+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
10570+ BX lr
10571+
10572+
10573+
10574+ .global enable_ccnt @ export this function for the linker
10575+ /* Enable the CCNT */
10576+ /* void enable_ccnt(void) */
10577+enable_ccnt:
10578+ MOV r0, #0x80000000 /* Set C bit */
10579+ MCR p15, 0, r0, c9, c12, 1 /* Write CNTENS Register */
10580+ BX lr
10581+
10582+
10583+
10584+ .global disable_ccnt @ export this function for the linker
10585+ /* Disable the CCNT */
10586+ /* void disable_ccnt(void) */
10587+disable_ccnt:
10588+ MOV r0, #0x80000000 /* Clear C bit */
10589+ MCR p15, 0, r0, c9, c12, 2 /* Write CNTENC Register */
10590+ BX lr
10591+
10592+
10593+
10594+ .global enable_pmn @ export this function for the linker
10595+ /* Enable PMN{n} */
10596+ /* void enable_pmn(uint32_t counter) */
10597+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10598+enable_pmn: */
10599+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */
10600+ MOV r1, r1, LSL r0
10601+
10602+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */
10603+ BX lr
10604+
10605+
10606+
10607+ .global disable_pmn @ export this function for the linker
10608+ /* Enable PMN{n} */
10609+ /* void disable_pmn(uint32_t counter) */
10610+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10611+disable_pmn: */
10612+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */
10613+ MOV r1, r1, LSL r0
10614+
10615+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */
10616+ BX lr
10617+
10618+
10619+
10620+ .global enable_pmu_user_access @ export this function for the linker
10621+ /* Enables User mode access to the PMU (must be called in a priviledge */
10622+ /* void enable_pmu_user_access(void) */
10623+enable_pmu_user_access:
10624+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */
10625+ ORR r0, r0, #0x01 /* Set EN bit (bit 0) */
10626+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */
10627+ BX lr
10628+
10629+
10630+
10631+ .global disable_pmu_user_access @ export this function for the linke
10632+ /* Disables User mode access to the PMU (must be called in a priviledg */
10633+ /* void disable_pmu_user_access(void) */
10634+disable_pmu_user_access:
10635+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */
10636+ BIC r0, r0, #0x01 /* Clear EN bit (bit 0) */
10637+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */
10638+ BX lr
10639+
10640+
10641+ /* --------------------------------------------------------------- */
10642+ /* Counter read registers */
10643+ /* --------------------------------------------------------------- */
10644+
10645+ .global read_ccnt @ export this function for the linker
10646+ /* Returns the value of CCNT */
10647+ /* uint32_t read_ccnt(void) */
10648+read_ccnt:
10649+ MRC p15, 0, r0, c9, c13, 0 /* Read CCNT Register */
10650+ BX lr
10651+
10652+
10653+ .global read_pmn @ export this function for the linker
10654+ /* Returns the value of PMN{n} */
10655+ /* uint32_t read_pmn(uint32_t counter) */
10656+ /* counter = r0 = The counter to read (e.g. 0 for PMN0, 1 for PMN1) *
10657+read_pmn: */
10658+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */
10659+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */
10660+ MRC p15, 0, r0, c9, c13, 2 /* Read current PMNx Register */
10661+ BX lr
10662+
10663+
10664+ /* --------------------------------------------------------------- */
10665+ /* Software Increment */
10666+ /* --------------------------------------------------------------- */
10667+
10668+ .global pmu_software_increment @ export this function for the linker
10669+ /* Writes to software increment register */
10670+ /* void pmu_software_increment(uint32_t counter) */
10671+ /* counter = r0 = The counter to increment (e.g. 0 for PMN0, 1 for PMN
10672+pmu_software_increment: */
10673+ MOV r1, #0x01
10674+ MOV r1, r1, LSL r0
10675+ MCR p15, 0, r1, c9, c12, 4 /* Write SWINCR Register */
10676+ BX lr
10677+
10678+ /* --------------------------------------------------------------- */
10679+ /* Overflow & Interrupt Generation */
10680+ /* --------------------------------------------------------------- */
10681+
10682+ .global read_flags @ export this function for the linker
10683+ /* Returns the value of the overflow flags */
10684+ /* uint32_t read_flags(void) */
10685+read_flags:
10686+ MRC p15, 0, r0, c9, c12, 3 /* Read FLAG Register */
10687+ BX lr
10688+
10689+
10690+ .global write_flags @ export this function for the linker
10691+ /* Writes the overflow flags */
10692+ /* void write_flags(uint32_t flags) */
10693+write_flags:
10694+ MCR p15, 0, r0, c9, c12, 3 /* Write FLAG Register */
10695+ BX lr
10696+
10697+
10698+ .global enable_ccnt_irq @ export this function for the linker
10699+ /* Enables interrupt generation on overflow of the CCNT */
10700+ /* void enable_ccnt_irq(void) */
10701+enable_ccnt_irq:
10702+ MOV r0, #0x80000000
10703+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */
10704+ BX lr
10705+
10706+ .global disable_ccnt_irq @ export this function for the linker
10707+ /* Disables interrupt generation on overflow of the CCNT */
10708+ /* void disable_ccnt_irq(void) */
10709+disable_ccnt_irq:
10710+ MOV r0, #0x80000000
10711+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */
10712+ BX lr
10713+
10714+
10715+ .global enable_pmn_irq @ export this function for the linker
10716+ /* Enables interrupt generation on overflow of PMN{x} */
10717+ /* void enable_pmn_irq(uint32_t counter) */
10718+ /* counter = r0 = The counter to enable the interrupt for (e.g. 0 for
10719+enable_pmn_irq: */
10720+ MOV r1, #0x1 /* Use arg (r0) to set which counter */
10721+ MOV r0, r1, LSL r0
10722+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */
10723+ BX lr
10724+
10725+ .global disable_pmn_irq @ export this function for the linker
10726+ /* Disables interrupt generation on overflow of PMN{x} */
10727+ /* void disable_pmn_irq(uint32_t counter) */
10728+ /* counter = r0 = The counter to disable the interrupt for (e.g. 0 fo
10729+disable_pmn_irq: */
10730+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */
10731+ MOV r0, r1, LSL r0
10732+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */
10733+ BX lr
10734+
10735+ /* --------------------------------------------------------------- */
10736+ /* Reset Functions */
10737+ /* --------------------------------------------------------------- */
10738+
10739+ .global reset_pmn @ export this function for the linker
10740+ /* Resets the programmable counters */
10741+ /* void reset_pmn(void) */
10742+reset_pmn:
10743+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
10744+ ORR r0, r0, #0x02 /* Set P bit (Event Counter Reset) */
10745+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
10746+ BX lr
10747+
10748+
10749+ .global reset_ccnt @ export this function for the linker
10750+ /* Resets the CCNT */
10751+ /* void reset_ccnt(void) */
10752+reset_ccnt:
10753+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
10754+ ORR r0, r0, #0x04 /* Set C bit (Event Counter Reset) */
10755+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
10756+ BX lr
10757+
10758+
10759+ .end @end of code, this line is optional.
10760+/* ------------------------------------------------------------ */
10761+/* End of v7_pmu.s */
10762+/* ------------------------------------------------------------ */
10763+
10764+
10765--- /dev/null
10766+++ b/modules/hw/mmal/v7_pmu.h
10767@@ -0,0 +1,113 @@
10768+// ------------------------------------------------------------
10769+// PMU for Cortex-A/R (v7-A/R)
10770+// ------------------------------------------------------------
10771+
10772+#ifndef _V7_PMU_H
10773+#define _V7_PMU_H
10774+
10775+// Returns the number of progammable counters
10776+unsigned int getPMN(void);
10777+
10778+// Sets the event for a programmable counter to record
10779+// counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1)
10780+// event = r1 = The event code (from appropiate TRM or ARM Architecture Reference Manual)
10781+void pmn_config(unsigned int counter, unsigned int event);
10782+
10783+// Enables/disables the divider (1/64) on CCNT
10784+// divider = r0 = If 0 disable divider, else enable dvider
10785+void ccnt_divider(int divider);
10786+
10787+//
10788+// Enables and disables
10789+//
10790+
10791+// Global PMU enable
10792+// On ARM11 this enables the PMU, and the counters start immediately
10793+// On Cortex this enables the PMU, there are individual enables for the counters
10794+void enable_pmu(void);
10795+
10796+// Global PMU disable
10797+// On Cortex, this overrides the enable state of the individual counters
10798+void disable_pmu(void);
10799+
10800+// Enable the CCNT
10801+void enable_ccnt(void);
10802+
10803+// Disable the CCNT
10804+void disable_ccnt(void);
10805+
10806+// Enable PMN{n}
10807+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10808+void enable_pmn(unsigned int counter);
10809+
10810+// Enable PMN{n}
10811+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
10812+void disable_pmn(unsigned int counter);
10813+
10814+//
10815+// Read counter values
10816+//
10817+
10818+// Returns the value of CCNT
10819+unsigned int read_ccnt(void);
10820+
10821+// Returns the value of PMN{n}
10822+// counter = The counter to read (e.g. 0 for PMN0, 1 for PMN1)
10823+unsigned int read_pmn(unsigned int counter);
10824+
10825+//
10826+// Overflow and interrupts
10827+//
10828+
10829+// Returns the value of the overflow flags
10830+unsigned int read_flags(void);
10831+
10832+// Writes the overflow flags
10833+void write_flags(unsigned int flags);
10834+
10835+// Enables interrupt generation on overflow of the CCNT
10836+void enable_ccnt_irq(void);
10837+
10838+// Disables interrupt generation on overflow of the CCNT
10839+void disable_ccnt_irq(void);
10840+
10841+// Enables interrupt generation on overflow of PMN{x}
10842+// counter = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
10843+void enable_pmn_irq(unsigned int counter);
10844+
10845+// Disables interrupt generation on overflow of PMN{x}
10846+// counter = r0 = The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
10847+void disable_pmn_irq(unsigned int counter);
10848+
10849+//
10850+// Counter reset functions
10851+//
10852+
10853+// Resets the programmable counters
10854+void reset_pmn(void);
10855+
10856+// Resets the CCNT
10857+void reset_ccnt(void);
10858+
10859+//
10860+// Software Increment
10861+
10862+// Writes to software increment register
10863+// counter = The counter to increment (e.g. 0 for PMN0, 1 for PMN1)
10864+void pmu_software_increment(unsigned int counter);
10865+
10866+//
10867+// User mode access
10868+//
10869+
10870+// Enables User mode access to the PMU (must be called in a priviledged mode)
10871+void enable_pmu_user_access(void);
10872+
10873+// Disables User mode access to the PMU (must be called in a priviledged mode)
10874+void disable_pmu_user_access(void);
10875+
10876+#endif
10877+// ------------------------------------------------------------
10878+// End of v7_pmu.h
10879+// ------------------------------------------------------------
10880+
10881--- a/modules/hw/mmal/vout.c
10882+++ b/modules/hw/mmal/vout.c
10883@@ -27,21 +27,28 @@
10884 #endif
10885
10886 #include <math.h>
10887+#include <stdatomic.h>
10888
10889 #include <vlc_common.h>
10890-#include <vlc_atomic.h>
10891 #include <vlc_plugin.h>
10892 #include <vlc_threads.h>
10893 #include <vlc_vout_display.h>
10894+#include <vlc_modules.h>
10895
10896-#include "mmal_picture.h"
10897-
10898+#pragma GCC diagnostic push
10899+#pragma GCC diagnostic ignored "-Wbad-function-cast"
10900 #include <bcm_host.h>
10901+#pragma GCC diagnostic pop
10902 #include <interface/mmal/mmal.h>
10903 #include <interface/mmal/util/mmal_util.h>
10904 #include <interface/mmal/util/mmal_default_components.h>
10905 #include <interface/vmcs_host/vc_tvservice.h>
10906-#include <interface/vmcs_host/vc_dispmanx.h>
10907+
10908+#include "mmal_picture.h"
10909+#include "subpic.h"
10910+#include "transform_ops.h"
10911+
10912+#define TRACE_ALL 0
10913
10914 #define MAX_BUFFERS_IN_TRANSIT 1
10915 #define VC_TV_MAX_MODE_IDS 127
10916@@ -50,10 +57,28 @@
10917 #define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.")
10918 #define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.")
10919
10920-#define MMAL_BLANK_BACKGROUND_NAME "mmal-blank-background"
10921-#define MMAL_BLANK_BACKGROUND_TEXT N_("Blank screen below video.")
10922-#define MMAL_BLANK_BACKGROUND_LONGTEXT N_("Render blank screen below video. " \
10923- "Increases VideoCore load.")
10924+#define MMAL_DISPLAY_NAME "mmal-display"
10925+#define MMAL_DISPLAY_TEXT N_("Output device for Rpi fullscreen.")
10926+#define MMAL_DISPLAY_LONGTEXT N_("Output device for Rpi fullscreen. " \
10927+"Valid values are HDMI-1,HDMI-2. By default if qt-fullscreen-screennumber " \
10928+"is specified (or set by Fullscreen Output Device in Preferences) " \
10929+"HDMI-<qt-fullscreen-screennumber+1> will be used, otherwise HDMI-1.")
10930+
10931+#define MMAL_VOUT_TRANSFORM_NAME "mmal-vout-transform"
10932+#define MMAL_VOUT_TRANSFORM_TEXT N_("Video transform for Rpi fullscreen.")
10933+#define MMAL_VOUT_TRANSFORM_LONGTEXT N_("Video transform for Rpi fullscreen."\
10934+"Transforms availible: auto, 0, 90, 180, 270, hflip, vflip, transpose, antitranspose")
10935+
10936+#define MMAL_VOUT_WINDOW_NAME "mmal-vout-window"
10937+#define MMAL_VOUT_WINDOW_TEXT N_("Display window for Rpi fullscreen")
10938+#define MMAL_VOUT_WINDOW_LONGTEXT N_("Display window for Rpi fullscreen."\
10939+"fullscreen|<width>x<height>+<x>+<y>")
10940+
10941+#define MMAL_VOUT_TRANSPARENT_NAME "mmal-vout-transparent"
10942+#define MMAL_VOUT_TRANSPARENT_TEXT N_("Enable layers beneeth the vodeo layer.")
10943+#define MMAL_VOUT_TRANSPARENT_LONGTEXT N_("Enable layers beneath the video layer."\
10944+" By default these are disabled."\
10945+" Having the lower layers enabled can impact video performance")
10946
10947 #define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate"
10948 #define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.")
10949@@ -68,332 +93,628 @@
10950 #define PHASE_OFFSET_TARGET ((double)0.25)
10951 #define PHASE_CHECK_INTERVAL 100
10952
10953-static int Open(vlc_object_t *);
10954-static void Close(vlc_object_t *);
10955-
10956-vlc_module_begin()
10957- set_shortname(N_("MMAL vout"))
10958- set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
10959- set_capability("vout display", 90)
10960- add_shortcut("mmal_vout")
10961- add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
10962- add_bool(MMAL_BLANK_BACKGROUND_NAME, true, MMAL_BLANK_BACKGROUND_TEXT,
10963- MMAL_BLANK_BACKGROUND_LONGTEXT, true);
10964- add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
10965- MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
10966- add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
10967- MMAL_NATIVE_INTERLACE_LONGTEXT, false)
10968- set_callbacks(Open, Close)
10969-vlc_module_end()
10970+#define SUBS_MAX 4
10971
10972-struct dmx_region_t {
10973- struct dmx_region_t *next;
10974- picture_t *picture;
10975- VC_RECT_T bmp_rect;
10976- VC_RECT_T src_rect;
10977- VC_RECT_T dst_rect;
10978- VC_DISPMANX_ALPHA_T alpha;
10979- DISPMANX_ELEMENT_HANDLE_T element;
10980- DISPMANX_RESOURCE_HANDLE_T resource;
10981- int32_t pos_x;
10982- int32_t pos_y;
10983-};
10984+typedef struct vout_subpic_s {
10985+ MMAL_COMPONENT_T *component;
10986+ subpic_reg_stash_t sub;
10987+} vout_subpic_t;
10988
10989 struct vout_display_sys_t {
10990- vlc_cond_t buffer_cond;
10991- vlc_mutex_t buffer_mutex;
10992 vlc_mutex_t manage_mutex;
10993
10994- plane_t planes[3]; /* Depending on video format up to 3 planes are used */
10995- picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */
10996- picture_pool_t *picture_pool;
10997-
10998+ vcsm_init_type_t init_type;
10999 MMAL_COMPONENT_T *component;
11000 MMAL_PORT_T *input;
11001 MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/
11002- struct dmx_region_t *dmx_region;
11003 int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */
11004
11005- uint32_t buffer_size; /* size of actual mmal buffers */
11006 int buffers_in_transit; /* number of buffers currently pushed to mmal component */
11007 unsigned num_buffers; /* number of buffers allocated at mmal port */
11008
11009- DISPMANX_DISPLAY_HANDLE_T dmx_handle;
11010- DISPMANX_ELEMENT_HANDLE_T bkg_element;
11011- DISPMANX_RESOURCE_HANDLE_T bkg_resource;
11012- unsigned display_width;
11013- unsigned display_height;
11014+ int display_id;
11015+ MMAL_RECT_T win_rect; // Window rect after transform(s)
11016+ MMAL_RECT_T display_rect; // Actual shape of display (x, y always 0)
11017+ MMAL_RECT_T req_win; // User requested window (w=0 => fullscreen)
11018+
11019+ MMAL_RECT_T spu_rect; // Output rectangle in cfg coords (for subpic placement)
11020+ MMAL_RECT_T dest_rect; // Output rectangle in display coords
11021+ MMAL_DISPLAYTRANSFORM_T dest_transform; // Dest window coord transform
11022+ MMAL_DISPLAYTRANSFORM_T display_transform; // "Native" display transform
11023+ MMAL_DISPLAYTRANSFORM_T video_transform; // Combined config+native transform
11024
11025- int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
11026- int i_frame_rate;
11027+ unsigned int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
11028+ unsigned int i_frame_rate;
11029
11030 int next_phase_check; /* lowpass for phase check frequency */
11031 int phase_offset; /* currently applied offset to presentation time in ns */
11032 int layer; /* the dispman layer (z-index) used for video rendering */
11033+ bool transparent; // Do not disable layers beneath ours
11034
11035 bool need_configure_display; /* indicates a required display reconfigure to main thread */
11036 bool adjust_refresh_rate;
11037 bool native_interlaced;
11038 bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */
11039 bool b_progressive;
11040- bool opaque; /* indicated use of opaque picture format (zerocopy) */
11041-};
11042+ bool force_config;
11043
11044-static const vlc_fourcc_t subpicture_chromas[] = {
11045- VLC_CODEC_RGBA,
11046- 0
11047-};
11048+ vout_subpic_t subs[SUBS_MAX];
11049+ // Stash for subpics derived from the passed subpicture rather than
11050+ // included with the main pic
11051+ MMAL_BUFFER_HEADER_T * subpic_bufs[SUBS_MAX];
11052+
11053+ picture_pool_t * pic_pool;
11054+
11055+ struct vout_isp_conf_s {
11056+ MMAL_COMPONENT_T *component;
11057+ MMAL_PORT_T * input;
11058+ MMAL_PORT_T * output;
11059+ MMAL_QUEUE_T * out_q;
11060+ MMAL_POOL_T * in_pool;
11061+ MMAL_POOL_T * out_pool;
11062+ bool pending;
11063+ } isp;
11064
11065-/* Utility functions */
11066-static inline uint32_t align(uint32_t x, uint32_t y);
11067-static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
11068- const video_format_t *fmt);
11069+ MMAL_POOL_T * copy_pool;
11070+ MMAL_BUFFER_HEADER_T * copy_buf;
11071
11072-/* VLC vout display callbacks */
11073-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count);
11074-static void vd_prepare(vout_display_t *vd, picture_t *picture,
11075- subpicture_t *subpicture);
11076-static void vd_display(vout_display_t *vd, picture_t *picture,
11077- subpicture_t *subpicture);
11078-static int vd_control(vout_display_t *vd, int query, va_list args);
11079-static void vd_manage(vout_display_t *vd);
11080-
11081-/* MMAL callbacks */
11082-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
11083-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
11084+ // Subpic blend if we have to do it here
11085+ vzc_pool_ctl_t * vzc;
11086+};
11087
11088-/* TV service */
11089-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height);
11090-static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
11091- uint32_t param2);
11092-static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
11093-static int set_latency_target(vout_display_t *vd, bool enable);
11094
11095-/* DispManX */
11096-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture);
11097-static void close_dmx(vout_display_t *vd);
11098-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
11099- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region);
11100-static void dmx_region_update(struct dmx_region_t *dmx_region,
11101- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture);
11102-static void dmx_region_delete(struct dmx_region_t *dmx_region,
11103- DISPMANX_UPDATE_HANDLE_T update);
11104-static void show_background(vout_display_t *vd, bool enable);
11105-static void maintain_phase_sync(vout_display_t *vd);
11106+// ISP setup
11107
11108-static int Open(vlc_object_t *object)
11109+static inline bool want_isp(const vout_display_t * const vd)
11110 {
11111- vout_display_t *vd = (vout_display_t *)object;
11112- vout_display_sys_t *sys;
11113- uint32_t buffer_pitch, buffer_height;
11114- vout_display_place_t place;
11115- MMAL_DISPLAYREGION_T display_region;
11116- MMAL_STATUS_T status;
11117- int ret = VLC_SUCCESS;
11118- unsigned i;
11119+ return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10);
11120+}
11121
11122- if (vout_display_IsWindowed(vd))
11123- return VLC_EGENERIC;
11124+static inline bool want_copy(const vout_display_t * const vd)
11125+{
11126+ return (vd->fmt.i_chroma == VLC_CODEC_I420 || vd->fmt.i_chroma == VLC_CODEC_I420_10L);
11127+}
11128
11129- sys = calloc(1, sizeof(struct vout_display_sys_t));
11130- if (!sys)
11131- return VLC_ENOMEM;
11132- vd->sys = sys;
11133+static inline vlc_fourcc_t req_chroma(const vout_display_t * const vd)
11134+{
11135+ return !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma) && !want_copy(vd) ?
11136+ VLC_CODEC_I420 :
11137+ vd->fmt.i_chroma;
11138+}
11139
11140- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
11141- bcm_host_init();
11142+static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc)
11143+{
11144+ switch (fcc){
11145+ case VLC_CODEC_MMAL_OPAQUE:
11146+ return MMAL_ENCODING_OPAQUE;
11147+ case VLC_CODEC_MMAL_ZC_SAND8:
11148+ return MMAL_ENCODING_YUVUV128;
11149+ case VLC_CODEC_MMAL_ZC_SAND10:
11150+ return MMAL_ENCODING_YUVUV64_10;
11151+ case VLC_CODEC_MMAL_ZC_SAND30:
11152+ return MMAL_ENCODING_YUV10_COL;
11153+ case VLC_CODEC_MMAL_ZC_I420:
11154+ case VLC_CODEC_I420:
11155+ return MMAL_ENCODING_I420;
11156+ default:
11157+ break;
11158+ }
11159+ return MMAL_ENCODING_I420;
11160+}
11161
11162- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE;
11163+static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate)
11164+{
11165+ const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ;
11166+ const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height;
11167+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
11168
11169- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
11170- if (status != MMAL_SUCCESS) {
11171- msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
11172- MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
11173- ret = VLC_EGENERIC;
11174- goto out;
11175+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
11176+ es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
11177+ es_fmt->encoding_variant = 0;
11178+
11179+ v_fmt->width = (w + 31) & ~31;
11180+ v_fmt->height = (h + 15) & ~15;
11181+ v_fmt->crop.x = 0;
11182+ v_fmt->crop.y = 0;
11183+ v_fmt->crop.width = w;
11184+ v_fmt->crop.height = h;
11185+ if (vd->fmt.i_sar_num == 0 || vd->fmt.i_sar_den == 0) {
11186+ v_fmt->par.num = 1;
11187+ v_fmt->par.den = 1;
11188+ } else {
11189+ v_fmt->par.num = vd->fmt.i_sar_num;
11190+ v_fmt->par.den = vd->fmt.i_sar_den;
11191 }
11192+ v_fmt->frame_rate.num = vd->fmt.i_frame_rate;
11193+ v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base;
11194+ v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space);
11195
11196- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
11197- status = mmal_port_enable(sys->component->control, control_port_cb);
11198- if (status != MMAL_SUCCESS) {
11199- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
11200- sys->component->control->name, status, mmal_status_to_string(status));
11201- ret = VLC_EGENERIC;
11202- goto out;
11203+ msg_Dbg(vd, "WxH: %dx%d, Crop: %dx%d", v_fmt->width, v_fmt->height, v_fmt->crop.width, v_fmt->crop.height);
11204+}
11205+
11206+static MMAL_RECT_T
11207+display_src_rect(const vout_display_t * const vd, const video_format_t * const src)
11208+{
11209+ const bool wants_isp = want_isp(vd);
11210+
11211+ // Scale source derived cropping to actual picture shape
11212+ return (MMAL_RECT_T){
11213+ .x = wants_isp ? 0 : src->i_x_offset * vd->fmt.i_width / src->i_width,
11214+ .y = wants_isp ? 0 : src->i_y_offset * vd->fmt.i_height / src->i_height,
11215+ .width = src->i_visible_width * vd->fmt.i_width / src->i_width,
11216+ .height = src->i_visible_height * vd->fmt.i_height / src->i_height
11217+ };
11218+}
11219+
11220+static void isp_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
11221+{
11222+#if TRACE_ALL
11223+ vout_display_t * const vd = (vout_display_t *)port->userdata;
11224+ pic_ctx_mmal_t * ctx = buf->user_data;
11225+ msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf,
11226+ buf->flags, (long long)buf->pts);
11227+#else
11228+ VLC_UNUSED(port);
11229+#endif
11230+
11231+ mmal_buffer_header_release(buf);
11232+
11233+#if TRACE_ALL
11234+ msg_Dbg(vd, ">>> %s", __func__);
11235+#endif
11236+}
11237+
11238+static void isp_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
11239+{
11240+ vout_display_t *vd = (vout_display_t *)port->userdata;
11241+ MMAL_STATUS_T status;
11242+
11243+ if (buffer->cmd == MMAL_EVENT_ERROR) {
11244+ status = *(uint32_t *)buffer->data;
11245+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
11246 }
11247
11248- sys->input = sys->component->input[0];
11249- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
11250+ mmal_buffer_header_release(buffer);
11251+}
11252
11253- if (sys->opaque) {
11254- sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
11255- sys->i_planes = 1;
11256- sys->buffer_size = sys->input->buffer_size_recommended;
11257- } else {
11258- sys->input->format->encoding = MMAL_ENCODING_I420;
11259- vd->fmt.i_chroma = VLC_CODEC_I420;
11260- buffer_pitch = align(vd->fmt.i_width, 32);
11261- buffer_height = align(vd->fmt.i_height, 16);
11262- sys->i_planes = 3;
11263- sys->buffer_size = 3 * buffer_pitch * buffer_height / 2;
11264- }
11265-
11266- sys->input->format->es->video.width = vd->fmt.i_width;
11267- sys->input->format->es->video.height = vd->fmt.i_height;
11268- sys->input->format->es->video.crop.x = 0;
11269- sys->input->format->es->video.crop.y = 0;
11270- sys->input->format->es->video.crop.width = vd->fmt.i_width;
11271- sys->input->format->es->video.crop.height = vd->fmt.i_height;
11272- sys->input->format->es->video.par.num = vd->source.i_sar_num;
11273- sys->input->format->es->video.par.den = vd->source.i_sar_den;
11274+static void isp_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
11275+{
11276+ if (buf->cmd == 0 && buf->length != 0)
11277+ {
11278+ // The filter structure etc. should always exist if we have contents
11279+ // but might not on later flushes as we shut down
11280+ vout_display_t * const vd = (vout_display_t *)port->userdata;
11281+ struct vout_isp_conf_s *const isp = &vd->sys->isp;
11282
11283- status = mmal_port_format_commit(sys->input);
11284- if (status != MMAL_SUCCESS) {
11285- msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
11286- sys->input->name, status, mmal_status_to_string(status));
11287- ret = VLC_EGENERIC;
11288- goto out;
11289+#if TRACE_ALL
11290+ msg_Dbg(vd, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts);
11291+#endif
11292+ mmal_queue_put(isp->out_q, buf);
11293+#if TRACE_ALL
11294+ msg_Dbg(vd, ">>> %s: out Q len=%d", __func__, mmal_queue_length(isp->out_q));
11295+#endif
11296 }
11297- sys->input->buffer_size = sys->input->buffer_size_recommended;
11298+ else
11299+ {
11300+ mmal_buffer_header_reset(buf);
11301+ mmal_buffer_header_release(buf);
11302+ }
11303+}
11304
11305- vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
11306- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
11307- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
11308- display_region.fullscreen = MMAL_FALSE;
11309- display_region.src_rect.x = vd->fmt.i_x_offset;
11310- display_region.src_rect.y = vd->fmt.i_y_offset;
11311- display_region.src_rect.width = vd->fmt.i_visible_width;
11312- display_region.src_rect.height = vd->fmt.i_visible_height;
11313- display_region.dest_rect.x = place.x;
11314- display_region.dest_rect.y = place.y;
11315- display_region.dest_rect.width = place.width;
11316- display_region.dest_rect.height = place.height;
11317- display_region.layer = sys->layer;
11318- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
11319- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
11320- status = mmal_port_parameter_set(sys->input, &display_region.hdr);
11321- if (status != MMAL_SUCCESS) {
11322- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
11323- status, mmal_status_to_string(status));
11324- ret = VLC_EGENERIC;
11325- goto out;
11326+static void isp_empty_out_q(struct vout_isp_conf_s * const isp)
11327+{
11328+ MMAL_BUFFER_HEADER_T * buf;
11329+ // We can be called as part of error recovery so allow for missing Q
11330+ if (isp->out_q == NULL)
11331+ return;
11332+
11333+ while ((buf = mmal_queue_get(isp->out_q)) != NULL)
11334+ mmal_buffer_header_release(buf);
11335+}
11336+
11337+static void isp_flush(struct vout_isp_conf_s * const isp)
11338+{
11339+ if (!isp->input->is_enabled)
11340+ mmal_port_disable(isp->input);
11341+
11342+ if (isp->output->is_enabled)
11343+ mmal_port_disable(isp->output);
11344+
11345+ isp_empty_out_q(isp);
11346+ isp->pending = false;
11347+}
11348+
11349+static MMAL_STATUS_T isp_prepare(vout_display_t * const vd, struct vout_isp_conf_s * const isp)
11350+{
11351+ MMAL_STATUS_T err;
11352+ MMAL_BUFFER_HEADER_T * buf;
11353+
11354+ if (!isp->output->is_enabled) {
11355+ if ((err = mmal_port_enable(isp->output, isp_output_cb)) != MMAL_SUCCESS)
11356+ {
11357+ msg_Err(vd, "ISP output port enable failed");
11358+ return err;
11359+ }
11360 }
11361
11362- for (i = 0; i < sys->i_planes; ++i) {
11363- sys->planes[i].i_lines = buffer_height;
11364- sys->planes[i].i_pitch = buffer_pitch;
11365- sys->planes[i].i_visible_lines = vd->fmt.i_visible_height;
11366- sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width;
11367+ while ((buf = mmal_queue_get(isp->out_pool->queue)) != NULL) {
11368+ if ((err = mmal_port_send_buffer(isp->output, buf)) != MMAL_SUCCESS)
11369+ {
11370+ msg_Err(vd, "ISP output port stuff failed");
11371+ return err;
11372+ }
11373+ }
11374
11375- if (i > 0) {
11376- sys->planes[i].i_lines /= 2;
11377- sys->planes[i].i_pitch /= 2;
11378- sys->planes[i].i_visible_lines /= 2;
11379- sys->planes[i].i_visible_pitch /= 2;
11380+ if (!isp->input->is_enabled) {
11381+ if ((err = mmal_port_enable(isp->input, isp_input_cb)) != MMAL_SUCCESS)
11382+ {
11383+ msg_Err(vd, "ISP input port enable failed");
11384+ return err;
11385 }
11386 }
11387+ return MMAL_SUCCESS;
11388+}
11389
11390- vlc_mutex_init(&sys->buffer_mutex);
11391- vlc_cond_init(&sys->buffer_cond);
11392- vlc_mutex_init(&sys->manage_mutex);
11393+static void isp_close(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
11394+{
11395+ struct vout_isp_conf_s * const isp = &vd_sys->isp;
11396+ VLC_UNUSED(vd);
11397
11398- vd->pool = vd_pool;
11399- vd->prepare = vd_prepare;
11400- vd->display = vd_display;
11401- vd->control = vd_control;
11402- vd->manage = vd_manage;
11403+ if (isp->component == NULL)
11404+ return;
11405
11406- vc_tv_register_callback(tvservice_cb, vd);
11407+ isp_flush(isp);
11408
11409- if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) {
11410- vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height);
11411- } else {
11412- sys->display_width = vd->cfg->display.width;
11413- sys->display_height = vd->cfg->display.height;
11414+ if (isp->component->control->is_enabled)
11415+ mmal_port_disable(isp->component->control);
11416+
11417+ if (isp->out_q != NULL) {
11418+ // 1st junk anything lying around
11419+ isp_empty_out_q(isp);
11420+
11421+ mmal_queue_destroy(isp->out_q);
11422+ isp->out_q = NULL;
11423 }
11424
11425- sys->dmx_handle = vc_dispmanx_display_open(0);
11426- vd->info.subpicture_chromas = subpicture_chromas;
11427+ if (isp->out_pool != NULL) {
11428+ mmal_port_pool_destroy(isp->output, isp->out_pool);
11429+ isp->out_pool = NULL;
11430+ }
11431
11432- vout_display_DeleteWindow(vd, NULL);
11433+ isp->input = NULL;
11434+ isp->output = NULL;
11435
11436-out:
11437- if (ret != VLC_SUCCESS)
11438- Close(object);
11439+ mmal_component_release(isp->component);
11440+ isp->component = NULL;
11441
11442- return ret;
11443+ return;
11444 }
11445
11446-static void Close(vlc_object_t *object)
11447+// Restuff into output rather than return to pool is we can
11448+static MMAL_BOOL_T isp_out_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata)
11449 {
11450- vout_display_t *vd = (vout_display_t *)object;
11451- vout_display_sys_t *sys = vd->sys;
11452- char response[20]; /* answer is hvs_update_fields=%1d */
11453- unsigned i;
11454+ struct vout_isp_conf_s * const isp = userdata;
11455+ VLC_UNUSED(pool);
11456+ if (isp->output->is_enabled) {
11457+ mmal_buffer_header_reset(buffer);
11458+ if (mmal_port_send_buffer(isp->output, buffer) == MMAL_SUCCESS)
11459+ return MMAL_FALSE;
11460+ }
11461+ return MMAL_TRUE;
11462+}
11463
11464- vc_tv_unregister_callback_full(tvservice_cb, vd);
11465+static MMAL_STATUS_T isp_setup(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
11466+{
11467+ struct vout_isp_conf_s * const isp = &vd_sys->isp;
11468+ MMAL_STATUS_T err;
11469
11470- if (sys->dmx_handle)
11471- close_dmx(vd);
11472+ if ((err = mmal_component_create(MMAL_COMPONENT_ISP_RESIZER, &isp->component)) != MMAL_SUCCESS) {
11473+ msg_Err(vd, "Cannot create ISP component");
11474+ return err;
11475+ }
11476+ isp->input = isp->component->input[0];
11477+ isp->output = isp->component->output[0];
11478
11479- if (sys->component && sys->component->control->is_enabled)
11480- mmal_port_disable(sys->component->control);
11481+ isp->component->control->userdata = (void *)vd;
11482+ if ((err = mmal_port_enable(isp->component->control, isp_control_port_cb)) != MMAL_SUCCESS) {
11483+ msg_Err(vd, "Failed to enable ISP control port");
11484+ goto fail;
11485+ }
11486
11487- if (sys->input && sys->input->is_enabled)
11488- mmal_port_disable(sys->input);
11489+ isp->input->userdata = (void *)vd;
11490+ display_set_format(vd, isp->input->format, false);
11491
11492- if (sys->component && sys->component->is_enabled)
11493- mmal_component_disable(sys->component);
11494+ if ((err = port_parameter_set_bool(isp->input, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
11495+ goto fail;
11496
11497- if (sys->pool)
11498- mmal_port_pool_destroy(sys->input, sys->pool);
11499+ if ((err = mmal_port_format_commit(isp->input)) != MMAL_SUCCESS) {
11500+ msg_Err(vd, "Failed to set ISP input format");
11501+ goto fail;
11502+ }
11503
11504- if (sys->component)
11505- mmal_component_release(sys->component);
11506+ isp->input->buffer_size = isp->input->buffer_size_recommended;
11507+ isp->input->buffer_num = 30;
11508
11509- if (sys->picture_pool)
11510- picture_pool_Release(sys->picture_pool);
11511- else
11512- for (i = 0; i < sys->num_buffers; ++i)
11513- if (sys->pictures[i]) {
11514- mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer);
11515- picture_Release(sys->pictures[i]);
11516- }
11517+ if ((isp->in_pool = mmal_pool_create(isp->input->buffer_num, 0)) == NULL)
11518+ {
11519+ msg_Err(vd, "Failed to create input pool");
11520+ goto fail;
11521+ }
11522
11523- vlc_mutex_destroy(&sys->buffer_mutex);
11524- vlc_cond_destroy(&sys->buffer_cond);
11525- vlc_mutex_destroy(&sys->manage_mutex);
11526+ if ((isp->out_q = mmal_queue_create()) == NULL)
11527+ {
11528+ err = MMAL_ENOMEM;
11529+ goto fail;
11530+ }
11531
11532- if (sys->native_interlaced) {
11533- if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
11534- response[18] != '0')
11535- msg_Warn(vd, "Could not reset hvs field mode");
11536+ display_set_format(vd, isp->output->format, true);
11537+
11538+ if ((err = port_parameter_set_bool(isp->output, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
11539+ goto fail;
11540+
11541+ if ((err = mmal_port_format_commit(isp->output)) != MMAL_SUCCESS) {
11542+ msg_Err(vd, "Failed to set ISP input format");
11543+ goto fail;
11544 }
11545
11546- free(sys->pictures);
11547- free(sys);
11548+ isp->output->buffer_size = isp->output->buffer_size_recommended;
11549+ isp->output->buffer_num = 2;
11550+ isp->output->userdata = (void *)vd;
11551+
11552+ if ((isp->out_pool = mmal_port_pool_create(isp->output, isp->output->buffer_num, isp->output->buffer_size)) == NULL)
11553+ {
11554+ msg_Err(vd, "Failed to make ISP port pool");
11555+ goto fail;
11556+ }
11557+
11558+ mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp);
11559+
11560+ if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS)
11561+ goto fail;
11562+
11563+ return MMAL_SUCCESS;
11564
11565- bcm_host_deinit();
11566+fail:
11567+ isp_close(vd, vd_sys);
11568+ return err;
11569 }
11570
11571-static inline uint32_t align(uint32_t x, uint32_t y) {
11572- uint32_t mod = x % y;
11573- if (mod == 0)
11574- return x;
11575+static MMAL_STATUS_T isp_check(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
11576+{
11577+ struct vout_isp_conf_s *const isp = &vd_sys->isp;
11578+ const bool has_isp = (isp->component != NULL);
11579+ const bool wants_isp = want_isp(vd);
11580+
11581+ if (has_isp == wants_isp)
11582+ {
11583+ // All OK - do nothing
11584+ }
11585+ else if (has_isp)
11586+ {
11587+ // ISP active but we don't want it
11588+ isp_flush(isp);
11589+
11590+ // Check we have everything back and then kill it
11591+ if (mmal_queue_length(isp->out_pool->queue) == isp->output->buffer_num)
11592+ isp_close(vd, vd_sys);
11593+ }
11594 else
11595- return x + y - mod;
11596+ {
11597+ // ISP closed but we want it
11598+ return isp_setup(vd, vd_sys);
11599+ }
11600+
11601+ return MMAL_SUCCESS;
11602+}
11603+
11604+/* TV service */
11605+static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
11606+ uint32_t param2);
11607+static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
11608+static int set_latency_target(vout_display_t *vd, bool enable);
11609+
11610+// Mmal
11611+static void maintain_phase_sync(vout_display_t *vd);
11612+
11613+
11614+
11615+static void vd_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
11616+{
11617+#if TRACE_ALL
11618+ vout_display_t * const vd = (vout_display_t *)port->userdata;
11619+ pic_ctx_mmal_t * ctx = buf->user_data;
11620+ msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf,
11621+ buf->flags, (long long)buf->pts);
11622+#else
11623+ VLC_UNUSED(port);
11624+#endif
11625+
11626+ mmal_buffer_header_release(buf);
11627+
11628+#if TRACE_ALL
11629+ msg_Dbg(vd, ">>> %s", __func__);
11630+#endif
11631+}
11632+
11633+static int query_resolution(vout_display_t *vd, const int display_id, unsigned *width, unsigned *height)
11634+{
11635+ TV_DISPLAY_STATE_T display_state = {0};
11636+ int ret = 0;
11637+
11638+ if (vc_tv_get_display_state_id(display_id, &display_state) == 0) {
11639+ msg_Dbg(vd, "State=%#x", display_state.state);
11640+ if (display_state.state & 0xFF) {
11641+ msg_Dbg(vd, "HDMI: %dx%d", display_state.display.hdmi.width, display_state.display.hdmi.height);
11642+ *width = display_state.display.hdmi.width;
11643+ *height = display_state.display.hdmi.height;
11644+ } else if (display_state.state & 0xFF00) {
11645+ msg_Dbg(vd, "SDTV: %dx%d", display_state.display.sdtv.width, display_state.display.sdtv.height);
11646+ *width = display_state.display.sdtv.width;
11647+ *height = display_state.display.sdtv.height;
11648+ } else {
11649+ msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
11650+ ret = -1;
11651+ }
11652+ } else {
11653+ msg_Warn(vd, "Failed to query display resolution");
11654+ ret = -1;
11655+ }
11656+
11657+ return ret;
11658+}
11659+
11660+static inline MMAL_RECT_T
11661+place_to_mmal_rect(const vout_display_place_t place)
11662+{
11663+ return (MMAL_RECT_T){
11664+ .x = place.x,
11665+ .y = place.y,
11666+ .width = place.width,
11667+ .height = place.height
11668+ };
11669+}
11670+
11671+static MMAL_RECT_T
11672+place_out(const vout_display_cfg_t * cfg,
11673+ const video_format_t * fmt,
11674+ const MMAL_RECT_T r)
11675+{
11676+ video_format_t tfmt;
11677+ vout_display_cfg_t tcfg;
11678+ vout_display_place_t place;
11679+
11680+ // Fix SAR if unknown
11681+ if (fmt->i_sar_den == 0 || fmt->i_sar_num == 0) {
11682+ tfmt = *fmt;
11683+ tfmt.i_sar_den = 1;
11684+ tfmt.i_sar_num = 1;
11685+ fmt = &tfmt;
11686+ }
11687+
11688+ // Override what VLC thinks might be going on with display size
11689+ // if we know better
11690+ if (r.width != 0 && r.height != 0)
11691+ {
11692+ tcfg = *cfg;
11693+ tcfg.display.width = r.width;
11694+ tcfg.display.height = r.height;
11695+ cfg = &tcfg;
11696+ }
11697+
11698+ vout_display_PlacePicture(&place, fmt, cfg, false);
11699+
11700+ place.x += r.x;
11701+ place.y += r.y;
11702+
11703+ return place_to_mmal_rect(place);
11704+}
11705+
11706+static MMAL_RECT_T
11707+rect_transform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t)
11708+{
11709+ if (is_transform_transpose(t))
11710+ s = rect_transpose(s);
11711+ if (is_transform_hflip(t))
11712+ s = rect_hflip(s, c);
11713+ if (is_transform_vflip(t) != 0)
11714+ s = rect_vflip(s, c);
11715+ return s;
11716+}
11717+
11718+static void
11719+place_dest_rect(vout_display_t * const vd,
11720+ const vout_display_cfg_t * const cfg,
11721+ const video_format_t * fmt)
11722+{
11723+ vout_display_sys_t * const sys = vd->sys;
11724+ sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect),
11725+ sys->display_rect, sys->dest_transform);
11726+}
11727+
11728+static void
11729+place_spu_rect(vout_display_t * const vd,
11730+ const vout_display_cfg_t * const cfg,
11731+ const video_format_t * fmt)
11732+{
11733+ vout_display_sys_t * const sys = vd->sys;
11734+ static const MMAL_RECT_T r0 = {0};
11735+
11736+ sys->spu_rect = place_out(cfg, fmt, r0);
11737+ sys->spu_rect.x = 0;
11738+ sys->spu_rect.y = 0;
11739+
11740+ // Copy place override logic for spu pos from video_output.c
11741+ // This info doesn't appear to reside anywhere natively
11742+
11743+ if (fmt->i_width * fmt->i_height >= (unsigned int)(sys->spu_rect.width * sys->spu_rect.height)) {
11744+ sys->spu_rect.width = fmt->i_visible_width;
11745+ sys->spu_rect.height = fmt->i_visible_height;
11746+ }
11747+
11748+ if (ORIENT_IS_SWAP(fmt->orientation))
11749+ sys->spu_rect = rect_transpose(sys->spu_rect);
11750+}
11751+
11752+static void
11753+place_rects(vout_display_t * const vd,
11754+ const vout_display_cfg_t * const cfg,
11755+ const video_format_t * fmt)
11756+{
11757+ place_dest_rect(vd, cfg, fmt);
11758+ place_spu_rect(vd, cfg, fmt);
11759+}
11760+
11761+static int
11762+set_input_region(vout_display_t * const vd, const video_format_t * const fmt)
11763+{
11764+ const vout_display_sys_t * const sys = vd->sys;
11765+ MMAL_DISPLAYREGION_T display_region = {
11766+ .hdr = {
11767+ .id = MMAL_PARAMETER_DISPLAYREGION,
11768+ .size = sizeof(MMAL_DISPLAYREGION_T)
11769+ },
11770+ .display_num = sys->display_id,
11771+ .fullscreen = MMAL_FALSE,
11772+ .transform = sys->video_transform,
11773+ .dest_rect = sys->dest_rect,
11774+ .src_rect = display_src_rect(vd, fmt),
11775+ .noaspect = MMAL_TRUE,
11776+ .mode = MMAL_DISPLAY_MODE_FILL,
11777+ .layer = sys->layer,
11778+ .alpha = 0xff | (sys->transparent ? 0 : (1 << 29)),
11779+ .set =
11780+ MMAL_DISPLAY_SET_NUM |
11781+ MMAL_DISPLAY_SET_FULLSCREEN |
11782+ MMAL_DISPLAY_SET_TRANSFORM |
11783+ MMAL_DISPLAY_SET_DEST_RECT |
11784+ MMAL_DISPLAY_SET_SRC_RECT |
11785+ MMAL_DISPLAY_SET_NOASPECT |
11786+ MMAL_DISPLAY_SET_MODE |
11787+ MMAL_DISPLAY_SET_LAYER |
11788+ MMAL_DISPLAY_SET_ALPHA
11789+ };
11790+ MMAL_STATUS_T status = mmal_port_parameter_set(sys->input, &display_region.hdr);
11791+ if (status != MMAL_SUCCESS) {
11792+ msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
11793+ status, mmal_status_to_string(status));
11794+ return -EINVAL;
11795+ }
11796+ return 0;
11797 }
11798
11799 static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
11800 const video_format_t *fmt)
11801 {
11802- vout_display_sys_t *sys = vd->sys;
11803- vout_display_place_t place;
11804- MMAL_DISPLAYREGION_T display_region;
11805+ vout_display_sys_t * const sys = vd->sys;
11806 MMAL_STATUS_T status;
11807
11808 if (!cfg && !fmt)
11809+ {
11810+ msg_Err(vd, "%s: Missing cfg & fmt", __func__);
11811 return -EINVAL;
11812+ }
11813+
11814+ isp_check(vd, sys);
11815
11816 if (fmt) {
11817 sys->input->format->es->video.par.num = fmt->i_sar_num;
11818@@ -412,30 +733,14 @@ static int configure_display(vout_displa
11819 if (!cfg)
11820 cfg = vd->cfg;
11821
11822- vout_display_PlacePicture(&place, fmt, cfg, false);
11823+ sys->video_transform = combine_transform(
11824+ vlc_to_mmal_transform(fmt->orientation), sys->display_transform);
11825
11826- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
11827- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
11828- display_region.fullscreen = MMAL_FALSE;
11829- display_region.src_rect.x = fmt->i_x_offset;
11830- display_region.src_rect.y = fmt->i_y_offset;
11831- display_region.src_rect.width = fmt->i_visible_width;
11832- display_region.src_rect.height = fmt->i_visible_height;
11833- display_region.dest_rect.x = place.x;
11834- display_region.dest_rect.y = place.y;
11835- display_region.dest_rect.width = place.width;
11836- display_region.dest_rect.height = place.height;
11837- display_region.layer = sys->layer;
11838- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
11839- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
11840- status = mmal_port_parameter_set(sys->input, &display_region.hdr);
11841- if (status != MMAL_SUCCESS) {
11842- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
11843- status, mmal_status_to_string(status));
11844+ place_rects(vd, cfg, fmt);
11845+
11846+ if (set_input_region(vd, fmt) != 0)
11847 return -EINVAL;
11848- }
11849
11850- show_background(vd, var_InheritBool(vd, MMAL_BLANK_BACKGROUND_NAME));
11851 sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME);
11852 sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED);
11853 if (sys->adjust_refresh_rate) {
11854@@ -446,204 +751,217 @@ static int configure_display(vout_displa
11855 return 0;
11856 }
11857
11858+static void kill_pool(vout_display_sys_t * const sys)
11859+{
11860+ if (sys->pic_pool != NULL) {
11861+ picture_pool_Release(sys->pic_pool);
11862+ sys->pic_pool = NULL;
11863+ }
11864+}
11865+
11866+// Actual picture pool for MMAL opaques is just a set of trivial containers
11867 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
11868 {
11869- vout_display_sys_t *sys = vd->sys;
11870- picture_resource_t picture_res;
11871- picture_pool_configuration_t picture_pool_cfg;
11872- video_format_t fmt = vd->fmt;
11873- MMAL_STATUS_T status;
11874- unsigned i;
11875+ vout_display_sys_t * const sys = vd->sys;
11876
11877- if (sys->picture_pool) {
11878- if (sys->num_buffers < count)
11879- msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures",
11880- count, sys->num_buffers);
11881+ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__,
11882+ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height);
11883
11884- goto out;
11885+ if (sys->pic_pool == NULL) {
11886+ sys->pic_pool = picture_pool_NewFromFormat(&vd->fmt, count);
11887 }
11888+ return sys->pic_pool;
11889+}
11890
11891- if (sys->opaque) {
11892- if (count <= NUM_ACTUAL_OPAQUE_BUFFERS)
11893- count = NUM_ACTUAL_OPAQUE_BUFFERS;
11894+static inline bool
11895+check_shape(vout_display_t * const vd, const picture_t * const p_pic)
11896+{
11897+ if (vd->fmt.i_width == p_pic->format.i_width &&
11898+ vd->fmt.i_height == p_pic->format.i_height)
11899+ return true;
11900+ return false;
11901+}
11902
11903- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
11904- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
11905- 1
11906- };
11907+static void vd_display(vout_display_t *vd, picture_t *p_pic,
11908+ subpicture_t *subpicture)
11909+{
11910+ vout_display_sys_t * const sys = vd->sys;
11911+ MMAL_STATUS_T err;
11912
11913- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
11914- if (status != MMAL_SUCCESS) {
11915- msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
11916- sys->input->name, status, mmal_status_to_string(status));
11917- goto out;
11918- }
11919+#if TRACE_ALL
11920+ {
11921+ char dbuf0[5];
11922+ msg_Dbg(vd, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %dx%d@%d,%d", __func__,
11923+ str_fourcc(dbuf0, p_pic->format.i_chroma), p_pic->format.i_width, p_pic->format.i_height,
11924+ p_pic->format.i_x_offset, p_pic->format.i_y_offset,
11925+ p_pic->format.i_visible_width, p_pic->format.i_visible_height,
11926+ p_pic->format.i_sar_num, p_pic->format.i_sar_den,
11927+ sys->dest_rect.width, sys->dest_rect.height, sys->dest_rect.x, sys->dest_rect.y);
11928 }
11929-
11930- if (count < sys->input->buffer_num_recommended)
11931- count = sys->input->buffer_num_recommended;
11932-
11933-#ifndef NDEBUG
11934- msg_Dbg(vd, "Creating picture pool with %u pictures", count);
11935 #endif
11936
11937- sys->input->buffer_num = count;
11938- status = mmal_port_enable(sys->input, input_port_cb);
11939- if (status != MMAL_SUCCESS) {
11940- msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
11941- sys->input->name, status, mmal_status_to_string(status));
11942- goto out;
11943+ // If we had subpics then we have attached them to the main pic in prepare
11944+ // so all we have to do here is delete the refs
11945+ if (subpicture != NULL) {
11946+ subpicture_Delete(subpicture);
11947 }
11948
11949- status = mmal_component_enable(sys->component);
11950- if (status != MMAL_SUCCESS) {
11951- msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
11952- sys->component->name, status, mmal_status_to_string(status));
11953- goto out;
11954+ if (!check_shape(vd, p_pic))
11955+ {
11956+ msg_Err(vd, "Pic/fmt shape mismatch");
11957+ goto fail;
11958+ }
11959+
11960+ if (!sys->input->is_enabled &&
11961+ (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS)
11962+ {
11963+ msg_Err(vd, "Input port enable failed");
11964+ goto fail;
11965+ }
11966+ // Stuff into input
11967+ // We assume the BH is already set up with values reflecting pic date etc.
11968+ if (sys->copy_buf != NULL) {
11969+ MMAL_BUFFER_HEADER_T *const buf = sys->copy_buf;
11970+ sys->copy_buf = NULL;
11971+#if TRACE_ALL
11972+ msg_Dbg(vd, "--- %s: Copy stuff", __func__);
11973+#endif
11974+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
11975+ {
11976+ mmal_buffer_header_release(buf);
11977+ msg_Err(vd, "Send copy buffer to render input failed");
11978+ goto fail;
11979+ }
11980 }
11981-
11982- sys->num_buffers = count;
11983- sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers,
11984- sys->input->buffer_size);
11985- if (!sys->pool) {
11986- msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
11987- count, sys->input->buffer_size);
11988- goto out;
11989+ else if (sys->isp.pending) {
11990+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q);
11991+ sys->isp.pending = false;
11992+#if TRACE_ALL
11993+ msg_Dbg(vd, "--- %s: ISP stuff", __func__);
11994+#endif
11995+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
11996+ {
11997+ mmal_buffer_header_release(buf);
11998+ msg_Err(vd, "Send ISP buffer to render input failed");
11999+ goto fail;
12000+ }
12001 }
12002-
12003- memset(&picture_res, 0, sizeof(picture_resource_t));
12004- sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *));
12005- for (i = 0; i < sys->num_buffers; ++i) {
12006- picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
12007- picture_res.p_sys->owner = (vlc_object_t *)vd;
12008- picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue);
12009-
12010- sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res);
12011- if (!sys->pictures[i]) {
12012- msg_Err(vd, "Failed to create picture");
12013- free(picture_res.p_sys);
12014- goto out;
12015+ else
12016+ {
12017+ MMAL_BUFFER_HEADER_T *const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->pool);
12018+ if (pic_buf == NULL)
12019+ {
12020+ msg_Err(vd, "Replicated buffer get fail");
12021+ goto fail;
12022 }
12023
12024- sys->pictures[i]->i_planes = sys->i_planes;
12025- memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t));
12026- }
12027
12028- memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
12029- picture_pool_cfg.picture_count = sys->num_buffers;
12030- picture_pool_cfg.picture = sys->pictures;
12031- picture_pool_cfg.lock = mmal_picture_lock;
12032+ // If dimensions have chnaged then fix that
12033+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
12034+ {
12035+ msg_Dbg(vd, "Reset port format");
12036+
12037+ // HVS can deal with on-line dimension changes
12038+ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
12039+ msg_Warn(vd, "Input format commit failed");
12040+ }
12041
12042- sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
12043- if (!sys->picture_pool) {
12044- msg_Err(vd, "Failed to create picture pool");
12045- goto out;
12046+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
12047+ {
12048+ mmal_buffer_header_release(pic_buf);
12049+ msg_Err(vd, "Send buffer to input failed");
12050+ goto fail;
12051+ }
12052 }
12053
12054-out:
12055- return sys->picture_pool;
12056-}
12057-
12058-static void vd_prepare(vout_display_t *vd, picture_t *picture,
12059- subpicture_t *subpicture)
12060-{
12061- vout_display_sys_t *sys = vd->sys;
12062- picture_sys_t *pic_sys = picture->p_sys;
12063-
12064- if (!sys->adjust_refresh_rate || pic_sys->displayed)
12065- return;
12066-
12067- /* Apply the required phase_offset to the picture, so that vd_display()
12068- * will be called at the corrected time from the core */
12069- picture->date += sys->phase_offset;
12070-}
12071-
12072-static void vd_display(vout_display_t *vd, picture_t *picture,
12073- subpicture_t *subpicture)
12074-{
12075- vout_display_sys_t *sys = vd->sys;
12076- picture_sys_t *pic_sys = picture->p_sys;
12077- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
12078- MMAL_STATUS_T status;
12079-
12080- if (picture->format.i_frame_rate != sys->i_frame_rate ||
12081- picture->format.i_frame_rate_base != sys->i_frame_rate_base ||
12082- picture->b_progressive != sys->b_progressive ||
12083- picture->b_top_field_first != sys->b_top_field_first) {
12084- sys->b_top_field_first = picture->b_top_field_first;
12085- sys->b_progressive = picture->b_progressive;
12086- sys->i_frame_rate = picture->format.i_frame_rate;
12087- sys->i_frame_rate_base = picture->format.i_frame_rate_base;
12088- configure_display(vd, NULL, &picture->format);
12089- }
12090-
12091- if (!pic_sys->displayed || !sys->opaque) {
12092- buffer->cmd = 0;
12093- buffer->length = sys->input->buffer_size;
12094- buffer->user_data = picture;
12095-
12096- status = mmal_port_send_buffer(sys->input, buffer);
12097- if (status == MMAL_SUCCESS)
12098- atomic_fetch_add(&sys->buffers_in_transit, 1);
12099-
12100- if (status != MMAL_SUCCESS) {
12101- msg_Err(vd, "Failed to send buffer to input port. Frame dropped");
12102- picture_Release(picture);
12103+ {
12104+ unsigned int sub_no = 0;
12105+ MMAL_BUFFER_HEADER_T **psub_bufs2 = sys->subpic_bufs;
12106+ const bool is_mmal_pic = hw_mmal_pic_is_mmal(p_pic);
12107+
12108+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
12109+ int rv;
12110+ MMAL_BUFFER_HEADER_T * const sub_buf = !is_mmal_pic ? NULL :
12111+ hw_mmal_pic_sub_buf_get(p_pic, sub_no);
12112+
12113+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd),
12114+ sub_buf != NULL ? sub_buf : *psub_bufs2++,
12115+ &sys->subs[sub_no].sub,
12116+ &p_pic->format,
12117+ &sys->dest_rect,
12118+ sys->display_transform,
12119+ p_pic->date)) == 0)
12120+ break;
12121+ else if (rv < 0)
12122+ goto fail;
12123 }
12124-
12125- pic_sys->displayed = true;
12126- } else {
12127- picture_Release(picture);
12128 }
12129
12130- display_subpicture(vd, subpicture);
12131+fail:
12132+ for (unsigned int i = 0; i != SUBS_MAX && sys->subpic_bufs[i] != NULL; ++i) {
12133+ mmal_buffer_header_release(sys->subpic_bufs[i]);
12134+ sys->subpic_bufs[i] = NULL;
12135+ }
12136
12137- if (subpicture)
12138- subpicture_Delete(subpicture);
12139+ picture_Release(p_pic);
12140
12141 if (sys->next_phase_check == 0 && sys->adjust_refresh_rate)
12142 maintain_phase_sync(vd);
12143 sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL;
12144-
12145- if (sys->opaque) {
12146- vlc_mutex_lock(&sys->buffer_mutex);
12147- while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT)
12148- vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex);
12149- vlc_mutex_unlock(&sys->buffer_mutex);
12150- }
12151 }
12152
12153 static int vd_control(vout_display_t *vd, int query, va_list args)
12154 {
12155- vout_display_sys_t *sys = vd->sys;
12156- vout_display_cfg_t cfg;
12157- const vout_display_cfg_t *tmp_cfg;
12158+ vout_display_sys_t * const sys = vd->sys;
12159 int ret = VLC_EGENERIC;
12160+ VLC_UNUSED(args);
12161
12162 switch (query) {
12163- case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
12164- tmp_cfg = va_arg(args, const vout_display_cfg_t *);
12165- if (tmp_cfg->display.width == sys->display_width &&
12166- tmp_cfg->display.height == sys->display_height) {
12167- cfg = *vd->cfg;
12168- cfg.display.width = sys->display_width;
12169- cfg.display.height = sys->display_height;
12170- if (configure_display(vd, &cfg, NULL) >= 0)
12171- ret = VLC_SUCCESS;
12172- }
12173- break;
12174-
12175 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
12176 case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
12177- if (configure_display(vd, NULL, &vd->source) >= 0)
12178+ if (configure_display(vd, vd->cfg, &vd->source) >= 0)
12179 ret = VLC_SUCCESS;
12180 break;
12181
12182- case VOUT_DISPLAY_RESET_PICTURES:
12183- vlc_assert_unreachable();
12184 case VOUT_DISPLAY_CHANGE_ZOOM:
12185- msg_Warn(vd, "Unsupported control query %d", query);
12186+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
12187+ case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
12188+ {
12189+ const vout_display_cfg_t * const cfg = va_arg(args, const vout_display_cfg_t *);
12190+
12191+ if (configure_display(vd, cfg, &vd->source) >= 0)
12192+ ret = VLC_SUCCESS;
12193+ break;
12194+ }
12195+
12196+ case VOUT_DISPLAY_RESET_PICTURES:
12197+ msg_Warn(vd, "Reset Pictures");
12198+ kill_pool(sys);
12199+ vd->fmt = vd->source; // Take (nearly) whatever source wants to give us
12200+ vd->fmt.i_chroma = req_chroma(vd); // Adjust chroma to something we can actaully deal with
12201+ ret = VLC_SUCCESS;
12202+ break;
12203+
12204+ case VOUT_DISPLAY_CHANGE_MMAL_HIDE:
12205+ {
12206+ MMAL_STATUS_T err;
12207+ unsigned int i;
12208+
12209+ msg_Dbg(vd, "Hide display");
12210+
12211+ for (i = 0; i != SUBS_MAX; ++i)
12212+ hw_mmal_subpic_flush(VLC_OBJECT(vd), &sys->subs[i].sub);
12213+
12214+ if (sys->input->is_enabled &&
12215+ (err = mmal_port_disable(sys->input)) != MMAL_SUCCESS)
12216+ {
12217+ msg_Err(vd, "Unable to disable port: err=%d", err);
12218+ break;
12219+ }
12220+ sys->force_config = true;
12221+ ret = VLC_SUCCESS;
12222 break;
12223+ }
12224
12225 default:
12226 msg_Warn(vd, "Unknown control query %d", query);
12227@@ -653,79 +971,207 @@ static int vd_control(vout_display_t *vd
12228 return ret;
12229 }
12230
12231+static void set_display_windows(vout_display_t *const vd, vout_display_sys_t *const sys)
12232+{
12233+ unsigned int width, height;
12234+ if (query_resolution(vd, sys->display_id, &width, &height) < 0) {
12235+ width = vd->cfg->display.width;
12236+ height = vd->cfg->display.height;
12237+ }
12238+ sys->display_rect = (MMAL_RECT_T){0, 0, width, height};
12239+
12240+ sys->win_rect = (sys->req_win.width != 0) ?
12241+ sys->req_win :
12242+ is_transform_transpose(sys->display_transform) ?
12243+ rect_transpose(sys->display_rect) : sys->display_rect;
12244+}
12245+
12246 static void vd_manage(vout_display_t *vd)
12247 {
12248- vout_display_sys_t *sys = vd->sys;
12249- unsigned width, height;
12250+ vout_display_sys_t *const sys = vd->sys;
12251
12252 vlc_mutex_lock(&sys->manage_mutex);
12253
12254 if (sys->need_configure_display) {
12255- close_dmx(vd);
12256- sys->dmx_handle = vc_dispmanx_display_open(0);
12257-
12258- if (query_resolution(vd, &width, &height) >= 0) {
12259- sys->display_width = width;
12260- sys->display_height = height;
12261- vout_display_SendEventDisplaySize(vd, width, height);
12262- }
12263-
12264 sys->need_configure_display = false;
12265+ set_display_windows(vd, sys);
12266 }
12267
12268 vlc_mutex_unlock(&sys->manage_mutex);
12269 }
12270
12271-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
12272+
12273+static int attach_subpics(vout_display_t * const vd, vout_display_sys_t * const sys,
12274+ subpicture_t * const subpicture)
12275 {
12276- vout_display_t *vd = (vout_display_t *)port->userdata;
12277- MMAL_STATUS_T status;
12278+ unsigned int n = 0;
12279
12280- if (buffer->cmd == MMAL_EVENT_ERROR) {
12281- status = *(uint32_t *)buffer->data;
12282- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
12283+ if (sys->vzc == NULL) {
12284+ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
12285+ {
12286+ msg_Err(vd, "Failed to allocate VZC");
12287+ return VLC_ENOMEM;
12288+ }
12289 }
12290
12291- mmal_buffer_header_release(buffer);
12292+ // Attempt to import the subpics
12293+ for (subpicture_t * spic = subpicture; spic != NULL; spic = spic->p_next)
12294+ {
12295+ for (subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) {
12296+ picture_t *const src = sreg->p_picture;
12297+
12298+#if TRACE_ALL
12299+ char dbuf0[5];
12300+ msg_Dbg(vd, " [%p:%p] Pos=%d,%d max=%dx%d, src=%dx%d/%dx%d o:%d, spu=%d,%d:%dx%d, vd->fmt=%dx%d/%dx%d, vd->source=%dx%d/%dx%d, cfg=%dx%d, zoom=%d/%d, Alpha=%d, Fmt=%s", src, src->p[0].p_pixels,
12301+ sreg->i_x, sreg->i_y,
12302+ sreg->i_max_width, sreg->i_max_height,
12303+ src->format.i_visible_width, src->format.i_visible_height,
12304+ src->format.i_width, src->format.i_height,
12305+ src->format.orientation,
12306+ sys->spu_rect.x, sys->spu_rect.y, sys->spu_rect.width, sys->spu_rect.height,
12307+ vd->fmt.i_visible_width, vd->fmt.i_visible_height,
12308+ vd->fmt.i_width, vd->fmt.i_height,
12309+ vd->source.i_visible_width, vd->source.i_visible_height,
12310+ vd->source.i_width, vd->source.i_height,
12311+ vd->cfg->display.width, vd->cfg->display.height,
12312+ vd->cfg->zoom.num, vd->cfg->zoom.den,
12313+ sreg->i_alpha,
12314+ str_fourcc(dbuf0, src->format.i_chroma));
12315+#endif
12316+
12317+ // At this point I think the subtitles are being placed in the
12318+ // coord space of the placed rectangle in the cfg display space
12319+ if ((sys->subpic_bufs[n] = hw_mmal_vzc_buf_from_pic(sys->vzc,
12320+ src,
12321+ (MMAL_RECT_T){.width = sys->spu_rect.width, .height=sys->spu_rect.height},
12322+ sreg->i_x, sreg->i_y,
12323+ sreg->i_alpha,
12324+ n == 0)) == NULL)
12325+ {
12326+ msg_Err(vd, "Failed to allocate vzc buffer for subpic");
12327+ return VLC_ENOMEM;
12328+ }
12329+
12330+ if (++n == SUBS_MAX)
12331+ return VLC_SUCCESS;
12332+ }
12333+ }
12334+ return VLC_SUCCESS;
12335 }
12336
12337-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
12338+
12339+static void vd_prepare(vout_display_t *vd, picture_t *p_pic,
12340+#if VLC_VER_3
12341+ subpicture_t *subpicture
12342+#else
12343+ subpicture_t *subpicture, vlc_tick_t date
12344+#endif
12345+ )
12346 {
12347- vout_display_t *vd = (vout_display_t *)port->userdata;
12348+ MMAL_STATUS_T err;
12349+ vout_display_sys_t * const sys = vd->sys;
12350+
12351+ vd_manage(vd);
12352+
12353+ if (!check_shape(vd, p_pic))
12354+ return;
12355+
12356+ if (sys->force_config ||
12357+ p_pic->format.i_frame_rate != sys->i_frame_rate ||
12358+ p_pic->format.i_frame_rate_base != sys->i_frame_rate_base ||
12359+ p_pic->b_progressive != sys->b_progressive ||
12360+ p_pic->b_top_field_first != sys->b_top_field_first)
12361+ {
12362+ sys->force_config = false;
12363+ sys->b_top_field_first = p_pic->b_top_field_first;
12364+ sys->b_progressive = p_pic->b_progressive;
12365+ sys->i_frame_rate = p_pic->format.i_frame_rate;
12366+ sys->i_frame_rate_base = p_pic->format.i_frame_rate_base;
12367+ configure_display(vd, NULL, &vd->source);
12368+ }
12369+
12370+ // Subpics can either turn up attached to the main pic or in the
12371+ // subpic list here - if they turn up here then process into temp
12372+ // buffers
12373+ if (subpicture != NULL) {
12374+ attach_subpics(vd, sys, subpicture);
12375+ }
12376+
12377+ // *****
12378+ if (want_copy(vd)) {
12379+ if (sys->copy_buf != NULL) {
12380+ msg_Err(vd, "Copy buf not NULL");
12381+ mmal_buffer_header_release(sys->copy_buf);
12382+ sys->copy_buf = NULL;
12383+ }
12384+
12385+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->copy_pool->queue);
12386+ // Copy 2d
12387+ hw_mmal_copy_pic_to_buf(buf->data, &buf->length, sys->input->format, p_pic);
12388+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
12389+
12390+ sys->copy_buf = buf;
12391+ }
12392+
12393+ if (isp_check(vd, sys) != MMAL_SUCCESS) {
12394+ return;
12395+ }
12396+
12397+ if (want_isp(vd))
12398+ {
12399+ struct vout_isp_conf_s * const isp = &sys->isp;
12400+ MMAL_BUFFER_HEADER_T * buf;
12401+
12402+ // This should be empty - make it so if it isn't
12403+ isp_empty_out_q(isp);
12404+ isp->pending = false;
12405+
12406+ // Stuff output
12407+ if (isp_prepare(vd, isp) != MMAL_SUCCESS)
12408+ return;
12409+
12410+ if ((buf = hw_mmal_pic_buf_replicated(p_pic, isp->in_pool)) == NULL)
12411+ {
12412+ msg_Err(vd, "Pic has no attached buffer");
12413+ return;
12414+ }
12415+
12416+ if ((err = mmal_port_send_buffer(isp->input, buf)) != MMAL_SUCCESS)
12417+ {
12418+ msg_Err(vd, "Send buffer to input failed");
12419+ mmal_buffer_header_release(buf);
12420+ return;
12421+ }
12422+
12423+ isp->pending = true;
12424+ }
12425+
12426+#if 0
12427+ VLC_UNUSED(date);
12428 vout_display_sys_t *sys = vd->sys;
12429- picture_t *picture = (picture_t *)buffer->user_data;
12430+ picture_sys_t *pic_sys = picture->p_sys;
12431
12432- if (picture)
12433- picture_Release(picture);
12434+ if (!sys->adjust_refresh_rate || pic_sys->displayed)
12435+ return;
12436
12437- vlc_mutex_lock(&sys->buffer_mutex);
12438- atomic_fetch_sub(&sys->buffers_in_transit, 1);
12439- vlc_cond_signal(&sys->buffer_cond);
12440- vlc_mutex_unlock(&sys->buffer_mutex);
12441+ /* Apply the required phase_offset to the picture, so that vd_display()
12442+ * will be called at the corrected time from the core */
12443+ picture->date += sys->phase_offset;
12444+#endif
12445 }
12446
12447-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
12448+
12449+static void vd_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
12450 {
12451- TV_DISPLAY_STATE_T display_state;
12452- int ret = 0;
12453+ vout_display_t *vd = (vout_display_t *)port->userdata;
12454+ MMAL_STATUS_T status;
12455
12456- if (vc_tv_get_display_state(&display_state) == 0) {
12457- if (display_state.state & 0xFF) {
12458- *width = display_state.display.hdmi.width;
12459- *height = display_state.display.hdmi.height;
12460- } else if (display_state.state & 0xFF00) {
12461- *width = display_state.display.sdtv.width;
12462- *height = display_state.display.sdtv.height;
12463- } else {
12464- msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
12465- ret = -1;
12466- }
12467- } else {
12468- msg_Warn(vd, "Failed to query display resolution");
12469- ret = -1;
12470+ if (buffer->cmd == MMAL_EVENT_ERROR) {
12471+ status = *(uint32_t *)buffer->data;
12472+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
12473 }
12474
12475- return ret;
12476+ mmal_buffer_header_release(buffer);
12477 }
12478
12479 static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
12480@@ -780,9 +1226,9 @@ static void adjust_refresh_rate(vout_dis
12481 double best_score, score;
12482 int i;
12483
12484- vc_tv_get_display_state(&display_state);
12485+ vc_tv_get_display_state_id(sys->display_id, &display_state);
12486 if(display_state.display.hdmi.mode != HDMI_MODE_OFF) {
12487- num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group,
12488+ num_modes = vc_tv_hdmi_get_supported_modes_new_id(sys->display_id, display_state.display.hdmi.group,
12489 supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL);
12490
12491 for (i = 0; i < num_modes; ++i) {
12492@@ -810,7 +1256,7 @@ static void adjust_refresh_rate(vout_dis
12493 if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) {
12494 msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32,
12495 supported_modes[best_id].frame_rate);
12496- vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI,
12497+ vc_tv_hdmi_power_on_explicit_new_id(sys->display_id, HDMI_MODE_HDMI,
12498 supported_modes[best_id].group,
12499 supported_modes[best_id].code);
12500 }
12501@@ -828,148 +1274,12 @@ static void adjust_refresh_rate(vout_dis
12502 }
12503 }
12504
12505-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture)
12506-{
12507- vout_display_sys_t *sys = vd->sys;
12508- struct dmx_region_t **dmx_region = &sys->dmx_region;
12509- struct dmx_region_t *unused_dmx_region;
12510- DISPMANX_UPDATE_HANDLE_T update = 0;
12511- picture_t *picture;
12512- video_format_t *fmt;
12513- struct dmx_region_t *dmx_region_next;
12514-
12515- if(subpicture) {
12516- subpicture_region_t *region = subpicture->p_region;
12517- while(region) {
12518- picture = region->p_picture;
12519- fmt = &region->fmt;
12520-
12521- if(!*dmx_region) {
12522- if(!update)
12523- update = vc_dispmanx_update_start(10);
12524- *dmx_region = dmx_region_new(vd, update, region);
12525- } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) ||
12526- ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) ||
12527- ((*dmx_region)->pos_x != region->i_x) ||
12528- ((*dmx_region)->pos_y != region->i_y) ||
12529- ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) {
12530- dmx_region_next = (*dmx_region)->next;
12531- if(!update)
12532- update = vc_dispmanx_update_start(10);
12533- dmx_region_delete(*dmx_region, update);
12534- *dmx_region = dmx_region_new(vd, update, region);
12535- (*dmx_region)->next = dmx_region_next;
12536- } else if((*dmx_region)->picture != picture) {
12537- if(!update)
12538- update = vc_dispmanx_update_start(10);
12539- dmx_region_update(*dmx_region, update, picture);
12540- }
12541-
12542- dmx_region = &(*dmx_region)->next;
12543- region = region->p_next;
12544- }
12545- }
12546-
12547- /* Remove remaining regions */
12548- unused_dmx_region = *dmx_region;
12549- while(unused_dmx_region) {
12550- dmx_region_next = unused_dmx_region->next;
12551- if(!update)
12552- update = vc_dispmanx_update_start(10);
12553- dmx_region_delete(unused_dmx_region, update);
12554- unused_dmx_region = dmx_region_next;
12555- }
12556- *dmx_region = NULL;
12557-
12558- if(update)
12559- vc_dispmanx_update_submit_sync(update);
12560-}
12561-
12562-static void close_dmx(vout_display_t *vd)
12563-{
12564- vout_display_sys_t *sys = vd->sys;
12565- DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10);
12566- struct dmx_region_t *dmx_region = sys->dmx_region;
12567- struct dmx_region_t *dmx_region_next;
12568-
12569- while(dmx_region) {
12570- dmx_region_next = dmx_region->next;
12571- dmx_region_delete(dmx_region, update);
12572- dmx_region = dmx_region_next;
12573- }
12574-
12575- vc_dispmanx_update_submit_sync(update);
12576- sys->dmx_region = NULL;
12577-
12578- show_background(vd, false);
12579-
12580- vc_dispmanx_display_close(sys->dmx_handle);
12581- sys->dmx_handle = DISPMANX_NO_HANDLE;
12582-}
12583-
12584-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
12585- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region)
12586-{
12587- vout_display_sys_t *sys = vd->sys;
12588- video_format_t *fmt = &region->fmt;
12589- struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t));
12590- uint32_t image_handle;
12591-
12592- dmx_region->pos_x = region->i_x;
12593- dmx_region->pos_y = region->i_y;
12594-
12595- vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width,
12596- fmt->i_visible_height);
12597- vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16,
12598- fmt->i_visible_height << 16);
12599- vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y,
12600- fmt->i_visible_width, fmt->i_visible_height);
12601-
12602- dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32,
12603- dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16),
12604- dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16),
12605- &image_handle);
12606- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
12607- region->p_picture->p[0].i_pitch,
12608- region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect);
12609-
12610- dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX;
12611- dmx_region->alpha.opacity = region->i_alpha;
12612- dmx_region->alpha.mask = DISPMANX_NO_HANDLE;
12613- dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle,
12614- sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource,
12615- &dmx_region->src_rect, DISPMANX_PROTECTION_NONE,
12616- &dmx_region->alpha, NULL, VC_IMAGE_ROT0);
12617-
12618- dmx_region->next = NULL;
12619- dmx_region->picture = region->p_picture;
12620-
12621- return dmx_region;
12622-}
12623-
12624-static void dmx_region_update(struct dmx_region_t *dmx_region,
12625- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
12626-{
12627- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
12628- picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect);
12629- vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource);
12630- dmx_region->picture = picture;
12631-}
12632-
12633-static void dmx_region_delete(struct dmx_region_t *dmx_region,
12634- DISPMANX_UPDATE_HANDLE_T update)
12635-{
12636- vc_dispmanx_element_remove(update, dmx_region->element);
12637- vc_dispmanx_resource_delete(dmx_region->resource);
12638- free(dmx_region);
12639-}
12640-
12641 static void maintain_phase_sync(vout_display_t *vd)
12642 {
12643 MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = {
12644 .hdr = { MMAL_PARAMETER_VIDEO_RENDER_STATS, sizeof(render_stats) },
12645 };
12646- int32_t frame_duration = 1000000 /
12647+ int32_t frame_duration = CLOCK_FREQ /
12648 ((double)vd->sys->i_frame_rate /
12649 vd->sys->i_frame_rate_base);
12650 vout_display_sys_t *sys = vd->sys;
12651@@ -1012,32 +1322,436 @@ static void maintain_phase_sync(vout_dis
12652 }
12653 }
12654
12655-static void show_background(vout_display_t *vd, bool enable)
12656+static void CloseMmalVout(vlc_object_t *object)
12657 {
12658- vout_display_sys_t *sys = vd->sys;
12659- uint32_t image_ptr, color = 0xFF000000;
12660- VC_RECT_T dst_rect, src_rect;
12661- DISPMANX_UPDATE_HANDLE_T update;
12662-
12663- if (enable && !sys->bkg_element) {
12664- sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1,
12665- &image_ptr);
12666- vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1);
12667- vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32,
12668- sizeof(color), &color, &dst_rect);
12669- vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16);
12670- vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0);
12671- update = vc_dispmanx_update_start(0);
12672- sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle,
12673- sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect,
12674- DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0);
12675- vc_dispmanx_update_submit_sync(update);
12676- } else if (!enable && sys->bkg_element) {
12677- update = vc_dispmanx_update_start(0);
12678- vc_dispmanx_element_remove(update, sys->bkg_element);
12679- vc_dispmanx_resource_delete(sys->bkg_resource);
12680- vc_dispmanx_update_submit_sync(update);
12681- sys->bkg_element = DISPMANX_NO_HANDLE;
12682- sys->bkg_resource = DISPMANX_NO_HANDLE;
12683+ vout_display_t * const vd = (vout_display_t *)object;
12684+ vout_display_sys_t * const sys = vd->sys;
12685+ char response[20]; /* answer is hvs_update_fields=%1d */
12686+
12687+#if TRACE_ALL
12688+ msg_Dbg(vd, "<<< %s", __func__);
12689+#endif
12690+
12691+ kill_pool(sys);
12692+
12693+ vc_tv_unregister_callback_full(tvservice_cb, vd);
12694+
12695+ // Shouldn't be anything here - but just in case
12696+ for (unsigned int i = 0; i != SUBS_MAX; ++i)
12697+ if (sys->subpic_bufs[i] != NULL)
12698+ mmal_buffer_header_release(sys->subpic_bufs[i]);
12699+
12700+ for (unsigned int i = 0; i != SUBS_MAX; ++i) {
12701+ vout_subpic_t * const sub = sys->subs + i;
12702+ if (sub->component != NULL) {
12703+ hw_mmal_subpic_close(VLC_OBJECT(vd), &sub->sub);
12704+ if (sub->component->control->is_enabled)
12705+ mmal_port_disable(sub->component->control);
12706+ if (sub->component->is_enabled)
12707+ mmal_component_disable(sub->component);
12708+ mmal_component_release(sub->component);
12709+ sub->component = NULL;
12710+ }
12711 }
12712+
12713+ if (sys->input && sys->input->is_enabled)
12714+ mmal_port_disable(sys->input);
12715+
12716+ if (sys->component && sys->component->control->is_enabled)
12717+ mmal_port_disable(sys->component->control);
12718+
12719+ if (sys->copy_buf != NULL)
12720+ mmal_buffer_header_release(sys->copy_buf);
12721+
12722+ if (sys->input != NULL && sys->copy_pool != NULL)
12723+ mmal_port_pool_destroy(sys->input, sys->copy_pool);
12724+
12725+ if (sys->component && sys->component->is_enabled)
12726+ mmal_component_disable(sys->component);
12727+
12728+ if (sys->pool)
12729+ mmal_pool_destroy(sys->pool);
12730+
12731+ if (sys->component)
12732+ mmal_component_release(sys->component);
12733+
12734+ isp_close(vd, sys);
12735+
12736+ hw_mmal_vzc_pool_release(sys->vzc);
12737+
12738+ vlc_mutex_destroy(&sys->manage_mutex);
12739+
12740+ if (sys->native_interlaced) {
12741+ if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
12742+ response[18] != '0')
12743+ msg_Warn(vd, "Could not reset hvs field mode");
12744+ }
12745+
12746+ cma_vcsm_exit(sys->init_type);;
12747+
12748+ free(sys);
12749+
12750+#if TRACE_ALL
12751+ msg_Dbg(vd, ">>> %s", __func__);
12752+#endif
12753+}
12754+
12755+
12756+static const struct {
12757+ const char * name;
12758+ int num;
12759+} display_name_to_num[] = {
12760+ {"auto", -1},
12761+ {"hdmi-1", DISPMANX_ID_HDMI0},
12762+ {"hdmi-2", DISPMANX_ID_HDMI1},
12763+ {NULL, -2}
12764+};
12765+
12766+static const struct {
12767+ const char * name;
12768+ int transform_num;
12769+} transform_name_to_num[] = {
12770+ {"auto", -1},
12771+ {"0", MMAL_DISPLAY_ROT0},
12772+ {"hflip", MMAL_DISPLAY_MIRROR_ROT0},
12773+ {"vflip", MMAL_DISPLAY_MIRROR_ROT180},
12774+ {"180", MMAL_DISPLAY_ROT180},
12775+ {"transpose", MMAL_DISPLAY_MIRROR_ROT90},
12776+ {"270", MMAL_DISPLAY_ROT270},
12777+ {"90", MMAL_DISPLAY_ROT90},
12778+ {"antitranspose", MMAL_DISPLAY_MIRROR_ROT270},
12779+ {NULL, -2}
12780+};
12781+
12782+static int find_display_num(const char * const name)
12783+{
12784+ unsigned int i;
12785+ for (i = 0; display_name_to_num[i].name != NULL && strcasecmp(display_name_to_num[i].name, name) != 0; ++i)
12786+ /* Loop */;
12787+ return display_name_to_num[i].num;
12788+}
12789+
12790+static int find_transform_num(const char * const name)
12791+{
12792+ unsigned int i;
12793+ for (i = 0; transform_name_to_num[i].name != NULL && strcasecmp(transform_name_to_num[i].name, name) != 0; ++i)
12794+ /* Loop */;
12795+ return transform_name_to_num[i].transform_num;
12796+}
12797+
12798+#if HAVE_X11_XLIB_H
12799+#include <X11/Xlib.h>
12800+#include <X11/extensions/Xrandr.h>
12801+static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd)
12802+{
12803+ Display * const x = XOpenDisplay(NULL);
12804+ Rotation cur_rot = 0;
12805+ MMAL_DISPLAYTRANSFORM_T trans;
12806+
12807+ if (x == NULL)
12808+ return MMAL_DISPLAY_ROT0;
12809+
12810+ XRRRotations(x, 0, &cur_rot);
12811+ XCloseDisplay(x);
12812+
12813+ // Convert to MMAL
12814+ // xrandr seems to rotate the other way to mmal
12815+
12816+ switch (cur_rot)
12817+ {
12818+ case 0:
12819+ case RR_Rotate_0:
12820+ trans = MMAL_DISPLAY_ROT0;
12821+ break;
12822+ case RR_Rotate_90:
12823+ trans = MMAL_DISPLAY_ROT270;
12824+ break;
12825+ case RR_Rotate_180:
12826+ trans = MMAL_DISPLAY_ROT180;
12827+ break;
12828+ case RR_Rotate_270:
12829+ trans = MMAL_DISPLAY_ROT90;
12830+ break;
12831+ case RR_Reflect_X:
12832+ trans = MMAL_DISPLAY_MIRROR_ROT0;
12833+ break;
12834+ case RR_Reflect_Y:
12835+ trans = MMAL_DISPLAY_MIRROR_ROT180;
12836+ break;
12837+ default:
12838+ msg_Info(vd, "Unexpected X rotation value: %#x", cur_rot);
12839+ trans = MMAL_DISPLAY_ROT0;
12840+ break;
12841+ }
12842+
12843+ return trans;
12844+}
12845+#else
12846+static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd)
12847+{
12848+ VLC_UNUSED(vd);
12849+ return MMAL_DISPLAY_ROT0;
12850+}
12851+#endif
12852+
12853+static MMAL_RECT_T str_to_rect(const char * s)
12854+{
12855+ MMAL_RECT_T rect = {0};
12856+ rect.width = strtoul(s, (char**)&s, 0);
12857+ if (*s == '\0')
12858+ return rect;
12859+ if (*s++ != 'x')
12860+ goto fail;
12861+ rect.height = strtoul(s, (char**)&s, 0);
12862+ if (*s == '\0')
12863+ return rect;
12864+ if (*s++ != '+')
12865+ goto fail;
12866+ rect.x = strtoul(s, (char**)&s, 0);
12867+ if (*s == '\0')
12868+ return rect;
12869+ if (*s++ != '+')
12870+ goto fail;
12871+ rect.y = strtoul(s, (char**)&s, 0);
12872+ if (*s != '\0')
12873+ goto fail;
12874+ return rect;
12875+
12876+fail:
12877+ return (MMAL_RECT_T){0,0,0,0};
12878+}
12879+
12880+static int OpenMmalVout(vlc_object_t *object)
12881+{
12882+ vout_display_t *vd = (vout_display_t *)object;
12883+ vout_display_sys_t *sys;
12884+ MMAL_STATUS_T status;
12885+ int ret = VLC_EGENERIC;
12886+ // At the moment all copy is via I420
12887+ const bool needs_copy = !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma);
12888+ const MMAL_FOURCC_T enc_in = needs_copy ? MMAL_ENCODING_I420 :
12889+ vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
12890+
12891+#if TRACE_ALL
12892+ msg_Dbg(vd, "<<< %s: o:%d", __func__, (int)vd->fmt.orientation);
12893+#endif
12894+
12895+ get_xrandr_rotation(vd);
12896+
12897+ sys = calloc(1, sizeof(struct vout_display_sys_t));
12898+ if (!sys)
12899+ return VLC_ENOMEM;
12900+ vd->sys = sys;
12901+
12902+ vlc_mutex_init(&sys->manage_mutex);
12903+
12904+ if ((sys->init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
12905+ {
12906+ msg_Err(vd, "VCSM init fail");
12907+ goto fail;
12908+ }
12909+
12910+ vc_tv_register_callback(tvservice_cb, vd);
12911+
12912+ sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
12913+ sys->transparent = var_InheritBool(vd, MMAL_VOUT_TRANSPARENT_NAME);
12914+
12915+ {
12916+ const char *display_name = var_InheritString(vd, MMAL_DISPLAY_NAME);
12917+ int qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber" );
12918+ int display_id = find_display_num(display_name);
12919+// sys->display_id = display_id < 0 ? vc_tv_get_default_display_id() : display_id;
12920+ sys->display_id = display_id >= 0 ? display_id :
12921+ qt_num == 1 ? DISPMANX_ID_HDMI1 : DISPMANX_ID_HDMI;
12922+ if (display_id < -1)
12923+ msg_Warn(vd, "Unknown display device: '%s'", display_name);
12924+ else
12925+ msg_Dbg(vd, "Display device: %s, qt=%d id=%d display=%d", display_name,
12926+ qt_num, display_id, sys->display_id);
12927+ }
12928+
12929+ {
12930+ const char *window_str = var_InheritString(vd, MMAL_VOUT_WINDOW_NAME);
12931+ sys->req_win = str_to_rect(window_str);
12932+ if (sys->req_win.width != 0)
12933+ msg_Dbg(vd, "Window: %dx%d @ %d,%d",
12934+ sys->req_win.width, sys->req_win.height,
12935+ sys->req_win.x, sys->req_win.y);
12936+ }
12937+
12938+ {
12939+ const char *transform_name = var_InheritString(vd, MMAL_VOUT_TRANSFORM_NAME);
12940+ int transform_num = find_transform_num(transform_name);
12941+ sys->display_transform = transform_num < 0 ?
12942+ get_xrandr_rotation(vd) :
12943+ (MMAL_DISPLAYTRANSFORM_T)transform_num;
12944+
12945+ if (transform_num < -1)
12946+ msg_Warn(vd, "Unknown vout transform: '%s'", transform_name);
12947+ else
12948+ msg_Dbg(vd, "Display transform: %s, mmal_display_transform=%d",
12949+ transform_name, (int)sys->display_transform);
12950+
12951+ sys->video_transform = combine_transform(
12952+ vlc_to_mmal_transform(vd->fmt.orientation), sys->display_transform);
12953+ sys->dest_transform = transform_inverse(sys->display_transform);
12954+ }
12955+
12956+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
12957+ if (status != MMAL_SUCCESS) {
12958+ msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
12959+ MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
12960+ goto fail;
12961+ }
12962+
12963+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
12964+ status = mmal_port_enable(sys->component->control, vd_control_port_cb);
12965+ if (status != MMAL_SUCCESS) {
12966+ msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
12967+ sys->component->control->name, status, mmal_status_to_string(status));
12968+ goto fail;
12969+ }
12970+
12971+ sys->input = sys->component->input[0];
12972+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
12973+
12974+ sys->input->format->encoding = enc_in;
12975+ sys->input->format->encoding_variant = 0;
12976+ sys->i_planes = 1;
12977+
12978+ display_set_format(vd, sys->input->format, want_isp(vd));
12979+
12980+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
12981+ if (status != MMAL_SUCCESS) {
12982+ msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
12983+ sys->input->name, status, mmal_status_to_string(status));
12984+ goto fail;
12985+ }
12986+
12987+ status = mmal_port_format_commit(sys->input);
12988+ if (status != MMAL_SUCCESS) {
12989+ msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
12990+ sys->input->name, status, mmal_status_to_string(status));
12991+ goto fail;
12992+ }
12993+
12994+ sys->input->buffer_size = sys->input->buffer_size_recommended;
12995+
12996+ if (!needs_copy) {
12997+ sys->input->buffer_num = 30;
12998+ }
12999+ else {
13000+ sys->input->buffer_num = 2;
13001+ if ((sys->copy_pool = mmal_port_pool_create(sys->input, 2, sys->input->buffer_size)) == NULL)
13002+ {
13003+ msg_Err(vd, "Cannot create copy pool");
13004+ goto fail;
13005+ }
13006+ }
13007+
13008+ set_display_windows(vd, sys);
13009+
13010+ configure_display(vd, vd->cfg, &vd->source);
13011+
13012+ status = mmal_port_enable(sys->input, vd_input_port_cb);
13013+ if (status != MMAL_SUCCESS) {
13014+ msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
13015+ sys->input->name, status, mmal_status_to_string(status));
13016+ goto fail;
13017+ }
13018+
13019+ status = mmal_component_enable(sys->component);
13020+ if (status != MMAL_SUCCESS) {
13021+ msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
13022+ sys->component->name, status, mmal_status_to_string(status));
13023+ goto fail;
13024+ }
13025+
13026+ if ((sys->pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
13027+ {
13028+ msg_Err(vd, "Failed to create input pool");
13029+ goto fail;
13030+ }
13031+
13032+ for (unsigned int i = 0; i != SUBS_MAX; ++i) {
13033+ vout_subpic_t * const sub = sys->subs + i;
13034+ if ((status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sub->component)) != MMAL_SUCCESS)
13035+ {
13036+ msg_Dbg(vd, "Failed to create subpic component %d", i);
13037+ goto fail;
13038+ }
13039+ sub->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
13040+ if ((status = mmal_port_enable(sub->component->control, vd_control_port_cb)) != MMAL_SUCCESS) {
13041+ msg_Err(vd, "Failed to enable control port %s on sub %d (status=%"PRIx32" %s)",
13042+ sys->component->control->name, i, status, mmal_status_to_string(status));
13043+ goto fail;
13044+ }
13045+ if ((status = hw_mmal_subpic_open(VLC_OBJECT(vd), &sub->sub, sub->component->input[0],
13046+ sys->display_id, sys->layer + i + 1)) != MMAL_SUCCESS) {
13047+ msg_Dbg(vd, "Failed to open subpic %d", i);
13048+ goto fail;
13049+ }
13050+ if ((status = mmal_component_enable(sub->component)) != MMAL_SUCCESS)
13051+ {
13052+ msg_Dbg(vd, "Failed to enable subpic component %d", i);
13053+ goto fail;
13054+ }
13055+ }
13056+
13057+ // If we can't deal with it directly ask for I420
13058+ vd->fmt.i_chroma = req_chroma(vd);
13059+
13060+ vd->info = (vout_display_info_t){
13061+ .is_slow = false,
13062+ .has_double_click = false,
13063+ .needs_hide_mouse = false,
13064+ .has_pictures_invalid = true,
13065+ .subpicture_chromas = hw_mmal_vzc_subpicture_chromas
13066+ };
13067+
13068+ vd->pool = vd_pool;
13069+ vd->prepare = vd_prepare;
13070+ vd->display = vd_display;
13071+ vd->control = vd_control;
13072+
13073+
13074+ msg_Dbg(vd, ">>> %s: ok", __func__);
13075+ return VLC_SUCCESS;
13076+
13077+fail:
13078+ CloseMmalVout(object);
13079+
13080+ msg_Dbg(vd, ">>> %s: rv=%d", __func__, ret);
13081+
13082+ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
13083 }
13084+
13085+vlc_module_begin()
13086+
13087+ add_submodule()
13088+
13089+ set_shortname(N_("MMAL vout"))
13090+ set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
13091+ set_capability("vout display", 16) // 1 point better than ASCII art
13092+ add_shortcut("mmal_vout")
13093+ set_category( CAT_VIDEO )
13094+ set_subcategory( SUBCAT_VIDEO_VOUT )
13095+
13096+ add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
13097+ add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
13098+ MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
13099+ add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
13100+ MMAL_NATIVE_INTERLACE_LONGTEXT, false)
13101+ add_string(MMAL_DISPLAY_NAME, "auto", MMAL_DISPLAY_TEXT,
13102+ MMAL_DISPLAY_LONGTEXT, false)
13103+ add_string(MMAL_VOUT_TRANSFORM_NAME, "auto", MMAL_VOUT_TRANSFORM_TEXT,
13104+ MMAL_VOUT_TRANSFORM_LONGTEXT, false)
13105+ add_string(MMAL_VOUT_WINDOW_NAME, "fullscreen", MMAL_VOUT_WINDOW_TEXT,
13106+ MMAL_VOUT_WINDOW_LONGTEXT, false)
13107+ add_bool(MMAL_VOUT_TRANSPARENT_NAME, false, MMAL_VOUT_TRANSPARENT_TEXT,
13108+ MMAL_VOUT_TRANSPARENT_LONGTEXT, false)
13109+ set_callbacks(OpenMmalVout, CloseMmalVout)
13110+
13111+vlc_module_end()
13112+
13113+
13114--- /dev/null
13115+++ b/modules/hw/mmal/xsplitter.c
13116@@ -0,0 +1,584 @@
13117+#ifdef HAVE_CONFIG_H
13118+#include "config.h"
13119+#endif
13120+
13121+#include <stdatomic.h>
13122+
13123+#include <vlc_common.h>
13124+#include <vlc_plugin.h>
13125+#include <vlc_threads.h>
13126+#include <vlc_vout_display.h>
13127+#include <vlc_modules.h>
13128+
13129+#include <bcm_host.h>
13130+#include <interface/mmal/mmal.h>
13131+#include <interface/mmal/util/mmal_util.h>
13132+#include <interface/mmal/util/mmal_default_components.h>
13133+
13134+#include "mmal_picture.h"
13135+
13136+#define TRACE_ALL 0
13137+
13138+typedef struct display_desc_s
13139+{
13140+ vout_display_t * vout;
13141+ unsigned int max_pels;
13142+} display_desc_t;
13143+
13144+typedef struct mmal_x11_sys_s
13145+{
13146+ bool use_mmal;
13147+ display_desc_t * cur_desc;
13148+ display_desc_t mmal_desc;
13149+ display_desc_t x_desc;
13150+ uint32_t changed;
13151+ vlc_fourcc_t subpicture_chromas[16];
13152+} mmal_x11_sys_t;
13153+
13154+#define MAX_GL_PELS (1920*1080)
13155+#define MAX_MMAL_PELS (4096*4096) // Should never be hit
13156+
13157+#if 0
13158+// Gen prog for the following table
13159+// Not done inline in case we end up pulling in FP libs we don't want
13160+#include <math.h>
13161+#include <stdio.h>
13162+
13163+int main(int argc, char *argv[])
13164+{
13165+ unsigned int i;
13166+ for (i = 0; i != 64; ++i)
13167+ {
13168+ printf(" [%2u]=%5u,", i, (unsigned int)(0.5 + (1/sqrt((i + 5)/4.0) * 65536.0)));
13169+ if (i % 4 == 3)
13170+ printf("\n");
13171+ }
13172+}
13173+#endif
13174+
13175+static const uint16_t sqrt_tab[64] = {
13176+ [ 0]=58617, [ 1]=53510, [ 2]=49541, [ 3]=46341,
13177+ [ 4]=43691, [ 5]=41449, [ 6]=39520, [ 7]=37837,
13178+ [ 8]=36353, [ 9]=35030, [10]=33843, [11]=32768,
13179+ [12]=31790, [13]=30894, [14]=30070, [15]=29309,
13180+ [16]=28602, [17]=27945, [18]=27330, [19]=26755,
13181+ [20]=26214, [21]=25705, [22]=25225, [23]=24770,
13182+ [24]=24339, [25]=23930, [26]=23541, [27]=23170,
13183+ [28]=22817, [29]=22479, [30]=22155, [31]=21845,
13184+ [32]=21548, [33]=21263, [34]=20988, [35]=20724,
13185+ [36]=20470, [37]=20225, [38]=19988, [39]=19760,
13186+ [40]=19539, [41]=19326, [42]=19119, [43]=18919,
13187+ [44]=18725, [45]=18536, [46]=18354, [47]=18176,
13188+ [48]=18004, [49]=17837, [50]=17674, [51]=17515,
13189+ [52]=17361, [53]=17211, [54]=17064, [55]=16921,
13190+ [56]=16782, [57]=16646, [58]=16514, [59]=16384,
13191+ [60]=16257, [61]=16134, [62]=16013, [63]=15895
13192+};
13193+#define SQRT_MAX (sizeof(sqrt_tab)/sizeof(sqrt_tab[0]) - 1)
13194+
13195+static bool cpy_fmt_limit_size(const display_desc_t * const dd,
13196+ video_format_t * const dst,
13197+ const video_format_t * const src)
13198+{
13199+ const unsigned int src_pel = src->i_visible_width * src->i_visible_height;
13200+
13201+ *dst = *src;
13202+
13203+ if (src_pel <= dd->max_pels)
13204+ return false;
13205+
13206+ // scaling factor sqrt(max_pel/cur_pel)
13207+ // sqrt done by lookup & 16 bit fixed-point maths - not exactly accurate but
13208+ // easily good enough & avoids floating point (which may be slow)
13209+ // src_pel > max_pel so n >= 0
13210+ // Rounding should be such that exact sqrts work and everything else rounds
13211+ // down
13212+ unsigned int n = ((src_pel * 4 - 1) / dd->max_pels) - 4;
13213+ unsigned int scale = sqrt_tab[n >= SQRT_MAX ? SQRT_MAX : n];
13214+
13215+ // Rescale width - rounding up to 16
13216+ unsigned int width = ((src->i_visible_width * scale + (16 << 16) - 1) >> 16) & ~15;
13217+ // Rescale height based on new width
13218+ unsigned int height = (src->i_visible_height * width + src->i_visible_width/2) / src->i_visible_width;
13219+
13220+// fprintf(stderr, "%dx%d -> %dx%d\n", src->i_visible_width, src->i_visible_height, width, height);
13221+
13222+ dst->i_width = width;
13223+ dst->i_visible_width = width;
13224+ dst->i_height = height;
13225+ dst->i_visible_height = height;
13226+ return true;
13227+}
13228+
13229+static void unload_display_module(vout_display_t * const x_vout)
13230+{
13231+ if (x_vout != NULL) {
13232+ if (x_vout->module != NULL) {
13233+ module_unneed(x_vout, x_vout->module);
13234+ }
13235+ vlc_object_release(x_vout);
13236+ }
13237+}
13238+
13239+static void CloseMmalX11(vlc_object_t *object)
13240+{
13241+ vout_display_t * const vd = (vout_display_t *)object;
13242+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13243+
13244+ msg_Dbg(vd, "<<< %s", __func__);
13245+
13246+ if (sys == NULL)
13247+ return;
13248+
13249+ unload_display_module(sys->x_desc.vout);
13250+
13251+ unload_display_module(sys->mmal_desc.vout);
13252+
13253+ free(sys);
13254+
13255+ msg_Dbg(vd, ">>> %s", __func__);
13256+}
13257+
13258+static void mmal_x11_event(vout_display_t * x_vd, int cmd, va_list args)
13259+{
13260+ vout_display_t * const vd = x_vd->owner.sys;
13261+#if TRACE_ALL
13262+ msg_Dbg(vd, "<<< %s (cmd=%d)", __func__, cmd);
13263+#endif
13264+
13265+ // Do not fall into the display assert if Invalid not supported
13266+ if (cmd == VOUT_DISPLAY_EVENT_PICTURES_INVALID &&
13267+ !vd->info.has_pictures_invalid)
13268+ return;
13269+
13270+ vd->owner.event(vd, cmd, args);
13271+}
13272+
13273+static vout_window_t * mmal_x11_window_new(vout_display_t * x_vd, unsigned type)
13274+{
13275+ vout_display_t * const vd = x_vd->owner.sys;
13276+#if TRACE_ALL
13277+ msg_Dbg(vd, "<<< %s (type=%d)", __func__, type);
13278+#endif
13279+ return vd->owner.window_new(vd, type);
13280+}
13281+
13282+static void mmal_x11_window_del(vout_display_t * x_vd, vout_window_t * win)
13283+{
13284+ vout_display_t * const vd = x_vd->owner.sys;
13285+#if TRACE_ALL
13286+ msg_Dbg(vd, "<<< %s", __func__);
13287+#endif
13288+ vd->owner.window_del(vd, win);
13289+}
13290+
13291+
13292+static int load_display_module(vout_display_t * const vd,
13293+ display_desc_t * const dd,
13294+ const char * const cap,
13295+ const char * const module_name)
13296+{
13297+ vout_display_t * const x_vout = vlc_object_create(vd, sizeof(*x_vout));
13298+
13299+ dd->vout = NULL;
13300+ if (!x_vout)
13301+ return -1;
13302+
13303+ x_vout->owner.sys = vd;
13304+ x_vout->owner.event = mmal_x11_event;
13305+ x_vout->owner.window_new = mmal_x11_window_new;
13306+ x_vout->owner.window_del = mmal_x11_window_del;
13307+
13308+ x_vout->cfg = vd->cfg;
13309+ x_vout->info = vd->info;
13310+ cpy_fmt_limit_size(dd, &x_vout->source, &vd->source);
13311+ cpy_fmt_limit_size(dd, &x_vout->fmt, &vd->fmt);
13312+
13313+ if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL)
13314+ {
13315+ msg_Err(vd, "Failed to open Xsplitter:%s module", module_name);
13316+ goto fail;
13317+ }
13318+
13319+ msg_Dbg(vd, "R/G/B: %08x/%08x/%08x", x_vout->fmt.i_rmask, x_vout->fmt.i_gmask, x_vout->fmt.i_bmask);
13320+
13321+ dd->vout = x_vout;
13322+ return 0;
13323+
13324+fail:
13325+ vlc_object_release(x_vout);
13326+ return -1;
13327+}
13328+
13329+
13330+/* Return a pointer over the current picture_pool_t* (mandatory).
13331+ *
13332+ * For performance reasons, it is best to provide at least count
13333+ * pictures but it is not mandatory.
13334+ * You can return NULL when you cannot/do not want to allocate
13335+ * pictures.
13336+ * The vout display module keeps the ownership of the pool and can
13337+ * destroy it only when closing or on invalid pictures control.
13338+ */
13339+static picture_pool_t * mmal_x11_pool(vout_display_t * vd, unsigned count)
13340+{
13341+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13342+ vout_display_t * const x_vd = sys->cur_desc->vout;
13343+#if TRACE_ALL
13344+ char buf0[5];
13345+ msg_Dbg(vd, "<<< %s (count=%d) %s:%dx%d->%s:%dx%d", __func__, count,
13346+ str_fourcc(buf0, vd->fmt.i_chroma),
13347+ vd->fmt.i_width, vd->fmt.i_height,
13348+ str_fourcc(buf0, x_vd->fmt.i_chroma),
13349+ x_vd->fmt.i_width, x_vd->fmt.i_height);
13350+#endif
13351+ picture_pool_t * pool = x_vd->pool(x_vd, count);
13352+#if TRACE_ALL
13353+ msg_Dbg(vd, ">>> %s: %p", __func__, pool);
13354+#endif
13355+ return pool;
13356+}
13357+
13358+/* Prepare a picture and an optional subpicture for display (optional).
13359+ *
13360+ * It is called before the next pf_display call to provide as much
13361+ * time as possible to prepare the given picture and the subpicture
13362+ * for display.
13363+ * You are guaranted that pf_display will always be called and using
13364+ * the exact same picture_t and subpicture_t.
13365+ * You cannot change the pixel content of the picture_t or of the
13366+ * subpicture_t.
13367+ */
13368+static void mmal_x11_prepare(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
13369+{
13370+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13371+ vout_display_t * const x_vd = sys->cur_desc->vout;
13372+#if TRACE_ALL
13373+ msg_Dbg(vd, "<<< %s", __func__);
13374+#endif
13375+ if (x_vd->prepare)
13376+ x_vd->prepare(x_vd, pic, sub);
13377+}
13378+
13379+/* Display a picture and an optional subpicture (mandatory).
13380+ *
13381+ * The picture and the optional subpicture must be displayed as soon as
13382+ * possible.
13383+ * You cannot change the pixel content of the picture_t or of the
13384+ * subpicture_t.
13385+ *
13386+ * This function gives away the ownership of the picture and of the
13387+ * subpicture, so you must release them as soon as possible.
13388+ */
13389+static void mmal_x11_display(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
13390+{
13391+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13392+ vout_display_t * const x_vd = sys->cur_desc->vout;
13393+
13394+#if TRACE_ALL
13395+ const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic);
13396+ msg_Dbg(vd, "<<< %s: fmt: %dx%d/%dx%d, pic:%dx%d, pts=%lld, mmal=%d/%d", __func__, vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height, pic->format.i_width, pic->format.i_height, (long long)pic->date,
13397+ is_mmal_pic, sys->use_mmal);
13398+#endif
13399+
13400+ if (x_vd->fmt.i_chroma != pic->format.i_chroma ||
13401+ x_vd->fmt.i_width != pic->format.i_width ||
13402+ x_vd->fmt.i_height != pic->format.i_height)
13403+ {
13404+ msg_Dbg(vd, "%s: Picture dropped", __func__);
13405+ picture_Release(pic);
13406+ if (sub != NULL)
13407+ subpicture_Delete(sub);
13408+ return;
13409+ }
13410+
13411+ x_vd->display(x_vd, pic, sub);
13412+}
13413+
13414+
13415+static int vout_display_Control(const display_desc_t * const dd, int query, ...)
13416+{
13417+ va_list args;
13418+ int result;
13419+
13420+ va_start(args, query);
13421+ result = dd->vout->control(dd->vout, query, args);
13422+ va_end(args);
13423+
13424+ return result;
13425+}
13426+
13427+static bool want_mmal_vout(vout_display_t * const vd, const mmal_x11_sys_t * const sys)
13428+{
13429+ return sys->mmal_desc.vout != NULL &&
13430+ (sys->x_desc.vout == NULL || var_InheritBool(vd, "fullscreen"));
13431+}
13432+
13433+static inline int
13434+up_rv(const int a, const int b)
13435+{
13436+ return a != 0 ? a : b;
13437+}
13438+
13439+static int
13440+reset_pictures(vout_display_t * const vd, const display_desc_t * const desc)
13441+{
13442+ int rv = 0;
13443+ VLC_UNUSED(vd);
13444+ if (desc->vout)
13445+ {
13446+ // If the display doesn't have has_pictures_invalid then it doesn't
13447+ // expect RESET_PICTURES
13448+ if (desc->vout->info.has_pictures_invalid)
13449+ vout_display_Control(desc, VOUT_DISPLAY_RESET_PICTURES);
13450+ }
13451+ return rv;
13452+}
13453+
13454+static int
13455+replay_controls(vout_display_t * const vd, const display_desc_t * const desc, const int32_t changed)
13456+{
13457+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_DISPLAY_FILLED)) != 0)
13458+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, vd->cfg);
13459+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_ZOOM)) != 0)
13460+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg);
13461+ if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) |
13462+ (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0)
13463+ cpy_fmt_limit_size(desc, &desc->vout->source, &vd->source);
13464+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT)) != 0)
13465+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT);
13466+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP)) != 0)
13467+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_CROP);
13468+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_VIEWPOINT)) != 0)
13469+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_VIEWPOINT, vd->cfg);
13470+ return 0;
13471+}
13472+
13473+/* Control on the module (mandatory) */
13474+static int mmal_x11_control(vout_display_t * vd, int ctl, va_list va)
13475+{
13476+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13477+ display_desc_t *x_desc = sys->cur_desc;
13478+ int rv;
13479+#if TRACE_ALL
13480+ msg_Dbg(vd, "<<< %s[%d] (ctl=%d)", __func__, sys->use_mmal, ctl);
13481+#endif
13482+ // Remember what we've told this vd - unwanted ctls ignored on replay
13483+ if (ctl >= 0 && ctl <= 31)
13484+ sys->changed |= (1 << ctl);
13485+
13486+ switch (ctl) {
13487+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
13488+ {
13489+ const vout_display_cfg_t * const cfg = va_arg(va, const vout_display_cfg_t *);
13490+ const bool want_mmal = want_mmal_vout(vd, sys);
13491+ const bool swap_vout = (sys->use_mmal != want_mmal);
13492+ display_desc_t * const new_desc = want_mmal ? &sys->mmal_desc : &sys->x_desc;
13493+
13494+ msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d",
13495+ cfg->display.width, cfg->display.height, sys->mmal_desc.vout, want_mmal,
13496+ var_InheritBool(vd, "fullscreen"));
13497+
13498+ // Repeat any control calls that we sent to the previous vd
13499+ if (swap_vout && sys->changed != 0) {
13500+ const uint32_t changed = sys->changed;
13501+ sys->changed = 0;
13502+ replay_controls(vd, new_desc, changed);
13503+ }
13504+
13505+ if (swap_vout) {
13506+ if (sys->use_mmal) {
13507+ vout_display_Control(x_desc, VOUT_DISPLAY_CHANGE_MMAL_HIDE);
13508+ }
13509+ vout_display_SendEventPicturesInvalid(vd);
13510+ }
13511+
13512+ rv = vout_display_Control(new_desc, ctl, cfg);
13513+ if (rv == VLC_SUCCESS) {
13514+ vd->fmt = new_desc->vout->fmt;
13515+ sys->cur_desc = new_desc;
13516+ sys->use_mmal = want_mmal;
13517+ }
13518+
13519+
13520+ break;
13521+ }
13522+
13523+ case VOUT_DISPLAY_RESET_PICTURES:
13524+ {
13525+ char dbuf0[5], dbuf1[5], dbuf2[5];
13526+ msg_Dbg(vd, "<<< %s: Pic reset: fmt: %s,%dx%d<-%s,%dx%d, source: %s,%dx%d/%dx%d", __func__,
13527+ str_fourcc(dbuf0, vd->fmt.i_chroma), vd->fmt.i_width, vd->fmt.i_height,
13528+ str_fourcc(dbuf1, x_desc->vout->fmt.i_chroma), x_desc->vout->fmt.i_width, x_desc->vout->fmt.i_height,
13529+ str_fourcc(dbuf2, vd->source.i_chroma), vd->source.i_width, vd->source.i_height, x_desc->vout->source.i_width,
13530+ x_desc->vout->source.i_height);
13531+ }
13532+ rv = reset_pictures(vd, &sys->x_desc);
13533+ rv = up_rv(rv, reset_pictures(vd, &sys->mmal_desc));
13534+
13535+ vd->fmt = x_desc->vout->fmt;
13536+ break;
13537+
13538+ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
13539+ case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
13540+ cpy_fmt_limit_size(x_desc, &x_desc->vout->source, &vd->source);
13541+
13542+ /* FALLTHRU */
13543+ default:
13544+ rv = x_desc->vout->control(x_desc->vout, ctl, va);
13545+// vd->fmt = x_vd->fmt;
13546+ break;
13547+ }
13548+#if TRACE_ALL
13549+ msg_Dbg(vd, ">>> %s (rv=%d)", __func__, rv);
13550+#endif
13551+ return rv;
13552+}
13553+
13554+#define DO_MANAGE 0
13555+
13556+#if DO_MANAGE
13557+/* Manage pending event (optional) */
13558+static void mmal_x11_manage(vout_display_t * vd)
13559+{
13560+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
13561+ vout_display_t * const x_vd = sys->cur_desc->vout;
13562+#if TRACE_ALL
13563+ msg_Dbg(vd, "<<< %s", __func__);
13564+#endif
13565+ x_vd->manage(x_vd);
13566+}
13567+#endif
13568+
13569+static int OpenMmalX11(vlc_object_t *object)
13570+{
13571+ vout_display_t * const vd = (vout_display_t *)object;
13572+ mmal_x11_sys_t * const sys = calloc(1, sizeof(*sys));
13573+ int ret = VLC_SUCCESS;
13574+
13575+ if (sys == NULL) {
13576+ return VLC_EGENERIC;
13577+ }
13578+ vd->sys = (vout_display_sys_t *)sys;
13579+
13580+ vd->info = (vout_display_info_t){
13581+ .is_slow = false,
13582+ .has_double_click = false,
13583+ .needs_hide_mouse = false,
13584+ .has_pictures_invalid = true,
13585+ .subpicture_chromas = NULL
13586+ };
13587+
13588+ {
13589+ char dbuf0[5];
13590+ msg_Dbg(vd, ">>> %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
13591+ str_fourcc(dbuf0, vd->fmt.i_chroma),
13592+ vd->fmt.i_width, vd->fmt.i_height,
13593+ vd->fmt.i_x_offset, vd->fmt.i_y_offset,
13594+ vd->fmt.i_visible_width, vd->fmt.i_visible_height,
13595+ vd->fmt.i_sar_num, vd->fmt.i_sar_den);
13596+ }
13597+
13598+ sys->x_desc.max_pels = MAX_GL_PELS;
13599+ sys->mmal_desc.max_pels = MAX_MMAL_PELS;
13600+
13601+ if (load_display_module(vd, &sys->x_desc, "vout display", "opengles2") == 0)
13602+ {
13603+ msg_Dbg(vd, "Opengles2 output found");
13604+ }
13605+ else
13606+ {
13607+ sys->x_desc.max_pels = MAX_MMAL_PELS;
13608+ if (load_display_module(vd, &sys->x_desc, "vout display", "xcb_x11") == 0)
13609+ msg_Dbg(vd, "X11 XCB output found");
13610+ }
13611+
13612+ if ((load_display_module(vd, &sys->mmal_desc, "vout display", "mmal_vout")) == 0)
13613+ msg_Dbg(vd, "MMAL output found");
13614+
13615+ if (sys->mmal_desc.vout == NULL && sys->x_desc.vout == NULL) {
13616+ char dbuf0[5], dbuf1[5];
13617+ msg_Info(vd, "No valid output found for vout (%s/%s)", str_fourcc(dbuf0, vd->fmt.i_chroma), str_fourcc(dbuf1, vd->source.i_chroma));
13618+ goto fail;
13619+ }
13620+
13621+ vd->pool = mmal_x11_pool;
13622+ vd->prepare = mmal_x11_prepare;
13623+ vd->display = mmal_x11_display;
13624+ vd->control = mmal_x11_control;
13625+#if DO_MANAGE
13626+ vd->manage = mmal_x11_manage;
13627+#endif
13628+
13629+ if (want_mmal_vout(vd, sys)) {
13630+ sys->cur_desc = &sys->mmal_desc;
13631+ sys->use_mmal = true;
13632+ }
13633+ else {
13634+ sys->cur_desc = &sys->x_desc;
13635+ sys->use_mmal = false;
13636+ }
13637+
13638+ if (sys->mmal_desc.vout == NULL || sys->x_desc.vout == NULL) {
13639+ vd->info = sys->cur_desc->vout->info;
13640+ vd->info.has_pictures_invalid = true; // Should make this unwanted
13641+ }
13642+ else {
13643+ // We have both - construct a combination
13644+ vd->info = (vout_display_info_t){
13645+ .is_slow = false,
13646+ .has_double_click = sys->mmal_desc.vout->info.has_double_click || sys->x_desc.vout->info.has_double_click,
13647+ .needs_hide_mouse = sys->mmal_desc.vout->info.needs_hide_mouse || sys->x_desc.vout->info.needs_hide_mouse,
13648+ .has_pictures_invalid = true,
13649+ };
13650+ // Construct intersection of subpicture chromas
13651+ // sys calloced so no need to add the terminating zero
13652+ if (sys->mmal_desc.vout->info.subpicture_chromas != NULL && sys->x_desc.vout->info.subpicture_chromas != NULL) {
13653+ unsigned int n = 0;
13654+ // N^2 - fix if we ever care
13655+ for (const vlc_fourcc_t * p1 = sys->mmal_desc.vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) {
13656+ for (const vlc_fourcc_t * p2 = sys->x_desc.vout->info.subpicture_chromas; *p2 != 0; ++p2) {
13657+ if (*p1 == *p2) {
13658+ sys->subpicture_chromas[n++] = *p1;
13659+ break;
13660+ }
13661+ }
13662+ }
13663+ if (n != 0)
13664+ vd->info.subpicture_chromas = sys->subpicture_chromas;
13665+ }
13666+ }
13667+ vd->fmt = sys->cur_desc->vout->fmt;
13668+
13669+#if TRACE_ALL
13670+ {
13671+ char dbuf0[5];
13672+ msg_Dbg(vd, ">>> %s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
13673+ module_get_name(sys->cur_desc->vout->module, false),
13674+ str_fourcc(dbuf0, vd->fmt.i_chroma),
13675+ vd->fmt.i_width, vd->fmt.i_height,
13676+ vd->fmt.i_x_offset, vd->fmt.i_y_offset,
13677+ vd->fmt.i_visible_width, vd->fmt.i_visible_height,
13678+ vd->fmt.i_sar_num, vd->fmt.i_sar_den);
13679+ }
13680+#endif
13681+ return VLC_SUCCESS;
13682+
13683+fail:
13684+ CloseMmalX11(VLC_OBJECT(vd));
13685+ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
13686+}
13687+
13688+
13689+
13690+
13691+vlc_module_begin()
13692+ set_shortname(N_("MMAL x11 splitter"))
13693+ set_description(N_("MMAL x11 splitter for Raspberry Pi"))
13694+ set_capability("vout display", 300) // Between GLES & GL
13695+ add_shortcut("mmal_x11")
13696+ set_category( CAT_VIDEO )
13697+ set_subcategory( SUBCAT_VIDEO_VOUT )
13698+ set_callbacks(OpenMmalX11, CloseMmalX11)
13699+vlc_module_end()
13700+
13701--- a/modules/video_output/opengl/egl.c
13702+++ b/modules/video_output/opengl/egl.c
13703@@ -43,6 +43,8 @@
13704 # include "../android/utils.h"
13705 #endif
13706
13707+#define REQUIRE_DMA_BUF_IMPORT 1
13708+
13709 typedef struct vlc_gl_sys_t
13710 {
13711 EGLDisplay display;
13712@@ -355,6 +357,14 @@ static int Open (vlc_object_t *obj, cons
13713 goto error;
13714 }
13715
13716+#if REQUIRE_DMA_BUF_IMPORT
13717+ if (!CheckToken(ext, "EGL_EXT_image_dma_buf_import"))
13718+ {
13719+ msg_Dbg(obj, "No dma_buf_import - fall back to X");
13720+ goto error;
13721+ }
13722+#endif
13723+
13724 const EGLint conf_attr[] = {
13725 EGL_RED_SIZE, 5,
13726 EGL_GREEN_SIZE, 5,
13727--- a/src/input/decoder.c
13728+++ b/src/input/decoder.c
13729@@ -1995,6 +1995,7 @@ void input_DecoderDelete( decoder_t *p_d
13730 vlc_mutex_lock( &p_owner->lock );
13731 p_owner->b_waiting = false;
13732 vlc_cond_signal( &p_owner->wait_request );
13733+ vlc_mutex_unlock( &p_owner->lock );
13734
13735 /* If the video output is paused or slow, or if the picture pool size was
13736 * under-estimated (e.g. greedy video filter, buggy decoder...), the
13737@@ -2005,7 +2006,6 @@ void input_DecoderDelete( decoder_t *p_d
13738 * worker threads (if any) and the decoder thread to terminate. */
13739 if( p_owner->p_vout != NULL )
13740 vout_Cancel( p_owner->p_vout, true );
13741- vlc_mutex_unlock( &p_owner->lock );
13742
13743 vlc_join( p_owner->thread, NULL );
13744
13745--- a/src/misc/fourcc.c
13746+++ b/src/misc/fourcc.c
13747@@ -755,8 +755,13 @@ static const struct
13748 { { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422,
13749 VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT },
13750 FAKE_FMT() },
13751- { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE,
13752- VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE },
13753+ { { VLC_CODEC_ANDROID_OPAQUE }, FAKE_FMT() },
13754+ { { VLC_CODEC_MMAL_OPAQUE, VLC_CODEC_MMAL_ZC_SAND30 },
13755+ FAKE_FMT() },
13756+ { { VLC_CODEC_MMAL_ZC_I420, VLC_CODEC_MMAL_ZC_SAND8,
13757+ VLC_CODEC_MMAL_ZC_SAND10, VLC_CODEC_MMAL_ZC_RGB32 },
13758+ FAKE_FMT() },
13759+ { { VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE },
13760 FAKE_FMT() },
13761 { { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B },
13762 FAKE_FMT() },
13763--- a/src/misc/picture.c
13764+++ b/src/misc/picture.c
13765@@ -365,10 +365,30 @@ void picture_CopyProperties( picture_t *
13766 p_dst->b_top_field_first = p_src->b_top_field_first;
13767 }
13768
13769+static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma)
13770+{
13771+ return i_chroma == VLC_CODEC_MMAL_OPAQUE ||
13772+ i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
13773+ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
13774+ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
13775+ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
13776+ i_chroma == VLC_CODEC_MMAL_ZC_SAND8;
13777+}
13778+
13779 void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src )
13780 {
13781- for( int i = 0; i < p_src->i_planes ; i++ )
13782- plane_CopyPixels( p_dst->p+i, p_src->p+i );
13783+ if( is_zc_chroma(p_src->format.i_chroma) )
13784+ {
13785+ assert(p_dst->i_planes == 0);
13786+ p_dst->i_planes = p_src->i_planes;
13787+ for( int i = 0; i < p_src->i_planes; i++ )
13788+ p_dst->p[i] = p_src->p[i];
13789+ }
13790+ else
13791+ {
13792+ for( int i = 0; i < p_src->i_planes; i++ )
13793+ plane_CopyPixels( p_dst->p+i, p_src->p+i );
13794+ }
13795
13796 assert( p_dst->context == NULL );
13797
13798--- a/src/video_output/video_output.c
13799+++ b/src/video_output/video_output.c
13800@@ -964,6 +964,17 @@ static picture_t *ConvertRGB32AndBlend(v
13801 return NULL;
13802 }
13803
13804+
13805+static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma)
13806+{
13807+ return i_chroma == VLC_CODEC_MMAL_OPAQUE ||
13808+ i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
13809+ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
13810+ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
13811+ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
13812+ i_chroma == VLC_CODEC_MMAL_ZC_SAND8;
13813+}
13814+
13815 static int ThreadDisplayRenderPicture(vout_thread_t *vout, bool is_forced)
13816 {
13817 vout_thread_sys_t *sys = vout->p;
13818@@ -1098,7 +1109,7 @@ static int ThreadDisplayRenderPicture(vo
13819 }
13820
13821 assert(vout_IsDisplayFiltered(vd) == !sys->display.use_dr);
13822- if (sys->display.use_dr && !is_direct) {
13823+ if (sys->display.use_dr && !is_direct && !is_zc_chroma(todisplay->format.i_chroma)) {
13824 picture_t *direct = NULL;
13825 if (likely(vout->p->display_pool != NULL))
13826 direct = picture_pool_Get(vout->p->display_pool);