[vlc] Rework _with_rpi case with neon
by Nicolas Chauvet
commit 1bc04eefc203c958c0bfe3c7d6ddb40294fa5232
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Thu Dec 20 21:53:51 2018 +0100
Rework _with_rpi case with neon
vlc.spec | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
---
diff --git a/vlc.spec b/vlc.spec
index 18dd3ec..95ee938 100644
--- a/vlc.spec
+++ b/vlc.spec
@@ -200,12 +200,11 @@ BuildRequires: pkgconfig(xcb-keysyms)
}
BuildRequires: xorg-x11-proto-devel
-%ifarch armv7hl armv7hnl
%{?_with_rpi:
+ExclusiveArch: armv7hnl
BuildRequires: raspberrypi-vc-devel
BuildRequires: raspberrypi-vc-static
}
-%endif
%if 0%{?rhel} == 7
BuildRequires: devtoolset-7-toolchain, devtoolset-7-libatomic-devel
5 years, 11 months
[xine-lib] Add support for rpi
by Xavier Bachelot
commit fdbce5594dcbcecd9dd49d1d604a800ebcb4da8f
Author: Xavier Bachelot <xavier(a)bachelot.org>
Date: Tue Nov 13 11:12:21 2018 +0100
Add support for rpi
xine-lib.spec | 4 ++++
1 file changed, 4 insertions(+)
---
diff --git a/xine-lib.spec b/xine-lib.spec
index 1a15921..6da88c8 100644
--- a/xine-lib.spec
+++ b/xine-lib.spec
@@ -63,6 +63,7 @@ BuildRequires: libvpx-devel
%if 0%{?fedora}
BuildRequires: libaom-devel
%endif
+%{?_with_rpi:BuildRequires: raspberrypi-vc-libs-devel}
%if 0%{?_with_freetype:1}
BuildRequires: fontconfig-devel
%endif # freetype
@@ -236,6 +237,7 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_libaom.so
%endif
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_libjpeg.so
+%{?_with_rpi:%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_libmmal.so}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_libvpx.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_lpcm.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_mad.so
@@ -289,6 +291,7 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_tls_openssl.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vdr.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_fb.so
+%{?_with_rpi:%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_mmal.so}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_opengl.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_opengl2.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_raw.so
@@ -334,6 +337,7 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
- Enable SSH and NFS input plugins.
- Enable TLS support.
- Enable AV1 support through libaom (Fedora only).
+- Add support for RPI.
* Thu Dec 06 2018 Antonio Trande <sagitter(a)fedoraproject.org> - 1.2.9-11
- Rebuild for ffmpeg-3.* on el7
5 years, 11 months
[xine-lib] Update snapshot
by Xavier Bachelot
commit 7890a37cad7ff20f424c7a4d17b2c68533feb72a
Author: Xavier Bachelot <xavier(a)bachelot.org>
Date: Tue Nov 13 10:36:00 2018 +0100
Update snapshot
.gitignore | 1 +
sources | 2 +-
xine-lib.spec | 6 +++---
3 files changed, 5 insertions(+), 4 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 6bfd372..191a789 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
xine-lib-1.2.6.tar.xz
/xine-lib-1.2.8.tar.xz
/xine-lib-1.2.9.tar.xz
+/xine-lib-1.2.9-20181129hg14263.tar.xz
diff --git a/sources b/sources
index 7860a58..c4b4923 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-cd42d2ba92f943d17736d9bca712b3d1 xine-lib-1.2.9.tar.xz
+03645249641c5c268ccb46de96670634 xine-lib-1.2.9-20181129hg14263.tar.xz
diff --git a/xine-lib.spec b/xine-lib.spec
index ad347ba..1a15921 100644
--- a/xine-lib.spec
+++ b/xine-lib.spec
@@ -10,8 +10,8 @@
%endif # ix86
%global snapshot 1
-%global date 20181022
-%global revision 14243
+%global date 20181129
+%global revision 14263
Summary: A multimedia engine
Name: xine-lib
@@ -329,7 +329,7 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%changelog
-* Mon Dec 10 2018 Xavier Bachelot <xavier(a)bachelot.org> 1.2.9-12.20181022hg14243
+* Mon Dec 10 2018 Xavier Bachelot <xavier(a)bachelot.org> 1.2.9-12.20181129hg14263
- Update to xine-lib snapshot.
- Enable SSH and NFS input plugins.
- Enable TLS support.
5 years, 11 months
[xine-lib] Enable AV1 support through libaom
by Xavier Bachelot
commit 4880d03123fa5301a6082d356d5010d5075b3bfd
Author: Xavier Bachelot <xavier(a)bachelot.org>
Date: Mon Oct 29 11:49:22 2018 +0100
Enable AV1 support through libaom
xine-lib.spec | 8 ++++++++
1 file changed, 8 insertions(+)
---
diff --git a/xine-lib.spec b/xine-lib.spec
index 991a444..ad347ba 100644
--- a/xine-lib.spec
+++ b/xine-lib.spec
@@ -59,6 +59,10 @@ BuildRequires: aalib-devel >= 1.4
BuildRequires: libcaca-devel >= 0.99-0.5.beta14
BuildRequires: ImageMagick-devel >= 6.2.4.6-1
BuildRequires: libvpx-devel
+# Not available on EL6 and EL7
+%if 0%{?fedora}
+BuildRequires: libaom-devel
+%endif
%if 0%{?_with_freetype:1}
BuildRequires: fontconfig-devel
%endif # freetype
@@ -228,6 +232,9 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_faad.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_ff.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_gsm610.so
+%if 0%{?fedora}
+%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_libaom.so
+%endif
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_libjpeg.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_libvpx.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_lpcm.so
@@ -326,6 +333,7 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
- Update to xine-lib snapshot.
- Enable SSH and NFS input plugins.
- Enable TLS support.
+- Enable AV1 support through libaom (Fedora only).
* Thu Dec 06 2018 Antonio Trande <sagitter(a)fedoraproject.org> - 1.2.9-11
- Rebuild for ffmpeg-3.* on el7
5 years, 11 months
[xine-lib] Enable TLS support
by Xavier Bachelot
commit 814e196d05181a276cad980c9c93dce419926d96
Author: Xavier Bachelot <xavier(a)bachelot.org>
Date: Mon Oct 29 11:48:49 2018 +0100
Enable TLS support
xine-lib.spec | 5 +++++
1 file changed, 5 insertions(+)
---
diff --git a/xine-lib.spec b/xine-lib.spec
index cf86589..991a444 100644
--- a/xine-lib.spec
+++ b/xine-lib.spec
@@ -91,6 +91,8 @@ BuildRequires: libsmbclient-devel
BuildRequires: libtool
BuildRequires: libssh2-devel
BuildRequires: libnfs-devel
+BuildRequires: gnutls-devel
+BuildRequires: openssl-devel
%description
@@ -276,6 +278,8 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_vcdo.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_nsf.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_sputext.so
+%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_tls_gnutls.so
+%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_tls_openssl.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vdr.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_fb.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_opengl.so
@@ -321,6 +325,7 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
* Mon Dec 10 2018 Xavier Bachelot <xavier(a)bachelot.org> 1.2.9-12.20181022hg14243
- Update to xine-lib snapshot.
- Enable SSH and NFS input plugins.
+- Enable TLS support.
* Thu Dec 06 2018 Antonio Trande <sagitter(a)fedoraproject.org> - 1.2.9-11
- Rebuild for ffmpeg-3.* on el7
5 years, 11 months
[xine-lib] Enable SSH and NFS input plugins
by Xavier Bachelot
commit 54f7ceb85c8ff6f081cc4037cb156f7931eece26
Author: Xavier Bachelot <xavier(a)bachelot.org>
Date: Mon Oct 29 11:47:53 2018 +0100
Enable SSH and NFS input plugins
xine-lib.spec | 5 +++++
1 file changed, 5 insertions(+)
---
diff --git a/xine-lib.spec b/xine-lib.spec
index 5f28731..cf86589 100644
--- a/xine-lib.spec
+++ b/xine-lib.spec
@@ -89,6 +89,8 @@ BuildRequires: pkgconfig(libpulse)
BuildRequires: gtk2-devel
BuildRequires: libsmbclient-devel
BuildRequires: libtool
+BuildRequires: libssh2-devel
+BuildRequires: libnfs-devel
%description
@@ -264,8 +266,10 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_dvd.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_mms.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_network.so
+%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_nfs.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_pvr.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_rtp.so
+%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_ssh.so
%{?el6:%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_v4l.so}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_v4l2.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_vcd.so
@@ -316,6 +320,7 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%changelog
* Mon Dec 10 2018 Xavier Bachelot <xavier(a)bachelot.org> 1.2.9-12.20181022hg14243
- Update to xine-lib snapshot.
+- Enable SSH and NFS input plugins.
* Thu Dec 06 2018 Antonio Trande <sagitter(a)fedoraproject.org> - 1.2.9-11
- Rebuild for ffmpeg-3.* on el7
5 years, 11 months
[xine-lib] Enable building from snapshot
by Xavier Bachelot
commit f13f5211fd896a3c68d3ed71ceee9ca052384d9d
Author: Xavier Bachelot <xavier(a)bachelot.org>
Date: Mon Oct 29 11:44:02 2018 +0100
Enable building from snapshot
xine-lib.spec | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
---
diff --git a/xine-lib.spec b/xine-lib.spec
index 92aea96..5f28731 100644
--- a/xine-lib.spec
+++ b/xine-lib.spec
@@ -9,13 +9,26 @@
%global have_vidix 0
%endif # ix86
+%global snapshot 1
+%global date 20181022
+%global revision 14243
+
Summary: A multimedia engine
Name: xine-lib
Version: 1.2.9
-Release: 11%{?dist}
+Release: 12%{?snapshot:.%{date}hg%{revision}}%{?dist}
License: GPLv2+
URL: http://www.xine-project.org/
+%if ! 0%{?snapshot}
Source0: http://downloads.sourceforge.net/xine/xine-lib-%{version}.tar.xz
+%else
+#hg clone http://hg.code.sf.net/p/xine/xine-lib-1.2 xine-lib-1.2
+#cd xine-lib-1.2
+#autoreconf -vif
+#./configure
+#make dist
+Source0: xine-lib-%{version}-%{date}hg%{revision}.tar.xz
+%endif
Provides: xine-lib(plugin-abi) = %{plugin_abi}
%{?_isa:Provides: xine-lib(plugin-abi)%{?_isa} = %{plugin_abi}}
@@ -106,7 +119,11 @@ This package contains extra plugins for %{name}:
%prep
+%if ! 0%{?snapshot}
%setup -q
+%else
+%setup -q -n %{name}-%{version}-%{date}hg%{revision}
+%endif
autoreconf -ivf
@@ -204,8 +221,6 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_a52.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_dts.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_dvaudio.so
-%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_dxr3_spu.so
-%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_dxr3_video.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_faad.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_ff.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_decode_gsm610.so
@@ -241,6 +256,7 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_dmx_pva.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_dmx_slave.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_dmx_video.so
+%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_dxr3.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_flac.so
%{!?el6:%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_bluray.so}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_inp_cdda.so
@@ -257,7 +273,6 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_nsf.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_sputext.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vdr.so
-%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_dxr3.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_fb.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_opengl.so
%{_libdir}/xine/plugins/%{plugin_abi}/xineplug_vo_out_opengl2.so
@@ -299,6 +314,9 @@ mkdir -p $RPM_BUILD_ROOT%{codecdir}
%changelog
+* Mon Dec 10 2018 Xavier Bachelot <xavier(a)bachelot.org> 1.2.9-12.20181022hg14243
+- Update to xine-lib snapshot.
+
* Thu Dec 06 2018 Antonio Trande <sagitter(a)fedoraproject.org> - 1.2.9-11
- Rebuild for ffmpeg-3.* on el7
5 years, 11 months
[vlc] Update changelog
by Nicolas Chauvet
commit 8eb541927097d532028367ffc015cc24f4c4dd75
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Thu Dec 20 21:30:26 2018 +0100
Update changelog
vlc.spec | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
---
diff --git a/vlc.spec b/vlc.spec
index 1c69c50..18dd3ec 100644
--- a/vlc.spec
+++ b/vlc.spec
@@ -44,7 +44,7 @@ Summary: The cross-platform open-source multimedia framework, player and server
Epoch: 1
Name: vlc
Version: 3.0.5
-Release: 0.1%{?dist}
+Release: 0.2%{?dist}
License: GPLv2+
URL: https://www.videolan.org
Source0: %{vlc_url}/%{?!vlc_tag:%{version}/}vlc-%{version}%{?vlc_tag}.tar.xz
@@ -577,6 +577,9 @@ fi || :
%changelog
+* Thu Dec 20 2018 Nicolas Chauvet <kwizart(a)gmail.com> - 1:3.0.5-0.2
+- Add mmal support
+
* Thu Dec 13 2018 Nicolas Chauvet <kwizart(a)gmail.com> - 1:3.0.5-0.1
- Bump Epoch
- Update to 20181213
5 years, 11 months
[vlc] Add mmal support
by Nicolas Chauvet
commit aae04322673caec5ba18deea56766fed8924d50b
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Thu Dec 20 18:59:26 2018 +0100
Add mmal support
mmal_1.patch | 6570 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
vlc.spec | 2 +
2 files changed, 6572 insertions(+)
---
diff --git a/mmal_1.patch b/mmal_1.patch
new file mode 100644
index 0000000..35bdeb2
--- /dev/null
+++ b/mmal_1.patch
@@ -0,0 +1,6570 @@
+--- a/configure.ac
++++ b/configure.ac
+@@ -3406,7 +3406,7 @@
+ VLC_ADD_PLUGIN([mmal])
+ VLC_ADD_LDFLAGS([mmal],[ -L/opt/vc/lib ])
+ VLC_ADD_CFLAGS([mmal],[ -isystem /opt/vc/include -isystem /opt/vc/include/interface/vcos/pthreads -isystem /opt/vc/include/interface/vmcs_host/linux ])
+- VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif ]) ], [
++ VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif -lvchiq_arm -lvcsm ]) ], [
+ AS_IF([test "${enable_mmal}" = "yes"],
+ [ AC_MSG_ERROR([Cannot find bcm library...]) ],
+ [ AC_MSG_WARN([Cannot find bcm library...]) ])
+--- a/modules/hw/mmal/Makefile.am
++++ b/modules/hw/mmal/Makefile.am
+@@ -1,23 +1,30 @@
+ include $(top_srcdir)/modules/common.am
+ mmaldir = $(pluginsdir)/mmal
+
+-AM_CFLAGS += $(CFLAGS_mmal)
+-AM_LDFLAGS += -rpath '$(mmaldir)' $(LDFLAGS_mmal)
++AM_CFLAGS += -pthread $(CFLAGS_mmal)
++AM_LDFLAGS += -pthread -rpath '$(mmaldir)' $(LDFLAGS_mmal)
+
+-libmmal_vout_plugin_la_SOURCES = vout.c mmal_picture.c mmal_picture.h
++libmmal_vout_plugin_la_SOURCES = vout.c subpic.c mmal_picture.c subpic.h mmal_picture.h
+ libmmal_vout_plugin_la_CFLAGS = $(AM_CFLAGS)
+ libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm
+ libmmal_vout_plugin_la_LIBADD = $(LIBS_mmal)
+ mmal_LTLIBRARIES = libmmal_vout_plugin.la
+
+-libmmal_codec_plugin_la_SOURCES = codec.c
++libmmal_codec_plugin_la_SOURCES = codec.c subpic.c mmal_picture.c blend_rgba_neon.S subpic.h mmal_picture.h
+ libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS)
+ libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
+ libmmal_codec_plugin_la_LIBADD = $(LIBS_mmal)
+ mmal_LTLIBRARIES += libmmal_codec_plugin.la
+
+-libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c
++libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c mmal_picture.h
+ libmmal_deinterlace_plugin_la_CFLAGS = $(AM_CFLAGS)
+ libmmal_deinterlace_plugin_la_LDFLAGS = $(AM_LDFLAGS)
+ libmmal_deinterlace_plugin_la_LIBADD = $(LIBS_mmal)
+ mmal_LTLIBRARIES += libmmal_deinterlace_plugin.la
++
++libmmal_xsplitter_plugin_la_SOURCES = xsplitter.c mmal_picture.c mmal_picture.h
++libmmal_xsplitter_plugin_la_CFLAGS = $(AM_CFLAGS)
++libmmal_xsplitter_plugin_la_LDFLAGS = $(AM_LDFLAGS)
++libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal)
++mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la
++
+--- /dev/null
++++ b/modules/hw/mmal/blend_rgba_neon.S
+@@ -0,0 +1,185 @@
++ .syntax unified
++ .arm
++// .thumb
++ .text
++ .align 16
++
++@ Deal with tail
++@ As the calculation on each pel is independant it doesn't matter
++@ where the pels are in in the registers as long as we are consistant
++@ with what we use for load & store. (Unused register fields will just
++@ calculate garbage that we then ignore.)
++
++tail_rgbx_rgba:
++ lsls r2, r3, #30 @ b2 -> C, b1 -> N
++ mov r12, r0
++ vldmcs r1!, {d2,d3}
++ vldmcs r0!, {d30, d31}
++ vldmmi r1!, {d1}
++ vldmmi r0!, {d29}
++ tst r3, #1
++ vldrne s1, [r1]
++ vldrne s9, [r0]
++ addne r1, #4
++ addne r0, #4
++
++ @ Alpha in hi byte of each word
++ vshr.u32 q9, q1, #24
++ vshr.u32 q8, q0, #24
++ vmov d28, d4 @ Can't load "s57" so cheat
++ vmul.u32 q9, q9, d8[0]
++ vmul.u32 q8, q8, d8[0]
++ vrshr.u32 q9, q9, #16
++ vrshr.u32 q8, q8, #16 @ 4 mix alpha values
++ vmul.u32 q9, q10
++ vmul.u32 q8, q10 @ dup every alpha into all 4 bytes of its word
++
++ vmull.u8 q3, d3, d19
++ vmull.u8 q2, d2, d18
++ vsub.u8 q9, q11, q9 @ gen the cplmnt
++
++ vmull.u8 q1, d1, d17
++ vmull.u8 q0, d0, d16
++ vsub.u8 q8, q11, q8
++
++ vmlal.u8 q3, d31, d19
++ vmlal.u8 q2, d30, d18
++ vmlal.u8 q1, d29, d17
++ vmlal.u8 q0, d28, d16
++
++ vsra.u16 q3, q3, #8
++ vsra.u16 q2, q2, #8
++ vsra.u16 q1, q1, #8
++ vsra.u16 q0, q0, #8
++
++ vrshrn.u16 d7, q3, #8
++ vrshrn.u16 d6, q2, #8
++ vrshrn.u16 d5, q1, #8
++ vrshrn.u16 d4, q0, #8
++
++ vbit q3, q12, q12 @ Set alpha to #255
++ vbit q2, q12, q12
++
++ lsls r2, r3, #30 @ b2 -> C, b1 -> N
++ vstmcs r12!, {d6,d7}
++ vstmmi r12!, {d5}
++ tst r3, #1
++ vstrne s9, [r12]
++ bx lr
++
++
++@ blend_rgbx_rgba_neon
++
++@ [r0] RGBx dest (Byte order: R, G, B, x)
++@ [r1] RGBA src merge (Byte order: R, G, B, A)
++@ r2 plane alpha
++@ r3 count (pixels)
++
++@ Whilst specified as RGBx+RGBA the only important part is the position of
++@ alpha, the other components are all treated the same
++@ Assumes little endian i.e. Alpha ends up in hi 8 bits of uint32_t
++
++#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
++#error Only little endian written (should be easy fix)
++#endif
++
++@ Implements /255 as ((x * 257) + 0x8000) >> 16
++@ This generates something in the range [(x+126)/255, (x+127)/255] which is good enough
++
++@ There is advantage to aligning src and/or dest - dest gives a bit more due to being used twice
++
++ .align 16
++ .global blend_rgbx_rgba_neon
++#ifdef __ELF__
++ .type blend_rgbx_rgba_neon, %function
++#endif
++
++blend_rgbx_rgba_neon:
++
++ push { r4, lr }
++ vpush { q4 } @ With reworked reg alloc this could be avoided
++ movw r12, #257
++ vmov.i8 q11, #0xff
++ mul r2, r12
++ vmov.u8 q10, #1
++ subs r3, #8
++ vmov s16, r2
++ vmov.u32 q12, #0xff000000
++ blt 2f
++
++ @ If < 16 bytes to move then don't bother trying to align
++ @ (a) This means the the align doesn't need to worry about r3 underflow
++ @ (b) The overhead would be greater than any gain
++ cmp r3, #8
++ ble 1f
++
++ @ Align r1 on a 32 byte boundary
++ mov r4, r3
++ neg r3, r0
++ ubfx r3, r3, #2, #3
++
++ cmp r3, #0
++ blne tail_rgbx_rgba
++
++ sub r3, r4, r3
++
++1:
++ vld1.32 { q0,q1 }, [r1]
++
++1:
++ @ Alpha in hi byte of each word
++ vshr.u32 q9, q1, #24
++ vshr.u32 q8, q0, #24
++ vmul.u32 q9, q9, d8[0]
++ vmul.u32 q8, q8, d8[0]
++
++ vrshr.u32 q9, q9, #16
++ vrshr.u32 q8, q8, #16 @ 4 mix alpha values
++ vmul.u32 q9, q10
++ vmul.u32 q8, q10 @ dup every alpha into all 4 bytes of its word
++
++ vld1.32 {q14,q15}, [r0]
++ subs r3, #8
++
++ vmull.u8 q3, d3, d19
++ vmull.u8 q2, d2, d18
++ vsub.u8 q9, q11, q9 @ gen the cplmnt
++
++ vmull.u8 q1, d1, d17
++ vmull.u8 q0, d0, d16
++ vsub.u8 q8, q11, q8
++ addge r1, #32
++
++ vmlal.u8 q3, d31, d19
++ vmlal.u8 q2, d30, d18
++ vmlal.u8 q1, d29, d17
++ vmlal.u8 q0, d28, d16
++
++ vsra.u16 q3, q3, #8
++ vsra.u16 q2, q2, #8
++ vsra.u16 q1, q1, #8
++ vsra.u16 q0, q0, #8
++
++ vrshrn.u16 d7, q3, #8
++ vrshrn.u16 d6, q2, #8
++ vrshrn.u16 d5, q1, #8
++ vrshrn.u16 d4, q0, #8
++
++ vld1.32 { q0,q1 }, [r1]
++
++ vbit q3, q12, q12 @ Set alpha to #255
++ vbit q2, q12, q12
++
++ vst1.32 { q2,q3 }, [r0]!
++ bge 1b
++ add r1, #32
++
++2:
++ cmp r3, #-8
++ blgt tail_rgbx_rgba
++
++ vpop { q4 }
++ pop { r4, pc }
++
++
++
+--- /dev/null
++++ b/modules/hw/mmal/blend_rgba_neon.h
+@@ -0,0 +1,15 @@
++#ifndef HW_MMAL_BLEND_RGBA_NEON_H
++#define HW_MMAL_BLEND_RGBA_NEON_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++extern void blend_rgbx_rgba_neon(void * dest, const void * src, int alpha, unsigned int n);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
++
+--- /dev/null
++++ b/modules/hw/mmal/blend_test.c
+@@ -0,0 +1,116 @@
++#include <stdio.h>
++#include <stdint.h>
++#include <memory.h>
++
++#include "blend_rgba_neon.h"
++
++#define RPI_PROFILE 1
++#define RPI_PROC_ALLOC 1
++#include "rpi_prof.h"
++
++static inline unsigned div255(unsigned v)
++{
++ // This models what we we do in the asm for / 255
++ // It generates something in the range [(i+126)/255, (i+127)/255] which is good enough
++ return ((v * 257) + 0x8000) >> 16;
++}
++
++static inline unsigned int a_merge(unsigned int dst, unsigned src, unsigned f)
++{
++ return div255((255 - f) * (dst) + src * f);
++}
++
++
++static void merge_line(void * dest, const void * src, int alpha, unsigned int n)
++{
++ unsigned int i;
++ const uint8_t * s_data = src;
++ uint8_t * d_data = dest;
++
++ for (i = 0; i != n; ++i) {
++ const uint32_t s_pel = ((const uint32_t *)s_data)[i];
++ const uint32_t d_pel = ((const uint32_t *)d_data)[i];
++ const unsigned int a = div255(alpha * (s_pel >> 24));
++ ((uint32_t *)d_data)[i] = 0xff000000 |
++ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 16) & 0xff, a) << 16) |
++ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) |
++ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 0) & 0xff, a) << 0 );
++ }
++}
++
++#define BUF_SIZE 256
++#define BUF_SLACK 16
++#define BUF_ALIGN 64
++#define BUF_ALLOC (BUF_SIZE + 2*BUF_SLACK + BUF_ALIGN)
++
++static void test_line(const uint32_t * const dx, const unsigned int d_off,
++ const uint32_t * const sx, const unsigned int s_off,
++ const unsigned int alpha, const unsigned int len, const int prof_no)
++{
++ uint32_t d0_buf[BUF_ALLOC];
++ uint32_t d1_buf[BUF_ALLOC];
++ const uint32_t * const s0 = sx + s_off;
++
++ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
++ uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
++ unsigned int i;
++
++ memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4);
++ memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4);
++
++ merge_line(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
++
++ PROFILE_START();
++ blend_rgbx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
++ PROFILE_ACC_N(prof_no);
++
++ for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) {
++ if (d0[i] != d1[i]) {
++ printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len);
++ }
++ }
++}
++
++
++int main(int argc, char *argv[])
++{
++ unsigned int i, j;
++ uint32_t d0_buf[BUF_ALLOC];
++ uint32_t s0_buf[BUF_ALLOC];
++
++ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + 63) & ~63) + 0;
++ uint32_t * const s0 = (uint32_t *)(((uintptr_t)s0_buf + 63) & ~63) + 0;
++
++ PROFILE_INIT();
++
++ for (i = 0; i != 255*255; ++i) {
++ unsigned int a = div255(i);
++ unsigned int b = (i + 127)/255;
++ unsigned int c = (i + 126)/255;
++ if (a != b && a != c)
++ printf("%d/255: %d != %d/%d\n", i, a, b, c);
++ }
++
++ for (i = 0; i != BUF_ALLOC; ++i) {
++ d0_buf[i] = 0xff00 | i;
++ s0_buf[i] = (i << 24) | 0xffffff;
++ }
++ for (i = 0; i != 256; ++i) {
++ test_line(d0, 0, s0, 0, i, 256, -1);
++ }
++ for (i = 0; i != 256; ++i) {
++ test_line(d0, 0, s0, 0, 128, i, -1);
++ }
++
++ for (j = 0; j != 16; ++j) {
++ for (i = 0; i != 256; ++i) {
++ test_line(d0, j & 3, s0, j >> 2, i, 256, j);
++ }
++ PROFILE_PRINTF_N(j);
++ }
++
++ printf("Done\n");
++
++ return 0;
++}
++
+--- a/modules/hw/mmal/codec.c
++++ b/modules/hw/mmal/codec.c
+@@ -26,10 +26,12 @@
+ #include "config.h"
+ #endif
+
++#include <stdatomic.h>
++
+ #include <vlc_common.h>
+-#include <vlc_atomic.h>
+ #include <vlc_plugin.h>
+ #include <vlc_codec.h>
++#include <vlc_filter.h>
+ #include <vlc_threads.h>
+
+ #include <bcm_host.h>
+@@ -38,255 +40,380 @@
+ #include <interface/mmal/util/mmal_default_components.h>
+
+ #include "mmal_picture.h"
++#include "subpic.h"
++#include "blend_rgba_neon.h"
++
++#define TRACE_ALL 0
+
+ /*
+ * This seems to be a bit high, but reducing it causes instabilities
+ */
+-#define NUM_EXTRA_BUFFERS 5
++#define NUM_EXTRA_BUFFERS 3
++//#define NUM_EXTRA_BUFFERS 6
++//#define NUM_EXTRA_BUFFERS 10
+ #define NUM_DECODER_BUFFER_HEADERS 30
+
+ #define MIN_NUM_BUFFERS_IN_TRANSIT 2
+
++#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize"
++#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp"
++#define MMAL_COMPONENT_HVS "vc.ril.hvs"
++
++#define MMAL_SLICE_HEIGHT 16
++#define MMAL_ALIGN_W 32
++#define MMAL_ALIGN_H 16
++
+ #define MMAL_OPAQUE_NAME "mmal-opaque"
+ #define MMAL_OPAQUE_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.")
+ #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.")
+
+-static int OpenDecoder(decoder_t *dec);
+-static void CloseDecoder(decoder_t *dec);
++#define MMAL_RESIZE_NAME "mmal-resize"
++#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.")
++#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.")
++
++#define MMAL_ISP_NAME "mmal-isp"
++#define MMAL_ISP_TEXT N_("Use mmal isp rather than hvs.")
++#define MMAL_ISP_LONGTEXT N_("Use mmal isp rather than hvs. This may be faster but has no blend.")
+
+-vlc_module_begin()
+- set_shortname(N_("MMAL decoder"))
+- set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
+- set_capability("video decoder", 90)
+- add_shortcut("mmal_decoder")
+- add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
+- set_callbacks(OpenDecoder, CloseDecoder)
+-vlc_module_end()
+
+-struct decoder_sys_t {
+- bool opaque;
++typedef struct decoder_sys_t
++{
+ MMAL_COMPONENT_T *component;
+ MMAL_PORT_T *input;
+ MMAL_POOL_T *input_pool;
+ MMAL_PORT_T *output;
+- MMAL_POOL_T *output_pool; /* only used for non-opaque mode */
++ hw_mmal_port_pool_ref_t *ppr;
+ MMAL_ES_FORMAT_T *output_format;
+- vlc_sem_t sem;
+
++ MMAL_STATUS_T err_stream;
+ bool b_top_field_first;
+ bool b_progressive;
+
++ bool b_flushed;
++
+ /* statistics */
+- int output_in_transit;
+- int input_in_transit;
+ atomic_bool started;
+-};
++} decoder_sys_t;
+
+-/* Utilities */
+-static int change_output_format(decoder_t *dec);
+-static int send_output_buffer(decoder_t *dec);
+-static void fill_output_port(decoder_t *dec);
+-
+-/* VLC decoder callback */
+-static int decode(decoder_t *dec, block_t *block);
+-static void flush_decoder(decoder_t *dec);
+-
+-/* MMAL callbacks */
+-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+
+-static int OpenDecoder(decoder_t *dec)
++typedef struct supported_mmal_enc_s {
++ struct {
++ MMAL_PARAMETER_HEADER_T header;
++ MMAL_FOURCC_T encodings[64];
++ } supported;
++ int n;
++} supported_mmal_enc_t;
++
++static supported_mmal_enc_t supported_mmal_enc =
+ {
+- int ret = VLC_SUCCESS;
+- decoder_sys_t *sys;
+- MMAL_PARAMETER_UINT32_T extra_buffers;
+- MMAL_STATUS_T status;
++ {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}},
++ -1
++};
+
+- if (dec->fmt_in.i_codec != VLC_CODEC_MPGV &&
+- dec->fmt_in.i_codec != VLC_CODEC_H264)
+- return VLC_EGENERIC;
++#if TRACE_ALL || 1
++static const char * str_fourcc(char * buf, unsigned int fcc)
++{
++ if (fcc == 0)
++ return "----";
++ buf[0] = (fcc >> 0) & 0xff;
++ buf[1] = (fcc >> 8) & 0xff;
++ buf[2] = (fcc >> 16) & 0xff;
++ buf[3] = (fcc >> 24) & 0xff;
++ buf[4] = 0;
++ return buf;
++}
++#endif
+
+- sys = calloc(1, sizeof(decoder_sys_t));
+- if (!sys) {
+- ret = VLC_ENOMEM;
+- goto out;
++static bool is_enc_supported(const MMAL_FOURCC_T fcc)
++{
++ int i;
++
++ if (fcc == 0)
++ return false;
++ if (supported_mmal_enc.n == -1)
++ return true; // Unknown - say OK
++ for (i = 0; i < supported_mmal_enc.n; ++i) {
++ if (supported_mmal_enc.supported.encodings[i] == fcc)
++ return true;
+ }
+- dec->p_sys = sys;
++ return false;
++}
+
+- sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME);
+- bcm_host_init();
++static bool set_and_test_enc_supported(MMAL_PORT_T * port, const MMAL_FOURCC_T fcc)
++{
++ if (supported_mmal_enc.n >= 0)
++ /* already done */;
++ else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&supported_mmal_enc.supported) != MMAL_SUCCESS)
++ supported_mmal_enc.n = 0;
++ else
++ supported_mmal_enc.n = (supported_mmal_enc.supported.header.size - sizeof(supported_mmal_enc.supported.header)) /
++ sizeof(supported_mmal_enc.supported.encodings[0]);
+
+- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
+- MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++ return is_enc_supported(fcc);
++}
+
+- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
+- status = mmal_port_enable(sys->component->control, control_port_cb);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
+- sys->component->control->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
++static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc)
++{
++ switch (fcc){
++ case VLC_CODEC_MJPG:
++ return MMAL_ENCODING_MJPEG;
++ case VLC_CODEC_MP1V:
++ return MMAL_ENCODING_MP1V;
++ case VLC_CODEC_MPGV:
++ case VLC_CODEC_MP2V:
++ return MMAL_ENCODING_MP2V;
++ case VLC_CODEC_H263:
++ return MMAL_ENCODING_H263;
++ case VLC_CODEC_MP4V:
++ return MMAL_ENCODING_MP4V;
++ case VLC_CODEC_H264:
++ return MMAL_ENCODING_H264;
++ case VLC_CODEC_VP6:
++ return MMAL_ENCODING_VP6;
++ case VLC_CODEC_VP8:
++ return MMAL_ENCODING_VP8;
++ case VLC_CODEC_WMV1:
++ return MMAL_ENCODING_WMV1;
++ case VLC_CODEC_WMV2:
++ return MMAL_ENCODING_WMV2;
++ case VLC_CODEC_WMV3:
++ return MMAL_ENCODING_WMV3;
++ case VLC_CODEC_VC1:
++ return MMAL_ENCODING_WVC1;
++ case VLC_CODEC_THEORA:
++ return MMAL_ENCODING_THEORA;
++ default:
++ break;
+ }
++ return 0;
++}
+
+- sys->input = sys->component->input[0];
+- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
+- if (dec->fmt_in.i_codec == VLC_CODEC_MPGV)
+- sys->input->format->encoding = MMAL_ENCODING_MP2V;
+- else
+- sys->input->format->encoding = MMAL_ENCODING_H264;
+-
+- if (dec->fmt_in.i_codec == VLC_CODEC_H264) {
+- if (dec->fmt_in.i_extra > 0) {
+- status = mmal_format_extradata_alloc(sys->input->format,
+- dec->fmt_in.i_extra);
+- if (status == MMAL_SUCCESS) {
+- memcpy(sys->input->format->extradata, dec->fmt_in.p_extra,
+- dec->fmt_in.i_extra);
+- sys->input->format->extradata_size = dec->fmt_in.i_extra;
+- } else {
+- msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- }
+- }
++static MMAL_FOURCC_T vlc_to_mmal_pic_fourcc(const unsigned int fcc)
++{
++ switch (fcc){
++ case VLC_CODEC_I420:
++ return MMAL_ENCODING_I420;
++ case VLC_CODEC_RGB32: // _RGB32 doesn't exist in mmal magic mapping table
++ case VLC_CODEC_RGBA:
++ return MMAL_ENCODING_BGRA;
++ case VLC_CODEC_MMAL_OPAQUE:
++ return MMAL_ENCODING_OPAQUE;
++ default:
++ break;
+ }
++ return 0;
++}
+
+- status = mmal_port_format_commit(sys->input);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
++static MMAL_FOURCC_T pic_to_slice_mmal_fourcc(const MMAL_FOURCC_T fcc)
++{
++ switch (fcc){
++ case MMAL_ENCODING_I420:
++ return MMAL_ENCODING_I420_SLICE;
++ case MMAL_ENCODING_I422:
++ return MMAL_ENCODING_I422_SLICE;
++ case MMAL_ENCODING_ARGB:
++ return MMAL_ENCODING_ARGB_SLICE;
++ case MMAL_ENCODING_RGBA:
++ return MMAL_ENCODING_RGBA_SLICE;
++ case MMAL_ENCODING_ABGR:
++ return MMAL_ENCODING_ABGR_SLICE;
++ case MMAL_ENCODING_BGRA:
++ return MMAL_ENCODING_BGRA_SLICE;
++ case MMAL_ENCODING_RGB16:
++ return MMAL_ENCODING_RGB16_SLICE;
++ case MMAL_ENCODING_RGB24:
++ return MMAL_ENCODING_RGB24_SLICE;
++ case MMAL_ENCODING_RGB32:
++ return MMAL_ENCODING_RGB32_SLICE;
++ case MMAL_ENCODING_BGR16:
++ return MMAL_ENCODING_BGR16_SLICE;
++ case MMAL_ENCODING_BGR24:
++ return MMAL_ENCODING_BGR24_SLICE;
++ case MMAL_ENCODING_BGR32:
++ return MMAL_ENCODING_BGR32_SLICE;
++ default:
++ break;
+ }
+- sys->input->buffer_size = sys->input->buffer_size_recommended;
+- sys->input->buffer_num = sys->input->buffer_num_recommended;
++ return 0;
++}
+
+- status = mmal_port_enable(sys->input, input_port_cb);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
++#if 0
++static inline void draw_line(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int len, int inc)
++{
++ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x;
++ while (len-- != 0) {
++ *p = ~0U;
++ p += inc;
+ }
++}
+
+- sys->output = sys->component->output[0];
+- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
+
+- if (sys->opaque) {
+- extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
+- extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
+- extra_buffers.value = NUM_EXTRA_BUFFERS;
+- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
+- status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++static void draw_corners(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
++{
++ const unsigned int len = 20;
++ draw_line(pic_buf, pic_stride, x, y, len, 1);
++ draw_line(pic_buf, pic_stride, x, y, len, pic_stride);
++ draw_line(pic_buf, pic_stride, x + w - 1, y, len, -1);
++ draw_line(pic_buf, pic_stride, x + w - 1, y, len, pic_stride);
++ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -1);
++ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -(int)pic_stride);
++ draw_line(pic_buf, pic_stride, x, y + h - 1, len, 1);
++ draw_line(pic_buf, pic_stride, x, y + h - 1, len, -(int)pic_stride);
++}
++#endif
+
+- msg_Dbg(dec, "Activate zero-copy for output port");
+- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
+- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
+- 1
+- };
++// Buffer either attached to pic or released
++static picture_t * alloc_opaque_pic(decoder_t * const dec, MMAL_BUFFER_HEADER_T * const buf)
++{
++ decoder_sys_t *const dec_sys = dec->p_sys;
++ picture_t * const pic = decoder_NewPicture(dec);
+
+- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
+- sys->output->name, status, mmal_status_to_string(status));
+- goto out;
+- }
+- }
++ if (pic == NULL)
++ goto fail1;
+
+- status = mmal_port_enable(sys->output, output_port_cb);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)",
+- sys->output->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
++ if (buf->length == 0) {
++ msg_Err(dec, "%s: Empty buffer", __func__);
++ goto fail2;
+ }
+
+- status = mmal_component_enable(sys->component);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
+- sys->component->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++ if ((pic->context = hw_mmal_gen_context(buf, dec_sys->ppr)) == NULL)
++ goto fail2;
+
+- sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
++ buf_to_pic_copy_props(pic, buf);
+
+- if (sys->opaque) {
+- dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
+- dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
+- } else {
+- dec->fmt_out.i_codec = VLC_CODEC_I420;
+- dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
++#if TRACE_ALL
++ msg_Dbg(dec, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
++#endif
++
++ return pic;
++
++fail2:
++ picture_Release(pic);
++fail1:
++ // * maybe should recycle rather than release?
++ mmal_buffer_header_release(buf);
++ return NULL;
++}
++
++static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
++{
++ decoder_t *dec = (decoder_t *)port->userdata;
++ MMAL_STATUS_T status;
++
++#if TRACE_ALL
++ msg_Dbg(dec, "<<< %s: cmd=%d, data=%p", __func__, buffer->cmd, buffer->data);
++#endif
++
++ if (buffer->cmd == MMAL_EVENT_ERROR) {
++ status = *(uint32_t *)buffer->data;
++ dec->p_sys->err_stream = status;
++ msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
++ mmal_status_to_string(status));
+ }
+
+- dec->pf_decode = decode;
+- dec->pf_flush = flush_decoder;
++ mmal_buffer_header_release(buffer);
++}
++
++static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
++{
++ block_t * const block = (block_t *)buffer->user_data;
+
+- vlc_sem_init(&sys->sem, 0);
++ (void)port; // Unused
+
+-out:
+- if (ret != VLC_SUCCESS)
+- CloseDecoder(dec);
++#if TRACE_ALL
++ msg_Dbg((decoder_t *)port->userdata, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
++ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
++#endif
+
+- return ret;
++ mmal_buffer_header_reset(buffer);
++ mmal_buffer_header_release(buffer);
++
++ if (block != NULL)
++ block_Release(block);
+ }
+
+-static void CloseDecoder(decoder_t *dec)
++static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+ {
+- decoder_sys_t *sys = dec->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer;
++ // decoder structure only guaranteed valid if we have contents
+
+- if (!sys)
++ if (buffer->cmd == 0 && buffer->length != 0)
++ {
++ decoder_t * const dec = (decoder_t *)port->userdata;
++
++#if TRACE_ALL
++ msg_Dbg((decoder_t *)port->userdata, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
++ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
++#endif
++
++ picture_t *pic = alloc_opaque_pic(dec, buffer);
++#if TRACE_ALL
++ msg_Dbg(dec, "flags=%#x, video flags=%#x", buffer->flags, buffer->type->video.flags);
++#endif
++ if (pic == NULL)
++ msg_Err(dec, "Failed to allocate new picture");
++ else
++ decoder_QueueVideo(dec, pic);
++ // Buffer released or attached to pic - do not release again
+ return;
++ }
++ else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED)
++ {
++ decoder_t * const dec = (decoder_t *)port->userdata;
++ decoder_sys_t * const sys = dec->p_sys;
++ MMAL_EVENT_FORMAT_CHANGED_T * const fmt = mmal_event_format_changed_get(buffer);
++ MMAL_ES_FORMAT_T * const format = mmal_format_alloc();
+
+- if (sys->component && sys->component->control->is_enabled)
+- mmal_port_disable(sys->component->control);
++ if (format == NULL)
++ msg_Err(dec, "Failed to allocate new format");
++ else
++ {
++ mmal_format_full_copy(format, fmt->format);
++ format->encoding = MMAL_ENCODING_OPAQUE;
+
+- if (sys->input && sys->input->is_enabled)
+- mmal_port_disable(sys->input);
++ if (sys->output_format != NULL)
++ mmal_format_free(sys->output_format);
+
+- if (sys->output && sys->output->is_enabled)
+- mmal_port_disable(sys->output);
++ sys->output_format = format;
++ }
++ }
+
+- if (sys->component && sys->component->is_enabled)
+- mmal_component_disable(sys->component);
++ mmal_buffer_header_reset(buffer);
++ buffer->user_data = NULL;
++ mmal_buffer_header_release(buffer);
++}
+
+- if (sys->input_pool)
+- mmal_pool_destroy(sys->input_pool);
+
+- if (sys->output_format)
+- mmal_format_free(sys->output_format);
+
+- if (sys->output_pool)
+- mmal_pool_destroy(sys->output_pool);
++static void fill_output_port(decoder_t *dec)
++{
++ decoder_sys_t *sys = dec->p_sys;
+
+- if (sys->component)
+- mmal_component_release(sys->component);
++ if (decoder_UpdateVideoFormat(dec) != 0)
++ {
++ // If we have a new format don't bother stuffing the buffer
++ // We should get a reset RSN
++#if TRACE_ALL
++ msg_Dbg(dec, "%s: Updated", __func__);
++#endif
+
+- vlc_sem_destroy(&sys->sem);
+- free(sys);
++ return;
++ }
+
+- bcm_host_deinit();
++ hw_mmal_port_pool_ref_fill(sys->ppr);
++ return;
+ }
+
+ static int change_output_format(decoder_t *dec)
+ {
+ MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type;
+- decoder_sys_t *sys = dec->p_sys;
++ decoder_sys_t * const sys = dec->p_sys;
+ MMAL_STATUS_T status;
+- int pool_size;
+ int ret = 0;
+
++#if TRACE_ALL
++ msg_Dbg(dec, "%s: <<<", __func__);
++#endif
++
+ if (atomic_load(&sys->started)) {
+ mmal_format_full_copy(sys->output->format, sys->output_format);
+ status = mmal_port_format_commit(sys->output);
+@@ -300,7 +427,9 @@
+ }
+
+ port_reset:
++#if TRACE_ALL
+ msg_Dbg(dec, "%s: Do full port reset", __func__);
++#endif
+ status = mmal_port_disable(sys->output);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)",
+@@ -318,15 +447,7 @@
+ goto out;
+ }
+
+- if (sys->opaque) {
+- sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
+- pool_size = NUM_DECODER_BUFFER_HEADERS;
+- } else {
+- sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended,
+- MIN_NUM_BUFFERS_IN_TRANSIT);
+- pool_size = sys->output->buffer_num;
+- }
+-
++ sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
+ sys->output->buffer_size = sys->output->buffer_size_recommended;
+
+ status = mmal_port_enable(sys->output, output_port_cb);
+@@ -338,25 +459,14 @@
+ }
+
+ if (!atomic_load(&sys->started)) {
+- if (!sys->opaque) {
+- sys->output_pool = mmal_port_pool_create(sys->output, pool_size, 0);
+- msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num);
+- }
+-
+ atomic_store(&sys->started, true);
+
+ /* we need one picture from vout for each buffer header on the output
+ * port */
+- dec->i_extra_picture_buffers = pool_size;
+-
+- /* remove what VLC core reserves as it is part of the pool_size
+- * already */
+- if (dec->fmt_in.i_codec == VLC_CODEC_H264)
+- dec->i_extra_picture_buffers -= 19;
+- else
+- dec->i_extra_picture_buffers -= 3;
+-
++ dec->i_extra_picture_buffers = 10;
++#if TRACE_ALL
+ msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers);
++#endif
+ }
+
+ apply_fmt:
+@@ -382,12 +492,17 @@
+ sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive);
+ sys->b_top_field_first = sys->b_progressive ? true :
+ (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst);
++#if TRACE_ALL
+ msg_Dbg(dec, "Detected %s%s video (%d)",
+ sys->b_progressive ? "progressive" : "interlaced",
+ sys->b_progressive ? "" : (sys->b_top_field_first ? " tff" : " bff"),
+ interlace_type.eMode);
++#endif
+ }
+
++ // Tell the reset of the world we have changed format
++ ret = decoder_UpdateVideoFormat(dec);
++
+ out:
+ mmal_format_free(sys->output_format);
+ sys->output_format = NULL;
+@@ -395,144 +510,85 @@
+ return ret;
+ }
+
+-static int send_output_buffer(decoder_t *dec)
++static MMAL_STATUS_T
++set_extradata_and_commit(decoder_t * const dec, decoder_sys_t * const sys)
+ {
+- decoder_sys_t *sys = dec->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer;
+- picture_sys_t *p_sys;
+- picture_t *picture = NULL;
+ MMAL_STATUS_T status;
+- unsigned buffer_size = 0;
+- int ret = 0;
+
+- if (!sys->output->is_enabled)
+- return VLC_EGENERIC;
+-
+- /* If local output pool is allocated, use it - this is only the case for
+- * non-opaque modes */
+- if (sys->output_pool) {
+- buffer = mmal_queue_get(sys->output_pool->queue);
+- if (!buffer) {
+- msg_Warn(dec, "Failed to get new buffer");
+- return VLC_EGENERIC;
+- }
+- }
+-
+- if (!decoder_UpdateVideoFormat(dec))
+- picture = decoder_NewPicture(dec);
+- if (!picture) {
+- msg_Warn(dec, "Failed to get new picture");
+- ret = -1;
+- goto err;
+- }
+-
+- p_sys = picture->p_sys;
+- for (int i = 0; i < picture->i_planes; i++)
+- buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch;
+-
+- if (sys->output_pool) {
+- mmal_buffer_header_reset(buffer);
+- buffer->alloc_size = sys->output->buffer_size;
+- if (buffer_size < sys->output->buffer_size) {
+- msg_Err(dec, "Retrieved picture with too small data block (%d < %d)",
+- buffer_size, sys->output->buffer_size);
+- ret = VLC_EGENERIC;
+- goto err;
+- }
+-
+- if (!sys->opaque)
+- buffer->data = picture->p[0].p_pixels;
+- } else {
+- buffer = p_sys->buffer;
+- if (!buffer) {
+- msg_Warn(dec, "Picture has no buffer attached");
+- picture_Release(picture);
+- return VLC_EGENERIC;
+- }
+- buffer->data = p_sys->buffer->data;
+- }
+- buffer->user_data = picture;
+- buffer->cmd = 0;
+-
+- status = mmal_port_send_buffer(sys->output, buffer);
++ status = mmal_port_format_commit(sys->input);
+ if (status != MMAL_SUCCESS) {
+- msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)",
+- status, mmal_status_to_string(status));
+- ret = -1;
+- goto err;
+- }
+- atomic_fetch_add(&sys->output_in_transit, 1);
+-
+- return ret;
+-
+-err:
+- if (picture)
+- picture_Release(picture);
+- if (sys->output_pool && buffer) {
+- buffer->data = NULL;
+- mmal_buffer_header_release(buffer);
++ msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
+ }
+- return ret;
++ return status;
+ }
+
+-static void fill_output_port(decoder_t *dec)
++static MMAL_STATUS_T decoder_send_extradata(decoder_t * const dec, decoder_sys_t *const sys)
+ {
+- decoder_sys_t *sys = dec->p_sys;
+-
+- unsigned max_buffers_in_transit = 0;
+- int buffers_available = 0;
+- int buffers_to_send = 0;
+- int i;
++ if (dec->fmt_in.i_codec == VLC_CODEC_H264 &&
++ dec->fmt_in.i_extra > 0)
++ {
++ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->input_pool->queue);
++ MMAL_STATUS_T status;
++
++ mmal_buffer_header_reset(buf);
++ buf->cmd = 0;
++ buf->user_data = NULL;
++ buf->alloc_size = sys->input->buffer_size;
++ buf->length = dec->fmt_in.i_extra;
++ buf->data = dec->fmt_in.p_extra;
++ buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG;
+
+- if (sys->output_pool) {
+- max_buffers_in_transit = __MAX(sys->output_pool->headers_num,
+- MIN_NUM_BUFFERS_IN_TRANSIT);
+- buffers_available = mmal_queue_length(sys->output_pool->queue);
+- } else {
+- max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS;
+- buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit);
++ status = mmal_port_send_buffer(sys->input, buf);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(dec, "Failed to send extradata buffer to input port (status=%"PRIx32" %s)",
++ status, mmal_status_to_string(status));
++ return status;
++ }
+ }
+- buffers_to_send = max_buffers_in_transit - atomic_load(&sys->output_in_transit);
+-
+- if (buffers_to_send > buffers_available)
+- buffers_to_send = buffers_available;
+
+-#ifndef NDEBUG
+- msg_Dbg(dec, "Send %d buffers to output port (available: %d, "
+- "in_transit: %d, buffer_num: %d)",
+- buffers_to_send, buffers_available,
+- atomic_load(&sys->output_in_transit),
+- sys->output->buffer_num);
+-#endif
+- for (i = 0; i < buffers_to_send; ++i)
+- if (send_output_buffer(dec) < 0)
+- break;
++ return MMAL_SUCCESS;
+ }
+
+ static void flush_decoder(decoder_t *dec)
+ {
+- decoder_sys_t *sys = dec->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer;
+- MMAL_STATUS_T status;
++ decoder_sys_t *const sys = dec->p_sys;
+
+- msg_Dbg(dec, "Flushing decoder ports...");
+- mmal_port_flush(sys->output);
+- mmal_port_flush(sys->input);
+-
+- while (atomic_load(&sys->output_in_transit) ||
+- atomic_load(&sys->input_in_transit))
+- vlc_sem_wait(&sys->sem);
++#if TRACE_ALL
++ msg_Dbg(dec, "%s: <<<", __func__);
++#endif
++
++ if (!sys->b_flushed) {
++ mmal_port_disable(sys->input);
++ mmal_port_disable(sys->output);
++ // We can leave the input disabled, but we want the output enabled
++ // in order to sink any buffers returning from other modules
++ mmal_port_enable(sys->output, output_port_cb);
++ sys->b_flushed = true;
++ }
++#if TRACE_ALL
++ msg_Dbg(dec, "%s: >>>", __func__);
++#endif
+ }
+
+ static int decode(decoder_t *dec, block_t *block)
+ {
+ decoder_sys_t *sys = dec->p_sys;
+ MMAL_BUFFER_HEADER_T *buffer;
+- bool need_flush = false;
+ uint32_t len;
+ uint32_t flags = 0;
+ MMAL_STATUS_T status;
+
++#if TRACE_ALL
++ msg_Dbg(dec, "<<< %s: %lld/%lld", __func__, block == NULL ? -1LL : block->i_dts, block == NULL ? -1LL : block->i_pts);
++#endif
++
++ if (sys->err_stream != MMAL_SUCCESS) {
++ msg_Err(dec, "MMAL error reported by ctrl");
++ flush_decoder(dec);
++ return VLCDEC_ECRITICAL; /// I think they are all fatal
++ }
++
+ /*
+ * Configure output port if necessary
+ */
+@@ -541,18 +597,50 @@
+ msg_Err(dec, "Failed to change output port format");
+ }
+
+- if (!block)
+- goto out;
++ if (block == NULL)
++ return VLCDEC_SUCCESS;
+
+ /*
+ * Check whether full flush is required
+ */
+- if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
++ if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
++#if TRACE_ALL
++ msg_Dbg(dec, "%s: >>> Discontinuity", __func__);
++#endif
+ flush_decoder(dec);
++ }
++
++ if (block->i_buffer == 0)
++ {
+ block_Release(block);
+ return VLCDEC_SUCCESS;
+ }
+
++ // Reenable stuff if the last thing we did was flush
++ if (!sys->output->is_enabled &&
++ (status = mmal_port_enable(sys->output, output_port_cb)) != MMAL_SUCCESS)
++ {
++ msg_Err(dec, "Output port enable failed");
++ goto fail;
++ }
++
++ if (!sys->input->is_enabled)
++ {
++ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
++ goto fail;
++
++ if ((status = mmal_port_enable(sys->input, input_port_cb)) != MMAL_SUCCESS)
++ {
++ msg_Err(dec, "Input port enable failed");
++ goto fail;
++ }
++
++ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
++ goto fail;
++ }
++
++ // *** We cannot get a picture to put the result in 'till we have
++ // reported the size & the output stages have been set up
+ if (atomic_load(&sys->started))
+ fill_output_port(dec);
+
+@@ -563,18 +651,21 @@
+ if (block->i_flags & BLOCK_FLAG_CORRUPTED)
+ flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED;
+
+- while (block && block->i_buffer > 0) {
+- buffer = mmal_queue_timedwait(sys->input_pool->queue, 100);
++ while (block != NULL)
++ {
++ buffer = mmal_queue_wait(sys->input_pool->queue);
+ if (!buffer) {
+ msg_Err(dec, "Failed to retrieve buffer header for input data");
+- need_flush = true;
+- break;
++ goto fail;
+ }
++
+ mmal_buffer_header_reset(buffer);
+ buffer->cmd = 0;
+- buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts;
++ buffer->pts = block->i_pts != VLC_TICK_INVALID ? block->i_pts :
++ block->i_dts != VLC_TICK_INVALID ? block->i_dts : MMAL_TIME_UNKNOWN;
+ buffer->dts = block->i_dts;
+ buffer->alloc_size = sys->input->buffer_size;
++ buffer->user_data = NULL;
+
+ len = block->i_buffer;
+ if (len > buffer->alloc_size)
+@@ -590,89 +681,1304 @@
+ }
+ buffer->flags = flags;
+
++#if TRACE_ALL
++ msg_Dbg(dec, "%s: -- Send buffer: len=%d", __func__, len);
++#endif
+ status = mmal_port_send_buffer(sys->input, buffer);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)",
+ status, mmal_status_to_string(status));
+- break;
++ goto fail;
+ }
+- atomic_fetch_add(&sys->input_in_transit, 1);
++
++ // Reset flushed flag once we have sent a buf
++ sys->b_flushed = false;
+ }
++ return VLCDEC_SUCCESS;
+
+-out:
+- if (need_flush)
+- flush_decoder(dec);
++fail:
++ flush_decoder(dec);
++ return VLCDEC_ECRITICAL;
+
+- return VLCDEC_SUCCESS;
+ }
+
+-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
++
++static void CloseDecoder(decoder_t *dec)
+ {
+- decoder_t *dec = (decoder_t *)port->userdata;
++ decoder_sys_t *sys = dec->p_sys;
++
++#if TRACE_ALL
++ msg_Dbg(dec, "%s: <<<", __func__);
++#endif
++
++ if (!sys)
++ return;
++
++ if (sys->component != NULL) {
++ if (sys->input->is_enabled)
++ mmal_port_disable(sys->input);
++
++ if (sys->output->is_enabled)
++ mmal_port_disable(sys->output);
++
++ if (sys->component->control->is_enabled)
++ mmal_port_disable(sys->component->control);
++
++ if (sys->component->is_enabled)
++ mmal_component_disable(sys->component);
++
++ mmal_component_release(sys->component);
++ }
++
++ if (sys->input_pool != NULL)
++ mmal_pool_destroy(sys->input_pool);
++
++ if (sys->output_format != NULL)
++ mmal_format_free(sys->output_format);
++
++ hw_mmal_port_pool_ref_release(sys->ppr, false);
++
++ free(sys);
++
++ bcm_host_deinit();
++}
++
++static int OpenDecoder(decoder_t *dec)
++{
++ int ret = VLC_EGENERIC;
++ decoder_sys_t *sys;
+ MMAL_STATUS_T status;
++ const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec);
++
++#if TRACE_ALL
++ {
++ char buf1[5], buf2[5], buf2a[5];
++ char buf3[5], buf4[5];
++ msg_Dbg(dec, "%s: <<< (%s/%s)[%s] %dx%d -> (%s/%s) %dx%d", __func__,
++ str_fourcc(buf1, dec->fmt_in.i_codec),
++ str_fourcc(buf2, dec->fmt_in.video.i_chroma),
++ str_fourcc(buf2a, in_fcc),
++ dec->fmt_in.video.i_width, dec->fmt_in.video.i_height,
++ str_fourcc(buf3, dec->fmt_out.i_codec),
++ str_fourcc(buf4, dec->fmt_out.video.i_chroma),
++ dec->fmt_out.video.i_width, dec->fmt_out.video.i_height);
++ }
++#endif
++
++ if (!is_enc_supported(in_fcc))
++ return VLC_EGENERIC;
++
++ sys = calloc(1, sizeof(decoder_sys_t));
++ if (!sys) {
++ ret = VLC_ENOMEM;
++ goto fail;
++ }
++ dec->p_sys = sys;
++
++ bcm_host_init();
++
++ sys->err_stream = MMAL_SUCCESS;
++
++ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
++ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ sys->input = sys->component->input[0];
++ sys->output = sys->component->output[0];
++
++ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
++ sys->input->format->encoding = in_fcc;
++
++ if (!set_and_test_enc_supported(sys->input, in_fcc)) {
++#if TRACE_ALL
++ char cbuf[5];
++ msg_Dbg(dec, "Format not supported: %s", str_fourcc(cbuf, in_fcc));
++#endif
++ goto fail;
++ }
++
++ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
++ status = mmal_port_enable(sys->component->control, control_port_cb);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
++ sys->component->control->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
++ goto fail;
++
++ sys->input->buffer_size = sys->input->buffer_size_recommended;
++ sys->input->buffer_num = sys->input->buffer_num_recommended;
++
++ status = mmal_port_enable(sys->input, input_port_cb);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
++
++ status = port_parameter_set_uint32(sys->output, MMAL_PARAMETER_EXTRA_BUFFERS, NUM_EXTRA_BUFFERS);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
++ status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ status = port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, 1);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
++ sys->output->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ sys->output->format->encoding = MMAL_ENCODING_OPAQUE;
++ if ((status = mmal_port_format_commit(sys->output)) != MMAL_SUCCESS)
++ {
++ msg_Err(dec, "Failed to commit format on port %s (status=%"PRIx32" %s)",
++ sys->output->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
++ sys->output->buffer_size = sys->output->buffer_size_recommended;
++
++ sys->ppr = hw_mmal_port_pool_ref_create(sys->output, NUM_DECODER_BUFFER_HEADERS, sys->output->buffer_size);
++ if (sys->ppr == NULL) {
++ msg_Err(dec, "Failed to create output pool");
++ goto fail;
++ }
++
++ status = mmal_port_enable(sys->output, output_port_cb);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)",
++ sys->output->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ status = mmal_component_enable(sys->component);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
++ sys->component->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ if ((sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
++ {
++ msg_Err(dec, "Failed to create input pool");
++ goto fail;
++ }
++
++ sys->b_flushed = true;
++ dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
++ dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
++
++ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
++ goto fail;
++
++ dec->pf_decode = decode;
++ dec->pf_flush = flush_decoder;
++
++#if TRACE_ALL
++ msg_Dbg(dec, ">>> %s: ok", __func__);
++#endif
++ return 0;
++
++fail:
++ CloseDecoder(dec);
++#if TRACE_ALL
++msg_Dbg(dec, ">>> %s: FAIL: ret=%d", __func__, ret);
++#endif
++ return ret;
++}
++
++// ----------------------------
++
++#define CONV_MAX_LATENCY 1 // In frames
++
++typedef struct pic_fifo_s {
++ picture_t * head;
++ picture_t * tail;
++} pic_fifo_t;
++
++static inline picture_t * pic_fifo_get(pic_fifo_t * const pf)
++{
++ picture_t * const pic = pf->head;;
++ if (pic != NULL) {
++ pf->head = pic->p_next;
++ pic->p_next = NULL;
++ }
++ return pic;
++}
++
++static inline picture_t * pic_fifo_get_all(pic_fifo_t * const pf)
++{
++ picture_t * const pic = pf->head;;
++ pf->head = NULL;
++ return pic;
++}
++
++static inline void pic_fifo_release_all(pic_fifo_t * const pf)
++{
++ picture_t * pic;
++ while ((pic = pic_fifo_get(pf)) != NULL) {
++ picture_Release(pic);
++ }
++}
++
++static inline void pic_fifo_init(pic_fifo_t * const pf)
++{
++ pf->head = NULL;
++ pf->tail = NULL; // Not strictly needed
++}
++
++static inline void pic_fifo_put(pic_fifo_t * const pf, picture_t * pic)
++{
++ pic->p_next = NULL;
++ if (pf->head == NULL)
++ pf->head = pic;
++ else
++ pf->tail->p_next = pic;
++ pf->tail = pic;
++}
++
++#define SUBS_MAX 3
++
++typedef struct filter_sys_t {
++ MMAL_COMPONENT_T *component;
++ MMAL_PORT_T *input;
++ MMAL_PORT_T *output;
++ MMAL_POOL_T *out_pool; // Free output buffers
++ MMAL_POOL_T *in_pool; // Input pool to get BH for replication
++
++ subpic_reg_stash_t subs[SUBS_MAX];
++
++ pic_fifo_t ret_pics;
++
++ vlc_sem_t sem;
++ vlc_mutex_t lock;
++
++ bool b_top_field_first;
++ bool b_progressive;
++
++ MMAL_STATUS_T err_stream;
++ int in_count;
++
++ bool zero_copy;
++ const char * component_name;
++ MMAL_PORT_BH_CB_T in_port_cb_fn;
++ MMAL_PORT_BH_CB_T out_port_cb_fn;
++
++ uint64_t frame_seq;
++ mtime_t pts_stash[16];
++
++ // Slice specific tracking stuff
++ struct {
++ pic_fifo_t pics;
++ unsigned int line; // Lines filled
++ } slice;
++
++} filter_sys_t;
++
++
++static void pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic)
++{
++ unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3;
++ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
++
++ es_fmt->type = MMAL_ES_TYPE_VIDEO;
++ es_fmt->encoding_variant = es_fmt->encoding =
++ vlc_to_mmal_pic_fourcc(pic->format.i_chroma);
++
++ // Fill in crop etc.
++ vlc_to_mmal_video_fmt(es_fmt, &pic->format);
++ // Override width / height with strides
++ v_fmt->width = pic->p[0].i_pitch / bpp;
++ v_fmt->height = pic->p[0].i_lines;
++}
++
++
++static MMAL_STATUS_T conv_enable_in(filter_t * const p_filter, filter_sys_t * const sys)
++{
++ MMAL_STATUS_T err = MMAL_SUCCESS;
++
++ if (!sys->input->is_enabled &&
++ (err = mmal_port_enable(sys->input, sys->in_port_cb_fn)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
++ sys->input->name, err, mmal_status_to_string(err));
++ }
++ return err;
++}
++
++static MMAL_STATUS_T conv_enable_out(filter_t * const p_filter, filter_sys_t * const sys)
++{
++ MMAL_STATUS_T err = MMAL_SUCCESS;
++
++ if (!sys->output->is_enabled &&
++ (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
++ sys->output->name, err, mmal_status_to_string(err));
++ }
++ return err;
++}
++
++static void conv_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
++{
++ filter_t * const p_filter = (filter_t *)port->userdata;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "%s: <<< cmd=%d, data=%p, pic=%p", __func__, buffer->cmd, buffer->data, buffer->user_data);
++#endif
+
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
+- status = *(uint32_t *)buffer->data;
+- msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
++ MMAL_STATUS_T status = *(uint32_t *)buffer->data;
++
++ p_filter->p_sys->err_stream = status;
++
++ msg_Err(p_filter, "MMAL error %"PRIx32" \"%s\"", status,
+ mmal_status_to_string(status));
+ }
+
+ mmal_buffer_header_release(buffer);
+ }
+
+-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
++static void conv_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
+ {
+- block_t *block = (block_t *)buffer->user_data;
+- decoder_t *dec = (decoder_t *)port->userdata;
+- decoder_sys_t *sys = dec->p_sys;
+- buffer->user_data = NULL;
++#if TRACE_ALL
++ picture_context_t * ctx = buf->user_data;
++// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
++
++ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, ctx=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
++ __func__, buf->cmd, ctx, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
++#else
++ VLC_UNUSED(port);
++#endif
++
++ mmal_buffer_header_release(buf);
++
++#if TRACE_ALL
++ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
++#endif
++}
++
++static void conv_out_q_pic(filter_sys_t * const sys, picture_t * const pic)
++{
++ pic->p_next = NULL;
++
++ vlc_mutex_lock(&sys->lock);
++ pic_fifo_put(&sys->ret_pics, pic);
++ vlc_mutex_unlock(&sys->lock);
+
+- mmal_buffer_header_release(buffer);
+- if (block)
+- block_Release(block);
+- atomic_fetch_sub(&sys->input_in_transit, 1);
+ vlc_sem_post(&sys->sem);
+ }
+
+-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
++static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
+ {
+- decoder_t *dec = (decoder_t *)port->userdata;
+- decoder_sys_t *sys = dec->p_sys;
+- picture_t *picture;
+- MMAL_EVENT_FORMAT_CHANGED_T *fmt;
+- MMAL_ES_FORMAT_T *format;
+-
+- if (buffer->cmd == 0) {
+- picture = (picture_t *)buffer->user_data;
+- if (buffer->length > 0) {
+- picture->date = buffer->pts;
+- picture->b_progressive = sys->b_progressive;
+- picture->b_top_field_first = sys->b_top_field_first;
+- decoder_QueueVideo(dec, picture);
+- } else {
+- picture_Release(picture);
+- if (sys->output_pool) {
+- buffer->user_data = NULL;
+- buffer->alloc_size = 0;
+- buffer->data = NULL;
+- mmal_buffer_header_release(buffer);
++ filter_t * const p_filter = (filter_t *)port->userdata;
++ filter_sys_t * const sys = p_filter->p_sys;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld/%lld", __func__,
++ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size,
++ (long long)buf->pts, (long long)sys->pts_stash[(unsigned int)(buf->pts & 0xf)]);
++#endif
++ if (buf->cmd == 0) {
++ picture_t * const pic = (picture_t *)buf->user_data;
++
++ if (pic == NULL) {
++ msg_Err(p_filter, "%s: Buffer has no attached picture", __func__);
++ }
++ else if (buf->data == NULL || buf->length == 0)
++ {
++#if TRACE_ALL
++ msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
++#endif
++ picture_Release(pic);
++ }
++ else
++ {
++ buf_to_pic_copy_props(pic, buf);
++ pic->date = sys->pts_stash[(unsigned int)(buf->pts & 0xf)];
++
++// 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);
++
++ conv_out_q_pic(sys, pic);
++ }
++ }
++
++ buf->user_data = NULL; // Zap here to make sure we can't reuse later
++ mmal_buffer_header_release(buf);
++}
++
++
++static void slice_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
++{
++ filter_t * const p_filter = (filter_t *)port->userdata;
++ filter_sys_t * const sys = p_filter->p_sys;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld", __func__,
++ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, (long long)buf->pts);
++#endif
++
++ if (buf->cmd != 0)
++ {
++ mmal_buffer_header_release(buf);
++ return;
++ }
++
++ if (buf->data == NULL || buf->length == 0)
++ {
++#if TRACE_ALL
++ msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
++#endif
++ }
++ else
++ {
++ // Got slice
++ picture_t *pic = sys->slice.pics.head;
++ const unsigned int scale_lines = sys->output->format->es->video.height; // Expected lines of callback
++
++ if (pic == NULL) {
++ msg_Err(p_filter, "No output picture");
++ goto fail;
++ }
++
++ // Copy lines
++ // * single plane only - fix for I420
++ {
++ const unsigned int scale_n = __MIN(scale_lines - sys->slice.line, MMAL_SLICE_HEIGHT);
++ const unsigned int pic_lines = pic->p[0].i_lines;
++ const unsigned int copy_n = sys->slice.line + scale_n <= pic_lines ? scale_n :
++ sys->slice.line >= pic_lines ? 0 :
++ pic_lines - sys->slice.line;
++
++ const unsigned int src_stride = buf->type->video.pitch[0];
++ const unsigned int dst_stride = pic->p[0].i_pitch;
++ uint8_t *dst = pic->p[0].p_pixels + sys->slice.line * dst_stride;
++ const uint8_t *src = buf->data + buf->type->video.offset[0];
++
++ if (src_stride == dst_stride) {
++ if (copy_n != 0)
++ memcpy(dst, src, src_stride * copy_n);
++ }
++ else {
++ unsigned int i;
++ for (i = 0; i != copy_n; ++i) {
++ memcpy(dst, src, __MIN(dst_stride, src_stride));
++ dst += dst_stride;
++ src += src_stride;
++ }
+ }
++ sys->slice.line += scale_n;
+ }
+- atomic_fetch_sub(&sys->output_in_transit, 1);
+- vlc_sem_post(&sys->sem);
+- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
+- fmt = mmal_event_format_changed_get(buffer);
+
+- format = mmal_format_alloc();
+- mmal_format_full_copy(format, fmt->format);
++ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) {
+
+- if (sys->opaque)
+- format->encoding = MMAL_ENCODING_OPAQUE;
++ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) {
++ // Stuff doesn't add up...
++ msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags);
++ goto fail;
++ }
++ else {
++ sys->slice.line = 0;
++
++ vlc_mutex_lock(&sys->lock);
++ pic_fifo_get(&sys->slice.pics); // Remove head from Q
++ vlc_mutex_unlock(&sys->lock);
++
++ buf_to_pic_copy_props(pic, buf);
++ conv_out_q_pic(sys, pic);
++ }
++ }
++ }
++
++ // Put back
++ buf->user_data = NULL; // Zap here to make sure we can't reuse later
++ mmal_buffer_header_reset(buf);
++
++ if (mmal_port_send_buffer(sys->output, buf) != MMAL_SUCCESS) {
++ mmal_buffer_header_release(buf);
++ }
++ return;
++
++fail:
++ sys->err_stream = MMAL_EIO;
++ vlc_sem_post(&sys->sem); // If we were waiting then break us out - the flush should fix sem values
++}
++
++
++static void conv_flush(filter_t * p_filter)
++{
++ filter_sys_t * const sys = p_filter->p_sys;
++ unsigned int i;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "<<< %s", __func__);
++#endif
++
++ for (i = 0; i != SUBS_MAX; ++i) {
++ hw_mmal_subpic_flush(VLC_OBJECT(p_filter), sys->subs + i);
++ }
++
++ if (sys->input != NULL && sys->input->is_enabled)
++ mmal_port_disable(sys->input);
++
++ if (sys->output != NULL && sys->output->is_enabled)
++ mmal_port_disable(sys->output);
++
++ // Free up anything we may have already lying around
++ // Don't need lock as the above disables should have prevented anything
++ // happening in the background
++
++ pic_fifo_release_all(&sys->slice.pics);
++ pic_fifo_release_all(&sys->ret_pics);
++
++ // Reset sem values - easiest & most reliable way is to just kill & re-init
++ // This will also dig us out of situations where we have got out of sync somehow
++ vlc_sem_destroy(&sys->sem);
++ vlc_sem_init(&sys->sem, CONV_MAX_LATENCY);
++
++ // No buffers in either port now
++ sys->in_count = 0;
++
++ // Reset error status
++ sys->err_stream = MMAL_SUCCESS;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, ">>> %s", __func__);
++#endif
++}
++
++static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic)
++{
++ filter_sys_t * const sys = p_filter->p_sys;
++ picture_t * ret_pics;
++ MMAL_STATUS_T err;
++ const uint64_t frame_seq = ++sys->frame_seq;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "<<< %s", __func__);
++#endif
++#if 0
++ {
++ char dbuf0[5], dbuf1[5];
++ 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__,
++ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
++ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
++ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
++ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
++ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
++ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
++ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
++ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
++ }
++#endif
++
++ if (sys->err_stream != MMAL_SUCCESS) {
++ goto stream_fail;
++ }
++
++ if (p_pic->context == NULL) {
++ msg_Dbg(p_filter, "%s: No context", __func__);
++ }
++ else
++ {
++ unsigned int sub_no = 0;
++
++ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
++ int rv;
++ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter), p_pic, sub_no, sys->subs + sub_no,
++ &sys->output->format->es->video.crop, frame_seq)) == 0)
++ break;
++ else if (rv < 0)
++ goto fail;
++ }
++ }
++
++ // Reenable stuff if the last thing we did was flush
++ if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS ||
++ (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
++ goto fail;
++
++ // If ZC then we need to allocate the out pic before we stuff the input
++ if (sys->zero_copy) {
++ MMAL_BUFFER_HEADER_T * out_buf;
++ picture_t * const out_pic = filter_NewPicture(p_filter);
++
++ if (out_pic == NULL)
++ {
++ msg_Err(p_filter, "Failed to alloc required filter output pic");
++ goto fail;
++ }
++
++ vlc_mutex_lock(&sys->lock);
++ pic_fifo_put(&sys->slice.pics, out_pic);
++ vlc_mutex_unlock(&sys->lock);
++
++ // Poke any returned pic buffers into output
++ // In general this should only happen immediately after enable
++ while ((out_buf = mmal_queue_get(sys->out_pool->queue)) != NULL)
++ mmal_port_send_buffer(sys->output, out_buf);
++
++ ++sys->in_count;
++ }
++
++ // Stuff into input
++ // We assume the BH is already set up with values reflecting pic date etc.
++ {
++ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic);
++#if TRACE_ALL
++ msg_Dbg(p_filter, "In buf send: pic=%p, buf=%p, user=%p, pts=%lld/%lld",
++ p_pic, pic_buf, pic_buf->user_data, (long long)frame_seq, (long long)p_pic->date);
++#endif
++ if (pic_buf == NULL) {
++ msg_Err(p_filter, "Pic has no attached buffer");
++ goto fail;
++ }
++
++ sys->pts_stash[(frame_seq & 0xf)] = p_pic->date;
++ if ((err = port_send_replicated(sys->input, sys->in_pool, pic_buf, frame_seq)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Send buffer to input failed");
++ goto fail;
++ }
++
++ picture_Release(p_pic);
++ p_pic = NULL;
++ --sys->in_count;
++ }
++
++ if (!sys->zero_copy) {
++ MMAL_BUFFER_HEADER_T * out_buf;
++
++ while ((out_buf = sys->in_count < 0 ?
++ mmal_queue_wait(sys->out_pool->queue) : mmal_queue_get(sys->out_pool->queue)) != NULL)
++ {
++ picture_t * const out_pic = filter_NewPicture(p_filter);
++ char dbuf0[5];
++
++ if (out_pic == NULL) {
++ msg_Warn(p_filter, "Failed to alloc new filter output pic");
++ mmal_buffer_header_release(out_buf);
++ break;
++ }
++
++#if 0
++ msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d",
++ str_fourcc(dbuf0, out_pic->format.i_chroma),
++ out_pic->format.i_width, out_pic->format.i_height,
++ out_pic->format.i_x_offset, out_pic->format.i_y_offset,
++ out_pic->format.i_visible_width, out_pic->format.i_visible_height,
++ out_pic->format.i_sar_num, out_pic->format.i_sar_den);
++#endif
++
++ mmal_buffer_header_reset(out_buf);
++ out_buf->user_data = out_pic;
++ out_buf->data = out_pic->p[0].p_pixels;
++ out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines;
++ //**** stride ????
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "Out buf send: pic=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
++ p_pic, out_buf->user_data, out_buf->flags,
++ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts);
++#endif
++
++ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Send buffer to output failed");
++ mmal_buffer_header_release(out_buf);
++ break;
++ }
++
++ ++sys->in_count;
++ }
++ }
++
++ if (sys->in_count < 0)
++ {
++ msg_Err(p_filter, "Buffer count somehow negative");
++ goto fail;
++ }
++
++ // Avoid being more than 1 pic behind
++ vlc_sem_wait(&sys->sem);
++
++ // Return all pending buffers
++ vlc_mutex_lock(&sys->lock);
++ ret_pics = pic_fifo_get_all(&sys->ret_pics);
++ vlc_mutex_unlock(&sys->lock);
++
++ if (sys->err_stream != MMAL_SUCCESS)
++ goto stream_fail;
++
++ // Sink as many sem posts as we have pics
++ // (shouldn't normally wait, but there is a small race)
++ if (ret_pics != NULL)
++ {
++ picture_t *next_pic = ret_pics->p_next;
++#if 0
++ char dbuf0[5];
++
++ msg_Dbg(p_filter, "pic_out %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d",
++ str_fourcc(dbuf0, ret_pics->format.i_chroma),
++ ret_pics->format.i_width, ret_pics->format.i_height,
++ ret_pics->format.i_x_offset, ret_pics->format.i_y_offset,
++ ret_pics->format.i_visible_width, ret_pics->format.i_visible_height,
++ ret_pics->format.i_sar_num, ret_pics->format.i_sar_den);
++#endif
++ while (next_pic != NULL) {
++ vlc_sem_wait(&sys->sem);
++ next_pic = next_pic->p_next;
++ }
++ }
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
++#endif
++
++ return ret_pics;
++
++stream_fail:
++ msg_Err(p_filter, "MMAL error reported by callback");
++fail:
++ if (p_pic != NULL)
++ picture_Release(p_pic);
++ conv_flush(p_filter);
++ return NULL;
++}
++
++
++static void CloseConverter(vlc_object_t * obj)
++{
++ filter_t * const p_filter = (filter_t *)obj;
++ filter_sys_t * const sys = p_filter->p_sys;
++ unsigned int i;
+
+- sys->output_format = format;
++#if TRACE_ALL
++ msg_Dbg(obj, "<<< %s", __func__);
++#endif
++
++ if (sys == NULL)
++ return;
++
++ // Disables input & output ports
++ conv_flush(p_filter);
++
++ if (sys->component && sys->component->control->is_enabled)
++ mmal_port_disable(sys->component->control);
++
++ if (sys->component && sys->component->is_enabled)
++ mmal_component_disable(sys->component);
++
++ for (i = 0; i != SUBS_MAX; ++i) {
++ hw_mmal_subpic_close(VLC_OBJECT(p_filter), sys->subs + i);
++ }
++
++ if (sys->out_pool)
++ {
++ if (sys->zero_copy)
++ mmal_port_pool_destroy(sys->output, sys->out_pool);
++ else
++ mmal_pool_destroy(sys->out_pool);
++ }
++
++ if (sys->in_pool != NULL)
++ mmal_pool_destroy(sys->in_pool);
++
++ if (sys->component)
++ mmal_component_release(sys->component);
++
++ vlc_sem_destroy(&sys->sem);
++ vlc_mutex_destroy(&sys->lock);
++
++ free(sys);
++}
++
++
++static int conv_set_output(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const pic, const MMAL_FOURCC_T pic_enc)
++{
++ MMAL_STATUS_T status;
++
++ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
++ sys->output->format->type = MMAL_ES_TYPE_VIDEO;
++ sys->output->format->encoding = pic_enc;
++ sys->output->format->encoding_variant = sys->output->format->encoding;
++ vlc_to_mmal_video_fmt(sys->output->format, &p_filter->fmt_out.video);
++
++ // Override default format width/height if we have a pic we need to match
++ if (pic != NULL)
++ {
++ pic_to_format(sys->output->format, pic);
++ MMAL_VIDEO_FORMAT_T *fmt = &sys->output->format->es->video;
++ msg_Dbg(p_filter, "%s: %dx%d [(0,0) %dx%d]", __func__, fmt->width, fmt->height, fmt->crop.width, fmt->crop.height);
++ }
++
++ mmal_log_dump_format(sys->output->format);
++
++ status = mmal_port_format_commit(sys->output);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(p_filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
++ sys->output->name, status, mmal_status_to_string(status));
++ return -1;
++ }
++
++ sys->output->buffer_num = __MAX(sys->zero_copy ? 16 : 2, sys->output->buffer_num_recommended);
++ sys->output->buffer_size = sys->output->buffer_size_recommended;
++
++ if (conv_enable_out(p_filter, sys) != MMAL_SUCCESS)
++ return -1;
++
++ return 0;
++}
++
++static int OpenConverter(vlc_object_t * obj)
++{
++ filter_t * const p_filter = (filter_t *)obj;
++ int ret = VLC_EGENERIC;
++ filter_sys_t *sys;
++ MMAL_STATUS_T status;
++ MMAL_FOURCC_T enc_out;
++ const MMAL_FOURCC_T enc_in = MMAL_ENCODING_OPAQUE;
++ bool use_resizer;
++ bool use_isp;
++ int gpu_mem;
++
++ if (enc_in != vlc_to_mmal_pic_fourcc(p_filter->fmt_in.i_codec) ||
++ (enc_out = vlc_to_mmal_pic_fourcc(p_filter->fmt_out.i_codec)) == 0)
++ return VLC_EGENERIC;
+
+- mmal_buffer_header_release(buffer);
++ use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME);
++ use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME);
++
++ if (use_resizer) {
++ // use resizer overrides use_isp
++ use_isp = false;
++ }
++
++ // Check we have a sliced version of the fourcc if we want the resizer
++ if (use_resizer &&
++ (enc_out = pic_to_slice_mmal_fourcc(enc_out)) == 0)
++ return VLC_EGENERIC;
++
++ gpu_mem = hw_mmal_get_gpu_mem();
++
++ {
++ char dbuf0[5], dbuf1[5];
++ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] sar:%d/%d (gpu=%d)", __func__,
++ use_resizer ? "resize" : use_isp ? "isp" : "hvs",
++ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
++ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
++ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
++ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
++ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
++ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
++ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
++ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den,
++ gpu_mem);
++ }
++
++ sys = calloc(1, sizeof(filter_sys_t));
++ if (!sys) {
++ ret = VLC_ENOMEM;
++ goto fail;
++ }
++ p_filter->p_sys = sys;
++
++ // Init stuff the we destroy unconditionaly in Close first
++ vlc_mutex_init(&sys->lock);
++ vlc_sem_init(&sys->sem, CONV_MAX_LATENCY);
++ sys->err_stream = MMAL_SUCCESS;
++ pic_fifo_init(&sys->ret_pics);
++ pic_fifo_init(&sys->slice.pics);
++
++ sys->in_port_cb_fn = conv_input_port_cb;
++ if (use_resizer) {
++ sys->zero_copy = true;
++ sys->component_name = MMAL_COMPONENT_DEFAULT_RESIZER;
++ sys->out_port_cb_fn = slice_output_port_cb;
++ }
++ else if (use_isp) {
++ sys->zero_copy = false; // Copy directly into filter picture
++ sys->component_name = MMAL_COMPONENT_ISP_RESIZER;
++ sys->out_port_cb_fn = conv_output_port_cb;
+ } else {
+- mmal_buffer_header_release(buffer);
++ sys->zero_copy = false; // Copy directly into filter picture
++ sys->component_name = MMAL_COMPONENT_HVS;
++ sys->out_port_cb_fn = conv_output_port_cb;
++ }
++
++ status = mmal_component_create(sys->component_name, &sys->component);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
++ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
++ goto fail;
++ }
++ sys->output = sys->component->output[0];
++ sys->input = sys->component->input[0];
++
++ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
++ status = mmal_port_enable(sys->component->control, conv_control_port_cb);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
++ sys->component->control->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
++ sys->input->format->type = MMAL_ES_TYPE_VIDEO;
++ sys->input->format->encoding = enc_in;
++ sys->input->format->encoding_variant = MMAL_ENCODING_I420;
++ vlc_to_mmal_video_fmt(sys->input->format, &p_filter->fmt_in.video);
++ port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, 1);
++
++ mmal_log_dump_format(sys->input->format);
++
++ status = mmal_port_format_commit(sys->input);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(p_filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++ sys->input->buffer_size = sys->input->buffer_size_recommended;
++ sys->input->buffer_num = NUM_DECODER_BUFFER_HEADERS;
++
++ if (conv_enable_in(p_filter, sys) != MMAL_SUCCESS)
++ goto fail;
++
++ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->zero_copy);
++
++ if (sys->zero_copy) {
++ // If zc then we will do stride conversion when we copy to arm side
++ // so no need to worry about actual pic dimensions here
++ if (conv_set_output(p_filter, sys, NULL, enc_out) != 0)
++ goto fail;
++ }
++ else {
++ picture_t *pic = filter_NewPicture(p_filter);
++ int err = conv_set_output(p_filter, sys, pic, enc_out);
++ picture_Release(pic);
++ if (err != 0) {
++ goto fail;
++ }
++ }
++
++ status = mmal_component_enable(sys->component);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(p_filter, "Failed to enable component %s (status=%"PRIx32" %s)",
++ sys->component->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ msg_Dbg(p_filter, "Outpool: zc=%d, num=%d, size=%d", sys->zero_copy, sys->output->buffer_num, sys->output->buffer_size);
++ sys->out_pool = sys->zero_copy ?
++ mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size) :
++ mmal_pool_create(sys->output->buffer_num, 0);
++
++ if (sys->out_pool == NULL) {
++ msg_Err(p_filter, "Failed to create output pool");
++ goto fail;
++ }
++ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
++ {
++ msg_Err(p_filter, "Failed to create input pool");
++ goto fail;
++ }
++
++ {
++ unsigned int i;
++ for (i = 0; i != SUBS_MAX; ++i) {
++ if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], i + 1) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Failed to open subpic %d", i);
++ goto fail;
++ }
++ }
++ }
++
++ p_filter->pf_video_filter = conv_filter;
++ p_filter->pf_flush = conv_flush;
++ // video_drain NIF in filter structure
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, ">>> %s: ok", __func__);
++#endif
++
++ return VLC_SUCCESS;
++
++fail:
++ CloseConverter(obj);
++#if TRACE_ALL
++msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret);
++#endif
++ return ret;
++}
++
++
++typedef struct blend_sys_s {
++ vzc_pool_ctl_t * vzc;
++ const picture_t * last_dst; // Not a ref, just a hint that we have a new pic
++} blend_sys_t;
++
++static void FilterBlendMmal(filter_t *p_filter,
++ picture_t *dst, const picture_t * src,
++ int x_offset, int y_offset, int alpha)
++{
++ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
++#if TRACE_ALL
++ 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);
++#endif
++ // If nothing to do then do nothing
++ if (alpha == 0 ||
++ src->format.i_visible_height == 0 ||
++ src->format.i_visible_width == 0)
++ {
++ return;
++ }
++
++ if (dst->context == NULL)
++ msg_Err(p_filter, "MMAL pic missing context");
++ else
++ {
++ // cast away src const so we can ref it
++ MMAL_BUFFER_HEADER_T *buf = hw_mmal_vzc_buf_from_pic(sys->vzc, (picture_t *)src, dst,
++ dst != sys->last_dst || !hw_mmal_pic_has_sub_bufs(dst));
++ if (buf == NULL) {
++ msg_Err(p_filter, "Failed to allocate vzc buffer for subpic");
++ return;
++ }
++ MMAL_DISPLAYREGION_T * const reg = hw_mmal_vzc_buf_region(buf);
++
++ reg->set |=
++ MMAL_DISPLAY_SET_ALPHA | MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_DEST_RECT;
++
++ reg->fullscreen = 0;
++
++ reg->alpha = (uint32_t)(alpha & 0xff) | (1U << 31);
++
++ hw_mmal_vzc_buf_set_dest_rect(buf, x_offset, y_offset, src->format.i_visible_width, src->format.i_visible_height);
++
++ reg->dest_rect = (MMAL_RECT_T){0, 0, 0, 0};
++
++ hw_mmal_pic_sub_buf_add(dst, buf);
++
++ sys->last_dst = dst;
+ }
+ }
++
++static void FlushBlendMmal(filter_t * p_filter)
++{
++ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
++ sys->last_dst = NULL;
++ hw_mmal_vzc_pool_flush(sys->vzc);
++}
++
++static int OpenBlendMmal(vlc_object_t *object)
++{
++ filter_t * const p_filter = (filter_t *)object;
++ const vlc_fourcc_t vfcc_src = p_filter->fmt_in.video.i_chroma;
++ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
++
++ {
++ char dbuf0[5], dbuf1[5];
++ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %dx%d]->%s,%dx%d [(%d,%d) %dx%d]", __func__,
++ "blend",
++ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
++ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
++ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
++ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
++ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
++ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
++ }
++
++ if (vfcc_dst != VLC_CODEC_MMAL_OPAQUE || vfcc_src != VLC_CODEC_RGBA) {
++ return VLC_EGENERIC;
++ }
++
++ {
++ blend_sys_t * const sys = calloc(1, sizeof (*sys));
++ if (sys == NULL)
++ return VLC_ENOMEM;
++ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
++ {
++ free(sys);
++ return VLC_ENOMEM;
++ }
++ p_filter->p_sys = (filter_sys_t *)sys;
++ }
++
++ p_filter->pf_video_blend = FilterBlendMmal;
++ p_filter->pf_flush = FlushBlendMmal;
++
++ return VLC_SUCCESS;
++}
++
++static void CloseBlendMmal(vlc_object_t *object)
++{
++ filter_t * const p_filter = (filter_t *)object;
++ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
++
++ hw_mmal_vzc_pool_release(sys->vzc);
++ free(sys);
++}
++
++// ---------------------------------------------------------------------------
++
++static inline unsigned div255(unsigned v)
++{
++ /* It is exact for 8 bits, and has a max error of 1 for 9 and 10 bits
++ * while respecting full opacity/transparency */
++ return ((v >> 8) + v + 1) >> 8;
++ //return v / 255;
++}
++
++static inline unsigned int a_merge(unsigned int dst, unsigned src, unsigned f)
++{
++ return div255((255 - f) * (dst) + src * f);
++}
++
++
++static void FilterBlendNeon(filter_t *p_filter,
++ picture_t *dst_pic, const picture_t * src_pic,
++ int x_offset, int y_offset, int alpha)
++{
++ const uint8_t * s_data;
++ uint8_t * d_data;
++ int width = src_pic->format.i_visible_width;
++ int height = src_pic->format.i_visible_height;
++
++#if TRACE_ALL
++ 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);
++#else
++ VLC_UNUSED(p_filter);
++#endif
++
++ if (alpha == 0 ||
++ src_pic->format.i_visible_height == 0 ||
++ src_pic->format.i_visible_width == 0)
++ {
++ return;
++ }
++
++ x_offset += dst_pic->format.i_x_offset;
++ y_offset += dst_pic->format.i_y_offset;
++
++ // Deal with R/B overrun
++ if (x_offset + width >= (int)(dst_pic->format.i_x_offset + dst_pic->format.i_visible_width))
++ width = dst_pic->format.i_x_offset + dst_pic->format.i_visible_width - x_offset;
++ if (y_offset + height >= (int)(dst_pic->format.i_y_offset + dst_pic->format.i_visible_height))
++ height = dst_pic->format.i_y_offset + dst_pic->format.i_visible_height - y_offset;
++
++ if (width <= 0 || height <= 0) {
++ return;
++ }
++
++ // *** L/U overrun
++
++ s_data = src_pic->p[0].p_pixels +
++ src_pic->p[0].i_pixel_pitch * src_pic->format.i_x_offset +
++ src_pic->p[0].i_pitch * src_pic->format.i_y_offset;
++ d_data = dst_pic->p[0].p_pixels +
++ dst_pic->p[0].i_pixel_pitch * x_offset +
++ dst_pic->p[0].i_pitch * y_offset;
++
++
++ do {
++#if 1
++ blend_rgbx_rgba_neon(d_data, s_data, alpha, width);
++#else
++ int i;
++ for (i = 0; i != width; ++i) {
++ const uint32_t s_pel = ((const uint32_t *)s_data)[i];
++ const uint32_t d_pel = ((const uint32_t *)d_data)[i];
++ const unsigned int a = div255(alpha * (s_pel >> 24));
++ ((uint32_t *)d_data)[i] = 0xff000000 |
++ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 16) & 0xff, a) << 16) |
++ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) |
++ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 0) & 0xff, a) << 0 );
++ }
++#endif
++ s_data += src_pic->p[0].i_pitch;
++ d_data += dst_pic->p[0].i_pitch;
++ } while (--height > 0);
++}
++
++static void CloseBlendNeon(vlc_object_t *object)
++{
++ VLC_UNUSED(object);
++}
++
++static int OpenBlendNeon(vlc_object_t *object)
++{
++ filter_t * const p_filter = (filter_t *)object;
++ const vlc_fourcc_t vfcc_src = p_filter->fmt_in.video.i_chroma;
++ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
++
++ {
++ char dbuf0[5], dbuf1[5];
++ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %dx%d]->%s,%dx%d [(%d,%d) %dx%d]", __func__,
++ "blend",
++ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
++ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
++ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
++ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
++ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
++ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
++ }
++
++ if (vfcc_dst != VLC_CODEC_RGB32 || vfcc_src != VLC_CODEC_RGBA) {
++ return VLC_EGENERIC;
++ }
++
++ p_filter->pf_video_blend = FilterBlendNeon;
++ return VLC_SUCCESS;
++}
++
++vlc_module_begin()
++ set_category( CAT_INPUT )
++ set_subcategory( SUBCAT_INPUT_VCODEC )
++ set_shortname(N_("MMAL decoder"))
++ set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
++ set_capability("video decoder", 90)
++ add_shortcut("mmal_decoder")
++ add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
++ set_callbacks(OpenDecoder, CloseDecoder)
++
++ add_submodule()
++ set_category( CAT_VIDEO )
++ set_subcategory( SUBCAT_VIDEO_VFILTER )
++ set_shortname(N_("MMAL converterer"))
++ set_description(N_("MMAL conversion filter"))
++ add_shortcut("mmal_converter")
++ set_capability( "video converter", 900 )
++ add_bool(MMAL_RESIZE_NAME, /* default */ false, MMAL_RESIZE_TEXT, MMAL_RESIZE_LONGTEXT, /* advanced option */ false)
++ add_bool(MMAL_ISP_NAME, /* default */ false, MMAL_ISP_TEXT, MMAL_ISP_LONGTEXT, /* advanced option */ false)
++ set_callbacks(OpenConverter, CloseConverter)
++
++ add_submodule()
++ set_category( CAT_VIDEO )
++ set_subcategory( SUBCAT_VIDEO_VFILTER )
++ set_description(N_("Video pictures blending for MMAL"))
++ add_shortcut("mmal_blend")
++ set_capability("video blending", 120)
++ set_callbacks(OpenBlendMmal, CloseBlendMmal)
++
++ add_submodule()
++ set_category( CAT_VIDEO )
++ set_subcategory( SUBCAT_VIDEO_VFILTER )
++ set_description(N_("Video pictures blending for neon"))
++ add_shortcut("neon_blend")
++ set_capability("video blending", 110)
++ set_callbacks(OpenBlendNeon, CloseBlendNeon)
++
++vlc_module_end()
++
++
+--- a/modules/hw/mmal/deinterlace.c
++++ b/modules/hw/mmal/deinterlace.c
+@@ -26,11 +26,12 @@
+ #include "config.h"
+ #endif
+
+-#include <vlc_picture_pool.h>
++#include <stdatomic.h>
++
+ #include <vlc_common.h>
++#include <vlc_picture_pool.h>
+ #include <vlc_plugin.h>
+ #include <vlc_filter.h>
+-#include <vlc_atomic.h>
+
+ #include "mmal_picture.h"
+
+@@ -41,466 +42,602 @@
+
+ #define MIN_NUM_BUFFERS_IN_TRANSIT 2
+
+-#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu"
+-#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.")
+-#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.")
++#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu"
++#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.")
++#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.")
+
+-static int Open(filter_t *filter);
+-static void Close(filter_t *filter);
++#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv"
++#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace")
++#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace")
++
++#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast"
++#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace")
++#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace")
++
++#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none"
++#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace")
++#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\
++ "This is the default for > SD if < 96M gpu-mem")
++
++#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate"
++#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate")
++#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input")
++
++#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate"
++#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate")
++#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input")
+
+-vlc_module_begin()
+- set_shortname(N_("MMAL deinterlace"))
+- set_description(N_("MMAL-based deinterlace filter plugin"))
+- set_capability("video filter", 0)
+- set_category(CAT_VIDEO)
+- set_subcategory(SUBCAT_VIDEO_VFILTER)
+- set_callbacks(Open, Close)
+- add_shortcut("deinterlace")
+- add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT,
+- MMAL_DEINTERLACE_QPU_LONGTEXT, true);
+-vlc_module_end()
+
+-struct filter_sys_t {
++typedef struct filter_sys_t
++{
+ MMAL_COMPONENT_T *component;
+ MMAL_PORT_T *input;
+ MMAL_PORT_T *output;
++ MMAL_POOL_T *in_pool;
++ hw_mmal_port_pool_ref_t *out_ppr;
+
+- MMAL_QUEUE_T *filtered_pictures;
+- vlc_sem_t sem;
++ MMAL_QUEUE_T * out_q;
+
+- atomic_bool started;
++ bool half_rate;
++ bool use_qpu;
++ bool use_fast;
++ bool use_passthrough;
++ unsigned int seq_in;
++ unsigned int seq_out;
++} filter_sys_t;
+
+- /* statistics */
+- int output_in_transit;
+- int input_in_transit;
+-};
+-
+-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+-static picture_t *deinterlace(filter_t *filter, picture_t *picture);
+-static void flush(filter_t *filter);
+
+ #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
+
+-static int Open(filter_t *filter)
+-{
+- int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
+- (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base /
+- filter->fmt_in.video.i_frame_rate : 0;
+- bool use_qpu = var_InheritBool(filter, MMAL_DEINTERLACE_QPU);
++#define TRACE_ALL 0
+
+- MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
+- { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
+- MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
+- 4,
+- { 3, frame_duration, 0, use_qpu }
+- };
+
+- int ret = VLC_SUCCESS;
+- MMAL_STATUS_T status;
+- filter_sys_t *sys;
+
+- msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!",
+- frame_duration, use_qpu ? "used" : "unused");
++// Buffer attached to pic on success, is still valid on failure
++static picture_t * di_alloc_opaque(filter_t * const p_filter, MMAL_BUFFER_HEADER_T * const buf)
++{
++ filter_sys_t *const filter_sys = p_filter->p_sys;
++ picture_t * const pic = filter_NewPicture(p_filter);
+
+- if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
+- return VLC_EGENERIC;
++ if (pic == NULL)
++ goto fail1;
+
+- if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
+- return VLC_EGENERIC;
++ if (buf->length == 0) {
++ msg_Err(p_filter, "%s: Empty buffer", __func__);
++ goto fail2;
++ }
+
+- sys = calloc(1, sizeof(filter_sys_t));
+- if (!sys)
+- return VLC_ENOMEM;
+- filter->p_sys = sys;
++ if ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL)
++ goto fail2;
+
+- bcm_host_init();
++ buf_to_pic_copy_props(pic, buf);
+
+- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
+- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++#if TRACE_ALL
++ msg_Dbg(p_filter, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
++#endif
+
+- status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
+- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++ return pic;
+
+- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
+- status = mmal_port_enable(sys->component->control, control_port_cb);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
+- sys->component->control->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++fail2:
++ picture_Release(pic);
++fail1:
++// mmal_buffer_header_release(buf);
++ return NULL;
++}
+
+- sys->input = sys->component->input[0];
+- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
+- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
+- sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
+- sys->input->format->es->video.width = filter->fmt_in.video.i_width;
+- sys->input->format->es->video.height = filter->fmt_in.video.i_height;
+- sys->input->format->es->video.crop.x = 0;
+- sys->input->format->es->video.crop.y = 0;
+- sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width;
+- sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height;
+- sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num;
+- sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den;
++static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
++{
++#if TRACE_ALL
++ pic_ctx_mmal_t * ctx = buffer->user_data;
++// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
++
++ msg_Dbg((filter_t *)port->userdata, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buffer->cmd, ctx, buffer,
++ buffer->flags, (long long)buffer->pts);
++#else
++ VLC_UNUSED(port);
++#endif
+
+- es_format_Copy(&filter->fmt_out, &filter->fmt_in);
+- filter->fmt_out.video.i_frame_rate *= 2;
++ mmal_buffer_header_release(buffer);
+
+- status = mmal_port_format_commit(sys->input);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
++#if TRACE_ALL
++ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
++#endif
++}
++
++static void di_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
++{
++ if (buf->cmd == 0 && buf->length != 0)
++ {
++ // The filter structure etc. should always exist if we have contents
++ // but might not on later flushes as we shut down
++ filter_t * const p_filter = (filter_t *)port->userdata;
++ filter_sys_t * const sys = p_filter->p_sys;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts);
++#endif
++ mmal_queue_put(sys->out_q, buf);
++#if TRACE_ALL
++ msg_Dbg(p_filter, ">>> %s: out Q len=%d", __func__, mmal_queue_length(sys->out_q));
++#endif
+ }
+- sys->input->buffer_size = sys->input->buffer_size_recommended;
+- sys->input->buffer_num = sys->input->buffer_num_recommended;
++ else
++ {
++ mmal_buffer_header_reset(buf);
++ mmal_buffer_header_release(buf);
++ }
++}
+
+- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
+- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
+- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
+- 1
+- };
++static MMAL_STATUS_T fill_output_from_q(filter_t * const p_filter, filter_sys_t * const sys, MMAL_QUEUE_T * const q)
++{
++ MMAL_BUFFER_HEADER_T * out_buf;
+
+- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- goto out;
++ while ((out_buf = mmal_queue_get(q)) != NULL)
++ {
++ MMAL_STATUS_T err;
++ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Send buffer to output failed");
++ mmal_queue_put_back(q, out_buf);
++ return err;
+ }
+ }
++ return MMAL_SUCCESS;
++}
+
+- status = mmal_port_enable(sys->input, input_port_cb);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++static inline unsigned int seq_inc(unsigned int x)
++{
++ return x + 1 >= 16 ? 1 : x + 1;
++}
+
+- sys->output = sys->component->output[0];
+- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
+- mmal_format_full_copy(sys->output->format, sys->input->format);
++static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq)
++{
++ return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq);
++}
+
+- status = mmal_port_format_commit(sys->output);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++static picture_t *deinterlace(filter_t * p_filter, picture_t * p_pic)
++{
++ filter_sys_t * const sys = p_filter->p_sys;
++ picture_t *ret_pics = NULL;
++ MMAL_STATUS_T err;
+
+- sys->output->buffer_num = 3;
++#if TRACE_ALL
++ msg_Dbg(p_filter, "<<< %s", __func__);
++#endif
+
+- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
+- MMAL_PARAMETER_UINT32_T extra_buffers = {
+- { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) },
+- 5
+- };
+- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
+- status, mmal_status_to_string(status));
+- goto out;
++ // Reenable stuff if the last thing we did was flush
++ // Output should always be enabled
++ if (!sys->input->is_enabled &&
++ (err = mmal_port_enable(sys->input, di_input_port_cb)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Input port enable failed");
++ goto fail;
++ }
++
++ // Fill output from anything that has turned up in pool Q
++ if (hw_mmal_port_pool_ref_fill(sys->out_ppr) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Out port fill fail");
++ goto fail;
++ }
++
++ // Stuff into input
++ // We assume the BH is already set up with values reflecting pic date etc.
++ {
++ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic);
++ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->in_pool->queue);
++
++ if ((err = mmal_buffer_header_replicate(buf, pic_buf)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Failed to replicate input buffer: %d", err);
++ goto fail;
+ }
+
+- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
+- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
+- 1
+- };
++#if TRACE_ALL
++ msg_Dbg(p_filter, "In buf send: pic=%p, buf=%p/%p, ctx=%p, flags=%#x, len=%d/%d, pts=%lld",
++ p_pic, pic_buf, buf, pic_buf->user_data, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
++#endif
+
+- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
+- sys->output->name, status, mmal_status_to_string(status));
+- goto out;
++ picture_Release(p_pic);
++
++ // Add a sequence to the flags so we can track what we have actually
++ // deinterlaced
++ buf->flags = (buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0));
++ sys->seq_in = seq_inc(sys->seq_in);
++
++ if ((err = mmal_port_send_buffer(sys->input, buf)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Send buffer to input failed");
++ goto fail;
+ }
+ }
+
+- status = mmal_port_enable(sys->output, output_port_cb);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
+- sys->output->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
++ // Return anything that is in the out Q
++ {
++ MMAL_BUFFER_HEADER_T * out_buf;
++ picture_t ** pp_pic = &ret_pics;
++
++ // Advanced di has a 3 frame latency, so if the seq delta is greater
++ // than that then we are expecting at least two frames of output. Wait
++ // for one of those.
++ while ((out_buf = (seq_delta(sys->seq_in, sys->seq_out) > 3 ? mmal_queue_wait(sys->out_q) : mmal_queue_get(sys->out_q))) != NULL)
++ {
++ picture_t * const out_pic = di_alloc_opaque(p_filter, out_buf);
++ const unsigned int seq_out = (out_buf->flags / MMAL_BUFFER_HEADER_FLAG_USER0) & 0xf;
++
++ if (out_pic == NULL) {
++ msg_Warn(p_filter, "Failed to alloc new filter output pic");
++ mmal_queue_put_back(sys->out_q, out_buf);
++ break;
++ }
++
++#if TRACE_ALL
++ 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));
++#endif
++
++ *pp_pic = out_pic;
++ pp_pic = &out_pic->p_next;
++
++ // Ignore 0 seqs
++ // Don't think these should actually happen
++ if (seq_out != 0)
++ sys->seq_out = seq_out;
++ }
+ }
+
+- status = mmal_component_enable(sys->component);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
+- sys->component->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
++#if TRACE_ALL
++ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
++#endif
++
++ return ret_pics;
++
++fail:
++ picture_Release(p_pic);
++ return NULL;
++}
++
++static void di_flush(filter_t *p_filter)
++{
++ filter_sys_t * const sys = p_filter->p_sys;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "<<< %s", __func__);
++#endif
++
++ if (sys->input != NULL && sys->input->is_enabled)
++ mmal_port_disable(sys->input);
++
++ if (sys->output != NULL && sys->output->is_enabled)
++ {
++ // Wedge anything we've got into the output port as that will free the underlying buffers
++ fill_output_from_q(p_filter, sys, sys->out_q);
++
++ mmal_port_disable(sys->output);
++
++ // If that dumped anything real into the out_q then have another go
++ if (mmal_queue_length(sys->out_q) != 0)
++ {
++ mmal_port_enable(sys->output, di_output_port_cb);
++ fill_output_from_q(p_filter, sys, sys->out_q);
++ mmal_port_disable(sys->output);
++ // Out q should now be empty & should remain so until the input is reenabled
++ }
++ mmal_port_enable(sys->output, di_output_port_cb);
++
++ // Leaving the input disabled is fine - but we want to leave the output enabled
++ // so we can retrieve buffers that are still bound to pictures
+ }
+
+- sys->filtered_pictures = mmal_queue_create();
++ sys->seq_in = 1;
++ sys->seq_out = 1;
+
+- filter->pf_video_filter = deinterlace;
+- filter->pf_flush = flush;
++#if TRACE_ALL
++ msg_Dbg(p_filter, ">>> %s", __func__);
++#endif
++}
+
+- vlc_sem_init(&sys->sem, 0);
+
+-out:
+- if (ret != VLC_SUCCESS)
+- Close(filter);
++static void pass_flush(filter_t *p_filter)
++{
++ // Nothing to do
++ VLC_UNUSED(p_filter);
++}
+
+- return ret;
++static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic)
++{
++ VLC_UNUSED(p_filter);
++
++ p_pic->b_progressive = true;
++ return p_pic;
+ }
+
+-static void Close(filter_t *filter)
++
++static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+ {
+- filter_sys_t *sys = filter->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer;
++ filter_t *filter = (filter_t *)port->userdata;
++ MMAL_STATUS_T status;
+
+- if (!sys)
++ if (buffer->cmd == MMAL_EVENT_ERROR) {
++ status = *(uint32_t *)buffer->data;
++ msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
++ mmal_status_to_string(status));
++ }
++
++ mmal_buffer_header_reset(buffer);
++ mmal_buffer_header_release(buffer);
++}
++
++static void CloseMmalDeinterlace(filter_t *filter)
++{
++ filter_sys_t * const sys = filter->p_sys;
++
++#if TRACE_ALL
++ msg_Dbg(filter, "<<< %s", __func__);
++#endif
++
++ if (sys == NULL)
+ return;
+
+- if (sys->component && sys->component->control->is_enabled)
+- mmal_port_disable(sys->component->control);
++ if (sys->use_passthrough)
++ {
++ free(sys);
++ return;
++ }
+
+- if (sys->input && sys->input->is_enabled)
+- mmal_port_disable(sys->input);
++ di_flush(filter);
+
+- if (sys->output && sys->output->is_enabled)
+- mmal_port_disable(sys->output);
++ if (sys->component && sys->component->control->is_enabled)
++ mmal_port_disable(sys->component->control);
+
+ if (sys->component && sys->component->is_enabled)
+ mmal_component_disable(sys->component);
+
+- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
+- picture_t *pic = (picture_t *)buffer->user_data;
+- picture_Release(pic);
+- }
++ if (sys->in_pool != NULL)
++ mmal_pool_destroy(sys->in_pool);
++
++ hw_mmal_port_pool_ref_release(sys->out_ppr, false);
++ // Once we exit filter & sys are invalid so mark as such
++ sys->output->userdata = NULL;
+
+- if (sys->filtered_pictures)
+- mmal_queue_destroy(sys->filtered_pictures);
++ if (sys->out_q != NULL)
++ mmal_queue_destroy(sys->out_q);
+
+ if (sys->component)
+ mmal_component_release(sys->component);
+
+- vlc_sem_destroy(&sys->sem);
+ free(sys);
+
+ bcm_host_deinit();
+ }
+
+-static int send_output_buffer(filter_t *filter)
++
++static int OpenMmalDeinterlace(filter_t *filter)
+ {
+- filter_sys_t *sys = filter->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer;
++ int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
++ CLOCK_FREQ * filter->fmt_in.video.i_frame_rate_base /
++ filter->fmt_in.video.i_frame_rate : 0;
++
++ int ret = VLC_EGENERIC;
+ MMAL_STATUS_T status;
+- picture_t *picture;
+- int ret = 0;
++ filter_sys_t *sys;
+
+- if (!sys->output->is_enabled) {
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++ if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE ||
++ filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
++ return VLC_EGENERIC;
++
++#if TRACE_ALL
++ msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!",
++ frame_duration, use_qpu ? "used" : "unused");
++#endif
++
++ sys = calloc(1, sizeof(filter_sys_t));
++ if (!sys)
++ return VLC_ENOMEM;
++ filter->p_sys = sys;
++
++ sys->seq_in = 1;
++ sys->seq_out = 1;
++ sys->half_rate = false;
++ sys->use_qpu = true;
++ sys->use_fast = false;
++ sys->use_passthrough = false;
++
++ if (filter->fmt_in.video.i_width * filter->fmt_in.video.i_height > 768 * 576)
++ {
++ // We get stressed if we have to try too hard - so make life easier
++ sys->half_rate = true;
++ // Also check we actually have enough memory to do this
++ if (hw_mmal_get_gpu_mem() < (96 << 20))
++ sys->use_passthrough = true;
+
+- picture = filter_NewPicture(filter);
+- if (!picture) {
+- msg_Warn(filter, "Failed to get new picture");
+- ret = -1;
+- goto out;
+ }
+- picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate;
+- picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base;
+
+- buffer = picture->p_sys->buffer;
+- buffer->user_data = picture;
+- buffer->cmd = 0;
++ if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU))
++ sys->use_qpu = false;
++ if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV))
++ {
++ sys->use_fast = false;
++ sys->use_passthrough = false;
++ }
++ if (var_InheritBool(filter, MMAL_DEINTERLACE_FAST))
++ {
++ sys->use_fast = true;
++ sys->use_passthrough = false;
++ }
++ if (var_InheritBool(filter, MMAL_DEINTERLACE_NONE))
++ sys->use_passthrough = true;
++ if (var_InheritBool(filter, MMAL_DEINTERLACE_FULL_RATE))
++ sys->half_rate = false;
++ if (var_InheritBool(filter, MMAL_DEINTERLACE_HALF_RATE))
++ sys->half_rate = true;
++
++ if (sys->use_passthrough)
++ {
++ filter->pf_video_filter = pass_deinterlace;
++ filter->pf_flush = pass_flush;
++ return 0;
++ }
+
+- mmal_picture_lock(picture);
++ bcm_host_init();
+
+- status = mmal_port_send_buffer(sys->output, buffer);
++ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
+ if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)",
+- status, mmal_status_to_string(status));
+- mmal_buffer_header_release(buffer);
+- picture_Release(picture);
+- ret = -1;
+- } else {
+- atomic_fetch_add(&sys->output_in_transit, 1);
+- vlc_sem_post(&sys->sem);
++ msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
++ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
++ goto fail;
+ }
+
+-out:
+- return ret;
+-}
+-
+-static void fill_output_port(filter_t *filter)
+-{
+- filter_sys_t *sys = filter->p_sys;
+- /* allow at least 2 buffers in transit */
+- unsigned max_buffers_in_transit = __MAX(2, MIN_NUM_BUFFERS_IN_TRANSIT);
+- int buffers_available = sys->output->buffer_num -
+- atomic_load(&sys->output_in_transit) -
+- mmal_queue_length(sys->filtered_pictures);
+- int buffers_to_send = max_buffers_in_transit - sys->output_in_transit;
+- int i;
++ {
++ const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
++ { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
++ sys->use_fast ?
++ MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST :
++ MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
++ 4,
++ { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu }
++ };
+
+- if (buffers_to_send > buffers_available)
+- buffers_to_send = buffers_available;
++ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
++ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
++ goto fail;
++ }
++ }
+
+-#ifndef NDEBUG
+- msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
+- buffers_to_send, buffers_available, sys->output_in_transit,
+- sys->output->buffer_num);
+-#endif
+- for (i = 0; i < buffers_to_send; ++i) {
+- if (send_output_buffer(filter) < 0)
+- break;
++ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
++ status = mmal_port_enable(sys->component->control, control_port_cb);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
++ sys->component->control->name, status, mmal_status_to_string(status));
++ goto fail;
+ }
+-}
+
+-static picture_t *deinterlace(filter_t *filter, picture_t *picture)
+-{
+- filter_sys_t *sys = filter->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer;
+- picture_t *out_picture = NULL;
+- picture_t *ret = NULL;
+- MMAL_STATUS_T status;
+- unsigned i = 0;
++ sys->input = sys->component->input[0];
++ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
++ if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
++ sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
++ vlc_to_mmal_video_fmt(sys->input->format, &filter->fmt_in.video);
+
+- fill_output_port(filter);
++ es_format_Copy(&filter->fmt_out, &filter->fmt_in);
++ if (!sys->half_rate)
++ filter->fmt_out.video.i_frame_rate *= 2;
+
+- buffer = picture->p_sys->buffer;
+- buffer->user_data = picture;
+- buffer->pts = picture->date;
+- buffer->cmd = 0;
++ status = mmal_port_format_commit(sys->input);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++ sys->input->buffer_size = sys->input->buffer_size_recommended;
++ sys->input->buffer_num = 30;
++// sys->input->buffer_num = sys->input->buffer_num_recommended;
+
+- if (!picture->p_sys->displayed) {
+- status = mmal_port_send_buffer(sys->input, buffer);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)",
+- status, mmal_status_to_string(status));
+- picture_Release(picture);
+- } else {
+- picture->p_sys->displayed = true;
+- atomic_fetch_add(&sys->input_in_transit, 1);
+- vlc_sem_post(&sys->sem);
+- }
+- } else {
+- picture_Release(picture);
++ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
++ goto fail;
+ }
+
+- /*
+- * Send output buffers
+- */
+- while(atomic_load(&sys->started) && i < 2) {
+- if (buffer = mmal_queue_timedwait(sys->filtered_pictures, 2000)) {
+- i++;
+- if (!out_picture) {
+- out_picture = (picture_t *)buffer->user_data;
+- ret = out_picture;
+- } else {
+- out_picture->p_next = (picture_t *)buffer->user_data;
+- out_picture = out_picture->p_next;
+- }
+- out_picture->date = buffer->pts;
+- } else {
+- msg_Dbg(filter, "Failed waiting for filtered picture");
+- break;
+- }
++ status = mmal_port_enable(sys->input, di_input_port_cb);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
++ goto fail;
+ }
+- if (out_picture)
+- out_picture->p_next = NULL;
+
+- return ret;
+-}
++ sys->output = sys->component->output[0];
++ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
++ mmal_format_full_copy(sys->output->format, sys->input->format);
+
+-static void flush(filter_t *filter)
+-{
+- filter_sys_t *sys = filter->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer;
++ status = port_parameter_set_uint32(sys->output, MMAL_PARAMETER_EXTRA_BUFFERS, 5);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
++ status, mmal_status_to_string(status));
++ goto fail;
++ }
+
+- msg_Dbg(filter, "flush deinterlace filter");
++ status = port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, true);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
++ sys->output->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
+
+- msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)",
+- sys->input_in_transit, sys->output_in_transit);
+- mmal_port_flush(sys->output);
+- mmal_port_flush(sys->input);
+-
+- msg_Dbg(filter, "flush: wait for all buffers to be returned");
+- while (atomic_load(&sys->input_in_transit) ||
+- atomic_load(&sys->output_in_transit))
+- vlc_sem_wait(&sys->sem);
+-
+- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
+- picture_t *pic = (picture_t *)buffer->user_data;
+- msg_Dbg(filter, "flush: release already filtered pic %p",
+- (void *)pic);
+- picture_Release(pic);
++ status = mmal_port_format_commit(sys->output);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
++ goto fail;
+ }
+- atomic_store(&sys->started, false);
+- msg_Dbg(filter, "flush: done");
+-}
+
+-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+-{
+- filter_t *filter = (filter_t *)port->userdata;
+- MMAL_STATUS_T status;
++ sys->output->buffer_size = sys->output->buffer_size_recommended;
++ sys->output->buffer_num = 30;
++// sys->output->buffer_num = sys->output->buffer_num_recommended;
+
+- if (buffer->cmd == MMAL_EVENT_ERROR) {
+- status = *(uint32_t *)buffer->data;
+- msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
+- mmal_status_to_string(status));
++ if ((sys->out_q = mmal_queue_create()) == NULL)
++ {
++ msg_Err(filter, "Failed to create out Q");
++ goto fail;
+ }
+
+- mmal_buffer_header_release(buffer);
+-}
++ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
++ {
++ msg_Err(filter, "Failed to create input pool");
++ goto fail;
++ }
+
+-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+-{
+- picture_t *picture = (picture_t *)buffer->user_data;
+- filter_t *filter = (filter_t *)port->userdata;
+- filter_sys_t *sys = filter->p_sys;
++ sys->out_ppr = hw_mmal_port_pool_ref_create(sys->output, sys->output->buffer_num, sys->output->buffer_size);
++ if (sys->out_ppr == NULL) {
++ msg_Err(filter, "Failed to create output pool");
++ goto fail;
++ }
+
+- if (picture) {
+- picture_Release(picture);
+- } else {
+- msg_Warn(filter, "Got buffer without picture on input port - OOOPS");
+- mmal_buffer_header_release(buffer);
++ status = mmal_port_enable(sys->output, di_output_port_cb);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
++ sys->output->name, status, mmal_status_to_string(status));
++ goto fail;
+ }
+
+- atomic_fetch_sub(&sys->input_in_transit, 1);
+- vlc_sem_post(&sys->sem);
++ status = mmal_component_enable(sys->component);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
++ sys->component->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ filter->pf_video_filter = deinterlace;
++ filter->pf_flush = di_flush;
++ return 0;
++
++fail:
++ CloseMmalDeinterlace(filter);
++ return ret;
+ }
+
+-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+-{
+- filter_t *filter = (filter_t *)port->userdata;
+- filter_sys_t *sys = filter->p_sys;
+- picture_t *picture;
++vlc_module_begin()
++ set_shortname(N_("MMAL deinterlace"))
++ set_description(N_("MMAL-based deinterlace filter plugin"))
++ set_capability("video filter", 900)
++ set_category(CAT_VIDEO)
++ set_subcategory(SUBCAT_VIDEO_VFILTER)
++ set_callbacks(OpenMmalDeinterlace, CloseMmalDeinterlace)
++ add_shortcut("deinterlace")
++ add_bool(MMAL_DEINTERLACE_NO_QPU, false, MMAL_DEINTERLACE_NO_QPU_TEXT,
++ MMAL_DEINTERLACE_NO_QPU_LONGTEXT, true);
++ add_bool(MMAL_DEINTERLACE_ADV, false, MMAL_DEINTERLACE_ADV_TEXT,
++ MMAL_DEINTERLACE_ADV_LONGTEXT, true);
++ add_bool(MMAL_DEINTERLACE_FAST, false, MMAL_DEINTERLACE_FAST_TEXT,
++ MMAL_DEINTERLACE_FAST_LONGTEXT, true);
++ add_bool(MMAL_DEINTERLACE_NONE, false, MMAL_DEINTERLACE_NONE_TEXT,
++ MMAL_DEINTERLACE_NONE_LONGTEXT, true);
++ add_bool(MMAL_DEINTERLACE_HALF_RATE, false, MMAL_DEINTERLACE_HALF_RATE_TEXT,
++ MMAL_DEINTERLACE_HALF_RATE_LONGTEXT, true);
++ add_bool(MMAL_DEINTERLACE_FULL_RATE, false, MMAL_DEINTERLACE_FULL_RATE_TEXT,
++ MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true);
++
++vlc_module_end()
+
+- if (buffer->cmd == 0) {
+- if (buffer->length > 0) {
+- atomic_store(&sys->started, true);
+- mmal_queue_put(sys->filtered_pictures, buffer);
+- picture = (picture_t *)buffer->user_data;
+- } else {
+- picture = (picture_t *)buffer->user_data;
+- picture_Release(picture);
+- }
+
+- atomic_fetch_sub(&sys->output_in_transit, 1);
+- vlc_sem_post(&sys->sem);
+- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
+- msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled");
+- mmal_buffer_header_release(buffer);
+- } else {
+- mmal_buffer_header_release(buffer);
+- }
+-}
+--- a/modules/hw/mmal/mmal_picture.c
++++ b/modules/hw/mmal/mmal_picture.c
+@@ -21,25 +21,847 @@
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
++// We would really like to use vlc_thread.h but the detach thread stuff can't be
++// used here :-(
++#include <pthread.h>
++
++#include <stdatomic.h>
++
+ #include <vlc_common.h>
+ #include <vlc_picture.h>
+ #include <interface/mmal/mmal.h>
++#include <interface/mmal/util/mmal_util.h>
++#include <interface/mmal/util/mmal_default_components.h>
++#include <interface/vmcs_host/vcgencmd.h>
++#include <interface/vcsm/user-vcsm.h>
+
+ #include "mmal_picture.h"
+
+-int mmal_picture_lock(picture_t *picture)
++
++static void flush_range(void * start, size_t len)
++{
++ struct vcsm_user_clean_invalid2_s * b = calloc(1,
++ sizeof(struct vcsm_user_clean_invalid2_s) + sizeof(struct vcsm_user_clean_invalid2_block_s));
++
++ b->op_count = 1,
++
++ b->s[0] = (struct vcsm_user_clean_invalid2_block_s){
++ .invalidate_mode = 3,
++ .block_count = 1,
++ .start_address = start,
++ .block_size = len,
++ .inter_block_stride = 0
++ };
++
++ vcsm_clean_invalid2(b);
++
++ free(b);
++}
++
++MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs)
++{
++ switch (vlc_cs)
++ {
++ case COLOR_SPACE_BT601:
++ return MMAL_COLOR_SPACE_ITUR_BT601;
++ case COLOR_SPACE_BT709:
++ return MMAL_COLOR_SPACE_ITUR_BT709;
++ default:
++ break;
++ }
++ return MMAL_COLOR_SPACE_UNKNOWN;
++}
++
++void vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc)
++{
++ MMAL_VIDEO_FORMAT_T * const vf_mmal = &es_fmt->es->video;
++
++ vf_mmal->width = (vf_vlc->i_width + 31) & ~31;
++ vf_mmal->height = (vf_vlc->i_height + 15) & ~15;;
++ vf_mmal->crop.x = vf_vlc->i_x_offset;
++ vf_mmal->crop.y = vf_vlc->i_y_offset;
++ vf_mmal->crop.width = vf_vlc->i_visible_width;
++ vf_mmal->crop.height = vf_vlc->i_visible_height;
++ if (vf_vlc->i_sar_num == 0 || vf_vlc->i_sar_den == 0) {
++ vf_mmal->par.num = 1;
++ vf_mmal->par.den = 1;
++ } else {
++ vf_mmal->par.num = vf_vlc->i_sar_num;
++ vf_mmal->par.den = vf_vlc->i_sar_den;
++ }
++ vf_mmal->frame_rate.num = vf_vlc->i_frame_rate;
++ vf_mmal->frame_rate.den = vf_vlc->i_frame_rate_base;
++ vf_mmal->color_space = vlc_to_mmal_color_space(vf_vlc->space);
++}
++
++hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
++ const unsigned int headers, const uint32_t payload_size)
++{
++ hw_mmal_port_pool_ref_t * ppr = calloc(1, sizeof(hw_mmal_port_pool_ref_t));
++ if (ppr == NULL)
++ return NULL;
++
++ if ((ppr->pool = mmal_port_pool_create(port, headers, payload_size)) == NULL)
++ goto fail;
++
++ ppr->port = port;
++ atomic_store(&ppr->refs, 1);
++ return ppr;
++
++fail:
++ free(ppr);
++ return NULL;
++}
++
++static void do_detached(void *(*fn)(void *), void * v)
++{
++ pthread_t dothread;
++ pthread_create(&dothread, NULL, fn, v);
++ pthread_detach(dothread);
++}
++
++// Destroy a ppr - aranged s.t. it has the correct prototype for a pthread
++static void * kill_ppr(void * v)
++{
++ hw_mmal_port_pool_ref_t * const ppr = v;
++ if (ppr->port->is_enabled)
++ mmal_port_disable(ppr->port); // Avoid annoyed messages from MMAL when we kill the pool
++ mmal_port_pool_destroy(ppr->port, ppr->pool);
++ free(ppr);
++ return NULL;
++}
++
++void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb)
++{
++ if (ppr == NULL)
++ return;
++ if (atomic_fetch_sub(&ppr->refs, 1) != 1)
++ return;
++ if (in_cb)
++ do_detached(kill_ppr, ppr);
++ else
++ kill_ppr(ppr);
++}
++
++// Put buffer in port if possible - if not then release to pool
++// Returns true if sent, false if recycled
++static bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf)
++{
++ mmal_buffer_header_reset(buf);
++ buf->user_data = NULL;
++
++ if (mmal_port_send_buffer(ppr->port, buf) == MMAL_SUCCESS)
++ return true;
++ mmal_buffer_header_release(buf);
++ return false;
++}
++
++MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr)
++{
++ MMAL_BUFFER_HEADER_T * buf;
++ MMAL_STATUS_T err = MMAL_SUCCESS;
++
++ while ((buf = mmal_queue_get(ppr->pool->queue)) != NULL) {
++ if ((err = mmal_port_send_buffer(ppr->port, buf)) != MMAL_SUCCESS)
++ {
++ mmal_queue_put_back(ppr->pool->queue, buf);
++ break;
++ }
++ }
++ return err;
++}
++
++void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn)
++{
++ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
++ unsigned int i;
++
++ for (i = 0; i != ctx->buf_count; ++i) {
++ mmal_buffer_header_release(ctx->bufs[i]);
++ }
++ free(ctx);
++}
++
++picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn)
++{
++ const pic_ctx_mmal_t * const src_ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
++ pic_ctx_mmal_t * const dst_ctx = calloc(1, sizeof(*dst_ctx));
++ unsigned int i;
++
++ if (dst_ctx == NULL)
++ return NULL;
++
++ // Copy
++ dst_ctx->cmn = src_ctx->cmn;
++
++ dst_ctx->buf_count = src_ctx->buf_count;
++ for (i = 0; i != src_ctx->buf_count; ++i) {
++ dst_ctx->bufs[i] = src_ctx->bufs[i];
++ mmal_buffer_header_acquire(dst_ctx->bufs[i]);
++ }
++
++ return &dst_ctx->cmn;
++}
++
++static MMAL_BOOL_T
++buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata)
++{
++ hw_mmal_port_pool_ref_t * const ppr = userdata;
++
++ // Kill the callback - otherwise we will go in circles!
++ mmal_buffer_header_pre_release_cb_set(buf, (MMAL_BH_PRE_RELEASE_CB_T)0, NULL);
++ mmal_buffer_header_acquire(buf); // Ref it again
++
++ // As we have re-acquired the buffer we need a full release
++ // (not continue) to zap the ref count back to zero
++ // This is "safe" 'cos we have already reset the cb
++ hw_mmal_port_pool_ref_recycle(ppr, buf);
++ hw_mmal_port_pool_ref_release(ppr, true); // Assume in callback
++
++ return MMAL_TRUE;
++}
++
++// Buffer belongs to context on successful return from this fn
++// is still valid on failure
++picture_context_t *
++hw_mmal_gen_context(MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr)
++{
++ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
++
++ if (ctx == NULL)
++ return NULL;
++
++ hw_mmal_port_pool_ref_acquire(ppr);
++ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr);
++
++ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
++ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
++
++ ctx->bufs[0] = buf;
++ ctx->buf_count = 1;
++
++ buf->user_data = NULL;
++ return &ctx->cmn;
++}
++
++int hw_mmal_get_gpu_mem(void) {
++ static int stashed_val = -2;
++ VCHI_INSTANCE_T vchi_instance;
++ VCHI_CONNECTION_T *vchi_connection = NULL;
++ char rbuf[1024] = { 0 };
++
++ if (stashed_val >= -1)
++ return stashed_val;
++
++ if (vchi_initialise(&vchi_instance) != 0)
++ goto fail0;
++
++ //create a vchi connection
++ if (vchi_connect(NULL, 0, vchi_instance) != 0)
++ goto fail0;
++
++ vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1);
++
++ //send the gencmd for the argument
++ if (vc_gencmd_send("get_mem gpu") != 0)
++ goto fail;
++
++ if (vc_gencmd_read_response(rbuf, sizeof(rbuf) - 1) != 0)
++ goto fail;
++
++ if (strncmp(rbuf, "gpu=", 4) != 0)
++ goto fail;
++
++ char *p;
++ unsigned long m = strtoul(rbuf + 4, &p, 10);
++
++ if (p[0] != 'M' || p[1] != '\0')
++ stashed_val = -1;
++ else
++ stashed_val = (int)m << 20;
++
++ vc_gencmd_stop();
++
++ //close the vchi connection
++ vchi_disconnect(vchi_instance);
++
++ return stashed_val;
++
++fail:
++ vc_gencmd_stop();
++ vchi_disconnect(vchi_instance);
++fail0:
++ stashed_val = -1;
++ return -1;
++};
++
++// ===========================================================================
++
++typedef struct pool_ent_s
++{
++ struct pool_ent_s * next;
++ struct pool_ent_s * prev;
++
++ atomic_int ref_count;
++ unsigned int seq;
++
++ size_t size;
++
++ int vcsm_hdl;
++ int vc_hdl;
++ void * buf;
++
++ unsigned int width;
++ unsigned int height;
++
++ picture_t * pic;
++} pool_ent_t;
++
++
++typedef struct ent_list_hdr_s
++{
++ pool_ent_t * ents;
++ pool_ent_t * tail;
++ unsigned int n;
++} ent_list_hdr_t;
++
++#define ENT_LIST_HDR_INIT (ent_list_hdr_t){ \
++ .ents = NULL, \
++ .tail = NULL, \
++ .n = 0 \
++}
++
++struct vzc_pool_ctl_s
++{
++ atomic_int ref_count;
++
++ ent_list_hdr_t ent_pool;
++ ent_list_hdr_t ents_cur;
++ ent_list_hdr_t ents_prev;
++
++ unsigned int max_n;
++ unsigned int seq;
++
++ vlc_mutex_t lock;
++
++ MMAL_POOL_T * buf_pool;
++};
++
++typedef struct vzc_subbuf_ent_s
++{
++ pool_ent_t * ent;
++ MMAL_RECT_T pic_rect;
++ MMAL_RECT_T orig_dest_rect;
++ MMAL_DISPLAYREGION_T dreg;
++} vzc_subbuf_ent_t;
++
++
++static pool_ent_t * ent_extract(ent_list_hdr_t * const elh, pool_ent_t * const ent)
++{
++// printf("List %p [%d]: Ext %p\n", elh, elh->n, ent);
++
++ if (ent == NULL)
++ return NULL;
++
++ if (ent->next == NULL)
++ elh->tail = ent->prev;
++ else
++ ent->next->prev = ent->prev;
++
++ if (ent->prev == NULL)
++ elh->ents = ent->next;
++ else
++ ent->prev->next = ent->next;
++
++ ent->prev = ent->next = NULL;
++
++ --elh->n;
++
++ return ent; // For convienience
++}
++
++static inline pool_ent_t * ent_extract_tail(ent_list_hdr_t * const elh)
++{
++ return ent_extract(elh, elh->tail);
++}
++
++static void ent_add_head(ent_list_hdr_t * const elh, pool_ent_t * const ent)
++{
++// printf("List %p [%d]: Add %p\n", elh, elh->n, ent);
++
++ if ((ent->next = elh->ents) == NULL)
++ elh->tail = ent;
++ else
++ ent->next->prev = ent;
++
++ ent->prev = NULL;
++ elh->ents = ent;
++ ++elh->n;
++}
++
++static void ent_free(pool_ent_t * const ent)
++{
++// printf("Free ent: %p\n", ent);
++ if (ent != NULL) {
++ // If we still have a ref to a pic - kill it now
++ if (ent->pic != NULL)
++ picture_Release(ent->pic);
++
++ // Free contents
++ vcsm_unlock_hdl(ent->vcsm_hdl);
++
++ vcsm_free(ent->vcsm_hdl);
++
++ free(ent);
++ }
++}
++
++static void ent_free_list(ent_list_hdr_t * const elh)
++{
++ pool_ent_t * ent = elh->ents;
++
++// printf("Free list: %p [%d]\n", elh, elh->n);
++
++ *elh = ENT_LIST_HDR_INIT;
++
++ while (ent != NULL) {
++ pool_ent_t * const t = ent;
++ ent = t->next;
++ ent_free(t);
++ }
++}
++
++static void ent_list_move(ent_list_hdr_t * const dst, ent_list_hdr_t * const src)
++{
++// printf("Move %p->%p\n", src, dst);
++
++ *dst = *src;
++ *src = ENT_LIST_HDR_INIT;
++}
++
++// Scans "backwards" as that should give us the fastest match if we are
++// presented with pics in the same order each time
++static pool_ent_t * ent_list_extract_pic_ent(ent_list_hdr_t * const elh, picture_t * const pic)
++{
++ pool_ent_t *ent = elh->tail;
++
++// printf("Find list: %p [%d]; pic:%p\n", elh, elh->n, pic);
++
++ while (ent != NULL) {
++// printf("Check ent: %p, pic:%p\n", ent, ent->pic);
++
++ if (ent->pic == pic)
++ return ent_extract(elh, ent);
++ ent = ent->prev;
++ }
++ return NULL;
++}
++
++#define POOL_ENT_ALLOC_BLOCK 0x10000
++
++static pool_ent_t * pool_ent_alloc_new(size_t req_size)
+ {
+- picture_sys_t *pic_sys = picture->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
++ pool_ent_t * ent = calloc(1, sizeof(*ent));
++ const size_t alloc_size = (req_size + POOL_ENT_ALLOC_BLOCK - 1) & ~(POOL_ENT_ALLOC_BLOCK - 1);
++
++ if (ent == NULL)
++ return NULL;
++
++ ent->next = ent->prev = NULL;
++
++ // Alloc from vcsm
++ if ((ent->vcsm_hdl = vcsm_malloc_cache(alloc_size, VCSM_CACHE_TYPE_HOST, (char *)"vlc-subpic")) == -1)
++ goto fail1;
++ if ((ent->vc_hdl = vcsm_vc_hdl_from_hdl(ent->vcsm_hdl)) == 0)
++ goto fail2;
++ if ((ent->buf = vcsm_lock(ent->vcsm_hdl)) == NULL)
++ goto fail2;
++
++ ent->size = alloc_size;
++ return ent;
++
++fail2:
++ vcsm_free(ent->vcsm_hdl);
++fail1:
++ free(ent);
++ return NULL;
++}
++
++static inline pool_ent_t * pool_ent_ref(pool_ent_t * const ent)
++{
++// int n = atomic_fetch_add(&ent->ref_count, 1) + 1;
++// printf("Ref: %p: %d\n", ent, n);
++ atomic_fetch_add(&ent->ref_count, 1);
++ return ent;
++}
++
++static void pool_recycle(vzc_pool_ctl_t * const pc, pool_ent_t * const ent)
++{
++ pool_ent_t * xs = NULL;
++ int n;
++
++ if (ent == NULL)
++ return;
++
++ n = atomic_fetch_sub(&ent->ref_count, 1) - 1;
++
++// printf("%s: Pool: %p: Ent: %p: %d\n", __func__, &pc->ent_pool, ent, n);
++
++ if (n != 0)
++ return;
+
+- int offset = 0;
+- picture->p[0].p_pixels = buffer->data;
+- for (int i = 1; i < picture->i_planes; i++) {
+- offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines;
+- picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset;
++ if (ent->pic != NULL) {
++ picture_Release(ent->pic);
++ ent->pic = NULL;
+ }
+
+- pic_sys->displayed = false;
++ vlc_mutex_lock(&pc->lock);
+
+- return VLC_SUCCESS;
++ // If we have a full pool then extract the LRU and free it
++ // Free done outside mutex
++ if (pc->ent_pool.n >= pc->max_n)
++ xs = ent_extract_tail(&pc->ent_pool);
++
++ ent_add_head(&pc->ent_pool, ent);
++
++ vlc_mutex_unlock(&pc->lock);
++
++ ent_free(xs);
+ }
++
++// * This could be made more efficient, but this is easy
++static void pool_recycle_list(vzc_pool_ctl_t * const pc, ent_list_hdr_t * const elh)
++{
++ pool_ent_t * ent;
++ while ((ent = ent_extract_tail(elh)) != NULL) {
++ pool_recycle(pc, ent);
++ }
++}
++
++static pool_ent_t * pool_best_fit(vzc_pool_ctl_t * const pc, size_t req_size)
++{
++ pool_ent_t * best = NULL;
++
++ vlc_mutex_lock(&pc->lock);
++
++ {
++ pool_ent_t * ent = pc->ent_pool.ents;
++
++ // Simple scan
++ while (ent != NULL) {
++ if (ent->size >= req_size && ent->size <= req_size * 2 + POOL_ENT_ALLOC_BLOCK &&
++ (best == NULL || best->size > ent->size))
++ best = ent;
++ ent = ent->next;
++ }
++
++ // extract best from chain if we've found it
++ ent_extract(&pc->ent_pool, best);
++ }
++
++ vlc_mutex_unlock(&pc->lock);
++
++ if (best == NULL)
++ best = pool_ent_alloc_new(req_size);
++
++ if ((best->seq = ++pc->seq) == 0)
++ best->seq = ++pc->seq; // Never allow to be zero
++
++ atomic_store(&best->ref_count, 1);
++ return best;
++}
++
++bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt)
++{
++ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
++ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
++
++ es_fmt->type = MMAL_ES_TYPE_VIDEO;
++ es_fmt->encoding = MMAL_ENCODING_BGRA;
++ es_fmt->encoding_variant = MMAL_ENCODING_BGRA;
++
++ v_fmt->width = ent->width;
++ v_fmt->height = ent->height;
++ v_fmt->crop.x = 0;
++ v_fmt->crop.y = 0;
++ v_fmt->crop.width = ent->width;
++ v_fmt->crop.height = ent->height;
++
++ return true;
++}
++
++void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
++ uint32_t * const pWidth, uint32_t * const pHeight)
++{
++ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
++ *pWidth = ent->width;
++ *pHeight = ent->height;
++}
++
++
++MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf)
++{
++ vzc_subbuf_ent_t * sb = buf->user_data;
++ return &sb->dreg;
++}
++
++void hw_mmal_vzc_buf_set_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const int x, const int y, const int w, const int h)
++{
++ vzc_subbuf_ent_t * sb = buf->user_data;
++ sb->orig_dest_rect.x = x;
++ sb->orig_dest_rect.y = y;
++ sb->orig_dest_rect.width = w;
++ sb->orig_dest_rect.height = h;
++}
++
++static inline int rescale_x(int x, int mul, int div)
++{
++ return div == 0 ? x * mul : (x * mul + div/2) / div;
++}
++
++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)
++{
++ d->x = rescale_x(s->x, mul_rect->width, div_rect->width);
++ d->y = rescale_x(s->y, mul_rect->height, div_rect->height);
++ d->width = rescale_x(s->width, mul_rect->width, div_rect->width);
++ d->height = rescale_x(s->height, mul_rect->height, div_rect->height);
++}
++
++void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect)
++{
++ vzc_subbuf_ent_t * sb = buf->user_data;
++ if (scale_rect == NULL) {
++ sb->dreg.dest_rect = sb->orig_dest_rect;
++ }
++ else
++ {
++ rescale_rect(&sb->dreg.dest_rect, &sb->orig_dest_rect,
++ scale_rect, &sb->pic_rect);
++ }
++}
++
++unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf)
++{
++ vzc_subbuf_ent_t * sb = buf->user_data;
++ return sb->ent->seq;
++}
++
++
++// The intent with the ents_cur & ents_last stuff is to remember the buffers
++// we used on the last frame and reuse them on the current one if they are the
++// same. Unfortunately detection of "is_first" is only a heuristic (there are
++// no rules governing the order in which things are blended) so we must deal
++// (fairly) gracefully with it never (or always) being set.
++
++MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic, const picture_t * const dst_pic, const bool is_first)
++{
++ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(pc->buf_pool->queue);
++ vzc_subbuf_ent_t * sb;
++
++ if (buf == NULL)
++ return NULL;
++
++ if ((sb = calloc(1, sizeof(*sb))) == NULL)
++ goto fail1;
++
++ // If first or we've had a lot of stuff move everything to the last list
++ // (we could deal more gracefully with the "too many" case but it shouldn't
++ // really happen)
++ if (is_first || pc->ents_cur.n >= CTX_BUFS_MAX) {
++ pool_recycle_list(pc, &pc->ents_prev);
++ ent_list_move(&pc->ents_prev, &pc->ents_cur);
++ }
++
++ sb->dreg.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
++ sb->dreg.hdr.size = sizeof(sb->dreg);
++ buf->user_data = sb;
++
++ {
++ // ?? Round start offset as well as length
++ const video_format_t *const fmt = &pic->format;
++
++ const unsigned int bpp = (fmt->i_bits_per_pixel + 7) >> 3;
++ const unsigned int xl = (fmt->i_x_offset & ~15);
++ const unsigned int xr = (fmt->i_x_offset + fmt->i_visible_width + 15) & ~15;
++ const size_t dst_stride = (xr - xl) * bpp;
++ const size_t dst_lines = ((fmt->i_visible_height + 15) & ~15);
++ const size_t dst_size = dst_stride * dst_lines;
++
++ pool_ent_t * ent = ent_list_extract_pic_ent(&pc->ents_prev, pic);
++
++ // If we didn't find ent in last then look in cur in case is_first
++ // isn't working
++ if (ent == NULL)
++ ent = ent_list_extract_pic_ent(&pc->ents_cur, pic);
++
++// printf("ent_found: %p\n", ent);
++
++ if (ent == NULL)
++ {
++ if ((ent = pool_best_fit(pc, dst_size)) == NULL)
++ goto fail2;
++ ent->pic = picture_Hold(pic);
++ }
++
++ ent_add_head(&pc->ents_cur, ent);
++
++ sb->ent = pool_ent_ref(ent);
++ hw_mmal_vzc_pool_ref(pc);
++
++ // Copy data
++ buf->next = NULL;
++ buf->cmd = 0;
++ buf->data = (uint8_t *)(ent->vc_hdl);
++ buf->alloc_size = buf->length = dst_size;
++ buf->offset = 0;
++ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
++ buf->pts = buf->dts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
++ buf->type->video = (MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T){
++ .planes = 1,
++ .pitch = { dst_stride }
++ };
++
++ // Remember offsets
++ sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT;
++
++// 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);
++
++ sb->dreg.src_rect = (MMAL_RECT_T){
++ .x = (fmt->i_x_offset - xl),
++ .y = 0,
++ .width = fmt->i_visible_width,
++ .height = fmt->i_visible_height
++ };
++
++ sb->pic_rect = (MMAL_RECT_T){
++ .x = dst_pic->format.i_x_offset,
++ .y = dst_pic->format.i_y_offset,
++ .width = dst_pic->format.i_visible_width,
++ .height = dst_pic->format.i_visible_height
++ };
++
++ ent->width = dst_stride / bpp;
++ ent->height = dst_lines;
++
++ // 2D copy
++ {
++ unsigned int i;
++ uint8_t *d = ent->buf;
++ const uint8_t *s = pic->p[0].p_pixels + xl * bpp + fmt->i_y_offset * pic->p[0].i_pitch;
++ for (i = 0; i != fmt->i_visible_height; ++i) {
++ memcpy(d, s, dst_stride);
++ d += dst_stride;
++ s += pic->p[0].i_pitch;
++ }
++
++ // And make sure it is actually in memory
++ flush_range(ent->buf, d - (uint8_t *)ent->buf);
++ }
++ }
++
++ return buf;
++
++fail2:
++ free(sb);
++fail1:
++ mmal_buffer_header_release(buf);
++ return NULL;
++}
++
++void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc)
++{
++ pool_recycle_list(pc, &pc->ents_prev);
++ pool_recycle_list(pc, &pc->ents_cur);
++}
++
++static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc)
++{
++
++// printf("<<< %s\n", __func__);
++
++ hw_mmal_vzc_pool_flush(pc);
++
++ ent_free_list(&pc->ent_pool);
++
++ if (pc->buf_pool != NULL)
++ mmal_pool_destroy(pc->buf_pool);
++
++ vlc_mutex_destroy(&pc->lock);
++
++// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash
++
++ free (pc);
++
++ vcsm_exit();
++
++// printf(">>> %s\n", __func__);
++}
++
++void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc)
++{
++ int n;
++
++ if (pc == NULL)
++ return;
++
++ n = atomic_fetch_sub(&pc->ref_count, 1) - 1;
++
++ if (n != 0)
++ return;
++
++ hw_mmal_vzc_pool_delete(pc);
++}
++
++void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc)
++{
++ atomic_fetch_add(&pc->ref_count, 1);
++}
++
++static MMAL_BOOL_T vcz_pool_release_cb(MMAL_POOL_T * buf_pool, MMAL_BUFFER_HEADER_T *buf, void *userdata)
++{
++ vzc_pool_ctl_t * const pc = userdata;
++ vzc_subbuf_ent_t * const sb = buf->user_data;
++
++ VLC_UNUSED(buf_pool);
++
++// printf("<<< %s\n", __func__);
++
++ if (sb != NULL) {
++ buf->user_data = NULL;
++ pool_recycle(pc, sb->ent);
++ hw_mmal_vzc_pool_release(pc);
++ free(sb);
++ }
++
++// printf(">>> %s\n", __func__);
++
++ return MMAL_TRUE;
++}
++
++vzc_pool_ctl_t * hw_mmal_vzc_pool_new()
++{
++ vzc_pool_ctl_t * const pc = calloc(1, sizeof(*pc));
++
++ if (pc == NULL)
++ return NULL;
++
++ vcsm_init();
++
++ pc->max_n = 8;
++ vlc_mutex_init(&pc->lock); // Must init before potential destruction
++
++ if ((pc->buf_pool = mmal_pool_create(64, 0)) == NULL)
++ {
++ hw_mmal_vzc_pool_delete(pc);
++ return NULL;
++ }
++
++ atomic_store(&pc->ref_count, 1);
++
++ mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc);
++
++ return pc;
++}
++
++
++
+--- a/modules/hw/mmal/mmal_picture.h
++++ b/modules/hw/mmal/mmal_picture.h
+@@ -24,19 +24,209 @@
+ #ifndef VLC_MMAL_MMAL_PICTURE_H_
+ #define VLC_MMAL_MMAL_PICTURE_H_
+
++#include <stdatomic.h>
++
+ #include <vlc_common.h>
+ #include <interface/mmal/mmal.h>
+
+ /* Think twice before changing this. Incorrect values cause havoc. */
+ #define NUM_ACTUAL_OPAQUE_BUFFERS 30
+
+-struct picture_sys_t {
+- vlc_object_t *owner;
++#ifndef VLC_TICK_INVALID
++#define VLC_TICK_INVALID VLC_TS_INVALID
++#define VLC_VER_3 1
++#else
++#define VLC_VER_3 0
++#endif
++
++typedef struct mmal_port_pool_ref_s
++{
++ atomic_uint refs;
++ MMAL_POOL_T * pool;
++ MMAL_PORT_T * port;
++} hw_mmal_port_pool_ref_t;
++
++typedef struct pic_ctx_subpic_s {
++ picture_t * subpic;
++ int x, y;
++ int alpha;
++} pic_ctx_subpic_t;
++
++
++#define CTX_BUFS_MAX 4
++
++typedef struct pic_ctx_mmal_s {
++ picture_context_t cmn; // PARENT: Common els at start
++
++ unsigned int buf_count;
++ MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX];
++
++#if 0
++ MMAL_BUFFER_HEADER_T * buf;
++ hw_mmal_port_pool_ref_t * ppr;
++
++ MMAL_BUFFER_HEADER_T * sub_bufs;
++ MMAL_BUFFER_HEADER_T * sub_tail;
++
++ vlc_object_t * obj;
++#endif
++} pic_ctx_mmal_t;
++
++
++MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs);
++void vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc);
+
+- MMAL_BUFFER_HEADER_T *buffer;
+- bool displayed;
+-};
++hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
++ const unsigned int headers, const uint32_t payload_size);
++void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb);
++//bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf);
++MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr);
++static inline void hw_mmal_port_pool_ref_acquire(hw_mmal_port_pool_ref_t * const ppr)
++{
++ atomic_fetch_add(&ppr->refs, 1);
++}
++
++static inline int hw_mmal_pic_has_sub_bufs(picture_t * const pic)
++{
++ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
++ return ctx->buf_count > 1;
++}
++
++static inline void hw_mmal_pic_sub_buf_add(picture_t * const pic, MMAL_BUFFER_HEADER_T * const sub)
++{
++ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
++
++ if (ctx->buf_count >= CTX_BUFS_MAX) {
++ mmal_buffer_header_release(sub);
++ return;
++ }
++
++ ctx->bufs[ctx->buf_count++] = sub;
++}
++
++static inline MMAL_BUFFER_HEADER_T * hw_mmal_pic_sub_buf_get(picture_t * const pic, const unsigned int n)
++{
++ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
++
++ return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1];
++}
++
++picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn);
++void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn);
++picture_context_t * hw_mmal_gen_context(MMAL_BUFFER_HEADER_T * buf,
++ hw_mmal_port_pool_ref_t * const ppr);
++
++int hw_mmal_get_gpu_mem(void);
++
++
++static inline MMAL_STATUS_T port_parameter_set_uint32(MMAL_PORT_T * port, uint32_t id, uint32_t val)
++{
++ const MMAL_PARAMETER_UINT32_T param = {
++ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_UINT32_T)},
++ .value = val
++ };
++ return mmal_port_parameter_set(port, ¶m.hdr);
++}
++
++static inline MMAL_STATUS_T port_parameter_set_bool(MMAL_PORT_T * port, uint32_t id, int val)
++{
++ const MMAL_PARAMETER_BOOLEAN_T param = {
++ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_BOOLEAN_T)},
++ .enable = val
++ };
++ return mmal_port_parameter_set(port, ¶m.hdr);
++}
++
++static inline MMAL_STATUS_T port_send_replicated(MMAL_PORT_T * const port, MMAL_POOL_T * const rep_pool,
++ MMAL_BUFFER_HEADER_T * const src_buf,
++ const uint64_t seq)
++{
++ MMAL_STATUS_T err;
++ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
++
++ if (rep_buf == NULL)
++ return MMAL_ENOSPC;
++
++ if ((err = mmal_buffer_header_replicate(rep_buf, src_buf)) != MMAL_SUCCESS)
++ return err;
++
++ rep_buf->pts = seq;
++
++ if ((err = mmal_port_send_buffer(port, rep_buf)) != MMAL_SUCCESS)
++ {
++ mmal_buffer_header_release(rep_buf);
++ return err;
++ }
++
++ return MMAL_SUCCESS;
++}
++
++static inline void pic_to_buf_copy_props(MMAL_BUFFER_HEADER_T * const buf, const picture_t * const pic)
++{
++ if (!pic->b_progressive)
++ {
++ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
++ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
++ }
++ else
++ {
++ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
++ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
++ }
++ if (pic->b_top_field_first)
++ {
++ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
++ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
++ }
++ else
++ {
++ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
++ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
++ }
++ buf->pts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
++ buf->dts = buf->pts;
++}
++
++static inline void buf_to_pic_copy_props(picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf)
++{
++ // Contrary to docn the interlace & tff flags turn up in the header flags rather than the
++ // video specific flags (which appear to be currently unused).
++ pic->b_progressive = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED) == 0;
++ pic->b_top_field_first = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST) != 0;
++
++ pic->date = buf->pts != MMAL_TIME_UNKNOWN ? buf->pts :
++ buf->dts != MMAL_TIME_UNKNOWN ? buf->dts :
++ VLC_TICK_INVALID;
++}
++
++// Retrieve buf from pic & update with pic props
++static inline MMAL_BUFFER_HEADER_T * pic_mmal_buffer(const picture_t *const pic)
++{
++ MMAL_BUFFER_HEADER_T * const buf = ((pic_ctx_mmal_t *)pic->context)->bufs[0];
++ if (buf != NULL)
++ pic_to_buf_copy_props(buf, pic);
++
++ return buf;
++}
++
++struct vzc_pool_ctl_s;
++typedef struct vzc_pool_ctl_s vzc_pool_ctl_t;
++
++bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt);
++MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf);
++void hw_mmal_vzc_buf_set_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const int x, const int y, const int w, const int h);
++void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect);
++unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf);
++MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic, const picture_t * const dst_pic, const bool is_first);
++void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
++ uint32_t * const pWidth, uint32_t * const pHeight);
++
++void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc);
++void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc);
++void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc);
++vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void);
+
+-int mmal_picture_lock(picture_t *picture);
++#define VOUT_DISPLAY_CHANGE_MMAL_BASE 1024
++#define VOUT_DISPLAY_CHANGE_MMAL_HIDE (VOUT_DISPLAY_CHANGE_MMAL_BASE + 0)
+
+ #endif
+--- /dev/null
++++ b/modules/hw/mmal/subpic.c
+@@ -0,0 +1,222 @@
++/*****************************************************************************
++ * mmal.c: MMAL-based decoder plugin for Raspberry Pi
++ *****************************************************************************
++ * Authors: jc(a)kynesim.co.uk
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU Lesser General Public License as published by
++ * the Free Software Foundation; either version 2.1 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
++ *****************************************************************************/
++
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <stdatomic.h>
++
++#include <vlc_common.h>
++#include <vlc_plugin.h>
++#include <vlc_codec.h>
++#include <vlc_filter.h>
++#include <vlc_threads.h>
++
++#include <bcm_host.h>
++#include <interface/mmal/mmal.h>
++#include <interface/mmal/util/mmal_util.h>
++#include <interface/mmal/util/mmal_default_components.h>
++
++#include "mmal_picture.h"
++#include "subpic.h"
++
++
++#define TRACE_ALL 0
++
++static inline bool cmp_rect(const MMAL_RECT_T * const a, const MMAL_RECT_T * const b)
++{
++ return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height;
++}
++
++void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const sub)
++{
++ VLC_UNUSED(p_filter);
++ if (sub->port != NULL && sub->port->is_enabled)
++ mmal_port_disable(sub->port);
++ sub->seq = 0;
++}
++
++void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe)
++{
++ hw_mmal_subpic_flush(p_filter, spe);
++
++ if (spe->pool != NULL)
++ mmal_pool_destroy(spe->pool);
++
++ // Zap to avoid any accidental reuse
++ *spe = (subpic_reg_stash_t){NULL};
++}
++
++MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, const unsigned int layer)
++{
++ MMAL_STATUS_T err;
++
++ // Start by zapping all to zero
++ *spe = (subpic_reg_stash_t){NULL};
++
++ if ((err = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Failed to set sub port zero copy");
++ return err;
++ }
++
++ if ((spe->pool = mmal_pool_create(30, 0)) == NULL)
++ {
++ msg_Err(p_filter, "Failed to create sub pool");
++ return MMAL_ENOMEM;
++ }
++
++ port->userdata = (void *)p_filter;
++ spe->port = port;
++ spe->layer = layer;
++
++ return MMAL_SUCCESS;
++}
++
++static void conv_subpic_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
++{
++#if TRACE_ALL
++ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, user=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
++ __func__, buf->cmd, buf->user_data, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
++#else
++ VLC_UNUSED(port);
++#endif
++
++ mmal_buffer_header_release(buf); // Will extract & release pic in pool callback
++}
++
++
++int hw_mmal_subpic_update(vlc_object_t * const p_filter,
++ picture_t * const p_pic, const unsigned int sub_no,
++ subpic_reg_stash_t * const spe,
++ const MMAL_RECT_T * const scale_out,
++ const uint64_t pts)
++{
++ MMAL_STATUS_T err;
++ MMAL_BUFFER_HEADER_T * const sub_buf = hw_mmal_pic_sub_buf_get(p_pic, sub_no);
++
++ if (sub_buf == NULL)
++ {
++ if (spe->port->is_enabled && spe->seq != 0)
++ {
++ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(spe->pool->queue);
++
++ if (buf == NULL) {
++ msg_Err(p_filter, "Buffer get for subpic failed");
++ return -1;
++ }
++#if TRACE_ALL
++ msg_Dbg(p_filter, "Remove pic for sub %d", sub_no);
++#endif
++ buf->cmd = 0;
++ buf->data = NULL;
++ buf->alloc_size = 0;
++ buf->offset = 0;
++ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
++ buf->pts = pts;
++ buf->dts = MMAL_TIME_UNKNOWN;
++ buf->user_data = NULL;
++
++ if ((err = mmal_port_send_buffer(spe->port, buf)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Send buffer to subput failed");
++ mmal_buffer_header_release(buf);
++ return -1;
++ }
++
++ spe->seq = 0;
++ }
++ }
++ else
++ {
++ const unsigned int seq = hw_mmal_vzc_buf_seq(sub_buf);
++ bool needs_update = (spe->seq != seq);
++
++ hw_mmal_vzc_buf_scale_dest_rect(sub_buf, scale_out);
++
++ if (hw_mmal_vzc_buf_set_format(sub_buf, spe->port->format))
++ {
++ MMAL_DISPLAYREGION_T * const dreg = hw_mmal_vzc_buf_region(sub_buf);
++ MMAL_VIDEO_FORMAT_T *const v_fmt = &spe->port->format->es->video;
++
++ v_fmt->frame_rate.den = p_pic->format.i_frame_rate_base;
++ v_fmt->frame_rate.num = p_pic->format.i_frame_rate;
++ v_fmt->par.den = p_pic->format.i_sar_den;
++ v_fmt->par.num = p_pic->format.i_sar_num;
++ v_fmt->color_space = MMAL_COLOR_SPACE_UNKNOWN;
++
++
++ if (needs_update || dreg->alpha != spe->alpha || !cmp_rect(&dreg->dest_rect, &spe->dest_rect)) {
++
++ spe->alpha = dreg->alpha;
++ spe->dest_rect = dreg->dest_rect;
++ needs_update = true;
++#if TRACE_ALL
++ msg_Dbg(p_filter, "Update region for sub %d", sub_no);
++#endif
++ dreg->layer = spe->layer;
++ dreg->set |= MMAL_DISPLAY_SET_LAYER;
++
++ if ((err = mmal_port_parameter_set(spe->port, &dreg->hdr)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Set display region on subput failed");
++ return -1;
++ }
++
++ if ((err = mmal_port_format_commit(spe->port)) != MMAL_SUCCESS)
++ {
++ msg_Dbg(p_filter, "%s: Subpic commit fail: %d", __func__, err);
++ return -1;
++ }
++ }
++ }
++
++ if (!spe->port->is_enabled)
++ {
++ spe->port->buffer_num = 30;
++ spe->port->buffer_size = spe->port->buffer_size_recommended; // Not used but shuts up the error checking
++
++ if ((err = mmal_port_enable(spe->port, conv_subpic_cb)) != MMAL_SUCCESS)
++ {
++ msg_Dbg(p_filter, "%s: Subpic enable fail: %d", __func__, err);
++ return -1;
++ }
++ }
++
++ if (needs_update)
++ {
++#if TRACE_ALL
++ msg_Dbg(p_filter, "Update pic for sub %d", sub_no);
++#endif
++ if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Send buffer to subput failed");
++ return -1;
++ }
++
++ spe->seq = seq;
++ }
++ }
++ return 1;
++}
++
++
++
+--- /dev/null
++++ b/modules/hw/mmal/subpic.h
+@@ -0,0 +1,28 @@
++#ifndef VLC_HW_MMAL_SUBPIC_H_
++#define VLC_HW_MMAL_SUBPIC_H_
++
++typedef struct subpic_reg_stash_s
++{
++ MMAL_PORT_T * port;
++ MMAL_POOL_T * pool;
++ unsigned int layer;
++ // Shadow vars so we can tell if stuff has changed
++ MMAL_RECT_T dest_rect;
++ unsigned int alpha;
++ unsigned int seq;
++} subpic_reg_stash_t;
++
++int hw_mmal_subpic_update(vlc_object_t * const p_filter,
++ picture_t * const p_pic, const unsigned int sub_no,
++ subpic_reg_stash_t * const stash,
++ const MMAL_RECT_T * const scale_out,
++ const uint64_t pts);
++
++void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
++
++void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
++
++MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, const unsigned int layer);
++
++#endif
++
+--- a/modules/hw/mmal/vout.c
++++ b/modules/hw/mmal/vout.c
+@@ -27,21 +27,24 @@
+ #endif
+
+ #include <math.h>
++#include <stdatomic.h>
+
+ #include <vlc_common.h>
+-#include <vlc_atomic.h>
+ #include <vlc_plugin.h>
+ #include <vlc_threads.h>
+ #include <vlc_vout_display.h>
++#include <vlc_modules.h>
+
+ #include "mmal_picture.h"
++#include "subpic.h"
+
+ #include <bcm_host.h>
+ #include <interface/mmal/mmal.h>
+ #include <interface/mmal/util/mmal_util.h>
+ #include <interface/mmal/util/mmal_default_components.h>
+ #include <interface/vmcs_host/vc_tvservice.h>
+-#include <interface/vmcs_host/vc_dispmanx.h>
++
++#define TRACE_ALL 0
+
+ #define MAX_BUFFERS_IN_TRANSIT 1
+ #define VC_TV_MAX_MODE_IDS 127
+@@ -50,11 +53,6 @@
+ #define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.")
+ #define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.")
+
+-#define MMAL_BLANK_BACKGROUND_NAME "mmal-blank-background"
+-#define MMAL_BLANK_BACKGROUND_TEXT N_("Blank screen below video.")
+-#define MMAL_BLANK_BACKGROUND_LONGTEXT N_("Render blank screen below video. " \
+- "Increases VideoCore load.")
+-
+ #define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate"
+ #define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.")
+ #define MMAL_ADJUST_REFRESHRATE_LONGTEXT N_("Adjust HDMI refresh rate to the video.")
+@@ -68,64 +66,30 @@
+ #define PHASE_OFFSET_TARGET ((double)0.25)
+ #define PHASE_CHECK_INTERVAL 100
+
+-static int Open(vlc_object_t *);
+-static void Close(vlc_object_t *);
++#define SUBS_MAX 4
+
+-vlc_module_begin()
+- set_shortname(N_("MMAL vout"))
+- set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
+- set_capability("vout display", 90)
+- add_shortcut("mmal_vout")
+- add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
+- add_bool(MMAL_BLANK_BACKGROUND_NAME, true, MMAL_BLANK_BACKGROUND_TEXT,
+- MMAL_BLANK_BACKGROUND_LONGTEXT, true);
+- add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
+- MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
+- add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
+- MMAL_NATIVE_INTERLACE_LONGTEXT, false)
+- set_callbacks(Open, Close)
+-vlc_module_end()
+-
+-struct dmx_region_t {
+- struct dmx_region_t *next;
+- picture_t *picture;
+- VC_RECT_T bmp_rect;
+- VC_RECT_T src_rect;
+- VC_RECT_T dst_rect;
+- VC_DISPMANX_ALPHA_T alpha;
+- DISPMANX_ELEMENT_HANDLE_T element;
+- DISPMANX_RESOURCE_HANDLE_T resource;
+- int32_t pos_x;
+- int32_t pos_y;
+-};
++typedef struct vout_subpic_s {
++ MMAL_COMPONENT_T *component;
++ subpic_reg_stash_t sub;
++} vout_subpic_t;
+
+ struct vout_display_sys_t {
+- vlc_cond_t buffer_cond;
+- vlc_mutex_t buffer_mutex;
+ vlc_mutex_t manage_mutex;
+
+- plane_t planes[3]; /* Depending on video format up to 3 planes are used */
+- picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */
+- picture_pool_t *picture_pool;
+-
+ MMAL_COMPONENT_T *component;
+ MMAL_PORT_T *input;
+ MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/
+- struct dmx_region_t *dmx_region;
+ int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */
+
+ uint32_t buffer_size; /* size of actual mmal buffers */
+ int buffers_in_transit; /* number of buffers currently pushed to mmal component */
+ unsigned num_buffers; /* number of buffers allocated at mmal port */
+
+- DISPMANX_DISPLAY_HANDLE_T dmx_handle;
+- DISPMANX_ELEMENT_HANDLE_T bkg_element;
+- DISPMANX_RESOURCE_HANDLE_T bkg_resource;
+ unsigned display_width;
+ unsigned display_height;
+
+- int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
+- int i_frame_rate;
++ unsigned int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
++ unsigned int i_frame_rate;
+
+ int next_phase_check; /* lowpass for phase check frequency */
+ int phase_offset; /* currently applied offset to presentation time in ns */
+@@ -136,32 +100,18 @@
+ bool native_interlaced;
+ bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */
+ bool b_progressive;
+- bool opaque; /* indicated use of opaque picture format (zerocopy) */
+-};
++ bool force_config;
+
+-static const vlc_fourcc_t subpicture_chromas[] = {
+- VLC_CODEC_RGBA,
+- 0
++ vout_subpic_t subs[SUBS_MAX];
++
++ picture_pool_t * pic_pool;
+ };
+
++
+ /* Utility functions */
+-static inline uint32_t align(uint32_t x, uint32_t y);
+ static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
+ const video_format_t *fmt);
+
+-/* VLC vout display callbacks */
+-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count);
+-static void vd_prepare(vout_display_t *vd, picture_t *picture,
+- subpicture_t *subpicture);
+-static void vd_display(vout_display_t *vd, picture_t *picture,
+- subpicture_t *subpicture);
+-static int vd_control(vout_display_t *vd, int query, va_list args);
+-static void vd_manage(vout_display_t *vd);
+-
+-/* MMAL callbacks */
+-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
+-
+ /* TV service */
+ static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height);
+ static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
+@@ -169,221 +119,52 @@
+ static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
+ static int set_latency_target(vout_display_t *vd, bool enable);
+
+-/* DispManX */
+-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture);
+-static void close_dmx(vout_display_t *vd);
+-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
+- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region);
+-static void dmx_region_update(struct dmx_region_t *dmx_region,
+- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture);
+-static void dmx_region_delete(struct dmx_region_t *dmx_region,
+- DISPMANX_UPDATE_HANDLE_T update);
+-static void show_background(vout_display_t *vd, bool enable);
++// Mmal
+ static void maintain_phase_sync(vout_display_t *vd);
+
+-static int Open(vlc_object_t *object)
+-{
+- vout_display_t *vd = (vout_display_t *)object;
+- vout_display_sys_t *sys;
+- uint32_t buffer_pitch, buffer_height;
+- vout_display_place_t place;
+- MMAL_DISPLAYREGION_T display_region;
+- MMAL_STATUS_T status;
+- int ret = VLC_SUCCESS;
+- unsigned i;
+
+- if (vout_display_IsWindowed(vd))
+- return VLC_EGENERIC;
+-
+- sys = calloc(1, sizeof(struct vout_display_sys_t));
+- if (!sys)
+- return VLC_ENOMEM;
+- vd->sys = sys;
+-
+- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
+- bcm_host_init();
+-
+- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE;
+-
+- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
+- MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
+-
+- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
+- status = mmal_port_enable(sys->component->control, control_port_cb);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
+- sys->component->control->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
+-
+- sys->input = sys->component->input[0];
+- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
+-
+- if (sys->opaque) {
+- sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
+- sys->i_planes = 1;
+- sys->buffer_size = sys->input->buffer_size_recommended;
+- } else {
+- sys->input->format->encoding = MMAL_ENCODING_I420;
+- vd->fmt.i_chroma = VLC_CODEC_I420;
+- buffer_pitch = align(vd->fmt.i_width, 32);
+- buffer_height = align(vd->fmt.i_height, 16);
+- sys->i_planes = 3;
+- sys->buffer_size = 3 * buffer_pitch * buffer_height / 2;
+- }
+-
+- sys->input->format->es->video.width = vd->fmt.i_width;
+- sys->input->format->es->video.height = vd->fmt.i_height;
+- sys->input->format->es->video.crop.x = 0;
+- sys->input->format->es->video.crop.y = 0;
+- sys->input->format->es->video.crop.width = vd->fmt.i_width;
+- sys->input->format->es->video.crop.height = vd->fmt.i_height;
+- sys->input->format->es->video.par.num = vd->source.i_sar_num;
+- sys->input->format->es->video.par.den = vd->source.i_sar_den;
++static void vd_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
++{
++#if TRACE_ALL
++ vout_display_t * const vd = (vout_display_t *)port->userdata;
++ pic_ctx_mmal_t * ctx = buf->user_data;
++ msg_Dbg(vd, "<<< %s[%d] cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf,
++ buf->flags, (long long)buf->pts);
++#else
++ VLC_UNUSED(port);
++#endif
+
+- status = mmal_port_format_commit(sys->input);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
+- sys->input->buffer_size = sys->input->buffer_size_recommended;
++ mmal_buffer_header_release(buf);
+
+- vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
+- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
+- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
+- display_region.fullscreen = MMAL_FALSE;
+- display_region.src_rect.x = vd->fmt.i_x_offset;
+- display_region.src_rect.y = vd->fmt.i_y_offset;
+- display_region.src_rect.width = vd->fmt.i_visible_width;
+- display_region.src_rect.height = vd->fmt.i_visible_height;
+- display_region.dest_rect.x = place.x;
+- display_region.dest_rect.y = place.y;
+- display_region.dest_rect.width = place.width;
+- display_region.dest_rect.height = place.height;
+- display_region.layer = sys->layer;
+- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
+- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
+- status = mmal_port_parameter_set(sys->input, &display_region.hdr);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
+- status, mmal_status_to_string(status));
+- ret = VLC_EGENERIC;
+- goto out;
+- }
++#if TRACE_ALL
++ msg_Dbg(vd, ">>> %s", __func__);
++#endif
++}
+
+- for (i = 0; i < sys->i_planes; ++i) {
+- sys->planes[i].i_lines = buffer_height;
+- sys->planes[i].i_pitch = buffer_pitch;
+- sys->planes[i].i_visible_lines = vd->fmt.i_visible_height;
+- sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width;
++static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
++{
++ TV_DISPLAY_STATE_T display_state;
++ int ret = 0;
+
+- if (i > 0) {
+- sys->planes[i].i_lines /= 2;
+- sys->planes[i].i_pitch /= 2;
+- sys->planes[i].i_visible_lines /= 2;
+- sys->planes[i].i_visible_pitch /= 2;
++ if (vc_tv_get_display_state(&display_state) == 0) {
++ if (display_state.state & 0xFF) {
++ *width = display_state.display.hdmi.width;
++ *height = display_state.display.hdmi.height;
++ } else if (display_state.state & 0xFF00) {
++ *width = display_state.display.sdtv.width;
++ *height = display_state.display.sdtv.height;
++ } else {
++ msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
++ ret = -1;
+ }
+- }
+-
+- vlc_mutex_init(&sys->buffer_mutex);
+- vlc_cond_init(&sys->buffer_cond);
+- vlc_mutex_init(&sys->manage_mutex);
+-
+- vd->pool = vd_pool;
+- vd->prepare = vd_prepare;
+- vd->display = vd_display;
+- vd->control = vd_control;
+- vd->manage = vd_manage;
+-
+- vc_tv_register_callback(tvservice_cb, vd);
+-
+- if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) {
+- vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height);
+ } else {
+- sys->display_width = vd->cfg->display.width;
+- sys->display_height = vd->cfg->display.height;
++ msg_Warn(vd, "Failed to query display resolution");
++ ret = -1;
+ }
+
+- sys->dmx_handle = vc_dispmanx_display_open(0);
+- vd->info.subpicture_chromas = subpicture_chromas;
+-
+- vout_display_DeleteWindow(vd, NULL);
+-
+-out:
+- if (ret != VLC_SUCCESS)
+- Close(object);
+-
+ return ret;
+ }
+
+-static void Close(vlc_object_t *object)
+-{
+- vout_display_t *vd = (vout_display_t *)object;
+- vout_display_sys_t *sys = vd->sys;
+- char response[20]; /* answer is hvs_update_fields=%1d */
+- unsigned i;
+-
+- vc_tv_unregister_callback_full(tvservice_cb, vd);
+-
+- if (sys->dmx_handle)
+- close_dmx(vd);
+-
+- if (sys->component && sys->component->control->is_enabled)
+- mmal_port_disable(sys->component->control);
+-
+- if (sys->input && sys->input->is_enabled)
+- mmal_port_disable(sys->input);
+-
+- if (sys->component && sys->component->is_enabled)
+- mmal_component_disable(sys->component);
+-
+- if (sys->pool)
+- mmal_port_pool_destroy(sys->input, sys->pool);
+-
+- if (sys->component)
+- mmal_component_release(sys->component);
+-
+- if (sys->picture_pool)
+- picture_pool_Release(sys->picture_pool);
+- else
+- for (i = 0; i < sys->num_buffers; ++i)
+- if (sys->pictures[i]) {
+- mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer);
+- picture_Release(sys->pictures[i]);
+- }
+-
+- vlc_mutex_destroy(&sys->buffer_mutex);
+- vlc_cond_destroy(&sys->buffer_cond);
+- vlc_mutex_destroy(&sys->manage_mutex);
+-
+- if (sys->native_interlaced) {
+- if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
+- response[18] != '0')
+- msg_Warn(vd, "Could not reset hvs field mode");
+- }
+-
+- free(sys->pictures);
+- free(sys);
+-
+- bcm_host_deinit();
+-}
+-
+-static inline uint32_t align(uint32_t x, uint32_t y) {
+- uint32_t mod = x % y;
+- if (mod == 0)
+- return x;
+- else
+- return x + y - mod;
+-}
+-
+ static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
+ const video_format_t *fmt)
+ {
+@@ -426,8 +207,9 @@
+ display_region.dest_rect.width = place.width;
+ display_region.dest_rect.height = place.height;
+ display_region.layer = sys->layer;
++ display_region.alpha = 0xff | (1 << 29);
+ display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
+- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
++ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_ALPHA;
+ status = mmal_port_parameter_set(sys->input, &display_region.hdr);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
+@@ -435,7 +217,6 @@
+ return -EINVAL;
+ }
+
+- show_background(vd, var_InheritBool(vd, MMAL_BLANK_BACKGROUND_NAME));
+ sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME);
+ sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED);
+ if (sys->adjust_refresh_rate) {
+@@ -446,171 +227,103 @@
+ return 0;
+ }
+
+-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
++static void kill_pool(vout_display_sys_t * const sys)
+ {
+- vout_display_sys_t *sys = vd->sys;
+- picture_resource_t picture_res;
+- picture_pool_configuration_t picture_pool_cfg;
+- video_format_t fmt = vd->fmt;
+- MMAL_STATUS_T status;
+- unsigned i;
+-
+- if (sys->picture_pool) {
+- if (sys->num_buffers < count)
+- msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures",
+- count, sys->num_buffers);
+-
+- goto out;
++ if (sys->pic_pool != NULL) {
++ picture_pool_Release(sys->pic_pool);
++ sys->pic_pool = NULL;
+ }
++}
+
+- if (sys->opaque) {
+- if (count <= NUM_ACTUAL_OPAQUE_BUFFERS)
+- count = NUM_ACTUAL_OPAQUE_BUFFERS;
++// Actual picture pool for MMAL opaques is just a set of trivial containers
++static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
++{
++ vout_display_sys_t * const sys = vd->sys;
+
+- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
+- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
+- 1
+- };
++ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__,
++ 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);
+
+- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- goto out;
+- }
++ if (sys->pic_pool == NULL) {
++ sys->pic_pool = picture_pool_NewFromFormat(&vd->fmt, count);
+ }
++ return sys->pic_pool;
++}
+
+- if (count < sys->input->buffer_num_recommended)
+- count = sys->input->buffer_num_recommended;
++static void vd_display(vout_display_t *vd, picture_t *p_pic,
++ subpicture_t *subpicture)
++{
++ vout_display_sys_t * const sys = vd->sys;
++ MMAL_STATUS_T err;
+
+-#ifndef NDEBUG
+- msg_Dbg(vd, "Creating picture pool with %u pictures", count);
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s", __func__);
+ #endif
+
+- sys->input->buffer_num = count;
+- status = mmal_port_enable(sys->input, input_port_cb);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
+- sys->input->name, status, mmal_status_to_string(status));
+- goto out;
+- }
+-
+- status = mmal_component_enable(sys->component);
+- if (status != MMAL_SUCCESS) {
+- msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
+- sys->component->name, status, mmal_status_to_string(status));
+- goto out;
++ // Not expecting subpictures in the current setup
++ // Subpics should be attached to the main pic
++ if (subpicture != NULL) {
++ subpicture_Delete(subpicture);
+ }
+
+- sys->num_buffers = count;
+- sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers,
+- sys->input->buffer_size);
+- if (!sys->pool) {
+- msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
+- count, sys->input->buffer_size);
+- goto out;
++ if (sys->force_config ||
++ p_pic->format.i_frame_rate != sys->i_frame_rate ||
++ p_pic->format.i_frame_rate_base != sys->i_frame_rate_base ||
++ p_pic->b_progressive != sys->b_progressive ||
++ p_pic->b_top_field_first != sys->b_top_field_first)
++ {
++ sys->force_config = false;
++ sys->b_top_field_first = p_pic->b_top_field_first;
++ sys->b_progressive = p_pic->b_progressive;
++ sys->i_frame_rate = p_pic->format.i_frame_rate;
++ sys->i_frame_rate_base = p_pic->format.i_frame_rate_base;
++ configure_display(vd, NULL, &p_pic->format);
+ }
+
+- memset(&picture_res, 0, sizeof(picture_resource_t));
+- sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *));
+- for (i = 0; i < sys->num_buffers; ++i) {
+- picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
+- picture_res.p_sys->owner = (vlc_object_t *)vd;
+- picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue);
+
+- sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res);
+- if (!sys->pictures[i]) {
+- msg_Err(vd, "Failed to create picture");
+- free(picture_res.p_sys);
+- goto out;
++ if (!sys->input->is_enabled &&
++ (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS)
++ {
++ msg_Err(vd, "Input port enable failed");
++ goto fail;
++ }
++
++ // Stuff into input
++ // We assume the BH is already set up with values reflecting pic date etc.
++ {
++ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic);
++ if ((err = port_send_replicated(sys->input, sys->pool, pic_buf, pic_buf->pts)) != MMAL_SUCCESS)
++ {
++ msg_Err(vd, "Send buffer to input failed");
++ goto fail;
+ }
+-
+- sys->pictures[i]->i_planes = sys->i_planes;
+- memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t));
+ }
+
+- memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
+- picture_pool_cfg.picture_count = sys->num_buffers;
+- picture_pool_cfg.picture = sys->pictures;
+- picture_pool_cfg.lock = mmal_picture_lock;
+-
+- sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
+- if (!sys->picture_pool) {
+- msg_Err(vd, "Failed to create picture pool");
+- goto out;
++ if (p_pic->context == NULL) {
++ msg_Dbg(vd, "%s: No context", __func__);
+ }
++ else
++ {
++ unsigned int sub_no = 0;
+
+-out:
+- return sys->picture_pool;
+-}
+-
+-static void vd_prepare(vout_display_t *vd, picture_t *picture,
+- subpicture_t *subpicture)
+-{
+- vout_display_sys_t *sys = vd->sys;
+- picture_sys_t *pic_sys = picture->p_sys;
+-
+- if (!sys->adjust_refresh_rate || pic_sys->displayed)
+- return;
+-
+- /* Apply the required phase_offset to the picture, so that vd_display()
+- * will be called at the corrected time from the core */
+- picture->date += sys->phase_offset;
+-}
+-
+-static void vd_display(vout_display_t *vd, picture_t *picture,
+- subpicture_t *subpicture)
+-{
+- vout_display_sys_t *sys = vd->sys;
+- picture_sys_t *pic_sys = picture->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
+- MMAL_STATUS_T status;
+-
+- if (picture->format.i_frame_rate != sys->i_frame_rate ||
+- picture->format.i_frame_rate_base != sys->i_frame_rate_base ||
+- picture->b_progressive != sys->b_progressive ||
+- picture->b_top_field_first != sys->b_top_field_first) {
+- sys->b_top_field_first = picture->b_top_field_first;
+- sys->b_progressive = picture->b_progressive;
+- sys->i_frame_rate = picture->format.i_frame_rate;
+- sys->i_frame_rate_base = picture->format.i_frame_rate_base;
+- configure_display(vd, NULL, &picture->format);
+- }
+-
+- if (!pic_sys->displayed || !sys->opaque) {
+- buffer->cmd = 0;
+- buffer->length = sys->input->buffer_size;
+- buffer->user_data = picture;
+-
+- status = mmal_port_send_buffer(sys->input, buffer);
+- if (status == MMAL_SUCCESS)
+- atomic_fetch_add(&sys->buffers_in_transit, 1);
+-
+- if (status != MMAL_SUCCESS) {
+- msg_Err(vd, "Failed to send buffer to input port. Frame dropped");
+- picture_Release(picture);
++ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
++ int rv;
++ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd), p_pic, sub_no, &sys->subs[sub_no].sub,
++ &(MMAL_RECT_T){.width = sys->display_width, .height = sys->display_height},
++ p_pic->date)) == 0)
++ break;
++ else if (rv < 0)
++ goto fail;
+ }
+-
+- pic_sys->displayed = true;
+- } else {
+- picture_Release(picture);
+ }
+
+- display_subpicture(vd, subpicture);
+-
+- if (subpicture)
+- subpicture_Delete(subpicture);
++ picture_Release(p_pic);
+
+ if (sys->next_phase_check == 0 && sys->adjust_refresh_rate)
+ maintain_phase_sync(vd);
+ sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL;
+
+- if (sys->opaque) {
+- vlc_mutex_lock(&sys->buffer_mutex);
+- while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT)
+- vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex);
+- vlc_mutex_unlock(&sys->buffer_mutex);
+- }
++fail:
++ /* NOP */;
+ }
+
+ static int vd_control(vout_display_t *vd, int query, va_list args)
+@@ -640,11 +353,39 @@
+ break;
+
+ case VOUT_DISPLAY_RESET_PICTURES:
+- vlc_assert_unreachable();
++ msg_Warn(vd, "Reset Pictures");
++ kill_pool(sys);
++ vd->fmt = vd->source; // Take whatever source wants to give us
++ ret = VLC_SUCCESS;
++ break;
++
+ case VOUT_DISPLAY_CHANGE_ZOOM:
+ msg_Warn(vd, "Unsupported control query %d", query);
+ break;
+
++ case VOUT_DISPLAY_CHANGE_MMAL_HIDE:
++ {
++ MMAL_STATUS_T err;
++ unsigned int i;
++
++ msg_Dbg(vd, "Hide display");
++
++ for (i = 0; i != SUBS_MAX; ++i)
++ hw_mmal_subpic_flush(VLC_OBJECT(vd), &sys->subs[i].sub);
++
++ if (sys->input->is_enabled &&
++ (err = mmal_port_disable(sys->input)) != MMAL_SUCCESS)
++ {
++ msg_Err(vd, "Unable to disable port: err=%d", err);
++ ret = VLC_EGENERIC;
++ break;
++ }
++ sys->force_config = true;
++
++ ret = VLC_SUCCESS;
++ break;
++ }
++
+ default:
+ msg_Warn(vd, "Unknown control query %d", query);
+ break;
+@@ -661,13 +402,11 @@
+ vlc_mutex_lock(&sys->manage_mutex);
+
+ if (sys->need_configure_display) {
+- close_dmx(vd);
+- sys->dmx_handle = vc_dispmanx_display_open(0);
+-
+ if (query_resolution(vd, &width, &height) >= 0) {
+ sys->display_width = width;
+ sys->display_height = height;
+- vout_display_SendEventDisplaySize(vd, width, height);
++// msg_Dbg(vd, "%s: %dx%d", __func__, width, height);
++// vout_window_ReportSize(vd->cfg->window, width, height);
+ }
+
+ sys->need_configure_display = false;
+@@ -676,56 +415,45 @@
+ vlc_mutex_unlock(&sys->manage_mutex);
+ }
+
+-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+-{
+- vout_display_t *vd = (vout_display_t *)port->userdata;
+- MMAL_STATUS_T status;
+-
+- if (buffer->cmd == MMAL_EVENT_ERROR) {
+- status = *(uint32_t *)buffer->data;
+- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
+- }
+-
+- mmal_buffer_header_release(buffer);
+-}
+-
+-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
++static void vd_prepare(vout_display_t *vd, picture_t *picture,
++#if VLC_VER_3
++ subpicture_t *subpicture
++#else
++ subpicture_t *subpicture, vlc_tick_t date
++#endif
++ )
+ {
+- vout_display_t *vd = (vout_display_t *)port->userdata;
++ VLC_UNUSED(picture);
++ VLC_UNUSED(subpicture);
++// VLC_UNUSED(date);
++
++ vd_manage(vd);
++#if 0
++ VLC_UNUSED(date);
+ vout_display_sys_t *sys = vd->sys;
+- picture_t *picture = (picture_t *)buffer->user_data;
++ picture_sys_t *pic_sys = picture->p_sys;
+
+- if (picture)
+- picture_Release(picture);
++ if (!sys->adjust_refresh_rate || pic_sys->displayed)
++ return;
+
+- vlc_mutex_lock(&sys->buffer_mutex);
+- atomic_fetch_sub(&sys->buffers_in_transit, 1);
+- vlc_cond_signal(&sys->buffer_cond);
+- vlc_mutex_unlock(&sys->buffer_mutex);
++ /* Apply the required phase_offset to the picture, so that vd_display()
++ * will be called at the corrected time from the core */
++ picture->date += sys->phase_offset;
++#endif
+ }
+
+-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
++
++static void vd_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
+ {
+- TV_DISPLAY_STATE_T display_state;
+- int ret = 0;
++ vout_display_t *vd = (vout_display_t *)port->userdata;
++ MMAL_STATUS_T status;
+
+- if (vc_tv_get_display_state(&display_state) == 0) {
+- if (display_state.state & 0xFF) {
+- *width = display_state.display.hdmi.width;
+- *height = display_state.display.hdmi.height;
+- } else if (display_state.state & 0xFF00) {
+- *width = display_state.display.sdtv.width;
+- *height = display_state.display.sdtv.height;
+- } else {
+- msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
+- ret = -1;
+- }
+- } else {
+- msg_Warn(vd, "Failed to query display resolution");
+- ret = -1;
++ if (buffer->cmd == MMAL_EVENT_ERROR) {
++ status = *(uint32_t *)buffer->data;
++ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
+ }
+
+- return ret;
++ mmal_buffer_header_release(buffer);
+ }
+
+ static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
+@@ -828,148 +556,12 @@
+ }
+ }
+
+-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture)
+-{
+- vout_display_sys_t *sys = vd->sys;
+- struct dmx_region_t **dmx_region = &sys->dmx_region;
+- struct dmx_region_t *unused_dmx_region;
+- DISPMANX_UPDATE_HANDLE_T update = 0;
+- picture_t *picture;
+- video_format_t *fmt;
+- struct dmx_region_t *dmx_region_next;
+-
+- if(subpicture) {
+- subpicture_region_t *region = subpicture->p_region;
+- while(region) {
+- picture = region->p_picture;
+- fmt = ®ion->fmt;
+-
+- if(!*dmx_region) {
+- if(!update)
+- update = vc_dispmanx_update_start(10);
+- *dmx_region = dmx_region_new(vd, update, region);
+- } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) ||
+- ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) ||
+- ((*dmx_region)->pos_x != region->i_x) ||
+- ((*dmx_region)->pos_y != region->i_y) ||
+- ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) {
+- dmx_region_next = (*dmx_region)->next;
+- if(!update)
+- update = vc_dispmanx_update_start(10);
+- dmx_region_delete(*dmx_region, update);
+- *dmx_region = dmx_region_new(vd, update, region);
+- (*dmx_region)->next = dmx_region_next;
+- } else if((*dmx_region)->picture != picture) {
+- if(!update)
+- update = vc_dispmanx_update_start(10);
+- dmx_region_update(*dmx_region, update, picture);
+- }
+-
+- dmx_region = &(*dmx_region)->next;
+- region = region->p_next;
+- }
+- }
+-
+- /* Remove remaining regions */
+- unused_dmx_region = *dmx_region;
+- while(unused_dmx_region) {
+- dmx_region_next = unused_dmx_region->next;
+- if(!update)
+- update = vc_dispmanx_update_start(10);
+- dmx_region_delete(unused_dmx_region, update);
+- unused_dmx_region = dmx_region_next;
+- }
+- *dmx_region = NULL;
+-
+- if(update)
+- vc_dispmanx_update_submit_sync(update);
+-}
+-
+-static void close_dmx(vout_display_t *vd)
+-{
+- vout_display_sys_t *sys = vd->sys;
+- DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10);
+- struct dmx_region_t *dmx_region = sys->dmx_region;
+- struct dmx_region_t *dmx_region_next;
+-
+- while(dmx_region) {
+- dmx_region_next = dmx_region->next;
+- dmx_region_delete(dmx_region, update);
+- dmx_region = dmx_region_next;
+- }
+-
+- vc_dispmanx_update_submit_sync(update);
+- sys->dmx_region = NULL;
+-
+- show_background(vd, false);
+-
+- vc_dispmanx_display_close(sys->dmx_handle);
+- sys->dmx_handle = DISPMANX_NO_HANDLE;
+-}
+-
+-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
+- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region)
+-{
+- vout_display_sys_t *sys = vd->sys;
+- video_format_t *fmt = ®ion->fmt;
+- struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t));
+- uint32_t image_handle;
+-
+- dmx_region->pos_x = region->i_x;
+- dmx_region->pos_y = region->i_y;
+-
+- vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width,
+- fmt->i_visible_height);
+- vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16,
+- fmt->i_visible_height << 16);
+- vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y,
+- fmt->i_visible_width, fmt->i_visible_height);
+-
+- dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32,
+- dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16),
+- dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16),
+- &image_handle);
+- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
+- region->p_picture->p[0].i_pitch,
+- region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect);
+-
+- dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX;
+- dmx_region->alpha.opacity = region->i_alpha;
+- dmx_region->alpha.mask = DISPMANX_NO_HANDLE;
+- dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle,
+- sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource,
+- &dmx_region->src_rect, DISPMANX_PROTECTION_NONE,
+- &dmx_region->alpha, NULL, VC_IMAGE_ROT0);
+-
+- dmx_region->next = NULL;
+- dmx_region->picture = region->p_picture;
+-
+- return dmx_region;
+-}
+-
+-static void dmx_region_update(struct dmx_region_t *dmx_region,
+- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
+-{
+- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
+- picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect);
+- vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource);
+- dmx_region->picture = picture;
+-}
+-
+-static void dmx_region_delete(struct dmx_region_t *dmx_region,
+- DISPMANX_UPDATE_HANDLE_T update)
+-{
+- vc_dispmanx_element_remove(update, dmx_region->element);
+- vc_dispmanx_resource_delete(dmx_region->resource);
+- free(dmx_region);
+-}
+-
+ static void maintain_phase_sync(vout_display_t *vd)
+ {
+ MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = {
+ .hdr = { MMAL_PARAMETER_VIDEO_RENDER_STATS, sizeof(render_stats) },
+ };
+- int32_t frame_duration = 1000000 /
++ int32_t frame_duration = CLOCK_FREQ /
+ ((double)vd->sys->i_frame_rate /
+ vd->sys->i_frame_rate_base);
+ vout_display_sys_t *sys = vd->sys;
+@@ -1012,32 +604,258 @@
+ }
+ }
+
+-static void show_background(vout_display_t *vd, bool enable)
++static void CloseMmalVout(vlc_object_t *object)
+ {
+- vout_display_sys_t *sys = vd->sys;
+- uint32_t image_ptr, color = 0xFF000000;
+- VC_RECT_T dst_rect, src_rect;
+- DISPMANX_UPDATE_HANDLE_T update;
+-
+- if (enable && !sys->bkg_element) {
+- sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1,
+- &image_ptr);
+- vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1);
+- vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32,
+- sizeof(color), &color, &dst_rect);
+- vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16);
+- vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0);
+- update = vc_dispmanx_update_start(0);
+- sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle,
+- sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect,
+- DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0);
+- vc_dispmanx_update_submit_sync(update);
+- } else if (!enable && sys->bkg_element) {
+- update = vc_dispmanx_update_start(0);
+- vc_dispmanx_element_remove(update, sys->bkg_element);
+- vc_dispmanx_resource_delete(sys->bkg_resource);
+- vc_dispmanx_update_submit_sync(update);
+- sys->bkg_element = DISPMANX_NO_HANDLE;
+- sys->bkg_resource = DISPMANX_NO_HANDLE;
++ vout_display_t * const vd = (vout_display_t *)object;
++ vout_display_sys_t * const sys = vd->sys;
++ char response[20]; /* answer is hvs_update_fields=%1d */
++
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s", __func__);
++#endif
++
++ kill_pool(sys);
++
++ vc_tv_unregister_callback_full(tvservice_cb, vd);
++
++ if (sys->component && sys->component->control->is_enabled)
++ mmal_port_disable(sys->component->control);
++
++ {
++ unsigned int i;
++ for (i = 0; i != SUBS_MAX; ++i) {
++ vout_subpic_t * const sub = sys->subs + i;
++ if (sub->component != NULL) {
++ hw_mmal_subpic_close(VLC_OBJECT(vd), &sub->sub);
++ if (sub->component->control->is_enabled)
++ mmal_port_disable(sub->component->control);
++ if (sub->component->is_enabled)
++ mmal_component_disable(sub->component);
++ mmal_component_release(sub->component);
++ sub->component = NULL;
++ }
++ }
+ }
++
++ if (sys->input && sys->input->is_enabled)
++ mmal_port_disable(sys->input);
++
++ if (sys->component && sys->component->is_enabled)
++ mmal_component_disable(sys->component);
++
++ if (sys->pool)
++ mmal_pool_destroy(sys->pool);
++
++ if (sys->component)
++ mmal_component_release(sys->component);
++
++ vlc_mutex_destroy(&sys->manage_mutex);
++
++ if (sys->native_interlaced) {
++ if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
++ response[18] != '0')
++ msg_Warn(vd, "Could not reset hvs field mode");
++ }
++
++ free(sys);
++
++ bcm_host_deinit();
++
++#if TRACE_ALL
++ msg_Dbg(vd, ">>> %s", __func__);
++#endif
+ }
++
++static int OpenMmalVout(vlc_object_t *object)
++{
++ vout_display_t *vd = (vout_display_t *)object;
++ vout_display_sys_t *sys;
++ vout_display_place_t place;
++ MMAL_DISPLAYREGION_T display_region;
++ MMAL_STATUS_T status;
++ int ret = VLC_EGENERIC;
++
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s", __func__);
++#endif
++ if (vd->fmt.i_chroma != VLC_CODEC_MMAL_OPAQUE)
++ {
++#if TRACE_ALL
++ msg_Dbg(vd, ">>> %s: Format not MMAL", __func__);
++#endif
++ return VLC_EGENERIC;
++ }
++
++ sys = calloc(1, sizeof(struct vout_display_sys_t));
++ if (!sys)
++ return VLC_ENOMEM;
++ vd->sys = sys;
++
++ sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
++
++ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
++ MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
++ status = mmal_port_enable(sys->component->control, vd_control_port_cb);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
++ sys->component->control->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ sys->input = sys->component->input[0];
++ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
++
++ sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
++ sys->i_planes = 1;
++ sys->buffer_size = sys->input->buffer_size_recommended;
++
++ sys->input->format->es->video.width = vd->fmt.i_width;
++ sys->input->format->es->video.height = vd->fmt.i_height;
++ sys->input->format->es->video.crop.x = 0;
++ sys->input->format->es->video.crop.y = 0;
++ sys->input->format->es->video.crop.width = vd->fmt.i_width;
++ sys->input->format->es->video.crop.height = vd->fmt.i_height;
++ sys->input->format->es->video.par.num = vd->source.i_sar_num;
++ sys->input->format->es->video.par.den = vd->source.i_sar_den;
++
++ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ status = mmal_port_format_commit(sys->input);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++ sys->input->buffer_size = sys->input->buffer_size_recommended;
++ sys->input->buffer_num = 30;
++
++ vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
++ display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
++ display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
++ display_region.fullscreen = MMAL_FALSE;
++ display_region.src_rect.x = vd->fmt.i_x_offset;
++ display_region.src_rect.y = vd->fmt.i_y_offset;
++ display_region.src_rect.width = vd->fmt.i_visible_width;
++ display_region.src_rect.height = vd->fmt.i_visible_height;
++ display_region.dest_rect.x = place.x;
++ display_region.dest_rect.y = place.y;
++ display_region.dest_rect.width = place.width;
++ display_region.dest_rect.height = place.height;
++ display_region.layer = sys->layer;
++ display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
++ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
++ status = mmal_port_parameter_set(sys->input, &display_region.hdr);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
++ status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ status = mmal_port_enable(sys->input, vd_input_port_cb);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
++ sys->input->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ status = mmal_component_enable(sys->component);
++ if (status != MMAL_SUCCESS) {
++ msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
++ sys->component->name, status, mmal_status_to_string(status));
++ goto fail;
++ }
++
++ if ((sys->pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
++ {
++ msg_Err(vd, "Failed to create input pool");
++ goto fail;
++ }
++
++ {
++ unsigned int i;
++ for (i = 0; i != SUBS_MAX; ++i) {
++ vout_subpic_t * const sub = sys->subs + i;
++ if ((status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sub->component)) != MMAL_SUCCESS)
++ {
++ msg_Dbg(vd, "Failed to create subpic component %d", i);
++ goto fail;
++ }
++ sub->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
++ if ((status = mmal_port_enable(sub->component->control, vd_control_port_cb)) != MMAL_SUCCESS) {
++ msg_Err(vd, "Failed to enable control port %s on sub %d (status=%"PRIx32" %s)",
++ sys->component->control->name, i, status, mmal_status_to_string(status));
++ goto fail;
++ }
++ if ((status = hw_mmal_subpic_open(VLC_OBJECT(vd), &sub->sub, sub->component->input[0], sys->layer + i + 1)) != MMAL_SUCCESS) {
++ msg_Dbg(vd, "Failed to open subpic %d", i);
++ goto fail;
++ }
++ if ((status = mmal_component_enable(sub->component)) != MMAL_SUCCESS)
++ {
++ msg_Dbg(vd, "Failed to enable subpic component %d", i);
++ goto fail;
++ }
++ }
++ }
++
++
++ vlc_mutex_init(&sys->manage_mutex);
++
++ vd->pool = vd_pool;
++ vd->prepare = vd_prepare;
++ vd->display = vd_display;
++ vd->control = vd_control;
++
++ vc_tv_register_callback(tvservice_cb, vd);
++
++ if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) {
++// vout_window_ReportSize(vd->cfg->window,
++// sys->display_width, sys->display_height);
++ } else {
++ sys->display_width = vd->cfg->display.width;
++ sys->display_height = vd->cfg->display.height;
++ }
++
++ msg_Dbg(vd, ">>> %s: ok", __func__);
++ return VLC_SUCCESS;
++
++fail:
++ CloseMmalVout(object);
++
++ msg_Dbg(vd, ">>> %s: rv=%d", __func__, ret);
++
++ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
++}
++
++vlc_module_begin()
++
++ add_submodule()
++
++ set_shortname(N_("MMAL vout"))
++ set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
++ set_capability("vout display", 0)
++ add_shortcut("mmal_vout")
++ set_category( CAT_VIDEO )
++ set_subcategory( SUBCAT_VIDEO_VOUT )
++
++ add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
++ add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
++ MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
++ add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
++ MMAL_NATIVE_INTERLACE_LONGTEXT, false)
++ set_callbacks(OpenMmalVout, CloseMmalVout)
++
++vlc_module_end()
++
++
+--- /dev/null
++++ b/modules/hw/mmal/xsplitter.c
+@@ -0,0 +1,365 @@
++#ifdef HAVE_CONFIG_H
++#include "config.h"
++#endif
++
++#include <stdatomic.h>
++
++#include <vlc_common.h>
++#include <vlc_plugin.h>
++#include <vlc_threads.h>
++#include <vlc_vout_display.h>
++#include <vlc_modules.h>
++
++#include <bcm_host.h>
++#include <interface/mmal/mmal.h>
++#include <interface/mmal/util/mmal_util.h>
++#include <interface/mmal/util/mmal_default_components.h>
++
++#include "mmal_picture.h"
++
++#define TRACE_ALL 0
++
++typedef struct mmal_x11_sys_s
++{
++ bool use_mmal;
++ vout_display_t * cur_vout;
++ vout_display_t * mmal_vout;
++ vout_display_t * x_vout;
++ uint32_t changed;
++} mmal_x11_sys_t;
++
++static void unload_display_module(vout_display_t * const x_vout)
++{
++ if (x_vout != NULL) {
++ if (x_vout->module != NULL) {
++ module_unneed(x_vout, x_vout->module);
++ }
++ vlc_object_release(x_vout);
++ }
++}
++
++static void CloseMmalX11(vlc_object_t *object)
++{
++ vout_display_t * const vd = (vout_display_t *)object;
++ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
++
++ msg_Dbg(vd, "<<< %s", __func__);
++
++ if (sys == NULL)
++ return;
++
++ unload_display_module(sys->x_vout);
++
++ unload_display_module(sys->mmal_vout);
++
++ free(sys);
++
++ msg_Dbg(vd, ">>> %s", __func__);
++}
++
++static void mmal_x11_event(vout_display_t * x_vd, int cmd, va_list args)
++{
++ vout_display_t * const vd = x_vd->owner.sys;
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s (cmd=%d)", __func__, cmd);
++#endif
++ vd->owner.event(vd, cmd, args);
++}
++
++static vout_window_t * mmal_x11_window_new(vout_display_t * x_vd, unsigned type)
++{
++ vout_display_t * const vd = x_vd->owner.sys;
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s (type=%d)", __func__, type);
++#endif
++ return vd->owner.window_new(vd, type);
++}
++
++static void mmal_x11_window_del(vout_display_t * x_vd, vout_window_t * win)
++{
++ vout_display_t * const vd = x_vd->owner.sys;
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s", __func__);
++#endif
++ vd->owner.window_del(vd, win);
++}
++
++
++static vout_display_t * load_display_module(vout_display_t * const vd,
++ const char * const cap, const char * const module_name)
++{
++ vout_display_t * const x_vout = vlc_object_create(vd, sizeof(*x_vout));
++
++ if (!x_vout)
++ return NULL;
++
++ x_vout->owner.sys = vd;
++ x_vout->owner.event = mmal_x11_event;
++ x_vout->owner.window_new = mmal_x11_window_new;
++ x_vout->owner.window_del = mmal_x11_window_del;
++
++ x_vout->cfg = vd->cfg;
++ x_vout->source = vd->source;
++ x_vout->info = vd->info;
++
++ x_vout->fmt = vd->fmt;
++
++ if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL)
++ {
++ msg_Err(vd, "Failed to find X11 module");
++ goto fail;
++ }
++
++ return x_vout;
++
++fail:
++ vlc_object_release(x_vout);
++ return NULL;
++}
++
++
++/* Return a pointer over the current picture_pool_t* (mandatory).
++ *
++ * For performance reasons, it is best to provide at least count
++ * pictures but it is not mandatory.
++ * You can return NULL when you cannot/do not want to allocate
++ * pictures.
++ * The vout display module keeps the ownership of the pool and can
++ * destroy it only when closing or on invalid pictures control.
++ */
++static picture_pool_t * mmal_x11_pool(vout_display_t * vd, unsigned count)
++{
++ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
++ vout_display_t * const x_vd = sys->cur_vout;
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s (count=%d) %dx%d", __func__, count, x_vd->fmt.i_width, x_vd->fmt.i_height);
++#endif
++ return x_vd->pool(x_vd, count);
++}
++
++/* Prepare a picture and an optional subpicture for display (optional).
++ *
++ * It is called before the next pf_display call to provide as much
++ * time as possible to prepare the given picture and the subpicture
++ * for display.
++ * You are guaranted that pf_display will always be called and using
++ * the exact same picture_t and subpicture_t.
++ * You cannot change the pixel content of the picture_t or of the
++ * subpicture_t.
++ */
++static void mmal_x11_prepare(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
++{
++ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
++ vout_display_t * const x_vd = sys->cur_vout;
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s", __func__);
++#endif
++ if (x_vd->prepare)
++ x_vd->prepare(x_vd, pic, sub);
++}
++
++/* Display a picture and an optional subpicture (mandatory).
++ *
++ * The picture and the optional subpicture must be displayed as soon as
++ * possible.
++ * You cannot change the pixel content of the picture_t or of the
++ * subpicture_t.
++ *
++ * This function gives away the ownership of the picture and of the
++ * subpicture, so you must release them as soon as possible.
++ */
++static void mmal_x11_display(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
++{
++ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
++ vout_display_t * const x_vd = sys->cur_vout;
++ const bool is_mmal_pic = (pic->format.i_chroma == VLC_CODEC_MMAL_OPAQUE);
++
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s: fmt: %dx%d/%dx%d, pic:%dx%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);
++#endif
++
++ if (sys->use_mmal != is_mmal_pic) {
++ msg_Dbg(vd, "%s: Picture dropped", __func__);
++ picture_Release(pic);
++ if (sub != NULL)
++ subpicture_Delete(sub);
++ return;
++ }
++
++ x_vd->display(x_vd, pic, sub);
++}
++
++
++static int vout_display_Control(vout_display_t *vd, int query, ...)
++{
++ va_list args;
++ int result;
++
++ va_start(args, query);
++ result = vd->control(vd, query, args);
++ va_end(args);
++
++ return result;
++}
++
++/* Control on the module (mandatory) */
++static int mmal_x11_control(vout_display_t * vd, int ctl, va_list va)
++{
++ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
++ vout_display_t *x_vd = sys->cur_vout;
++ int rv;
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s[%d] (ctl=%d)", __func__, sys->use_mmal, ctl);
++#endif
++ // Remember what we've told this vd - unwanted ctls ignored on replay
++ if (ctl >= 0 && ctl <= 31)
++ sys->changed |= (1 << ctl);
++
++ switch (ctl) {
++ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
++ {
++ const vout_display_cfg_t * cfg = va_arg(va, const vout_display_cfg_t *);
++ const bool want_mmal = sys->mmal_vout != NULL && var_InheritBool(vd, "fullscreen");
++ vout_display_t *new_vd = want_mmal ? sys->mmal_vout : sys->x_vout;
++
++ msg_Dbg(vd, "Change size: %d, %d", cfg->display.width, cfg->display.height);
++
++ if (sys->use_mmal != want_mmal) {
++ if (sys->use_mmal) {
++ vout_display_Control(x_vd, VOUT_DISPLAY_CHANGE_MMAL_HIDE);
++ }
++ vout_display_SendEventPicturesInvalid(x_vd);
++ }
++
++ rv = vout_display_Control(new_vd, ctl, cfg);
++ if (rv == VLC_SUCCESS) {
++ vd->fmt = new_vd->fmt;
++ sys->cur_vout = new_vd;
++ sys->use_mmal = want_mmal;
++ }
++
++ // Repeat any control calls that we sent to the previous vd
++ if (sys->changed != 0) {
++ const uint32_t changed = sys->changed;
++ sys->changed = 0;
++ if ((changed & (1 << VOUT_DISPLAY_CHANGE_DISPLAY_FILLED)) != 0)
++ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, vd->cfg);
++ if ((changed & (1 << VOUT_DISPLAY_CHANGE_ZOOM)) != 0)
++ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg);
++ if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) | (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0)
++ new_vd->source = vd->source;
++ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT)) != 0)
++ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT);
++ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP)) != 0)
++ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP);
++ if ((changed & (1 << VOUT_DISPLAY_CHANGE_VIEWPOINT)) != 0)
++ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg);
++ }
++
++ break;
++ }
++
++ case VOUT_DISPLAY_RESET_PICTURES:
++ msg_Dbg(vd, "Reset pictures");
++ rv = x_vd->control(x_vd, ctl, va);
++ msg_Dbg(vd, "<<< %s: Pic reset: fmt: %dx%d<-%dx%d, source: %dx%d/%dx%d", __func__,
++ vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height,
++ vd->source.i_width, vd->source.i_height, x_vd->source.i_width, x_vd->source.i_height);
++ vd->fmt = x_vd->fmt;
++ break;
++
++ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
++ case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
++ x_vd->source = vd->source;
++ default:
++ rv = x_vd->control(x_vd, ctl, va);
++// vd->fmt = x_vd->fmt;
++ break;
++ }
++#if TRACE_ALL
++ msg_Dbg(vd, ">>> %s (rv=%d)", __func__, rv);
++#endif
++ return rv;
++}
++
++#define DO_MANAGE 0
++
++#if DO_MANAGE
++/* Manage pending event (optional) */
++static void mmal_x11_manage(vout_display_t * vd)
++{
++ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
++ vout_display_t * const x_vd = sys->cur_vout;
++#if TRACE_ALL
++ msg_Dbg(vd, "<<< %s", __func__);
++#endif
++ x_vd->manage(x_vd);
++}
++#endif
++
++static const char * str_fourcc(char * buf, unsigned int fcc)
++{
++ if (fcc == 0)
++ return "----";
++ buf[0] = (fcc >> 0) & 0xff;
++ buf[1] = (fcc >> 8) & 0xff;
++ buf[2] = (fcc >> 16) & 0xff;
++ buf[3] = (fcc >> 24) & 0xff;
++ buf[4] = 0;
++ return buf;
++}
++
++
++static int OpenMmalX11(vlc_object_t *object)
++{
++ vout_display_t * const vd = (vout_display_t *)object;
++ mmal_x11_sys_t * const sys = calloc(1, sizeof(*sys));
++ int ret = VLC_SUCCESS;
++
++ if (sys == NULL) {
++ return VLC_EGENERIC;
++ }
++ vd->sys = (vout_display_sys_t *)sys;
++
++ if ((sys->x_vout = load_display_module(vd, "vout display", "xcb_x11")) == NULL)
++ goto fail;
++
++ if ((sys->mmal_vout = load_display_module(vd, "vout display", "mmal_vout")) == NULL)
++ {
++ // Winge but do no more than that - route everything to X
++ char dbuf0[5], dbuf1[5];
++ msg_Info(vd, "Not a valid format for mmal vout (%s/%s)", str_fourcc(dbuf0, vd->fmt.i_chroma), str_fourcc(dbuf1, vd->source.i_chroma));
++ }
++
++ sys->cur_vout = sys->x_vout;
++ vd->info = sys->cur_vout->info;
++ vd->fmt = sys->cur_vout->fmt;
++
++ vd->pool = mmal_x11_pool;
++ vd->prepare = mmal_x11_prepare;
++ vd->display = mmal_x11_display;
++ vd->control = mmal_x11_control;
++#if DO_MANAGE
++ vd->manage = mmal_x11_manage;
++#endif
++
++ return VLC_SUCCESS;
++
++fail:
++ CloseMmalX11(VLC_OBJECT(vd));
++ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
++}
++
++
++
++
++vlc_module_begin()
++ set_shortname(N_("MMAL x11 splitter"))
++ set_description(N_("MMAL x11 splitter for Raspberry Pi"))
++ set_capability("vout display", 900)
++ add_shortcut("mmal_x11")
++ set_category( CAT_VIDEO )
++ set_subcategory( SUBCAT_VIDEO_VOUT )
++ set_callbacks(OpenMmalX11, CloseMmalX11)
++vlc_module_end()
++
diff --git a/vlc.spec b/vlc.spec
index 8649944..1c69c50 100644
--- a/vlc.spec
+++ b/vlc.spec
@@ -48,6 +48,7 @@ Release: 0.1%{?dist}
License: GPLv2+
URL: https://www.videolan.org
Source0: %{vlc_url}/%{?!vlc_tag:%{version}/}vlc-%{version}%{?vlc_tag}.tar.xz
+Patch0: https://raw.githubusercontent.com/fedberry/vlc/master/mmal_1.patch
BuildRequires: desktop-file-utils
BuildRequires: libappstream-glib
@@ -202,6 +203,7 @@ BuildRequires: xorg-x11-proto-devel
%ifarch armv7hl armv7hnl
%{?_with_rpi:
BuildRequires: raspberrypi-vc-devel
+BuildRequires: raspberrypi-vc-static
}
%endif
5 years, 11 months