[vlc] disable libssh2 on el8
by Nicolas Chauvet
commit a49035837f010946ee2b5e0eabbcfa32917ac292
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Sat Jan 18 09:46:58 2020 +0100
disable libssh2 on el8
vlc.spec | 1 +
1 file changed, 1 insertion(+)
---
diff --git a/vlc.spec b/vlc.spec
index 1deb4ad..a3c5935 100644
--- a/vlc.spec
+++ b/vlc.spec
@@ -354,6 +354,7 @@ rm aclocal.m4 m4/lib*.m4 m4/lt*.m4 || :
%{?!_without_freeworld: --enable-realrtsp} \
--enable-flac \
--enable-tremor \
+%{?el8:--disable-libssh2} \
--enable-speex \
--enable-theora \
--enable-libass \
4 years, 10 months
[vlc] Enable mmal-avcodec for rpi
by Nicolas Chauvet
commit 268a8d92aed45460ec0f7ca9f34a84ef7033a991
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Sat Jan 18 09:35:09 2020 +0100
Enable mmal-avcodec for rpi
vlc.spec | 1 +
1 file changed, 1 insertion(+)
---
diff --git a/vlc.spec b/vlc.spec
index 8e4ec8e..1deb4ad 100644
--- a/vlc.spec
+++ b/vlc.spec
@@ -341,6 +341,7 @@ rm aclocal.m4 m4/lib*.m4 m4/lt*.m4 || :
--enable-omxil-vout \
--enable-rpi-omxil \
--enable-mmal \
+ --enable-mmal-avcodec \
} \
%{?_with_aom:--enable-aom} \
%{!?_with_a52dec:--disable-a52} \
4 years, 10 months
[vlc] Update snapshot
by Nicolas Chauvet
commit 2b644dfe100aeb795b90bf45eef02d7e552afa29
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Sat Jan 18 09:29:07 2020 +0100
Update snapshot
mmal_10.patch => mmal_16.patch | 6834 +++++++++++++++++++++++++---------------
sources | 2 +-
vlc.spec | 13 +-
3 files changed, 4230 insertions(+), 2619 deletions(-)
---
diff --git a/mmal_10.patch b/mmal_16.patch
similarity index 75%
rename from mmal_10.patch
rename to mmal_16.patch
index 6ed981e..b56b533 100644
--- a/mmal_10.patch
+++ b/mmal_16.patch
@@ -29,12 +29,13 @@
dnl evas plugin
--- a/include/vlc_fourcc.h
+++ b/include/vlc_fourcc.h
-@@ -365,6 +365,10 @@
+@@ -365,6 +365,11 @@
/* Broadcom MMAL opaque buffer type */
#define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L')
+#define VLC_CODEC_MMAL_ZC_SAND8 VLC_FOURCC('Z','S','D','8')
+#define VLC_CODEC_MMAL_ZC_SAND10 VLC_FOURCC('Z','S','D','0')
++#define VLC_CODEC_MMAL_ZC_SAND30 VLC_FOURCC('Z','S','D','3')
+#define VLC_CODEC_MMAL_ZC_I420 VLC_FOURCC('Z','4','2','0')
+#define VLC_CODEC_MMAL_ZC_RGB32 VLC_FOURCC('Z','R','G','B')
@@ -52,14 +53,14 @@
+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_SOURCES = vout.c subpic.c mmal_picture.c subpic.h mmal_cma.c mmal_picture.h mmal_piccpy_neon.S
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_SOURCES = codec.c subpic.c mmal_picture.c blend_rgba_neon.S subpic.h mmal_picture.h mmal_piccpy_neon.S\
+ mmal_cma.c mmal_cma.h
libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS)
libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
@@ -67,26 +68,26 @@
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_SOURCES = deinterlace.c mmal_picture.c mmal_cma.c mmal_picture.h mmal_piccpy_neon.S
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_SOURCES = xsplitter.c mmal_picture.c mmal_cma.c mmal_picture.h mmal_piccpy_neon.S
+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
+
-+libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c mmal_cma.h mmal_picture.h
++libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c mmal_cma.h mmal_picture.h mmal_piccpy_neon.S
+libmmal_converter_plugin_la_CFLAGS = $(AM_CFLAGS)
+libmmal_converter_plugin_la_LDFLAGS = $(AM_LDFLAGS)
+libmmal_converter_plugin_la_LIBADD = $(LIBS_mmal)
+mmal_LTLIBRARIES += libmmal_converter_plugin.la
+
+if HAVE_MMAL_AVCODEC
-+libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_picture.c mmal_picture.h
++libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_cma.c mmal_picture.c mmal_picture.h mmal_cma.h mmal_piccpy_neon.S
+libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS)
+libmmal_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
+libmmal_avcodec_plugin_la_LIBADD = $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(LIBS_mmal)
@@ -499,7 +500,7 @@
+
--- a/modules/hw/mmal/codec.c
+++ b/modules/hw/mmal/codec.c
-@@ -26,10 +26,12 @@
+@@ -26,267 +26,396 @@
#include "config.h"
#endif
@@ -512,8 +513,8 @@
+#include <vlc_filter.h>
#include <vlc_threads.h>
- #include <bcm_host.h>
-@@ -37,256 +39,383 @@
+-#include <bcm_host.h>
+ #include <interface/mmal/mmal.h>
#include <interface/mmal/util/mmal_util.h>
#include <interface/mmal/util/mmal_default_components.h>
@@ -527,21 +528,22 @@
+
+#define TRACE_ALL 0
+
++#define OPT_TO_FROM_ZC 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 5
+//#define NUM_EXTRA_BUFFERS 10
#define NUM_DECODER_BUFFER_HEADERS 30
- #define MIN_NUM_BUFFERS_IN_TRANSIT 2
-
+-#define MIN_NUM_BUFFERS_IN_TRANSIT 2
++#define CONVERTER_BUFFERS 4 // Buffers on the output of the converter
++
+#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.")
@@ -884,7 +886,7 @@
- ret = VLC_EGENERIC;
- goto out;
- }
-+ if ((pic->context = hw_mmal_gen_context(MMAL_ENCODING_OPAQUE, buf, dec_sys->ppr)) == NULL)
++ 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);
@@ -1199,7 +1201,7 @@
- ret = VLC_EGENERIC;
- goto err;
- }
-
+-
- if (!sys->opaque)
- buffer->data = picture->p[0].p_pixels;
- } else {
@@ -1213,7 +1215,7 @@
- }
- 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) {
@@ -1243,11 +1245,6 @@
+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)
+ {
@@ -1262,6 +1259,11 @@
+ buf->data = dec->fmt_in.p_extra;
+ buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG;
+- unsigned max_buffers_in_transit = 0;
+- int buffers_available = 0;
+- int buffers_to_send = 0;
+- int i;
+-
- if (sys->output_pool) {
- max_buffers_in_transit = __MAX(sys->output_pool->headers_num,
- MIN_NUM_BUFFERS_IN_TRANSIT);
@@ -1277,10 +1279,10 @@
+ }
}
- 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)",
@@ -1428,7 +1430,7 @@
len = block->i_buffer;
if (len > buffer->alloc_size)
-@@ -590,89 +685,1549 @@
+@@ -590,89 +685,1733 @@
}
buffer->flags = flags;
@@ -1501,8 +1503,6 @@
+
+ vlc_mutex_destroy(&sys->pic_lock);
+ free(sys);
-+
-+ bcm_host_deinit();
+}
+
+static int OpenDecoder(decoder_t *dec)
@@ -1512,7 +1512,7 @@
MMAL_STATUS_T status;
+ const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec);
+
-+#if TRACE_ALL
++#if TRACE_ALL || 1
+ {
+ char buf1[5], buf2[5], buf2a[5];
+ char buf3[5], buf4[5];
@@ -1538,8 +1538,6 @@
+ dec->p_sys = sys;
+ vlc_mutex_init(&sys->pic_lock);
+
-+ bcm_host_init();
-+
+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
+ msg_Err(dec, "VCSM init failed");
+ goto fail;
@@ -1702,25 +1700,23 @@
+ MMAL_POOL_T *out_pool; // Free output buffers
+ MMAL_POOL_T *in_pool; // Input pool to get BH for replication
+
-+ cma_pool_fixed_t * cma_out_pool;
++ cma_buf_pool_t * cma_in_pool;
++ cma_buf_pool_t * cma_out_pool;
+
+ subpic_reg_stash_t subs[SUBS_MAX];
+
+ pic_fifo_t ret_pics;
+
++ unsigned int pic_n;
+ 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 needs_copy_in;
+ bool is_cma;
+ bool is_sliced;
+ bool out_fmt_set;
-+ bool latency_set;
+ const char * component_name;
+ MMAL_PORT_BH_CB_T in_port_cb_fn;
+ MMAL_PORT_BH_CB_T out_port_cb_fn;
@@ -1734,6 +1730,7 @@
+ unsigned int line; // Lines filled
+ } slice;
+
++ vcsm_init_type_t vcsm_init_type;
+} filter_sys_t;
+
+
@@ -1747,7 +1744,7 @@
+ es_fmt->encoding_variant = 0;
+
+ // Fill in crop etc.
-+ vlc_to_mmal_video_fmt(es_fmt, &pic->format);
++ hw_mmal_vlc_fmt_to_mmal_fmt(es_fmt, &pic->format);
+ // Override width / height with strides if appropriate
+ if (bpp != 0) {
+ v_fmt->width = pic->p[0].i_pitch / bpp;
@@ -1777,7 +1774,7 @@
+ if (sys->is_cma)
+ {
+ if (sys->cma_out_pool == NULL &&
-+ (sys->cma_out_pool = cma_buf_pool_new()) == NULL)
++ (sys->cma_out_pool = cma_buf_pool_new(CONVERTER_BUFFERS, CONVERTER_BUFFERS, true, "mmal_resizer")) == NULL)
+ {
+ msg_Err(p_filter, "Failed to alloc cma buf pool");
+ return MMAL_ENOMEM;
@@ -1859,10 +1856,7 @@
}
-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
-+static const uint8_t shift_00[] = {0,0,0,0};
-+static const uint8_t shift_01[] = {0,1,1,1};
-+
-+static int cma_pic_set_data(filter_t * const p_filter, picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf)
++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;
@@ -1890,58 +1884,11 @@
- vlc_sem_post(&sys->sem);
- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
- fmt = mmal_event_format_changed_get(buffer);
-+ filter_sys_t *const sys = p_filter->p_sys;
-+ const MMAL_VIDEO_FORMAT_T * const mm_fmt = &sys->output->format->es->video;
-+ const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = &buf->type->video;
-+
-+ uint8_t * const data = cma_buf_pic_addr(pic);
-+ if (data == NULL) {
-+ return VLC_ENOMEM;
-+ }
-+
-+ const uint8_t * ws = shift_00;
-+ const uint8_t * hs = shift_00;
-+ int pb = 1;
-+
-+ switch (p_filter->fmt_out.video.i_chroma)
-+ {
-+ case VLC_CODEC_MMAL_ZC_RGB32:
-+ pb = 4;
-+ break;
++ filter_t * const p_filter = (filter_t *)port->userdata;
++ filter_sys_t * const sys = p_filter->p_sys;
- format = mmal_format_alloc();
- mmal_format_full_copy(format, fmt->format);
-+ case VLC_CODEC_MMAL_ZC_I420:
-+ case VLC_CODEC_MMAL_ZC_SAND8:
-+ hs = shift_01;
-+ break;
-
-- if (sys->opaque)
-- format->encoding = MMAL_ENCODING_OPAQUE;
-+ default:
-+ msg_Err(p_filter, "%s: Unexpected format", __func__);
-+ return VLC_EGENERIC;
-+ }
-+
-+ pic->i_planes = buf_vid->planes;
-+ for (unsigned int i = 0; i != buf_vid->planes; ++i) {
-+ pic->p[i] = (plane_t){
-+ .p_pixels = data + buf_vid->offset[i],
-+ .i_lines = mm_fmt->height >> hs[i],
-+ .i_pitch = buf_vid->pitch[i],
-+ .i_pixel_pitch = pb,
-+ .i_visible_lines = mm_fmt->crop.height >> hs[i],
-+ .i_visible_pitch = mm_fmt->crop.width >> ws[i]
-+ };
-+ }
-+ return VLC_SUCCESS;
-+}
-+
-+static void conv_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/%lld", __func__,
+ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size,
@@ -1949,7 +1896,9 @@
+#endif
+ if (buf->cmd == 0) {
+ picture_t * const pic = (picture_t *)buf->user_data;
-+
+
+- if (sys->opaque)
+- format->encoding = MMAL_ENCODING_OPAQUE;
+ if (pic == NULL) {
+ msg_Err(p_filter, "%s: Buffer has no attached picture", __func__);
+ }
@@ -1958,14 +1907,14 @@
+#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);
+
++ // Set pic data pointers from buf aux info now it has it
+ if (sys->is_cma) {
-+ if (cma_pic_set_data(p_filter, pic, buf) != VLC_SUCCESS)
++ if (cma_pic_set_data(pic, sys->output->format, buf) != VLC_SUCCESS)
+ msg_Err(p_filter, "Failed to set data");
+ }
+
@@ -1975,11 +1924,12 @@
+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 32, 0, 32, 32, 0xff00ff00);
+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 64, 0, 32, 32, 0xff0000ff);
+#endif
++
++ buf->user_data = NULL; // Responsability for this pic no longer with buffer
+ 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);
+}
+
@@ -2065,8 +2015,7 @@
+ }
+ }
+ }
-
-- sys->output_format = format;
++
+ // Put back
+ buf->user_data = NULL; // Zap here to make sure we can't reuse later
+ mmal_buffer_header_reset(buf);
@@ -2104,7 +2053,7 @@
+ if (sys->output != NULL && sys->output->is_enabled)
+ mmal_port_disable(sys->output);
+
-+ cma_buf_pool_deletez(&sys->cma_out_pool);
++// cma_buf_pool_deletez(&sys->cma_out_pool);
+
+ // Free up anything we may have already lying around
+ // Don't need lock as the above disables should have prevented anything
@@ -2127,12 +2076,9 @@
+ 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;
++ vlc_sem_init(&sys->sem, 0);
++ sys->pic_n = 0;
+
+ // Reset error status
+ sys->err_stream = MMAL_SUCCESS;
@@ -2142,11 +2088,6 @@
+#endif
+}
+
-+static void conv_flush_passthrough(filter_t * p_filter)
-+{
-+ VLC_UNUSED(p_filter);
-+}
-+
+static void conv_stash_fixup(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const p_pic)
+{
+ conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf);
@@ -2165,6 +2106,21 @@
+ }
+}
+
++// Output buffers may contain a pic ref on error or flush
++// Free it
++static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
++{
++ VLC_UNUSED(userdata);
++
++ picture_t * const pic = header->user_data;
++ header->user_data = NULL;
++
++ if (pic != NULL)
++ picture_Release(pic);
++
++ return MMAL_FALSE;
++}
++
+static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const pic)
+{
+ MMAL_STATUS_T status;
@@ -2173,11 +2129,11 @@
+ sys->output->format->type = MMAL_ES_TYPE_VIDEO;
+ sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
+ sys->output->format->encoding_variant = 0;
-+ vlc_to_mmal_video_fmt(sys->output->format, &p_filter->fmt_out.video);
++ hw_mmal_vlc_fmt_to_mmal_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)
+ {
++ // Override default format width/height if we have a pic we need to match
+ if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS)
+ {
+ char cbuf[5];
@@ -2189,6 +2145,11 @@
+ msg_Dbg(p_filter, "%s: %dx%d [(0,0) %dx%d]", __func__, fmt->width, fmt->height, fmt->crop.width, fmt->crop.height);
+ }
+
++ if (sys->is_sliced) {
++ // Override height for slice
++ sys->output->format->es->video.height = MMAL_SLICE_HEIGHT;
++ }
++
+ mmal_log_dump_format(sys->output->format);
+
+ status = mmal_port_format_commit(sys->output);
@@ -2200,8 +2161,7 @@
+
+ sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended);
+ sys->output->buffer_size = sys->output->buffer_size_recommended;
-
-- mmal_buffer_header_release(buffer);
++
+ if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS)
+ return status;
+
@@ -2215,14 +2175,12 @@
+ MMAL_STATUS_T err;
+ const uint64_t frame_seq = ++sys->frame_seq;
+ conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf);
++ MMAL_BUFFER_HEADER_T * out_buf = NULL;
+
+#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__,
++ 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,
@@ -2238,8 +2196,19 @@
+ goto stream_fail;
+ }
+
++ // Check pic fmt corresponds to what we have set up
++ // ??? ISP may require flush (disable) but actually seems quite happy
++ // without
++ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
++ {
++ msg_Dbg(p_filter, "Reset input port format");
++ mmal_port_format_commit(sys->input);
++ }
++
+ if (p_pic->context == NULL) {
-+ msg_Dbg(p_filter, "%s: No context", __func__);
++ // Can't have stashed subpics if not one of our pics
++ if (!sys->needs_copy_in)
++ msg_Dbg(p_filter, "%s: No context", __func__);
+ }
+ else if (sys->resizer_type == FILTER_RESIZER_HVS)
+ {
@@ -2276,6 +2245,8 @@
+ // so no need to worry about actual pic dimensions here
+ if ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS)
+ goto fail;
++
++ sys->out_pool = mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size);
+ }
+ else {
+ picture_t *pic = filter_NewPicture(p_filter);
@@ -2283,11 +2254,9 @@
+ picture_Release(pic);
+ if (err != MMAL_SUCCESS)
+ goto fail;
-+ }
+
-+ sys->out_pool = sys->is_sliced ?
-+ mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size) :
-+ mmal_pool_create(sys->output->buffer_num, 0);
++ sys->out_pool = mmal_pool_create(sys->output->buffer_num, 0);
++ }
+
+ if (sys->out_pool == NULL) {
+ msg_Err(p_filter, "Failed to create output pool");
@@ -2300,9 +2269,10 @@
+ (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->is_sliced) {
-+ MMAL_BUFFER_HEADER_T * out_buf;
++ // We attach pic to buf before stuffing the output port
++ // We could attach the pic on output for cma, but it is a lot easier to keep
++ // the code common.
++ {
+ picture_t * const out_pic = filter_NewPicture(p_filter);
+
+ if (out_pic == NULL)
@@ -2311,88 +2281,71 @@
+ 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;
-+ }
++ out_pic->format.i_sar_den = p_filter->fmt_out.video.i_sar_den;
++ out_pic->format.i_sar_num = p_filter->fmt_out.video.i_sar_num;
+
-+ stash->pts = 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;
++ if (sys->is_sliced) {
++ 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);
+ }
-+
-+ picture_Release(p_pic);
-+ p_pic = NULL;
-+ --sys->in_count;
-+ }
-+
-+ if (!sys->is_sliced) {
-+ 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)
++ else
+ {
-+ picture_t * const out_pic = filter_NewPicture(p_filter);
-+
-+ if (out_pic == NULL) {
-+ msg_Warn(p_filter, "Failed to alloc new filter output pic");
-+ mmal_buffer_header_release(out_buf);
-+ break;
++ // 1 in - 1 out
++ if ((out_buf = mmal_queue_wait(sys->out_pool->queue)) == NULL)
++ {
++ msg_Err(p_filter, "Failed to get output buffer");
++ picture_Release(out_pic);
++ goto fail;
+ }
++ mmal_buffer_header_reset(out_buf);
++
++ // Attach out_pic to the buffer & ensure it is freed when the buffer is released
++ // On a good send callback the pic will be extracted to avoid this
++ out_buf->user_data = out_pic;
++ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, NULL);
+
+#if 0
-+ char dbuf0[5];
-+ 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);
++ {
++ char dbuf0[5];
++ 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;
-+
+ if (sys->is_cma) {
+ int rv;
-+ if ((rv = cma_buf_pic_attach(sys->cma_out_pool, out_pic, sys->output->buffer_size)) != VLC_SUCCESS)
-+ {
++
++ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size);
++ if (cb == NULL) {
+ char dbuf0[5];
-+ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d",
++ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d",
+ str_fourcc(dbuf0, out_pic->format.i_chroma),
-+ rv);
++ sys->output->buffer_size);
+ goto fail;
+ }
-+ const unsigned int vc_h = cma_buf_pic_vc_handle(out_pic);
-+ if (vc_h == 0)
++ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable
++ out_buf->data = (uint8_t *)vc_h;
++ out_buf->alloc_size = sys->output->buffer_size;
++
++ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS)
+ {
-+ msg_Err(p_filter, "Pic has no vc handle");
++ char dbuf0[5];
++ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d",
++ str_fourcc(dbuf0, out_pic->format.i_chroma),
++ rv);
++ cma_buf_unref(cb);
+ goto fail;
+ }
-+ out_buf->data = (uint8_t *)vc_h;
-+ out_buf->alloc_size = sys->output->buffer_size;
+ }
+ else {
+ out_buf->data = out_pic->p[0].p_pixels;
@@ -2409,63 +2362,66 @@
+ 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;
++ goto fail;
+ }
-+
-+ ++sys->in_count;
++ out_buf = NULL;
+ }
+ }
+
-+ if (sys->in_count < 0)
++
++ // Stuff into input
++ // We assume the BH is already set up with values reflecting pic date etc.
++ stash->pts = p_pic->date;
+ {
-+ msg_Err(p_filter, "Buffer count somehow negative");
-+ goto fail;
-+ }
++ MMAL_BUFFER_HEADER_T *const pic_buf = sys->needs_copy_in ?
++ hw_mmal_pic_buf_copied(p_pic, sys->in_pool, sys->input, sys->cma_in_pool) :
++ hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
+
-+ // Avoid being more than 1 pic behind
-+ vlc_sem_wait(&sys->sem);
++ // Whether or not we extracted the pic_buf we are done with the picture
++ picture_Release(p_pic);
++ p_pic = NULL;
+
-+ // Set sem for delayed scale if not already set
-+ if (!sys->latency_set) {
-+ unsigned int i;
-+ sys->latency_set = true;
-+ for (i = 0; i != CONV_MAX_LATENCY; ++i) {
-+ vlc_sem_post(&sys->sem);
++ if (pic_buf == NULL) {
++ msg_Err(p_filter, "Pic has no attached buffer");
++ goto fail;
++ }
++
++ pic_buf->pts = frame_seq;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "In buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d/%d, pts=%lld",
++ p_pic, pic_buf->data, pic_buf->user_data, pic_buf->flags,
++ pic_buf->length, pic_buf->alloc_size, sys->input->buffer_size, (long long)pic_buf->pts);
++#endif
++
++ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
++ {
++ msg_Err(p_filter, "Send buffer to input failed");
++ mmal_buffer_header_release(pic_buf);
++ goto fail;
+ }
+ }
+
-+ // Return all pending buffers
++ // We have a 1 pic latency for everything except the 1st pic which we
++ // wait for.
++ // This means we get a single static pic out
++ if (sys->pic_n++ == 1) {
++#if TRACE_ALL
++ msg_Dbg(p_filter, ">>> %s: Pic1=NULL", __func__);
++#endif
++ return NULL;
++ }
++ vlc_sem_wait(&sys->sem);
++
++ // Return a single pending buffer
+ vlc_mutex_lock(&sys->lock);
-+ ret_pics = pic_fifo_get_all(&sys->ret_pics);
++ ret_pics = pic_fifo_get(&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;
-+
-+ conv_stash_fixup(p_filter, sys, ret_pics);
-+#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);
-+ conv_stash_fixup(p_filter, sys, next_pic);
-+ next_pic = next_pic->p_next;
-+ }
-+ }
++ conv_stash_fixup(p_filter, sys, ret_pics);
+
+#if TRACE_ALL
+ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
@@ -2476,21 +2432,18 @@
+stream_fail:
+ msg_Err(p_filter, "MMAL error reported by callback");
+fail:
++#if TRACE_ALL
++ msg_Err(p_filter, ">>> %s: FAIL", __func__);
++ picture_Release(ret_pics);
++#endif
++ if (out_buf != NULL)
++ mmal_buffer_header_release(out_buf);
+ if (p_pic != NULL)
+ picture_Release(p_pic);
+ conv_flush(p_filter);
+ return NULL;
+}
+
-+static picture_t *conv_filter_passthrough(filter_t *p_filter, picture_t *p_pic)
-+{
-+ VLC_UNUSED(p_filter);
-+#if TRACE_ALL
-+ msg_Dbg(p_filter, "<<< %s", __func__);
-+#endif
-+ return p_pic;
-+}
-+
+static void CloseConverter(vlc_object_t * obj)
+{
+ filter_t * const p_filter = (filter_t *)obj;
@@ -2507,6 +2460,9 @@
+ // Disables input & output ports
+ conv_flush(p_filter);
+
++ cma_buf_pool_deletez(&sys->cma_in_pool);
++ cma_buf_pool_deletez(&sys->cma_out_pool);
++
+ if (sys->component && sys->component->control->is_enabled)
+ mmal_port_disable(sys->component->control);
+
@@ -2534,60 +2490,53 @@
+ if (sys->component)
+ mmal_component_release(sys->component);
+
++ cma_vcsm_exit(sys->vcsm_init_type);
++
+ vlc_sem_destroy(&sys->sem);
+ vlc_mutex_destroy(&sys->lock);
+
++ p_filter->p_sys = NULL;
+ free(sys);
+}
+
+
-+static int open_converter_passthrough(filter_t * const p_filter)
++static inline MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt)
+{
-+ {
-+ 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] rgb:%#x:%#x:%#x sar:%d/%d", __func__,
-+ "passthrough",
-+ 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_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
-+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
-+ }
++ if (hw_mmal_chroma_is_mmal(fmt->i_chroma))
++ return vlc_to_mmal_video_fourcc(fmt);
+
++ if (fmt->i_chroma == VLC_CODEC_I420 ||
++ fmt->i_chroma == VLC_CODEC_I420_10L)
++ return MMAL_ENCODING_I420;
+
-+ p_filter->pf_video_filter = conv_filter_passthrough;
-+ p_filter->pf_flush = conv_flush_passthrough;
-+ return VLC_SUCCESS;
++ return 0;
++}
+
+- sys->output_format = format;
++static inline MMAL_FOURCC_T filter_enc_out(const video_format_t * const fmt)
++{
++ const MMAL_FOURCC_T mmes = vlc_to_mmal_video_fourcc(fmt);
++ // Can only copy out single plane stuff currently - this could be fixed!
++ return hw_mmal_chroma_is_mmal(fmt->i_chroma) || mmes != MMAL_ENCODING_I420 ? mmes : 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 = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video);
++ MMAL_FOURCC_T enc_out = filter_enc_out(&p_filter->fmt_out.video);
++ const MMAL_FOURCC_T enc_in = filter_enc_in(&p_filter->fmt_in.video);
+ bool use_resizer;
+ bool use_isp;
+ int gpu_mem;
+
-+ if ((enc_in != MMAL_ENCODING_OPAQUE &&
-+ enc_in != MMAL_ENCODING_YUVUV128 &&
-+ enc_in != MMAL_ENCODING_YUVUV64_10) ||
-+ (enc_out = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video)) == 0)
++ // At least in principle we should deal with any mmal format as input
++ if (enc_in == 0 || enc_out == 0)
+ return VLC_EGENERIC;
+
-+ if (enc_in == enc_out) {
-+ return open_converter_passthrough(p_filter);
-+ }
-+
+ use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME);
+ use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME);
+
@@ -2606,6 +2555,11 @@
+ if (enc_out == MMAL_ENCODING_I420) {
+ use_isp = true;
+ }
++ // Only HVS can deal with SAND30
++ if (enc_in == MMAL_ENCODING_YUV10_COL) {
++ if (use_isp || use_resizer)
++ return VLC_EGENERIC;
++ }
+
+
+ if (use_resizer) {
@@ -2645,7 +2599,8 @@
+ goto fail;
+ }
+ p_filter->p_sys = sys;
-+
+
+- mmal_buffer_header_release(buffer);
+ // Init stuff the we destroy unconditionaly in Close first
+ vlc_mutex_init(&sys->lock);
+ vlc_sem_init(&sys->sem, 0);
@@ -2653,8 +2608,14 @@
+ pic_fifo_init(&sys->ret_pics);
+ pic_fifo_init(&sys->slice.pics);
+
++ sys->needs_copy_in = !hw_mmal_chroma_is_mmal(p_filter->fmt_in.video.i_chroma);
+ sys->in_port_cb_fn = conv_input_port_cb;
+
++ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
++ msg_Err(p_filter, "VCSM init failed");
++ goto fail;
++ }
++
+ if (use_resizer) {
+ sys->resizer_type = FILTER_RESIZER_RESIZER;
+ sys->is_sliced = true;
@@ -2696,13 +2657,20 @@
+ 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;
- }
++ }
++
++ if (sys->needs_copy_in &&
++ (sys->cma_in_pool = cma_buf_pool_new(2, 2, true, "conv-copy-in")) == NULL)
++ {
++ msg_Err(p_filter, "Failed to allocate input CMA pool");
++ 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);
++ hw_mmal_vlc_fmt_to_mmal_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);
@@ -2738,7 +2706,7 @@
+ {
+ 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)
++ if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], -1, i + 1) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Failed to open subpic %d", i);
+ goto fail;
@@ -2763,18 +2731,205 @@
+ use_resizer = true;
+ msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer");
+ goto retry;
-+ }
+ }
+
+#if TRACE_ALL
+ msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret);
+#endif
+ return ret;
+ }
++
++#if OPT_TO_FROM_ZC
++//----------------------------------------------------------------------------
++//
++// Simple copy in to ZC
++
++typedef struct to_zc_sys_s {
++ vcsm_init_type_t vcsm_init_type;
++ cma_buf_pool_t * cma_out_pool;
++} to_zc_sys_t;
++
++
++static size_t buf_alloc_size(const vlc_fourcc_t i_chroma, const unsigned int width, const unsigned int height)
++{
++ const unsigned int pels = width * height;
++
++ switch (i_chroma)
++ {
++ case VLC_CODEC_MMAL_ZC_RGB32:
++ return pels * 4;
++ case VLC_CODEC_MMAL_ZC_I420:
++ return pels * 3 / 2;
++ default:
++ break;
++ }
++ return 0;
++}
++
++
++static picture_t *
++to_zc_filter(filter_t *p_filter, picture_t *in_pic)
++{
++ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys;
++#if TRACE_ALL
++ msg_Dbg(p_filter, "<<< %s", __func__);
++#endif
++
++ assert(p_filter->fmt_out.video.i_chroma == VLC_CODEC_MMAL_ZC_I420);
++
++ picture_t * const out_pic = filter_NewPicture(p_filter);
++ if (out_pic == NULL)
++ goto fail0;
++
++ MMAL_ES_SPECIFIC_FORMAT_T mm_vfmt = {.video={0}};
++ MMAL_ES_FORMAT_T mm_esfmt = {
++ .encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video),
++ .es = &mm_vfmt};
++
++ hw_mmal_vlc_fmt_to_mmal_fmt(&mm_esfmt, &p_filter->fmt_out.video);
++
++ const size_t buf_alloc = buf_alloc_size(p_filter->fmt_out.video.i_chroma,
++ mm_vfmt.video.width, mm_vfmt.video.height);
++ if (buf_alloc == 0)
++ goto fail1;
++ cma_buf_t *const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, buf_alloc);
++ if (cb == NULL)
++ goto fail1;
++
++ if (cma_buf_pic_attach(cb, out_pic) != VLC_SUCCESS)
++ goto fail2;
++ cma_pic_set_data(out_pic, &mm_esfmt, NULL);
++
++ hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), NULL, &mm_esfmt, in_pic);
++
++ // Copy pic properties
++ out_pic->date = in_pic->date;
++ out_pic->b_force = in_pic->b_force;
++ out_pic->b_progressive = in_pic->b_progressive;
++ out_pic->b_top_field_first = in_pic->b_top_field_first;
++ out_pic->i_nb_fields = in_pic->i_nb_fields;
++
++ picture_Release(in_pic);
++
++ return out_pic;
++
++fail2:
++ cma_buf_unref(cb);
++fail1:
++ picture_Release(out_pic);
++fail0:
++ picture_Release(in_pic);
++ return NULL;
++}
++
++static void to_zc_flush(filter_t * p_filter)
++{
++ VLC_UNUSED(p_filter);
++}
++
++static void CloseConverterToZc(vlc_object_t * obj)
++{
++ filter_t * const p_filter = (filter_t *)obj;
++ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys;
++
++ if (sys == NULL)
++ return;
++
++ p_filter->p_sys = NULL;
++
++ cma_buf_pool_deletez(&sys->cma_out_pool);
++ cma_vcsm_exit(sys->vcsm_init_type);
++
++ free(sys);
++}
++
++static bool to_zc_validate_fmt(const video_format_t * const f_in, const video_format_t * const f_out)
++{
++ if (!((f_in->i_chroma == VLC_CODEC_I420 || f_in->i_chroma == VLC_CODEC_I420_10L) &&
++ f_out->i_chroma == VLC_CODEC_MMAL_ZC_I420))
++ {
++ return false;
++ }
++ if (f_in->i_height != f_out->i_height ||
++ f_in->i_width != f_out->i_width)
++ {
++ return false;
++ }
++
++ return true;
++}
++
++static int OpenConverterToZc(vlc_object_t * obj)
++{
++ int ret = VLC_EGENERIC;
++ filter_t * const p_filter = (filter_t *)obj;
++
++ if (!to_zc_validate_fmt(&p_filter->fmt_in.video, &p_filter->fmt_out.video))
++ goto fail;
++
++ {
++ 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] rgb:%#x:%#x:%#x 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_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
++ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
++ }
++
++ to_zc_sys_t * const sys = calloc(1, sizeof(*sys));
++ if (!sys) {
++ ret = VLC_ENOMEM;
++ goto fail;
++ }
++ p_filter->p_sys = (filter_sys_t *)sys;
++
++ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
++ msg_Err(p_filter, "VCSM init failed");
++ goto fail;
++ }
++
++ if ((sys->cma_out_pool = cma_buf_pool_new(5, 5, true, "conv-to-zc")) == NULL)
++ {
++ msg_Err(p_filter, "Failed to allocate input CMA pool");
++ goto fail;
++ }
++
++ p_filter->pf_video_filter = to_zc_filter;
++ p_filter->pf_flush = to_zc_flush;
++ return VLC_SUCCESS;
++
++fail:
++ CloseConverterToZc(obj);
++ return ret;
++}
++
++//----------------------------------------------------------------------------
++//
++// Simple "copy" from ZC
++
++static void CloseConverterFromZc(vlc_object_t * obj)
++{
++ VLC_UNUSED(obj);
+}
+
++static int OpenConverterFromZc(vlc_object_t * obj)
++{
++ return VLC_EGENERIC;
++}
++#endif
++//----------------------------------------------------------------------------
+
+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
++ vcsm_init_type_t vcsm_init_type;
+} blend_sys_t;
+
+static void FilterBlendMmal(filter_t *p_filter,
@@ -2821,14 +2976,26 @@
+ hw_mmal_vzc_pool_flush(sys->vzc);
+}
+
++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;
++
++ if (sys != NULL) {
++ p_filter->p_sys = NULL;
++
++ hw_mmal_vzc_pool_release(sys->vzc);
++ cma_vcsm_exit(sys->vcsm_init_type);
++ free(sys);
++ }
++}
++
+static int OpenBlendMmal(vlc_object_t *object)
+{
+ filter_t * const p_filter = (filter_t *)object;
+ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
+
-+ if ((vfcc_dst != VLC_CODEC_MMAL_OPAQUE &&
-+ vfcc_dst != VLC_CODEC_MMAL_ZC_SAND8 &&
-+ vfcc_dst != VLC_CODEC_MMAL_ZC_SAND10) ||
++ if (!hw_mmal_chroma_is_mmal(vfcc_dst) ||
+ !hw_mmal_vzc_subpic_fmt_valid(&p_filter->fmt_in.video))
+ {
+ return VLC_EGENERIC;
@@ -2850,28 +3017,27 @@
+ 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;
++
++ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
++ msg_Err(p_filter, "VCSM init failed");
++ goto fail;
++ }
++
++ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
++ goto fail;
+ }
+
+ 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);
- }
++fail:
++ CloseBlendMmal(VLC_OBJECT(p_filter));
++ return VLC_ENOMEM;
++}
+
+// ---------------------------------------------------------------------------
+
@@ -3004,14 +3170,34 @@
+ add_submodule()
+ set_category( CAT_VIDEO )
+ set_subcategory( SUBCAT_VIDEO_VFILTER )
-+ set_shortname(N_("MMAL converterer"))
-+ set_description(N_("MMAL conversion filter"))
++ set_shortname(N_("MMAL resizer"))
++ set_description(N_("MMAL resizing 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)
+
++#if OPT_TO_FROM_ZC
++ add_submodule()
++ set_category( CAT_VIDEO )
++ set_subcategory( SUBCAT_VIDEO_VFILTER )
++ set_shortname(N_("MMAL to ZC"))
++ set_description(N_("MMAL conversion to ZC filter"))
++ add_shortcut("mmal_to_zc")
++ set_capability( "video converter", 901 )
++ set_callbacks(OpenConverterToZc, CloseConverterToZc)
++
++ add_submodule()
++ set_category( CAT_VIDEO )
++ set_subcategory( SUBCAT_VIDEO_VFILTER )
++ set_shortname(N_("MMAL from ZC"))
++ set_description(N_("MMAL conversion from ZC filter"))
++ add_shortcut("mmal_from_zc")
++ set_capability( "video converter", 902 )
++ set_callbacks(OpenConverterFromZc, CloseConverterFromZc)
++#endif
++
+ add_submodule()
+ set_category( CAT_VIDEO )
+ set_subcategory( SUBCAT_VIDEO_VFILTER )
@@ -3033,7 +3219,7 @@
+
--- /dev/null
+++ b/modules/hw/mmal/converter_mmal.c
-@@ -0,0 +1,448 @@
+@@ -0,0 +1,479 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
@@ -3062,28 +3248,13 @@
+
+#include <assert.h>
+
-+#define OPT_SAND 0
-+#define OPT_I420 1
-+#define OPT_RGB32 0
-+
-+
-+#if OPT_SAND
-+#define FMT_IN VLC_CODEC_MMAL_ZC_SAND8
-+#elif OPT_I420
-+#define FMT_IN VLC_CODEC_MMAL_ZC_I420
-+#elif OPT_RGB32
-+#define FMT_IN VLC_CODEC_MMAL_ZC_RGB32
-+#elif
-+#error Missing input format
-+#endif
-+
+#define TRACE_ALL 0
+
+typedef struct mmal_gl_converter_s
+{
+ EGLint drm_fourcc;
+ vcsm_init_type_t vcsm_init_type;
-+ struct cma_pic_context_s * last_ctx_ref;
++ cma_buf_t * last_cb;
+
+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
+} mmal_gl_converter_t;
@@ -3100,7 +3271,7 @@
+ return MMAL_FOURCC('Y','V','1','2');
+ case MMAL_ENCODING_I422:
+ return MMAL_FOURCC('Y','U','1','6');
-+ case MMAL_ENCODING_YUVUV128:
++// case MMAL_ENCODING_YUVUV128: // Doesn't actually work yet
+ case MMAL_ENCODING_NV12:
+ return MMAL_FOURCC('N','V','1','2');
+ case MMAL_ENCODING_NV21:
@@ -3133,7 +3304,6 @@
+static void tex_context_delete(tex_context_t * const tex)
+{
+ tex->DeleteTextures(1, &tex->texture);
-+
+ free(tex);
+}
+
@@ -3147,11 +3317,10 @@
+ return pic_ctx;
+}
+
-+static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic)
++static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic, cma_buf_t * const cb)
+{
+ mmal_gl_converter_t * const sys = tc->priv;
-+
-+ tex_context_t * tex = (tex_context_t *)cma_buf_pic_context2(pic);
++ tex_context_t * tex = (tex_context_t *)cma_buf_context2(cb);
+ if (tex != NULL)
+ return tex;
+
@@ -3170,12 +3339,12 @@
+ {
+ EGLint attribs[30];
+ EGLint * a = attribs;
-+ const int fd = cma_buf_pic_fd(pic);
-+ uint8_t * base_addr = cma_buf_pic_addr(pic);
++ const int fd = cma_buf_fd(cb);
++ uint8_t * base_addr = cma_buf_addr(cb);
+
+ if (pic->i_planes >= 4 || pic->i_planes <= 0)
+ {
-+ msg_Err(tc, "%s: Bad planes", __func__);
++ msg_Err(tc, "%s: Bad planes: %d", __func__, pic->i_planes);
+ goto fail;
+ }
+
@@ -3268,7 +3437,7 @@
+ tc->gl->egl.destroyImageKHR(tc->gl, image);
+ }
+
-+ if (cma_buf_pic_add_context2(pic, &tex->cmn) != VLC_SUCCESS)
++ if (cma_buf_add_context2(cb, &tex->cmn) != VLC_SUCCESS)
+ {
+ msg_Err(tc, "%s: add_context2 failed", __func__);
+ goto fail;
@@ -3288,7 +3457,12 @@
+{
+ mmal_gl_converter_t * const sys = tc->priv;
+#if TRACE_ALL
-+ msg_Err(tc, "%s: %d*%dx%d : %d*%dx%d", __func__, tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines);
++ {
++ char cbuf[5];
++ msg_Dbg(tc, "%s: %s %d*%dx%d : %d*%dx%d", __func__,
++ str_fourcc(cbuf, pic->format.i_chroma),
++ tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines);
++ }
+#endif
+ VLC_UNUSED(tex_width);
+ VLC_UNUSED(tex_height);
@@ -3301,14 +3475,21 @@
+ return VLC_EGENERIC;
+ }
+
-+ tex_context_t * const tex = get_tex_context(tc, pic);
++ cma_buf_t * const cb = cma_buf_pic_get(pic);
++ if (cb == NULL)
++ {
++ msg_Err(tc, "Pic missing cma buf");
++ return VLC_EGENERIC;
++ }
++
++ tex_context_t * const tex = get_tex_context(tc, pic, cb);
+ if (tex == NULL)
+ return VLC_EGENERIC;
+
+// tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture);
+
-+ cma_buf_pic_context_unref(sys->last_ctx_ref); // ?? Needed ??
-+ sys->last_ctx_ref = cma_buf_pic_context_ref(pic);
++ cma_buf_unref(sys->last_cb);
++ sys->last_cb = cma_buf_ref(cb);
+
+ textures[0] = tex->texture;
+ return VLC_SUCCESS;
@@ -3375,23 +3556,55 @@
+ if (sys == NULL)
+ return;
+
-+ cma_buf_pic_context_unref(sys->last_ctx_ref);
++ cma_buf_unref(sys->last_cb);
+ cma_vcsm_exit(sys->vcsm_init_type);
+ free(sys);
+}
+
++
++// Pick a chroma that we can convert to
++// Prefer I420 as smallest
++static vlc_fourcc_t chroma_in_out(const vlc_fourcc_t chroma_in)
++{
++ switch (chroma_in)
++ {
++ case VLC_CODEC_MMAL_OPAQUE:
++ case VLC_CODEC_MMAL_ZC_I420:
++ case VLC_CODEC_MMAL_ZC_SAND8:
++ case VLC_CODEC_MMAL_ZC_SAND10: // ISP only
++ return VLC_CODEC_MMAL_ZC_I420;
++ case VLC_CODEC_MMAL_ZC_SAND30: // HVS only
++ case VLC_CODEC_MMAL_ZC_RGB32:
++ return VLC_CODEC_MMAL_ZC_RGB32; // HVS can't generate YUV of any sort
++ default:
++ break;
++ }
++ return 0;
++}
++
++
+static int
+OpenGLConverter(vlc_object_t *obj)
+{
+ opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj;
+ int rv = VLC_EGENERIC;
+ const EGLint eglfmt = vlc_to_gl_fourcc(&tc->fmt);
++ const vlc_fourcc_t chroma_out = chroma_in_out(tc->fmt.i_chroma);
+
-+ // Accept Opaque (as it can definitely be converted) or what we actually want
-+ if (!(tc->fmt.i_chroma == FMT_IN ||
-+ tc->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE))
-+ {
++ // Do we know what to do with this?
++ if (chroma_out == 0)
+ return rv;
++
++ {
++ char dbuf0[5], dbuf1[5], dbuf2[5];
++ msg_Dbg(tc, "<<< %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__,
++ str_fourcc(dbuf0, tc->fmt.i_chroma),
++ str_fourcc(dbuf1, eglfmt),
++ tc->fmt.i_width, tc->fmt.i_height,
++ tc->fmt.i_x_offset, tc->fmt.i_y_offset,
++ tc->fmt.i_visible_width, tc->fmt.i_visible_height,
++ tc->fmt.i_sar_num, tc->fmt.i_sar_den,
++ str_fourcc(dbuf2, chroma_out));
+ }
+
+ if (tc->gl->ext != VLC_GL_EXT_EGL ||
@@ -3402,17 +3615,6 @@
+ return rv;
+ }
+
-+ {
-+ char dbuf0[5], dbuf1[5];
-+ msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
-+ str_fourcc(dbuf0, tc->fmt.i_chroma),
-+ str_fourcc(dbuf1, eglfmt),
-+ tc->fmt.i_width, tc->fmt.i_height,
-+ tc->fmt.i_x_offset, tc->fmt.i_y_offset,
-+ tc->fmt.i_visible_width, tc->fmt.i_visible_height,
-+ tc->fmt.i_sar_num, tc->fmt.i_sar_den);
-+ }
-+
+ if ((tc->priv = calloc(1, sizeof(mmal_gl_converter_t))) == NULL)
+ {
+ msg_Err(tc, "priv alloc failure");
@@ -3444,7 +3646,7 @@
+
+ if (eglfmt == 0)
+ {
-+ tc->fmt.i_chroma = FMT_IN;
++ tc->fmt.i_chroma = chroma_out;
+ tc->fmt.i_bits_per_pixel = 8;
+ if (tc->fmt.i_chroma == VLC_CODEC_MMAL_ZC_RGB32)
+ {
@@ -3465,6 +3667,21 @@
+
+ tc->handle_texs_gen = true; // We manage the texs
+ tc->pf_update = tc_mmal_update;
++
++#if TRACE_ALL
++ {
++ char dbuf0[5], dbuf1[5], dbuf2[5];
++ msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__,
++ str_fourcc(dbuf0, tc->fmt.i_chroma),
++ str_fourcc(dbuf1, sys->drm_fourcc),
++ tc->fmt.i_width, tc->fmt.i_height,
++ tc->fmt.i_x_offset, tc->fmt.i_y_offset,
++ tc->fmt.i_visible_width, tc->fmt.i_visible_height,
++ tc->fmt.i_sar_num, tc->fmt.i_sar_den,
++ str_fourcc(dbuf2, chroma_out));
++ }
++#endif
++
+ return VLC_SUCCESS;
+
+fail:
@@ -3499,23 +3716,28 @@
#include "mmal_picture.h"
-@@ -41,466 +42,569 @@
-
- #define MIN_NUM_BUFFERS_IN_TRANSIT 2
+@@ -39,468 +40,814 @@
+ #include <interface/mmal/util/mmal_util.h>
+ #include <interface/mmal/util/mmal_default_components.h>
--#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 MIN_NUM_BUFFERS_IN_TRANSIT 2
+#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_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_ADV "mmal-deinterlace-adv"
+#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace")
+#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace")
+-static int Open(filter_t *filter);
+-static void Close(filter_t *filter);
++#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")
+
-vlc_module_begin()
- set_shortname(N_("MMAL deinterlace"))
- set_description(N_("MMAL-based deinterlace filter plugin"))
@@ -3527,10 +3749,6 @@
- add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT,
- MMAL_DEINTERLACE_QPU_LONGTEXT, true);
-vlc_module_end()
-+#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. "\
@@ -3552,19 +3770,28 @@
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;
++
++ // Bind this lot somehow into ppr????
++ bool is_cma;
++ cma_buf_pool_t * cma_out_pool;
++ MMAL_POOL_T * out_pool;
++
++ hw_mmal_port_pool_ref_t *out_ppr;
++
++ bool half_rate;
++ bool use_qpu;
+ bool use_fast;
+ bool use_passthrough;
-+ unsigned int seq_in;
-+ unsigned int seq_out;
++ unsigned int seq_in; // Seq of next frame to submit (1-15) [Init=1]
++ unsigned int seq_out; // Seq of last frame received (1-15) [Init=15]
+
+- MMAL_QUEUE_T *filtered_pictures;
+- vlc_sem_t sem;
++ vcsm_init_type_t vcsm_init_type;
+
+- atomic_bool started;
+} filter_sys_t;
- /* statistics */
@@ -3581,12 +3808,19 @@
#define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
-static int Open(filter_t *filter)
--{
++#define TRACE_ALL 0
++
++
++
++// 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)
+ {
- 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
++ filter_sys_t *const filter_sys = p_filter->p_sys;
++ picture_t * const pic = filter_NewPicture(p_filter);
- MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
- { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
@@ -3594,40 +3828,45 @@
- 4,
- { 3, frame_duration, 0, use_qpu }
- };
++ if (pic == NULL)
++ goto fail1;
- int ret = VLC_SUCCESS;
- MMAL_STATUS_T status;
- filter_sys_t *sys;
++ if (buf->length == 0) {
++ msg_Err(p_filter, "%s: Empty buffer", __func__);
++ goto fail2;
++ }
- 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 ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL)
++ goto fail2;
- if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
- return VLC_EGENERIC;
-+ if (pic == NULL)
-+ goto fail1;
++ buf_to_pic_copy_props(pic, buf);
- 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;
-+ }
++#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
- sys = calloc(1, sizeof(filter_sys_t));
- if (!sys)
- return VLC_ENOMEM;
- filter->p_sys = sys;
-+ if ((pic->context = hw_mmal_gen_context(MMAL_ENCODING_OPAQUE, buf, filter_sys->out_ppr)) == NULL)
-+ goto fail2;
++ return pic;
- bcm_host_init();
-+ buf_to_pic_copy_props(pic, buf);
++fail2:
++ picture_Release(pic);
++fail1:
++// mmal_buffer_header_release(buf);
++ return NULL;
++}
- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
- if (status != MMAL_SUCCESS) {
@@ -3636,8 +3875,16 @@
- ret = VLC_EGENERIC;
- goto out;
- }
++static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
++{
+#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);
++ 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
- status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
@@ -3647,7 +3894,7 @@
- ret = VLC_EGENERIC;
- goto out;
- }
-+ return pic;
++ mmal_buffer_header_release(buffer);
- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
- status = mmal_port_enable(sys->component->control, control_port_cb);
@@ -3656,13 +3903,29 @@
- 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;
++#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
++ return;
+ }
- sys->input = sys->component->input[0];
- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
@@ -3676,21 +3939,12 @@
- 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
++ mmal_buffer_header_reset(buf); // User data stays intact so release will kill pic
++ mmal_buffer_header_release(buf);
++}
- 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) {
@@ -3698,36 +3952,9 @@
- 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 = {
@@ -3763,18 +3990,18 @@
- ret = VLC_EGENERIC;
- goto out;
- }
-+static inline unsigned int seq_inc(unsigned int x)
++// Output buffers may contain a pic ref on error or flush
++// Free it
++static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
+{
-+ return x + 1 >= 16 ? 1 : x + 1;
-+}
++ VLC_UNUSED(userdata);
- 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);
-+}
++ cma_buf_t * const cb = header->user_data;
++ header->user_data = NULL;
++ cma_buf_unref(cb); // Copes fine with NULL
- status = mmal_port_format_commit(sys->output);
- if (status != MMAL_SUCCESS) {
@@ -3782,16 +4009,106 @@
- sys->input->name, status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
-- }
++ return MMAL_FALSE;
++}
++
++static inline unsigned int seq_inc(unsigned int x)
++{
++ return x + 1 >= 16 ? 1 : x + 1;
++}
++
++static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq)
++{
++ return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq);
++}
++
+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;
++ MMAL_BUFFER_HEADER_T * out_buf = NULL;
++
++#if TRACE_ALL
++ msg_Dbg(p_filter, "<<< %s", __func__);
++#endif
++
++ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
++ {
++ // ****** Breaks on opaque (at least)
++
++ if (sys->input->is_enabled)
++ mmal_port_disable(sys->input);
++#if 0
++ if (sys->output->is_enabled)
++ mmal_port_disable(sys->output);
++
++ mmal_format_full_copy(sys->output->format, sys->input->format);
++ mmal_port_format_commit(sys->output);
++ sys->output->buffer_num = 30;
++ sys->output->buffer_size = sys->input->buffer_size_recommended;
++ mmal_port_enable(sys->output, di_output_port_cb);
++#endif
++ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
++ msg_Err(p_filter, "Failed to update pic format");
++ sys->input->buffer_num = 30;
++ sys->input->buffer_size = sys->input->buffer_size_recommended;
++ mmal_log_dump_format(sys->input->format);
++ }
++
++ // 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 reenable failed");
++ goto fail;
++ }
++
++ if (!sys->is_cma)
++ {
++ // 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;
++ }
+ }
++ else
++ {
++ // We are expecting one in - one out so simply wedge a new bufer
++ // into the output port. Flow control will happen on cma alloc.
++
++ if ((out_buf = mmal_queue_get(sys->out_pool->queue)) == NULL)
++ {
++ // Should never happen
++ msg_Err(p_filter, "Failed to get output buffer");
++ goto fail;
++ }
++ mmal_buffer_header_reset(out_buf);
- sys->output->buffer_num = 3;
++ // Attach cma_buf to the buffer & ensure it is freed when the buffer is released
++ // On a good send callback the pic will be extracted to avoid this
++ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, p_filter);
++
++ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size);
++ if ((out_buf->user_data = cb) == NULL) // Check & attach cb to buf
++ {
++ char dbuf0[5];
++ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d",
++ str_fourcc(dbuf0, p_pic->format.i_chroma),
++ sys->output->buffer_size);
++ goto fail;
++ }
++ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable
++ out_buf->data = (uint8_t *)vc_h;
++ out_buf->alloc_size = sys->output->buffer_size;
++
+#if TRACE_ALL
-+ msg_Dbg(p_filter, "<<< %s", __func__);
++ msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld",
++ p_pic, out_buf->data, out_buf->user_data, out_buf->flags,
++ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts);
+#endif
- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
@@ -3804,42 +4121,28 @@
- 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)
++ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
+ {
-+ msg_Err(p_filter, "Failed to replicate input buffer: %d", err);
++ msg_Err(p_filter, "Send buffer to output failed");
+ goto fail;
}
++ out_buf = NULL;
++ }
- 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
++ // Stuff into input
++ // We assume the BH is already set up with values reflecting pic date etc.
++ {
++ MMAL_BUFFER_HEADER_T * const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->in_pool);
++
++ if (pic_buf == NULL)
++ {
++ msg_Err(p_filter, "Pic has not attached buffer");
++ goto fail;
++ }
- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
- if (status != MMAL_SUCCESS) {
@@ -3850,12 +4153,13 @@
+
+ // 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));
++ pic_buf->flags = (pic_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)
++ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
+ {
+ msg_Err(p_filter, "Send buffer to input failed");
++ mmal_buffer_header_release(pic_buf);
+ goto fail;
}
}
@@ -3866,135 +4170,177 @@
- 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)
++ // seq_in is seq of the next frame we are going to submit (1-15, no 0)
++ // seq_out is last frame we removed from Q
++ // So after 4 frames sent (1st time we want to wait), 0 rx seq_in=5, seq_out=15, delta=5
++
++ while ((out_buf = (seq_delta(sys->seq_in, sys->seq_out) >= 5 ? mmal_queue_timedwait(sys->out_q, 1000) : mmal_queue_get(sys->out_q))) != NULL)
+ {
-+ 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;
++ int rv;
+
-+ 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;
-+ }
-
-- 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: 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
-
-- sys->filtered_pictures = mmal_queue_create();
-+ *pp_pic = out_pic;
-+ pp_pic = &out_pic->p_next;
-
-- filter->pf_video_filter = deinterlace;
-- filter->pf_flush = flush;
-+ // Ignore 0 seqs
++ picture_t * out_pic;
++
++ if (sys->is_cma)
++ {
++ // Alloc pic
++ if ((out_pic = filter_NewPicture(p_filter)) == NULL)
++ {
++ // Can't alloc pic - just stop extraction
++ mmal_queue_put_back(sys->out_q, out_buf);
++ out_buf = NULL;
++ msg_Warn(p_filter, "Failed to alloc new filter output pic");
++ break;
++ }
++
++ // Extract cma_buf from buf & attach to pic
++ cma_buf_t * const cb = (cma_buf_t *)out_buf->user_data;
++ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS)
++ {
++ char dbuf0[5];
++ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d",
++ str_fourcc(dbuf0, out_pic->format.i_chroma),
++ rv);
++ // cb still attached to buffer and will be freed with it
++ goto fail;
++ }
++ out_buf->user_data = NULL;
++
++ buf_to_pic_copy_props(out_pic, out_buf);
++
++ // Set pic data pointers from buf aux info now it has it
++ if ((rv = cma_pic_set_data(out_pic, sys->output->format, out_buf)) != VLC_SUCCESS)
++ {
++ char dbuf0[5];
++ msg_Err(p_filter, "Failed to set data: fmt=%s, rv=%d",
++ str_fourcc(dbuf0, sys->output->format->encoding),
++ rv);
++ }
++
++ out_buf->user_data = NULL; // Responsability for this pic no longer with buffer
++ mmal_buffer_header_release(out_buf);
++ }
++ else
++ {
++ out_pic = di_alloc_opaque(p_filter, out_buf);
++
++ if (out_pic == NULL) {
++ msg_Warn(p_filter, "Failed to alloc new filter output pic");
++ mmal_queue_put_back(sys->out_q, out_buf); // Wedge buf back into Q in the hope we can alloc a pic later
++ out_buf = NULL;
++ break;
++ }
++ }
++ out_buf = NULL; // Now attached to pic or recycled
++
++#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;
+ }
-+ }
++
++ // Crash on lockup
++ assert(ret_pics != NULL || seq_delta(sys->seq_in, sys->seq_out) < 5);
+ }
-- vlc_sem_init(&sys->sem, 0);
+- 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
-
--out:
-- if (ret != VLC_SUCCESS)
-- Close(filter);
++
+ return ret_pics;
-
-- return ret;
++
+fail:
++ if (out_buf != NULL)
++ mmal_buffer_header_release(out_buf);
+ picture_Release(p_pic);
+ return NULL;
- }
-
--static void Close(filter_t *filter)
++}
++
+static void di_flush(filter_t *p_filter)
- {
-- filter_sys_t *sys = filter->p_sys;
-- MMAL_BUFFER_HEADER_T *buffer;
++{
+ filter_sys_t * const sys = p_filter->p_sys;
-
-- if (!sys)
-- return;
--
-- if (sys->component && sys->component->control->is_enabled)
-- mmal_port_disable(sys->component->control);
++
+#if TRACE_ALL
+ msg_Dbg(p_filter, "<<< %s", __func__);
+#endif
-
-- if (sys->input && sys->input->is_enabled)
++
+ if (sys->input != NULL && sys->input->is_enabled)
- mmal_port_disable(sys->input);
-
-- if (sys->output && sys->output->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 (sys->component && sys->component->is_enabled)
-- mmal_component_disable(sys->component);
-+ // If that dumped anything real into the out_q then have another go
-+ if (mmal_queue_length(sys->out_q) != 0)
++ if (sys->is_cma)
++ {
++ MMAL_BUFFER_HEADER_T * buf;
++ mmal_port_disable(sys->output);
++ while ((buf = mmal_queue_get(sys->out_q)) != NULL)
++ mmal_buffer_header_release(buf);
++ }
++ else
+ {
-+ mmal_port_enable(sys->output, di_output_port_cb);
++ // 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);
-+ // Out q should now be empty & should remain so until the input is reenabled
++
++ // 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);
-
-- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
-- picture_t *pic = (picture_t *)buffer->user_data;
-- picture_Release(pic);
++
+ // 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
}
-- if (sys->filtered_pictures)
-- mmal_queue_destroy(sys->filtered_pictures);
+- sys->filtered_pictures = mmal_queue_create();
+ sys->seq_in = 1;
-+ sys->seq_out = 1;
++ sys->seq_out = 15;
-- if (sys->component)
-- mmal_component_release(sys->component);
+- filter->pf_video_filter = deinterlace;
+- filter->pf_flush = flush;
+#if TRACE_ALL
+ msg_Dbg(p_filter, ">>> %s", __func__);
+#endif
+}
-- vlc_sem_destroy(&sys->sem);
-- free(sys);
+- vlc_sem_init(&sys->sem, 0);
-- bcm_host_deinit();
+-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);
@@ -4003,34 +4349,22 @@
+ return p_pic;
}
--static int send_output_buffer(filter_t *filter)
+-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;
-- picture_t *picture;
-- int ret = 0;
++ MMAL_STATUS_T status;
-- if (!sys->output->is_enabled) {
-- ret = VLC_EGENERIC;
-- goto out;
+- 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));
- }
-
-- 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;
++ }
++
+ mmal_buffer_header_reset(buffer);
+ mmal_buffer_header_release(buffer);
+}
@@ -4038,112 +4372,129 @@
+static void CloseMmalDeinterlace(filter_t *filter)
+{
+ filter_sys_t * const sys = filter->p_sys;
-
-- buffer = picture->p_sys->buffer;
-- buffer->user_data = picture;
-- buffer->cmd = 0;
++
+#if TRACE_ALL
+ msg_Dbg(filter, "<<< %s", __func__);
+#endif
-
-- mmal_picture_lock(picture);
++
+ if (sys == NULL)
-+ return;
+ return;
-- status = mmal_port_send_buffer(sys->output, buffer);
-- 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);
+- if (sys->component && sys->component->control->is_enabled)
+- mmal_port_disable(sys->component->control);
+ if (sys->use_passthrough)
+ {
+ free(sys);
+ return;
- }
++ }
--out:
-- return ret;
+- 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);
-+
+
+ 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->output != NULL)
++ sys->output->userdata = NULL;
++
++ if (sys->is_cma)
++ {
++ if (sys->output && sys->output->is_enabled)
++ mmal_port_disable(sys->output);
++
++ cma_buf_pool_deletez(&sys->cma_out_pool);
+
++ if (sys->out_pool != NULL)
++ mmal_pool_destroy(sys->out_pool);
+ }
+
+- 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);
++ cma_vcsm_exit(sys->vcsm_init_type);
+
-+ if (sys->component)
-+ mmal_component_release(sys->component);
-+
-+ free(sys);
+ free(sys);
++}
+
-+ bcm_host_deinit();
+
+- bcm_host_deinit();
++static bool is_fmt_valid_in(const vlc_fourcc_t fmt)
++{
++ return fmt == VLC_CODEC_MMAL_OPAQUE ||
++ fmt == VLC_CODEC_MMAL_ZC_I420 ||
++ fmt == VLC_CODEC_MMAL_ZC_SAND8;
}
--static void fill_output_port(filter_t *filter)
-+
+-static int send_output_buffer(filter_t *filter)
+static int OpenMmalDeinterlace(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;
+- 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;
+ MMAL_STATUS_T status;
+- picture_t *picture;
+- int ret = 0;
+ filter_sys_t *sys;
+
+ msg_Dbg(filter, "<<< %s", __func__);
-
-- if (buffers_to_send > buffers_available)
-- buffers_to_send = buffers_available;
-+ if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE ||
-+ filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
++
++ if (!is_fmt_valid_in(filter->fmt_in.video.i_chroma) ||
++ filter->fmt_out.video.i_chroma != filter->fmt_in.video.i_chroma)
+ return VLC_EGENERIC;
--#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);
-+#if TRACE_ALL
-+ msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!",
-+ frame_duration, use_qpu ? "used" : "unused");
- #endif
-- for (i = 0; i < buffers_to_send; ++i) {
-- if (send_output_buffer(filter) < 0)
-- break;
-+
+- if (!sys->output->is_enabled) {
+- ret = VLC_EGENERIC;
+- goto out;
+ 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->seq_out = 15;
++ sys->is_cma = is_cma_buf_pic_chroma(filter->fmt_out.video.i_chroma);
++
++ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) {
++ msg_Err(filter, "VCSM init failed");
++ goto fail;
++ }
++
++ if (rpi_is_model_pi4())
++ {
++ sys->half_rate = true;
++ sys->use_qpu = false;
++ sys->use_fast = true;
++ }
++ else
++ {
++ 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)
@@ -4151,20 +4502,21 @@
+ // 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))
++ // Memory always comes from GPU if Opaque
++ // Assume we have plenty of memory if it comes from CMA
++ if ((!sys->is_cma || sys->vcsm_init_type == VCSM_INIT_LEGACY) &&
++ hw_mmal_get_gpu_mem() < (96 << 20))
++ {
+ sys->use_passthrough = true;
-+
++ msg_Warn(filter, "Deinterlace bypassed due to lack of GPU memory");
++ }
}
--}
--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;
+- picture = filter_NewPicture(filter);
+- if (!picture) {
+- msg_Warn(filter, "Failed to get new picture");
+- ret = -1;
+- goto out;
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU))
+ sys->use_qpu = false;
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV))
@@ -4188,12 +4540,33 @@
+ {
+ filter->pf_video_filter = pass_deinterlace;
+ filter->pf_flush = pass_flush;
++ // Don't need VCSM - get rid of it now
++ cma_vcsm_exit(sys->vcsm_init_type);
++ sys->vcsm_init_type = VCSM_INIT_NONE;
+ return 0;
+ }
+
-+ bcm_host_init();
++ {
++ char dbuf0[5], dbuf1[5];
++ msg_Dbg(filter, "%s: %s,%dx%d [(%d,%d) %d/%d] -> %s,%dx%d [(%d,%d) %dx%d]: %s %s %s", __func__,
++ str_fourcc(dbuf0, filter->fmt_in.video.i_chroma),
++ filter->fmt_in.video.i_width, filter->fmt_in.video.i_height,
++ filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset,
++ filter->fmt_in.video.i_visible_width, filter->fmt_in.video.i_visible_height,
++ str_fourcc(dbuf1, filter->fmt_out.video.i_chroma),
++ filter->fmt_out.video.i_width, filter->fmt_out.video.i_height,
++ filter->fmt_out.video.i_x_offset, filter->fmt_out.video.i_y_offset,
++ filter->fmt_out.video.i_visible_width, filter->fmt_out.video.i_visible_height,
++ sys->use_qpu ? "QPU" : "VPU",
++ sys->use_fast ? "FAST" : "ADV",
++ sys->use_passthrough ? "PASS" : sys->half_rate ? "HALF" : "FULL");
+ }
+- 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;
-- fill_output_port(filter);
+- buffer = picture->p_sys->buffer;
+- buffer->user_data = picture;
+- buffer->cmd = 0;
+ 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)",
@@ -4201,10 +4574,7 @@
+ goto fail;
+ }
-- buffer = picture->p_sys->buffer;
-- buffer->user_data = picture;
-- buffer->pts = picture->date;
-- buffer->cmd = 0;
+- mmal_picture_lock(picture);
+ {
+ const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
+ { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
@@ -4215,10 +4585,111 @@
+ { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu }
+ };
+- status = mmal_port_send_buffer(sys->output, buffer);
++ 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;
++ }
++ }
++
++ 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 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 enable control port %s (status=%"PRIx32" %s)",
++ sys->component->control->name, status, mmal_status_to_string(status));
++ goto fail;
+ }
+
+-out:
+- return ret;
+-}
++ sys->input = sys->component->input[0];
++ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
++ sys->input->format->encoding = vlc_to_mmal_video_fourcc(&filter->fmt_in.video);
++ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &filter->fmt_in.video);
+
+-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;
++ es_format_Copy(&filter->fmt_out, &filter->fmt_in);
++ if (!sys->half_rate)
++ filter->fmt_out.video.i_frame_rate *= 2;
+
+- if (buffers_to_send > buffers_available)
+- buffers_to_send = buffers_available;
++ 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;
+
+-#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;
++ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
++ {
++ msg_Err(filter, "Failed to create input pool");
++ 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;
++ 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;
++ }
+
+- fill_output_port(filter);
++ 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;
++ }
+
+- buffer = picture->p_sys->buffer;
+- buffer->user_data = picture;
+- buffer->pts = picture->date;
+- buffer->cmd = 0;
+
- if (!picture->p_sys->displayed) {
- status = mmal_port_send_buffer(sys->input, buffer);
-+ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
- if (status != MMAL_SUCCESS) {
+- 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);
@@ -4226,14 +4697,11 @@
- picture->p_sys->displayed = true;
- atomic_fetch_add(&sys->input_in_transit, 1);
- vlc_sem_post(&sys->sem);
-+ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
-+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
-+ goto fail;
- }
+- }
- } else {
- picture_Release(picture);
- }
-
+- }
+-
- /*
- * Send output buffers
- */
@@ -4252,11 +4720,9 @@
- msg_Dbg(filter, "Failed waiting for filtered picture");
- 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));
++ if ((sys->out_q = mmal_queue_create()) == NULL)
++ {
++ msg_Err(filter, "Failed to create out Q");
+ goto fail;
}
- if (out_picture)
@@ -4264,30 +4730,30 @@
- return ret;
-}
-+ 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);
-
+-
-static void flush(filter_t *filter)
-{
- filter_sys_t *sys = filter->p_sys;
- MMAL_BUFFER_HEADER_T *buffer;
-+ es_format_Copy(&filter->fmt_out, &filter->fmt_in);
-+ if (!sys->half_rate)
-+ filter->fmt_out.video.i_frame_rate *= 2;
++ sys->output = sys->component->output[0];
++ mmal_format_full_copy(sys->output->format, sys->input->format);
- msg_Dbg(filter, "flush deinterlace filter");
-+ 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;
++ if (!sys->is_cma)
++ {
++ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS)
++ 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;
++ else
++ {
++ // CMA stuff
++ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
++
++ if ((sys->cma_out_pool = cma_buf_pool_new(8, 8, true, "deinterlace")) == NULL)
++ {
++ msg_Err(filter, "Failed to alloc cma buf pool");
++ goto fail;
++ }
- msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)",
- sys->input_in_transit, sys->output_in_transit);
@@ -4304,49 +4770,51 @@
- msg_Dbg(filter, "flush: release already filtered pic %p",
- (void *)pic);
- picture_Release(pic);
-+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
-+ {
-+ msg_Err(filter, "Failed to create input pool");
-+ goto fail;
- }
+- }
- atomic_store(&sys->started, false);
- msg_Dbg(filter, "flush: done");
-}
++ // Rate control done by CMA in flight logic, so have "inexhaustable" pool here
++ if ((sys->out_pool = mmal_pool_create(30, 0)) == NULL)
++ {
++ msg_Err(filter, "Failed to alloc out pool");
++ goto fail;
++ }
-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;
-+ 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;
-+ }
++ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, true);
- if (buffer->cmd == MMAL_EVENT_ERROR) {
- status = *(uint32_t *)buffer->data;
- msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
- mmal_status_to_string(status));
-+ 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 ((status = mmal_port_format_commit(sys->output)) != MMAL_SUCCESS)
++ {
++ msg_Err(filter, "Output port format commit failed");
++ goto fail;
++ }
- mmal_buffer_header_release(buffer);
-}
++ sys->output->buffer_num = 30;
++ sys->output->buffer_size = sys->output->buffer_size_recommended;
-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;
-+ if ((sys->out_q = mmal_queue_create()) == NULL)
-+ {
-+ msg_Err(filter, "Failed to create out Q");
-+ goto fail;
++ // CB just drops all bufs into out_q
++ if ((status = mmal_port_enable(sys->output, di_output_port_cb)) != 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;
++ }
+ }
- if (picture) {
@@ -4354,12 +4822,6 @@
- } else {
- msg_Warn(filter, "Got buffer without picture on input port - OOOPS");
- mmal_buffer_header_release(buffer);
-+ sys->output = sys->component->output[0];
-+ mmal_format_full_copy(sys->output->format, sys->input->format);
-+
-+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS)
-+ goto fail;
-+
+ status = mmal_component_enable(sys->component);
+ if (status != MMAL_SUCCESS) {
+ msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
@@ -4405,6 +4867,7 @@
+ MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true);
+
+vlc_module_end()
++
- if (buffer->cmd == 0) {
- if (buffer->length > 0) {
@@ -4415,7 +4878,7 @@
- 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) {
@@ -4427,7 +4890,7 @@
-}
--- /dev/null
+++ b/modules/hw/mmal/mmal_avcodec.c
-@@ -0,0 +1,2275 @@
+@@ -0,0 +1,2172 @@
+/*****************************************************************************
+ * video.c: video decoder using the libavcodec library
+ *****************************************************************************
@@ -4455,11 +4918,8 @@
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
-+#ifdef HAVE_CONFIG_H
-+# include "config.h"
-+#endif
++#include "config.h"
+
-+#include <vlc_plugin.h>
+#include <vlc_common.h>
+#include <vlc_codec.h>
+#include <vlc_avcodec.h>
@@ -4467,125 +4927,310 @@
+#include <vlc_atomic.h>
+#include <assert.h>
+
-+#include "../../codec/avcodec/avcommon.h"
-+
-+#include <interface/mmal/mmal.h>
-+
+#include <libavcodec/avcodec.h>
+#include <libavutil/mem.h>
+#include <libavutil/pixdesc.h>
-+#include <libavcodec/rpi_zc.h>
+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
+#include <libavutil/mastering_display_metadata.h>
+#endif
+
++//#include "avcodec.h"
++//#include "va.h"
++
++#include <vlc_plugin.h>
++#include <libavutil/rpi_sand_fns.h>
++#include <libavcodec/rpi_zc.h>
++#include "../../codec/cc.h"
++#include "../../codec/avcodec/avcommon.h" // ??? Beware over inclusion
++#include "mmal_cma.h"
+#include "mmal_picture.h"
+
+#define TRACE_ALL 0
+
-+#define AVPROVIDER(lib) ((lib##_VERSION_MICRO < 100) ? "libav" : "ffmpeg")
-+
-+#ifdef HAVE_LIBAVCODEC_AVCODEC_H
-+#include <libavcodec/avcodec.h>
-+
-+/* LIBAVCODEC_VERSION_CHECK checks for the right version of libav and FFmpeg
-+ * a is the major version
-+ * b and c the minor and micro versions of libav
-+ * d and e the minor and micro versions of FFmpeg */
-+#define LIBAVCODEC_VERSION_CHECK( a, b, c, d, e ) \
-+ ( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \
-+ (LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) )
++#define BUFFERS_IN_FLIGHT 5 // Default max value for in flight buffers
++#define BUFFERS_IN_FLIGHT_UHD 3 // Fewer if very big
+
-+#ifndef AV_CODEC_FLAG_OUTPUT_CORRUPT
-+# define AV_CODEC_FLAG_OUTPUT_CORRUPT CODEC_FLAG_OUTPUT_CORRUPT
-+#endif
-+#ifndef AV_CODEC_FLAG_GRAY
-+# define AV_CODEC_FLAG_GRAY CODEC_FLAG_GRAY
-+#endif
-+#ifndef AV_CODEC_FLAG_DR1
-+# define AV_CODEC_FLAG_DR1 CODEC_FLAG_DR1
-+#endif
-+#ifndef AV_CODEC_FLAG_DELAY
-+# define AV_CODEC_FLAG_DELAY CODEC_FLAG_DELAY
-+#endif
-+#ifndef AV_CODEC_FLAG2_FAST
-+# define AV_CODEC_FLAG2_FAST CODEC_FLAG2_FAST
-+#endif
-+#ifndef FF_INPUT_BUFFER_PADDING_SIZE
-+# define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE
-+#endif
-+#ifndef AV_CODEC_FLAG_INTERLACED_DCT
-+# define AV_CODEC_FLAG_INTERLACED_DCT CODEC_FLAG_INTERLACED_DCT
-+#endif
-+#ifndef AV_CODEC_FLAG_INTERLACED_ME
-+# define AV_CODEC_FLAG_INTERLACED_ME CODEC_FLAG_INTERLACED_ME
-+#endif
-+#ifndef AV_CODEC_FLAG_GLOBAL_HEADER
-+# define AV_CODEC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER
-+#endif
-+#ifndef AV_CODEC_FLAG_LOW_DELAY
-+# define AV_CODEC_FLAG_LOW_DELAY CODEC_FLAG_LOW_DELAY
-+#endif
-+#ifndef AV_CODEC_CAP_SMALL_LAST_FRAME
-+# define AV_CODEC_CAP_SMALL_LAST_FRAME CODEC_CAP_SMALL_LAST_FRAME
-+#endif
-+#ifndef AV_INPUT_BUFFER_MIN_SIZE
-+# define AV_INPUT_BUFFER_MIN_SIZE FF_MIN_BUFFER_SIZE
-+#endif
-+#ifndef FF_MAX_B_FRAMES
-+# define FF_MAX_B_FRAMES 16 // FIXME: remove this
-+#endif
++#define MMAL_AVCODEC_BUFFERS "mmal-avcodec-buffers"
++#define MMAL_AVCODEC_BUFFERS_TEXT N_("In flight buffer count before blocking.")
++#define MMAL_AVCODEC_BUFFERS_LONGTEXT N_("In flight buffer count before blocking. " \
++"Beware that incautious changing of this can lead to lockup. " \
++"Zero will disable the module.")
+
-+#endif /* HAVE_LIBAVCODEC_AVCODEC_H */
+
-+#ifdef HAVE_LIBAVUTIL_AVUTIL_H
-+# include <libavutil/avutil.h>
++// Fwd declarations required due to wanting to avoid reworking the original
++// code too much
++static void MmalAvcodecCloseDecoder( vlc_object_t *obj );
+
-+/* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg
-+ * a is the major version
-+ * b and c the minor and micro versions of libav
-+ * d and e the minor and micro versions of FFmpeg */
-+#define LIBAVUTIL_VERSION_CHECK( a, b, c, d, e ) \
-+ ( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \
-+ (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) )
+
-+#if !LIBAVUTIL_VERSION_CHECK( 52, 11, 0, 32, 100 )
-+# define AV_PIX_FMT_FLAG_HWACCEL PIX_FMT_HWACCEL
-+#endif
++/*****************************************************************************
++ * decoder_sys_t : decoder descriptor
++ *****************************************************************************/
++struct decoder_sys_t
++{
++ AVCodecContext *p_context;
++ const AVCodec *p_codec;
+
-+#endif /* HAVE_LIBAVUTIL_AVUTIL_H */
++ /* Video decoder specific part */
++ date_t pts;
+
-+#if LIBAVUTIL_VERSION_MAJOR >= 55
-+# define FF_API_AUDIOCONVERT 1
-+#endif
++ /* Closed captions for decoders */
++ cc_data_t cc;
+
-+/* libavutil/pixfmt.h */
-+#ifndef PixelFormat
-+# define PixelFormat AVPixelFormat
-+#endif
++ /* for frame skipping algo */
++ bool b_hurry_up;
++ bool b_show_corrupted;
++ bool b_from_preroll;
++ enum AVDiscard i_skip_frame;
+
-+#ifdef HAVE_LIBAVFORMAT_AVFORMAT_H
-+# include <libavformat/avformat.h>
++ /* how many decoded frames are late */
++ int i_late_frames;
++ mtime_t i_late_frames_start;
++ mtime_t i_last_late_delay;
+
-+#define LIBAVFORMAT_VERSION_CHECK( a, b, c, d, e ) \
-+ ( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \
-+ (LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) )
++ /* for direct rendering */
++ bool b_direct_rendering;
++ atomic_bool b_dr_failure;
+
-+#endif
++ /* Hack to force display of still pictures */
++ bool b_first_frame;
+
-+/*****************************************************************************
-+ * Codec fourcc -> libavcodec Codec_id mapping
-+ * Sorted by AVCodecID enumeration order
-+ *****************************************************************************/
-+struct vlc_avcodec_fourcc
++
++ /* */
++ bool palette_sent;
++
++ /* VA API */
++// vlc_va_t *p_va;
++ enum PixelFormat pix_fmt;
++ int profile;
++ int level;
++
++ vlc_sem_t sem_mt;
++
++ // Rpi vars
++ cma_buf_pool_t * cma_pool;
++ bool pool_alloc_1;
++ vcsm_init_type_t vcsm_init_type;
++ int cma_in_flight_max;
++ // Debug
++ decoder_t * p_dec;
++};
++
++
++static vlc_fourcc_t
++ZcFindVlcChroma(const int i_ffmpeg_chroma)
+{
-+ vlc_fourcc_t i_fourcc;
-+ unsigned i_codec;
++ switch (i_ffmpeg_chroma)
++ {
++ // This is all we claim to deal with
++ // In theory RGB should be doable within our current framework
++ case AV_PIX_FMT_YUV420P:
++ return VLC_CODEC_MMAL_ZC_I420;
++ case AV_PIX_FMT_SAND128:
++ case AV_PIX_FMT_RPI4_8:
++ return VLC_CODEC_MMAL_ZC_SAND8;
++ case AV_PIX_FMT_SAND64_10:
++ return VLC_CODEC_MMAL_ZC_SAND10;
++ case AV_PIX_FMT_RPI4_10:
++ return VLC_CODEC_MMAL_ZC_SAND30;
++ default:
++ break;
++ }
++ return 0;
++}
++
++// Pix Fmt conv for MMal
++// video_fromat from ffmpeg pic_fmt
++static int
++ZcGetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma )
++{
++ fmt->i_rmask = 0;
++ fmt->i_gmask = 0;
++ fmt->i_bmask = 0;
++ fmt->i_chroma = ZcFindVlcChroma(i_ffmpeg_chroma);
++
++ return fmt->i_chroma == 0 ? -1 : 0;
++}
++
++
++// Format chooser is way simpler than vlc
++static enum PixelFormat
++ZcGetFormat(AVCodecContext *p_context, const enum PixelFormat *pi_fmt)
++{
++ enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt);
++ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++)
++ {
++ if (ZcFindVlcChroma(pi_fmt[i]) != 0)
++ return pi_fmt[i];
++ }
++ return swfmt;
++}
++
++
++static void cma_avbuf_pool_free(void * v)
++{
++ cma_buf_unref(v);
++}
++
++static unsigned int zc_buf_vcsm_handle(void * v)
++{
++ return cma_buf_vcsm_handle(v);
++}
++
++static unsigned int zc_buf_vc_handle(void * v)
++{
++ return cma_buf_vc_handle(v);
++}
++
++static void * zc_buf_map_arm(void * v)
++{
++ return cma_buf_addr(v);
++}
++
++static unsigned int zc_buf_map_vc(void * v)
++{
++ return cma_buf_vc_addr(v);
++}
++
++
++
++static const av_rpi_zc_buf_fn_tab_t zc_buf_fn_tab = {
++ .free = cma_avbuf_pool_free,
++
++ .vcsm_handle = zc_buf_vcsm_handle,
++ .vc_handle = zc_buf_vc_handle,
++ .map_arm = zc_buf_map_arm,
++ .map_vc = zc_buf_map_vc
+};
+
++
++static AVBufferRef *
++zc_alloc_buf(void * v, size_t size, const AVRpiZcFrameGeometry * geo)
++{
++ decoder_t * const dec = v;
++ decoder_sys_t * const sys = dec->p_sys;
++
++ VLC_UNUSED(geo);
++
++ assert(sys != NULL);
++
++ const unsigned int dec_pool_req = av_rpi_zc_get_decoder_pool_size(sys->p_context->opaque);
++ if (dec_pool_req != 0)
++ {
++ cma_buf_pool_resize(sys->cma_pool, dec_pool_req + sys->cma_in_flight_max, sys->cma_in_flight_max);
++
++ if (!sys->pool_alloc_1)
++ {
++ sys->pool_alloc_1 = true;
++ msg_Dbg(dec, "Pool size: (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size);
++ if (cma_buf_pool_fill(sys->cma_pool, size) != 0)
++ msg_Warn(dec, "Failed to preallocate decoder pool (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size);
++ }
++ }
++
++ void * const cmabuf = cma_buf_pool_alloc_buf(sys->cma_pool, size);
++
++ if (cmabuf == NULL)
++ {
++ msg_Err(dec, "CMA buf pool alloc buf failed");
++ return NULL;
++ }
++
++ AVBufferRef *const avbuf = av_rpi_zc_buf(cma_buf_size(cmabuf), 0, cmabuf, &zc_buf_fn_tab);
++
++ if (avbuf == NULL)
++ {
++ msg_Err(dec, "av_rpi_zc_buf failed");
++ cma_buf_unref(cmabuf);
++ return NULL;
++ }
++
++ return avbuf;
++}
++
++static void
++zc_free_pool(void * v)
++{
++ decoder_t * const dec = v;
++ cma_buf_pool_delete(dec->p_sys->cma_pool);
++}
++
++
++static const uint8_t shift_01[] = {0,1,1,1};
++static const uint8_t pb_1[] = {1,1,1,1};
++static const uint8_t pb_12[] = {1,2,2,2};
++static const uint8_t pb_24[] = {2,4,4,4};
++static const uint8_t pb_4[] = {4,4,4,4};
++
++static int set_pic_from_frame(picture_t * const pic, const AVFrame * const frame)
++{
++ const uint8_t * hs = shift_01;
++ const uint8_t * ws = shift_01;
++ const uint8_t * pb = pb_1;
++
++ switch (pic->format.i_chroma)
++ {
++ case VLC_CODEC_MMAL_ZC_RGB32:
++ pic->i_planes = 1;
++ pb = pb_4;
++ break;
++ case VLC_CODEC_MMAL_ZC_I420:
++ pic->i_planes = 3;
++ break;
++ case VLC_CODEC_MMAL_ZC_SAND8:
++ pic->i_planes = 2;
++ pb = pb_12;
++ break;
++ case VLC_CODEC_MMAL_ZC_SAND10:
++ case VLC_CODEC_MMAL_ZC_SAND30: // Lies: SAND30 is "special"
++ pic->i_planes = 2;
++ pb = pb_24;
++ break;
++ default:
++ return VLC_EGENERIC;
++ }
++
++ const cma_buf_t * const cb = cma_buf_pic_get(pic);
++ uint8_t * const data = cma_buf_addr(cb);
++ if (data == NULL) {
++ return VLC_ENOMEM;
++ }
++
++ uint8_t * frame_end = frame->data[0] + cma_buf_size(cb);
++ for (int i = 0; i != pic->i_planes; ++i) {
++ // Calculate lines from gap between planes
++ // This will give us an accurate "height" for later use by MMAL
++ const int lines = ((i + 1 == pic->i_planes ? frame_end : frame->data[i + 1]) -
++ frame->data[i]) / frame->linesize[i];
++ pic->p[i] = (plane_t){
++ .p_pixels = data + (frame->data[i] - frame->data[0]),
++ .i_lines = lines,
++ .i_pitch = frame->linesize[i],
++ .i_pixel_pitch = pb[i],
++ .i_visible_lines = av_frame_cropped_height(frame) >> hs[i],
++ .i_visible_pitch = av_frame_cropped_width(frame) >> ws[i]
++ };
++ }
++ return 0;
++}
++
++
++//============================================================================
++//
++// Nicked from avcodec/fourcc.c
++//
++// * Really we should probably use that directly
++
+/*
+ * Video Codecs
+ */
++
++struct vlc_avcodec_fourcc
++{
++ vlc_fourcc_t i_fourcc;
++ unsigned i_codec;
++};
++
++
+static const struct vlc_avcodec_fourcc video_codecs[] =
+{
+ { VLC_CODEC_MP1V, AV_CODEC_ID_MPEG1VIDEO },
@@ -4723,6 +5368,9 @@
+ /* AV_CODEC_ID_V210X */
+ { VLC_CODEC_TMV, AV_CODEC_ID_TMV },
+ { VLC_CODEC_V210, AV_CODEC_ID_V210 },
++#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 54, 50, 100 ) && LIBAVCODEC_VERSION_MICRO >= 100
++ { VLC_CODEC_VUYA, AV_CODEC_ID_AYUV },
++#endif
+ /* AV_CODEC_ID_DPX */
+ { VLC_CODEC_MAD, AV_CODEC_ID_MAD },
+ { VLC_CODEC_FRWU, AV_CODEC_ID_FRWU },
@@ -4804,7 +5452,6 @@
+ /* ffmpeg only: AV_CODEC_ID_AVRP */
+ /* ffmpeg only: AV_CODEC_ID_012V */
+ /* ffmpeg only: AV_CODEC_ID_AVUI */
-+ /* ffmpeg only: AV_CODEC_ID_AYUV */
+ /* ffmpeg only: AV_CODEC_ID_TARGA_Y216 */
+ /* ffmpeg only: AV_CODEC_ID_V308 */
+ /* ffmpeg only: AV_CODEC_ID_V408 */
@@ -4837,231 +5484,16 @@
+#endif
+};
+
-+/*
-+ * Audio Codecs
-+ */
-+static const struct vlc_avcodec_fourcc audio_codecs[] =
-+{
-+ /* PCM */
-+ { VLC_CODEC_S16L, AV_CODEC_ID_PCM_S16LE },
-+ { VLC_CODEC_S16B, AV_CODEC_ID_PCM_S16BE },
-+ { VLC_CODEC_U16L, AV_CODEC_ID_PCM_U16LE },
-+ { VLC_CODEC_U16B, AV_CODEC_ID_PCM_U16BE },
-+ { VLC_CODEC_S8, AV_CODEC_ID_PCM_S8 },
-+ { VLC_CODEC_U8, AV_CODEC_ID_PCM_U8 },
-+ { VLC_CODEC_MULAW, AV_CODEC_ID_PCM_MULAW },
-+ { VLC_CODEC_ALAW, AV_CODEC_ID_PCM_ALAW },
-+ { VLC_CODEC_S32L, AV_CODEC_ID_PCM_S32LE },
-+ { VLC_CODEC_S32B, AV_CODEC_ID_PCM_S32BE },
-+ { VLC_CODEC_U32L, AV_CODEC_ID_PCM_U32LE },
-+ { VLC_CODEC_U32B, AV_CODEC_ID_PCM_U32BE },
-+ { VLC_CODEC_S24L, AV_CODEC_ID_PCM_S24LE },
-+ { VLC_CODEC_S24B, AV_CODEC_ID_PCM_S24BE },
-+ { VLC_CODEC_U24L, AV_CODEC_ID_PCM_U24LE },
-+ { VLC_CODEC_U24B, AV_CODEC_ID_PCM_U24BE },
-+ { VLC_CODEC_S24DAUD, AV_CODEC_ID_PCM_S24DAUD },
-+ /* AV_CODEC_ID_PCM_ZORK */
-+ { VLC_CODEC_S16L_PLANAR, AV_CODEC_ID_PCM_S16LE_PLANAR },
-+ /* AV_CODEC_ID_PCM_DVD */
-+ { VLC_CODEC_F32B, AV_CODEC_ID_PCM_F32BE },
-+ { VLC_CODEC_F32L, AV_CODEC_ID_PCM_F32LE },
-+ { VLC_CODEC_F64B, AV_CODEC_ID_PCM_F64BE },
-+ { VLC_CODEC_F64L, AV_CODEC_ID_PCM_F64LE },
-+ { VLC_CODEC_BD_LPCM, AV_CODEC_ID_PCM_BLURAY },
-+ /* AV_CODEC_ID_PCM_LXF */
-+ /* AV_CODEC_ID_S302M */
-+ /* AV_CODEC_ID_PCM_S8_PLANAR */
-+ /* AV_CODEC_ID_PCM_S24LE_PLANAR */
-+ /* AV_CODEC_ID_PCM_S32LE_PLANAR */
-+ /* ffmpeg only: AV_CODEC_ID_PCM_S16BE_PLANAR */
-+
-+ /* ADPCM */
-+ { VLC_CODEC_ADPCM_IMA_QT, AV_CODEC_ID_ADPCM_IMA_QT },
-+ { VLC_CODEC_ADPCM_IMA_WAV, AV_CODEC_ID_ADPCM_IMA_WAV },
-+ /* AV_CODEC_ID_ADPCM_IMA_DK3 */
-+ /* AV_CODEC_ID_ADPCM_IMA_DK4 */
-+ { VLC_CODEC_ADPCM_IMA_WS, AV_CODEC_ID_ADPCM_IMA_WS },
-+ /* AV_CODEC_ID_ADPCM_IMA_SMJPEG */
-+ { VLC_CODEC_ADPCM_MS, AV_CODEC_ID_ADPCM_MS },
-+ { VLC_CODEC_ADPCM_4XM, AV_CODEC_ID_ADPCM_4XM },
-+ { VLC_CODEC_ADPCM_XA, AV_CODEC_ID_ADPCM_XA },
-+ { VLC_CODEC_ADPCM_ADX, AV_CODEC_ID_ADPCM_ADX },
-+ { VLC_CODEC_ADPCM_EA, AV_CODEC_ID_ADPCM_EA },
-+ { VLC_CODEC_ADPCM_G726, AV_CODEC_ID_ADPCM_G726 },
-+ { VLC_CODEC_ADPCM_CREATIVE, AV_CODEC_ID_ADPCM_CT },
-+ { VLC_CODEC_ADPCM_SWF, AV_CODEC_ID_ADPCM_SWF },
-+ { VLC_CODEC_ADPCM_YAMAHA, AV_CODEC_ID_ADPCM_YAMAHA },
-+ { VLC_CODEC_ADPCM_SBPRO_4, AV_CODEC_ID_ADPCM_SBPRO_4 },
-+ { VLC_CODEC_ADPCM_SBPRO_3, AV_CODEC_ID_ADPCM_SBPRO_3 },
-+ { VLC_CODEC_ADPCM_SBPRO_2, AV_CODEC_ID_ADPCM_SBPRO_2 },
-+ { VLC_CODEC_ADPCM_THP, AV_CODEC_ID_ADPCM_THP },
-+ { VLC_CODEC_ADPCM_IMA_AMV, AV_CODEC_ID_ADPCM_IMA_AMV },
-+ { VLC_CODEC_ADPCM_EA_R1, AV_CODEC_ID_ADPCM_EA_R1 },
-+ /* AV_CODEC_ID_ADPCM_EA_R3 */
-+ /* AV_CODEC_ID_ADPCM_EA_R2 */
-+ { VLC_CODEC_ADPCM_IMA_EA_SEAD, AV_CODEC_ID_ADPCM_IMA_EA_SEAD },
-+ /* AV_CODEC_ID_ADPCM_IMA_EA_EACS */
-+ /* AV_CODEC_ID_ADPCM_EA_XAS */
-+ /* AV_CODEC_ID_ADPCM_EA_MAXIS_XA */
-+ /* AV_CODEC_ID_ADPCM_IMA_ISS */
-+ { VLC_CODEC_ADPCM_G722, AV_CODEC_ID_ADPCM_G722 },
-+ { VLC_CODEC_ADPCM_IMA_APC, AV_CODEC_ID_ADPCM_IMA_APC },
-+ /* ffmpeg only: AV_CODEC_ID_VIMA */
-+ /* ffmpeg only: AV_CODEC_ID_ADPCM_AFC */
-+ /* ffmpeg only: AV_CODEC_ID_ADPCM_IMA_OKI */
-+ /* ffmpeg only: AV_CODEC_ID_ADPCM_DTK */
-+ /* ffmpeg only: AV_CODEC_ID_ADPCM_IMA_RAD */
-+ /* ffmpeg only: AV_CODEC_ID_ADPCM_G726LE */
-+
-+ /* AMR */
-+ { VLC_CODEC_AMR_NB, AV_CODEC_ID_AMR_NB },
-+ { VLC_CODEC_AMR_WB, AV_CODEC_ID_AMR_WB },
-+
-+ /* RealAudio */
-+ { VLC_CODEC_RA_144, AV_CODEC_ID_RA_144 },
-+ { VLC_CODEC_RA_288, AV_CODEC_ID_RA_288 },
-+
-+ /* DPCM */
-+ { VLC_CODEC_ROQ_DPCM, AV_CODEC_ID_ROQ_DPCM },
-+ { VLC_CODEC_INTERPLAY_DPCM, AV_CODEC_ID_INTERPLAY_DPCM },
-+ /* AV_CODEC_ID_XAN_DPCM */
-+ /* AV_CODEC_ID_SOL_DPCM */
-+
-+ /* audio codecs */
-+ { VLC_CODEC_MPGA, AV_CODEC_ID_MP2 },
-+ { VLC_CODEC_MP2, AV_CODEC_ID_MP2 },
-+ { VLC_CODEC_MP3, AV_CODEC_ID_MP3 },
-+ { VLC_CODEC_MP4A, AV_CODEC_ID_AAC },
-+ { VLC_CODEC_A52, AV_CODEC_ID_AC3 },
-+ { VLC_CODEC_DTS, AV_CODEC_ID_DTS },
-+ { VLC_CODEC_VORBIS, AV_CODEC_ID_VORBIS },
-+ { VLC_CODEC_DVAUDIO, AV_CODEC_ID_DVAUDIO },
-+ { VLC_CODEC_WMA1, AV_CODEC_ID_WMAV1 },
-+ { VLC_CODEC_WMA2, AV_CODEC_ID_WMAV2 },
-+ { VLC_CODEC_MACE3, AV_CODEC_ID_MACE3 },
-+ { VLC_CODEC_MACE6, AV_CODEC_ID_MACE6 },
-+ { VLC_CODEC_VMDAUDIO, AV_CODEC_ID_VMDAUDIO },
-+ { VLC_CODEC_FLAC, AV_CODEC_ID_FLAC },
-+ /* AV_CODEC_ID_MP3ADU */
-+ /* AV_CODEC_ID_MP3ON4 */
-+ { VLC_CODEC_SHORTEN, AV_CODEC_ID_SHORTEN },
-+ { VLC_CODEC_ALAC, AV_CODEC_ID_ALAC },
-+ /* AV_CODEC_ID_WESTWOOD_SND1 */
-+ { VLC_CODEC_GSM, AV_CODEC_ID_GSM },
-+ { VLC_CODEC_QDM2, AV_CODEC_ID_QDM2 },
-+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 100 )
-+ { VLC_CODEC_QDMC, AV_CODEC_ID_QDMC },
-+#endif
-+ { VLC_CODEC_COOK, AV_CODEC_ID_COOK },
-+ { VLC_CODEC_TRUESPEECH, AV_CODEC_ID_TRUESPEECH },
-+ { VLC_CODEC_TTA, AV_CODEC_ID_TTA },
-+ { VLC_CODEC_SMACKAUDIO, AV_CODEC_ID_SMACKAUDIO },
-+ { VLC_CODEC_QCELP, AV_CODEC_ID_QCELP },
-+ { VLC_CODEC_WAVPACK, AV_CODEC_ID_WAVPACK },
-+ { VLC_CODEC_DSICINAUDIO, AV_CODEC_ID_DSICINAUDIO },
-+ { VLC_CODEC_IMC, AV_CODEC_ID_IMC },
-+ { VLC_CODEC_MUSEPACK7, AV_CODEC_ID_MUSEPACK7 },
-+ { VLC_CODEC_MLP, AV_CODEC_ID_MLP },
-+ { VLC_CODEC_GSM_MS, AV_CODEC_ID_GSM_MS },
-+ { VLC_CODEC_ATRAC3, AV_CODEC_ID_ATRAC3 },
-+ { VLC_CODEC_APE, AV_CODEC_ID_APE },
-+ { VLC_CODEC_NELLYMOSER, AV_CODEC_ID_NELLYMOSER },
-+ { VLC_CODEC_MUSEPACK8, AV_CODEC_ID_MUSEPACK8 },
-+ { VLC_CODEC_SPEEX, AV_CODEC_ID_SPEEX },
-+ { VLC_CODEC_WMAS, AV_CODEC_ID_WMAVOICE },
-+ { VLC_CODEC_WMAP, AV_CODEC_ID_WMAPRO },
-+ { VLC_CODEC_WMAL, AV_CODEC_ID_WMALOSSLESS },
-+ { VLC_CODEC_ATRAC3P, AV_CODEC_ID_ATRAC3P },
-+ { VLC_CODEC_EAC3, AV_CODEC_ID_EAC3 },
-+ { VLC_CODEC_SIPR, AV_CODEC_ID_SIPR },
-+ /* AV_CODEC_ID_MP1 */
-+ { VLC_CODEC_TWINVQ, AV_CODEC_ID_TWINVQ },
-+ { VLC_CODEC_TRUEHD, AV_CODEC_ID_TRUEHD },
-+ { VLC_CODEC_ALS, AV_CODEC_ID_MP4ALS },
-+ { VLC_CODEC_ATRAC1, AV_CODEC_ID_ATRAC1 },
-+ { VLC_CODEC_BINKAUDIO_RDFT, AV_CODEC_ID_BINKAUDIO_RDFT },
-+ { VLC_CODEC_BINKAUDIO_DCT, AV_CODEC_ID_BINKAUDIO_DCT },
-+ { VLC_CODEC_MP4A, AV_CODEC_ID_AAC_LATM },
-+ /* AV_CODEC_ID_QDMC */
-+ /* AV_CODEC_ID_CELT */
-+ { VLC_CODEC_G723_1, AV_CODEC_ID_G723_1 },
-+ /* AV_CODEC_ID_G729 */
-+ /* AV_CODEC_ID_8SVX_EXP */
-+ /* AV_CODEC_ID_8SVX_FIB */
-+ { VLC_CODEC_BMVAUDIO, AV_CODEC_ID_BMV_AUDIO },
-+ { VLC_CODEC_RALF, AV_CODEC_ID_RALF },
-+ { VLC_CODEC_INDEO_AUDIO, AV_CODEC_ID_IAC },
-+ /* AV_CODEC_ID_ILBC */
-+ { VLC_CODEC_OPUS, AV_CODEC_ID_OPUS },
-+ /* AV_CODEC_ID_COMFORT_NOISE */
-+ { VLC_CODEC_TAK, AV_CODEC_ID_TAK },
-+ { VLC_CODEC_METASOUND, AV_CODEC_ID_METASOUND },
-+ /* AV_CODEC_ID_PAF_AUDIO */
-+ { VLC_CODEC_ON2AVC, AV_CODEC_ID_ON2AVC },
-+
-+ /* ffmpeg only: AV_CODEC_ID_FFWAVESYNTH */
-+ /* ffmpeg only: AV_CODEC_ID_SONIC */
-+ /* ffmpeg only: AV_CODEC_ID_SONIC_LS */
-+ /* ffmpeg only: AV_CODEC_ID_PAF_AUDIO */
-+ /* ffmpeg only: AV_CODEC_ID_EVRC */
-+ /* ffmpeg only: AV_CODEC_ID_SMV */
-+};
-+
-+/* Subtitle streams */
-+static const struct vlc_avcodec_fourcc spu_codecs[] =
-+{
-+ { VLC_CODEC_SPU, AV_CODEC_ID_DVD_SUBTITLE },
-+ { VLC_CODEC_DVBS, AV_CODEC_ID_DVB_SUBTITLE },
-+ { VLC_CODEC_SUBT, AV_CODEC_ID_TEXT },
-+ { VLC_CODEC_XSUB, AV_CODEC_ID_XSUB },
-+ { VLC_CODEC_SSA, AV_CODEC_ID_SSA },
-+ /* AV_CODEC_ID_MOV_TEXT */
-+ { VLC_CODEC_BD_PG, AV_CODEC_ID_HDMV_PGS_SUBTITLE },
-+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 100 )
-+ { VLC_CODEC_BD_TEXT, AV_CODEC_ID_HDMV_TEXT_SUBTITLE },
-+#endif
-+ { VLC_CODEC_TELETEXT, AV_CODEC_ID_DVB_TELETEXT },
-+ /* AV_CODEC_ID_SRT */
-+ /* ffmpeg only: AV_CODEC_ID_MICRODVD */
-+ /* ffmpeg only: AV_CODEC_ID_EIA_608 */
-+ /* ffmpeg only: AV_CODEC_ID_JACOSUB */
-+ /* ffmpeg only: AV_CODEC_ID_SAMI */
-+ /* ffmpeg only: AV_CODEC_ID_REALTEXT */
-+ /* ffmpeg only: AV_CODEC_ID_SUBVIEWER1 */
-+ /* ffmpeg only: AV_CODEC_ID_SUBVIEWER */
-+ /* ffmpeg only: AV_CODEC_ID_SUBRIP */
-+ /* ffmpeg only: AV_CODEC_ID_WEBVTT */
-+ /* ffmpeg only: AV_CODEC_ID_MPL2 */
-+ /* ffmpeg only: AV_CODEC_ID_VPLAYER */
-+ /* ffmpeg only: AV_CODEC_ID_PJS */
-+ /* ffmpeg only: AV_CODEC_ID_ASS */
-+};
-+
-+static bool GetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc,
++// *** Really we should probably use GetFfmpegCodec with a pre-kludge for the bits we care about
++static bool
++ZcGetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc,
+ unsigned *pi_ffmpeg_codec, const char **ppsz_name )
+{
+ const struct vlc_avcodec_fourcc *base;
+ size_t count;
+
-+ switch( cat )
-+ {
-+ case VIDEO_ES:
-+ base = video_codecs;
-+ count = ARRAY_SIZE(video_codecs);
-+ break;
-+ case AUDIO_ES:
-+ base = audio_codecs;
-+ count = ARRAY_SIZE(audio_codecs);
-+ break;
-+ case SPU_ES:
-+ base = spu_codecs;
-+ count = ARRAY_SIZE(spu_codecs);
-+ break;
-+ default:
-+ base = NULL;
-+ count = 0;
-+ }
-+
++ base = video_codecs;
++ count = ARRAY_SIZE(video_codecs);
+ i_fourcc = vlc_fourcc_GetCodec( cat, i_fourcc );
+
+ for( size_t i = 0; i < count; i++ )
@@ -5078,163 +5510,13 @@
+ return false;
+}
+
-+/*****************************************************************************
-+ * Chroma fourcc -> libavutil pixfmt mapping
-+ *****************************************************************************/
-+#if defined(WORDS_BIGENDIAN)
-+# define VLC_RGB_ES( fcc, leid, beid ) \
-+ { fcc, beid, 0, 0, 0 },
-+#else
-+# define VLC_RGB_ES( fcc, leid, beid ) \
-+ { fcc, leid, 0, 0, 0 },
-+#endif
-+
-+#define VLC_RGB( fcc, leid, beid, rmask, gmask, bmask ) \
-+ { fcc, leid, rmask, gmask, bmask }, \
-+ { fcc, beid, bmask, gmask, rmask }, \
-+ VLC_RGB_ES( fcc, leid, beid )
-+
-+
-+static const struct
-+{
-+ vlc_fourcc_t i_chroma;
-+ int i_chroma_id;
-+ uint32_t i_rmask;
-+ uint32_t i_gmask;
-+ uint32_t i_bmask;
-+
-+} chroma_table[] =
-+{
-+ // Sand
-+// {VLC_CODEC_MMAL_OPAQUE, AV_PIX_FMT_SAND128, 0, 0, 0 },
-+ {VLC_CODEC_MMAL_ZC_SAND8, AV_PIX_FMT_SAND128, 0, 0, 0 },
-+ {VLC_CODEC_MMAL_ZC_SAND10, AV_PIX_FMT_SAND64_10, 0, 0, 0 },
-+
-+ /* Planar YUV formats */
-+ {VLC_CODEC_I444, AV_PIX_FMT_YUV444P, 0, 0, 0 },
-+ {VLC_CODEC_J444, AV_PIX_FMT_YUVJ444P, 0, 0, 0 },
-+
-+ {VLC_CODEC_I440, AV_PIX_FMT_YUV440P, 0, 0, 0 },
-+ {VLC_CODEC_J440, AV_PIX_FMT_YUVJ440P, 0, 0, 0 },
+
-+ {VLC_CODEC_I422, AV_PIX_FMT_YUV422P, 0, 0, 0 },
-+ {VLC_CODEC_J422, AV_PIX_FMT_YUVJ422P, 0, 0, 0 },
+
-+ {VLC_CODEC_I420, AV_PIX_FMT_YUV420P, 0, 0, 0 },
-+ {VLC_CODEC_YV12, AV_PIX_FMT_YUV420P, 0, 0, 0 },
-+ {VLC_FOURCC('I','Y','U','V'), AV_PIX_FMT_YUV420P, 0, 0, 0 },
-+ {VLC_CODEC_J420, AV_PIX_FMT_YUVJ420P, 0, 0, 0 },
-+ {VLC_CODEC_I411, AV_PIX_FMT_YUV411P, 0, 0, 0 },
-+ {VLC_CODEC_I410, AV_PIX_FMT_YUV410P, 0, 0, 0 },
-+ {VLC_FOURCC('Y','V','U','9'), AV_PIX_FMT_YUV410P, 0, 0, 0 },
++//============================================================================
++// Derived from codec/avcodec/avcodec.c
+
-+ {VLC_CODEC_NV12, AV_PIX_FMT_NV12, 0, 0, 0 },
-+ {VLC_CODEC_NV21, AV_PIX_FMT_NV21, 0, 0, 0 },
-+
-+ {VLC_CODEC_I420_9L, AV_PIX_FMT_YUV420P9LE, 0, 0, 0 },
-+ {VLC_CODEC_I420_9B, AV_PIX_FMT_YUV420P9BE, 0, 0, 0 },
-+ {VLC_CODEC_I420_10L, AV_PIX_FMT_YUV420P10LE, 0, 0, 0 },
-+ {VLC_CODEC_I420_10B, AV_PIX_FMT_YUV420P10BE, 0, 0, 0 },
-+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) )
-+ {VLC_CODEC_I420_12L, AV_PIX_FMT_YUV420P12LE, 0, 0, 0 },
-+ {VLC_CODEC_I420_12B, AV_PIX_FMT_YUV420P12BE, 0, 0, 0 },
-+#endif
-+ {VLC_CODEC_I420_16L, AV_PIX_FMT_YUV420P16LE, 0, 0, 0 },
-+ {VLC_CODEC_I420_16B, AV_PIX_FMT_YUV420P16BE, 0, 0, 0 },
-+#ifdef AV_PIX_FMT_P010
-+ {VLC_CODEC_P010, AV_PIX_FMT_P010, 0, 0, 0 },
-+#endif
-+
-+ {VLC_CODEC_I422_9L, AV_PIX_FMT_YUV422P9LE, 0, 0, 0 },
-+ {VLC_CODEC_I422_9B, AV_PIX_FMT_YUV422P9BE, 0, 0, 0 },
-+ {VLC_CODEC_I422_10L, AV_PIX_FMT_YUV422P10LE, 0, 0, 0 },
-+ {VLC_CODEC_I422_10B, AV_PIX_FMT_YUV422P10BE, 0, 0, 0 },
-+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) )
-+ {VLC_CODEC_I422_12L, AV_PIX_FMT_YUV422P12LE, 0, 0, 0 },
-+ {VLC_CODEC_I422_12B, AV_PIX_FMT_YUV422P12BE, 0, 0, 0 },
-+#endif
-+
-+ {VLC_CODEC_YUV420A, AV_PIX_FMT_YUVA420P, 0, 0, 0 },
-+ {VLC_CODEC_YUV422A, AV_PIX_FMT_YUVA422P, 0, 0, 0 },
-+ {VLC_CODEC_YUVA, AV_PIX_FMT_YUVA444P, 0, 0, 0 },
-+
-+ {VLC_CODEC_YUVA_444_10L, AV_PIX_FMT_YUVA444P10LE, 0, 0, 0 },
-+ {VLC_CODEC_YUVA_444_10B, AV_PIX_FMT_YUVA444P10BE, 0, 0, 0 },
-+
-+ {VLC_CODEC_I444_9L, AV_PIX_FMT_YUV444P9LE, 0, 0, 0 },
-+ {VLC_CODEC_I444_9B, AV_PIX_FMT_YUV444P9BE, 0, 0, 0 },
-+ {VLC_CODEC_I444_10L, AV_PIX_FMT_YUV444P10LE, 0, 0, 0 },
-+ {VLC_CODEC_I444_10B, AV_PIX_FMT_YUV444P10BE, 0, 0, 0 },
-+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) )
-+ {VLC_CODEC_I444_12L, AV_PIX_FMT_YUV444P12LE, 0, 0, 0 },
-+ {VLC_CODEC_I444_12B, AV_PIX_FMT_YUV444P12BE, 0, 0, 0 },
-+#endif
-+ {VLC_CODEC_I444_16L, AV_PIX_FMT_YUV444P16LE, 0, 0, 0 },
-+ {VLC_CODEC_I444_16B, AV_PIX_FMT_YUV444P16BE, 0, 0, 0 },
-+
-+ /* Packed YUV formats */
-+ {VLC_CODEC_YUYV, AV_PIX_FMT_YUYV422, 0, 0, 0 },
-+ {VLC_FOURCC('Y','U','Y','V'), AV_PIX_FMT_YUYV422, 0, 0, 0 },
-+ {VLC_CODEC_UYVY, AV_PIX_FMT_UYVY422, 0, 0, 0 },
-+ {VLC_CODEC_YVYU, AV_PIX_FMT_YVYU422, 0, 0, 0 },
-+ {VLC_FOURCC('Y','4','1','1'), AV_PIX_FMT_UYYVYY411, 0, 0, 0 },
-+
-+ /* Packed RGB formats */
-+ VLC_RGB( VLC_FOURCC('R','G','B','4'), AV_PIX_FMT_RGB4, AV_PIX_FMT_BGR4, 0x10, 0x06, 0x01 )
-+ VLC_RGB( VLC_CODEC_RGB8, AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, 0xC0, 0x38, 0x07 )
-+
-+ VLC_RGB( VLC_CODEC_RGB15, AV_PIX_FMT_RGB555, AV_PIX_FMT_BGR555, 0x7c00, 0x03e0, 0x001f )
-+ VLC_RGB( VLC_CODEC_RGB16, AV_PIX_FMT_RGB565, AV_PIX_FMT_BGR565, 0xf800, 0x07e0, 0x001f )
-+ VLC_RGB( VLC_CODEC_RGB24, AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, 0xff0000, 0x00ff00, 0x0000ff )
-+
-+ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_RGB32, AV_PIX_FMT_BGR32, 0x00ff0000, 0x0000ff00, 0x000000ff )
-+ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_RGB32_1, AV_PIX_FMT_BGR32_1, 0xff000000, 0x00ff0000, 0x0000ff00 )
-+
-+#ifdef AV_PIX_FMT_0BGR32
-+ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_0BGR32, AV_PIX_FMT_0RGB32, 0x000000ff, 0x0000ff00, 0x00ff0000 )
-+#endif
-+
-+ {VLC_CODEC_RGBA, AV_PIX_FMT_RGBA, 0, 0, 0 },
-+ {VLC_CODEC_ARGB, AV_PIX_FMT_ARGB, 0, 0, 0 },
-+ {VLC_CODEC_BGRA, AV_PIX_FMT_BGRA, 0, 0, 0 },
-+ {VLC_CODEC_GREY, AV_PIX_FMT_GRAY8, 0, 0, 0},
-+
-+ /* Paletized RGB */
-+ {VLC_CODEC_RGBP, AV_PIX_FMT_PAL8, 0, 0, 0},
-+
-+ {VLC_CODEC_GBR_PLANAR, AV_PIX_FMT_GBRP, 0, 0, 0 },
-+ {VLC_CODEC_GBR_PLANAR_9L, AV_PIX_FMT_GBRP9LE, 0, 0, 0 },
-+ {VLC_CODEC_GBR_PLANAR_9B, AV_PIX_FMT_GBRP9BE, 0, 0, 0 },
-+ {VLC_CODEC_GBR_PLANAR_10L, AV_PIX_FMT_GBRP10LE, 0, 0, 0 },
-+ {VLC_CODEC_GBR_PLANAR_10B, AV_PIX_FMT_GBRP10BE, 0, 0, 0 },
-+
-+ /* XYZ */
-+#if LIBAVUTIL_VERSION_CHECK(52, 10, 0, 25, 100)
-+ {VLC_CODEC_XYZ12, AV_PIX_FMT_XYZ12, 0xfff0, 0xfff0, 0xfff0},
-+#endif
-+ { 0, 0, 0, 0, 0 }
-+};
-+
-+static int GetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma )
-+{
-+ /* TODO FIXME for rgb format we HAVE to set rgb mask/shift */
-+ for( int i = 0; chroma_table[i].i_chroma != 0; i++ )
-+ {
-+ if( chroma_table[i].i_chroma_id == i_ffmpeg_chroma )
-+ {
-+ fmt->i_rmask = chroma_table[i].i_rmask;
-+ fmt->i_gmask = chroma_table[i].i_gmask;
-+ fmt->i_bmask = chroma_table[i].i_bmask;
-+ fmt->i_chroma = chroma_table[i].i_chroma;
-+ return VLC_SUCCESS;
-+ }
-+ }
-+ return VLC_EGENERIC;
-+}
-+
-+//#include "../codec/cc.h"
-+
-+static AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec,
++static AVCodecContext *
++ZcFfmpeg_AllocContext( decoder_t *p_dec,
+ const AVCodec **restrict codecp )
+{
+ unsigned i_codec_id;
@@ -5242,7 +5524,7 @@
+ const AVCodec *p_codec = NULL;
+
+ /* *** determine codec type *** */
-+ if( !GetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec,
++ if( !ZcGetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec,
+ &i_codec_id, &psz_namecodec ) )
+ return NULL;
+
@@ -5267,7 +5549,18 @@
+ free( psz_decoder );
+ }
+ if( !p_codec )
-+ p_codec = avcodec_find_decoder( i_codec_id );
++// p_codec = avcodec_find_decoder( i_codec_id );
++ {
++ if( p_dec->fmt_in.i_codec != VLC_CODEC_HEVC )
++ p_codec = avcodec_find_decoder(i_codec_id);
++ else
++ {
++ psz_namecodec = rpi_is_model_pi4() ? "hevc" : "hevc_rpi";
++ msg_Info(p_dec, "Looking for HEVC decoder '%s'", psz_namecodec);
++ p_codec = avcodec_find_decoder_by_name(psz_namecodec);
++ }
++ }
++
+ if( !p_codec )
+ {
+ msg_Dbg( p_dec, "codec not found (%s)", psz_namecodec );
@@ -5286,7 +5579,12 @@
+ return avctx;
+}
+
-+static int ffmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx,
++/*****************************************************************************
++ * ffmpeg_OpenCodec:
++ *****************************************************************************/
++
++static int
++ZcFfmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx,
+ const AVCodec *codec )
+{
+ char *psz_opts = var_InheritString( p_dec, "avcodec-options" );
@@ -5298,7 +5596,7 @@
+ free(psz_opts);
+ }
+
-+ if (av_rpi_zc_init(ctx) != 0)
++ if (av_rpi_zc_init2(ctx, p_dec, zc_alloc_buf, zc_free_pool) != 0)
+ {
+ msg_Err(p_dec, "Failed to init AV ZC");
+ return VLC_EGENERIC;
@@ -5324,58 +5622,35 @@
+ return VLC_SUCCESS;
+}
+
++//============================================================================
++// Derived from 3.0.7.1 codec/avcodec/video.c
+
-+/*****************************************************************************
-+ * decoder_sys_t : decoder descriptor
-+ *****************************************************************************/
-+struct decoder_sys_t
++static inline void wait_mt(decoder_sys_t *sys)
+{
-+ AVCodecContext *p_context;
-+ const AVCodec *p_codec;
-+
-+ /* Video decoder specific part */
-+ date_t pts;
-+
-+ /* Closed captions for decoders */
-+// cc_data_t cc;
-+
-+ /* for frame skipping algo */
-+ bool b_hurry_up;
-+ bool b_show_corrupted;
-+ bool b_from_preroll;
-+ enum AVDiscard i_skip_frame;
-+
-+ /* how many decoded frames are late */
-+ int i_late_frames;
-+ mtime_t i_late_frames_start;
-+ mtime_t i_last_late_delay;
-+
-+ /* for direct rendering */
-+ bool b_direct_rendering;
-+ atomic_bool b_dr_failure;
-+
-+ /* Hack to force display of still pictures */
-+ bool b_first_frame;
-+
-+
-+ /* */
-+ bool palette_sent;
-+
-+ /* VA API */
-+// vlc_va_t *p_va;
-+ enum AVPixelFormat pix_fmt;
-+ int profile;
-+ int level;
++#if 1
++ // As we only ever update the output in our main thread this lock is
++ // redundant
++ VLC_UNUSED(sys);
++#else
++ vlc_sem_wait(&sys->sem_mt);
++#endif
++}
+
-+ MMAL_POOL_T * out_pool;
-+};
++static inline void post_mt(decoder_sys_t *sys)
++{
++#if 1
++ // As we only ever update the output in our main thread this lock is
++ // redundant
++ VLC_UNUSED(sys);
++#else
++ vlc_sem_post(&sys->sem_mt);
++#endif
++}
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+static void ffmpeg_InitCodec ( decoder_t * );
-+static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *,
-+ const enum PixelFormat * );
+static int DecodeVideo( decoder_t *, block_t * );
+static void Flush( decoder_t * );
+
@@ -5401,6 +5676,11 @@
+
+ video_format_Init(fmt, 0);
+
++#if 1
++ VLC_UNUSED(sw_pix_fmt);
++ if ((fmt->i_chroma = ZcFindVlcChroma(pix_fmt)) == 0)
++ return -1;
++#else
+ if (pix_fmt == sw_pix_fmt)
+ { /* software decoding */
+ int aligns[AV_NUM_DATA_POINTERS];
@@ -5417,8 +5697,9 @@
+
+ avcodec_align_dimensions2(ctx, &width, &height, aligns);
+ }
-+// else /* hardware decoding */
-+// fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt);
++ else /* hardware decoding */
++ fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt);
++#endif
+
+ if( width == 0 || height == 0 || width > 8192 || height > 8192 ||
+ width < ctx->width || height < ctx->height )
@@ -5470,8 +5751,21 @@
+ * __MAX(ctx->ticks_per_frame, 1);
+ }
+
-+ if( ctx->color_range == AVCOL_RANGE_JPEG )
++ /* FIXME we should only set the known values and let the core decide
++ * later of fallbacks, but we can't do that with a boolean */
++ switch ( ctx->color_range )
++ {
++ case AVCOL_RANGE_JPEG:
+ fmt->b_color_range_full = true;
++ break;
++ case AVCOL_RANGE_UNSPECIFIED:
++ fmt->b_color_range_full = !vlc_fourcc_IsYUV( fmt->i_chroma );
++ break;
++ case AVCOL_RANGE_MPEG:
++ default:
++ fmt->b_color_range_full = false;
++ break;
++ }
+
+ switch( ctx->colorspace )
+ {
@@ -5569,10 +5863,15 @@
+{
+ video_format_t fmt_out;
+ int val;
-+
++#if TRACE_ALL
++ msg_Dbg(dec, "<<< %s", __func__);
++#endif
+ val = lavc_GetVideoFormat(dec, &fmt_out, ctx, fmt, swfmt);
+ if (val)
++ {
++ msg_Dbg(dec, "Failed to get format");
+ return val;
++ }
+
+ /* always have date in fields/ticks units */
+ if(dec->p_sys->pts.i_divider_num)
@@ -5584,7 +5883,7 @@
+ __MAX(ctx->ticks_per_frame, 1),
+ fmt_out.i_frame_rate_base);
+
-+ fmt_out.p_palette = dec->fmt_out.video.p_palette;
++ fmt_out.p_palette = dec-> fmt_out.video.p_palette;
+ dec->fmt_out.video.p_palette = NULL;
+
+ es_format_Change(&dec->fmt_out, VIDEO_ES, fmt_out.i_chroma);
@@ -5597,516 +5896,646 @@
+ dec->fmt_out.video.mastering = dec->fmt_in.video.mastering;
+ dec->fmt_out.video.lighting = dec->fmt_in.video.lighting;
+
-+ return decoder_UpdateVideoFormat(dec);
++ val = decoder_UpdateVideoFormat(dec);
++#if TRACE_ALL
++ msg_Dbg(dec, ">>> %s: rv=%d", __func__, val);
++#endif
++ return val;
+}
+
-+/*****************************************************************************
-+ * Flush:
-+ *****************************************************************************/
-+static void Flush( decoder_t *p_dec )
++static int OpenVideoCodec( decoder_t *p_dec )
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
-+ AVCodecContext *p_context = p_sys->p_context;
-+
-+ date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */
-+ p_sys->i_late_frames = 0;
-+// cc_Flush( &p_sys->cc );
++ AVCodecContext *ctx = p_sys->p_context;
++ const AVCodec *codec = p_sys->p_codec;
++ int ret;
+
-+ /* Abort pictures in order to unblock all avcodec workers threads waiting
-+ * for a picture. This will avoid a deadlock between avcodec_flush_buffers
-+ * and workers threads */
-+ decoder_AbortPictures( p_dec, true );
++ if( ctx->extradata_size <= 0 )
++ {
++ if( codec->id == AV_CODEC_ID_VC1 ||
++ codec->id == AV_CODEC_ID_THEORA )
++ {
++ msg_Warn( p_dec, "waiting for extra data for codec %s",
++ codec->name );
++ return 1;
++ }
++ }
+
-+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
-+ if( avcodec_is_open( p_context ) )
-+ avcodec_flush_buffers( p_context );
++ ctx->width = p_dec->fmt_in.video.i_visible_width;
++ ctx->height = p_dec->fmt_in.video.i_visible_height;
+
-+ /* Reset cancel state to false */
-+ decoder_AbortPictures( p_dec, false );
-+}
++ ctx->coded_width = p_dec->fmt_in.video.i_width;
++ ctx->coded_height = p_dec->fmt_in.video.i_height;
+
-+static bool check_block_validity( decoder_sys_t *p_sys, block_t *block )
-+{
-+ if( !block)
-+ return true;
++ ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel;
++ p_sys->pix_fmt = AV_PIX_FMT_NONE;
++ p_sys->profile = -1;
++ p_sys->level = -1;
++ cc_Init( &p_sys->cc );
+
-+ if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )
++ set_video_color_settings( &p_dec->fmt_in.video, ctx );
++ if( p_dec->fmt_in.video.i_frame_rate_base &&
++ p_dec->fmt_in.video.i_frame_rate &&
++ (double) p_dec->fmt_in.video.i_frame_rate /
++ p_dec->fmt_in.video.i_frame_rate_base < 6 )
+ {
-+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
-+// cc_Flush( &p_sys->cc );
++ ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
++ }
+
-+ p_sys->i_late_frames = 0;
-+ if( block->i_flags & BLOCK_FLAG_CORRUPTED )
-+ {
-+ block_Release( block );
-+ return false;
-+ }
++ post_mt( p_sys );
++ ret = ZcFfmpeg_OpenCodec( p_dec, ctx, codec );
++ wait_mt( p_sys );
++ if( ret < 0 )
++ return ret;
++
++ switch( ctx->active_thread_type )
++ {
++ case FF_THREAD_FRAME:
++ msg_Dbg( p_dec, "using frame thread mode with %d threads",
++ ctx->thread_count );
++ break;
++ case FF_THREAD_SLICE:
++ msg_Dbg( p_dec, "using slice thread mode with %d threads",
++ ctx->thread_count );
++ break;
++ case 0:
++ if( ctx->thread_count > 1 )
++ msg_Warn( p_dec, "failed to enable threaded decoding" );
++ break;
++ default:
++ msg_Warn( p_dec, "using unknown thread mode with %d threads",
++ ctx->thread_count );
++ break;
+ }
-+ return true;
++ return 0;
+}
+
-+static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time)
++/*****************************************************************************
++ * InitVideo: initialize the video decoder
++ *****************************************************************************
++ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet
++ * opened (done after the first decoded frame).
++ *****************************************************************************/
++static int MmalAvcodecOpenDecoder( vlc_object_t *obj )
+{
-+ if( !block )
-+ return false;
-+ if( block->i_flags & BLOCK_FLAG_PREROLL )
++ decoder_t *p_dec = (decoder_t *)obj;
++ const AVCodec *p_codec;
++
++ int extra_buffers = var_InheritInteger(p_dec, MMAL_AVCODEC_BUFFERS);
++
++ if (extra_buffers < 0)
+ {
-+ /* Do not care about late frames when prerolling
-+ * TODO avoid decoding of non reference frame
-+ * (ie all B except for H264 where it depends only on nal_ref_idc) */
-+ p_sys->i_late_frames = 0;
-+ p_sys->b_from_preroll = true;
-+ p_sys->i_last_late_delay = INT64_MAX;
++ extra_buffers = p_dec->fmt_in.video.i_height * p_dec->fmt_in.video.i_width >= 1920 * 1088 ?
++ BUFFERS_IN_FLIGHT_UHD : BUFFERS_IN_FLIGHT;
+ }
+
-+ if( p_sys->i_late_frames <= 0 )
-+ return false;
++ if (extra_buffers <= 0)
++ {
++ msg_Dbg(p_dec, "%s: extra_buffers=%d - cannot use module", __func__, extra_buffers);
++ return VLC_EGENERIC;
++ }
+
-+ if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ))
++ const vcsm_init_type_t vcsm_type = cma_vcsm_init();
++ const int vcsm_size =
++ vcsm_type == VCSM_INIT_LEGACY ? hw_mmal_get_gpu_mem() : 512 << 20;
++
++#if 1
+ {
-+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
-+ block_Release( block );
-+ p_sys->i_late_frames--;
-+ return true;
++ char buf1[5], buf2[5], buf2a[5];
++ char buf3[5], buf4[5];
++ uint32_t in_fcc = 0;
++ msg_Dbg(p_dec, "%s: <<< (%s/%s)[%s] %dx%d -> (%s/%s) %dx%d [%s/%d] xb:%d", __func__,
++ str_fourcc(buf1, p_dec->fmt_in.i_codec),
++ str_fourcc(buf2, p_dec->fmt_in.video.i_chroma),
++ str_fourcc(buf2a, in_fcc),
++ p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height,
++ str_fourcc(buf3, p_dec->fmt_out.i_codec),
++ str_fourcc(buf4, p_dec->fmt_out.video.i_chroma),
++ p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height,
++ cma_vcsm_init_str(vcsm_type), vcsm_size, extra_buffers);
+ }
-+ return false;
-+}
++#endif
+
-+static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture )
-+{
-+ if( p_sys->i_late_frames <= 4)
-+ return false;
++ if( vcsm_type == VCSM_INIT_NONE )
++ return VLC_EGENERIC;
++#if 1
++ if( (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC &&
++ (vcsm_type == VCSM_INIT_CMA || vcsm_size < (96 << 20))) ||
++ (p_dec->fmt_in.i_codec == VLC_CODEC_HEVC &&
++ vcsm_size < (128 << 20)))
++ {
++ cma_vcsm_exit(vcsm_type);
++ return VLC_EGENERIC;
++ }
++#endif
+
-+ *b_need_output_picture = false;
-+ if( p_sys->i_late_frames < 12 )
++ AVCodecContext *p_context = ZcFfmpeg_AllocContext( p_dec, &p_codec );
++ if( p_context == NULL )
+ {
-+ p_context->skip_frame =
-+ (p_sys->i_skip_frame <= AVDISCARD_NONREF) ?
-+ AVDISCARD_NONREF : p_sys->i_skip_frame;
++ cma_vcsm_exit(vcsm_type);
++ return VLC_EGENERIC;
+ }
-+ else
++
++ int i_val;
++
++ /* Allocate the memory needed to store the decoder's structure */
++ decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) );
++ if( unlikely(p_sys == NULL) )
+ {
-+ /* picture too late, won't decode
-+ * but break picture until a new I, and for mpeg4 ...*/
-+ p_sys->i_late_frames--; /* needed else it will never be decrease */
-+ return true;
++ avcodec_free_context( &p_context );
++ cma_vcsm_exit(vcsm_type);
++ return VLC_ENOMEM;
+ }
-+ return false;
-+}
+
-+static void interpolate_next_pts( decoder_t *p_dec, AVFrame *frame )
-+{
-+ decoder_sys_t *p_sys = p_dec->p_sys;
-+ AVCodecContext *p_context = p_sys->p_context;
++ p_dec->p_sys = p_sys;
++ p_sys->p_context = p_context;
++ p_sys->p_codec = p_codec;
++ p_sys->p_dec = p_dec;
++// p_sys->p_va = NULL;
++ p_sys->cma_in_flight_max = extra_buffers;
++ p_sys->vcsm_init_type = vcsm_type;
++ vlc_sem_init( &p_sys->sem_mt, 0 );
+
-+ if( date_Get( &p_sys->pts ) == VLC_TS_INVALID ||
-+ p_sys->pts.i_divider_num == 0 )
-+ return;
++ /* ***** Fill p_context with init values ***** */
++ p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ?
++ p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec );
+
-+ int i_tick = p_context->ticks_per_frame;
-+ if( i_tick <= 0 )
-+ i_tick = 1;
++ /* ***** Get configuration of ffmpeg plugin ***** */
++ p_context->workaround_bugs =
++ var_InheritInteger( p_dec, "avcodec-workaround-bugs" );
++ p_context->err_recognition =
++ var_InheritInteger( p_dec, "avcodec-error-resilience" );
+
-+ /* interpolate the next PTS */
-+ date_Increment( &p_sys->pts, i_tick + frame->repeat_pict );
-+}
++ if( var_CreateGetBool( p_dec, "grayscale" ) )
++ p_context->flags |= AV_CODEC_FLAG_GRAY;
+
-+static void update_late_frame_count( decoder_t *p_dec, block_t *p_block, mtime_t current_time, mtime_t i_pts )
-+{
-+ decoder_sys_t *p_sys = p_dec->p_sys;
-+ /* Update frame late count (except when doing preroll) */
-+ mtime_t i_display_date = VLC_TS_INVALID;
-+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
-+ i_display_date = decoder_GetDisplayDate( p_dec, i_pts );
++ /* ***** Output always the frames ***** */
++ p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
+
-+ if( i_display_date > VLC_TS_INVALID && i_display_date <= current_time )
-+ {
-+ /* Out of preroll, consider only late frames on rising delay */
-+ if( p_sys->b_from_preroll )
-+ {
-+ if( p_sys->i_last_late_delay > current_time - i_display_date )
-+ {
-+ p_sys->i_last_late_delay = current_time - i_display_date;
-+ return;
-+ }
-+ p_sys->b_from_preroll = false;
-+ }
++ i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" );
++ if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL;
++ else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY;
++ else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR;
++ else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF;
++ else p_context->skip_loop_filter = AVDISCARD_DEFAULT;
+
-+ p_sys->i_late_frames++;
-+ if( p_sys->i_late_frames == 1 )
-+ p_sys->i_late_frames_start = current_time;
++ if( var_CreateGetBool( p_dec, "avcodec-fast" ) )
++ p_context->flags2 |= AV_CODEC_FLAG2_FAST;
+
-+ }
-+ else
-+ {
-+ p_sys->i_late_frames = 0;
-+ }
-+}
++ /* ***** libavcodec frame skipping ***** */
++ p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" );
++ p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" );
+
++ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" );
++ if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL;
++ else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY;
++ else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR;
++ else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF;
++ else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE;
++ else p_sys->i_skip_frame = AVDISCARD_DEFAULT;
++ p_context->skip_frame = p_sys->i_skip_frame;
+
-+static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic )
-+{
-+// decoder_sys_t *p_sys = p_dec->p_sys;
-+ bool format_changed = false;
++ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" );
++ if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL;
++ else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY;
++ else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR;
++ else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF;
++ else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE;
++ else p_context->skip_idct = AVDISCARD_DEFAULT;
+
-+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
-+#define FROM_AVRAT(default_factor, avrat) \
-+(uint64_t)(default_factor) * (avrat).num / (avrat).den
-+ const AVFrameSideData *metadata =
-+ av_frame_get_side_data( frame,
-+ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA );
-+ if ( metadata )
++ /* ***** libavcodec direct rendering ***** */
++ p_sys->b_direct_rendering = false;
++ atomic_init(&p_sys->b_dr_failure, false);
++ if( var_CreateGetBool( p_dec, "avcodec-dr" ) &&
++ (p_codec->capabilities & AV_CODEC_CAP_DR1) &&
++ /* No idea why ... but this fixes flickering on some TSCC streams */
++ p_sys->p_codec->id != AV_CODEC_ID_TSCC &&
++ p_sys->p_codec->id != AV_CODEC_ID_CSCD &&
++ p_sys->p_codec->id != AV_CODEC_ID_CINEPAK )
+ {
-+ const AVMasteringDisplayMetadata *hdr_meta =
-+ (const AVMasteringDisplayMetadata *) metadata->data;
-+ if ( hdr_meta->has_luminance )
-+ {
-+#define ST2086_LUMA_FACTOR 10000
-+ p_pic->format.mastering.max_luminance =
-+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance);
-+ p_pic->format.mastering.min_luminance =
-+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance);
-+ }
-+ if ( hdr_meta->has_primaries )
-+ {
-+#define ST2086_RED 2
-+#define ST2086_GREEN 0
-+#define ST2086_BLUE 1
-+#define LAV_RED 0
-+#define LAV_GREEN 1
-+#define LAV_BLUE 2
-+#define ST2086_PRIM_FACTOR 50000
-+ p_pic->format.mastering.primaries[ST2086_RED*2 + 0] =
-+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]);
-+ p_pic->format.mastering.primaries[ST2086_RED*2 + 1] =
-+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]);
-+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] =
-+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]);
-+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] =
-+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]);
-+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] =
-+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]);
-+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] =
-+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]);
-+ p_pic->format.mastering.white_point[0] =
-+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]);
-+ p_pic->format.mastering.white_point[1] =
-+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]);
-+ }
-+
-+ if ( memcmp( &p_dec->fmt_out.video.mastering,
-+ &p_pic->format.mastering,
-+ sizeof(p_pic->format.mastering) ) )
-+ {
-+ p_dec->fmt_out.video.mastering = p_pic->format.mastering;
-+ format_changed = true;
-+ }
-+#undef FROM_AVRAT
++ /* Some codecs set pix_fmt only after the 1st frame has been decoded,
++ * so we need to do another check in ffmpeg_GetFrameBuf() */
++ p_sys->b_direct_rendering = true;
+ }
++
++ p_context->get_format = ZcGetFormat;
++#if 0
++ p_context->get_format = ffmpeg_GetFormat;
++ /* Always use our get_buffer wrapper so we can calculate the
++ * PTS correctly */
++ p_context->get_buffer2 = lavc_GetFrame;
++ p_context->opaque = p_dec;
+#endif
-+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) )
-+ const AVFrameSideData *metadata_lt =
-+ av_frame_get_side_data( frame,
-+ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL );
-+ if ( metadata_lt )
++
++ int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" );
++ if( i_thread_count <= 0 )
++#if 1
+ {
-+ const AVContentLightMetadata *light_meta =
-+ (const AVContentLightMetadata *) metadata_lt->data;
-+ p_pic->format.lighting.MaxCLL = light_meta->MaxCLL;
-+ p_pic->format.lighting.MaxFALL = light_meta->MaxFALL;
-+ if ( memcmp( &p_dec->fmt_out.video.lighting,
-+ &p_pic->format.lighting,
-+ sizeof(p_pic->format.lighting) ) )
-+ {
-+ p_dec->fmt_out.video.lighting = p_pic->format.lighting;
-+ format_changed = true;
-+ }
++ // Pick 5 threads for everything on Pi except for HEVC where the h/w
++ // really limits the useful size to 3
++ i_thread_count = p_codec->id == AV_CODEC_ID_HEVC ? 3 : 5;
++ }
++#else
++ {
++ i_thread_count = vlc_GetCPUCount();
++ if( i_thread_count > 1 )
++ i_thread_count++;
++
++ //FIXME: take in count the decoding time
++#if VLC_WINSTORE_APP
++ i_thread_count = __MIN( i_thread_count, 6 );
++#else
++ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 );
++#endif
+ }
++ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 );
+#endif
++ msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count );
++ p_context->thread_count = i_thread_count;
++ p_context->thread_safe_callbacks = true;
+
-+ if (format_changed && decoder_UpdateVideoFormat( p_dec ))
-+ return -1;
-+#if 0
-+ const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC );
-+ if( p_avcc )
++ switch( p_codec->id )
+ {
-+ cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size );
-+ if( p_sys->cc.b_reorder || p_sys->cc.i_data )
-+ {
-+ block_t *p_cc = block_Alloc( p_sys->cc.i_data );
-+ if( p_cc )
-+ {
-+ memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
-+ if( p_sys->cc.b_reorder )
-+ p_cc->i_dts = p_cc->i_pts = p_pic->date;
-+ else
-+ p_cc->i_pts = p_cc->i_dts;
-+ decoder_cc_desc_t desc;
-+ desc.i_608_channels = p_sys->cc.i_608channels;
-+ desc.i_708_channels = p_sys->cc.i_708channels;
-+ desc.i_reorder_depth = 4;
-+ decoder_QueueCc( p_dec, p_cc, &desc );
-+ }
-+ cc_Flush( &p_sys->cc );
-+ }
++ case AV_CODEC_ID_MPEG4:
++ case AV_CODEC_ID_H263:
++ p_context->thread_type = 0;
++ break;
++ case AV_CODEC_ID_MPEG1VIDEO:
++ case AV_CODEC_ID_MPEG2VIDEO:
++ p_context->thread_type &= ~FF_THREAD_SLICE;
++ /* fall through */
++# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0))
++ case AV_CODEC_ID_H264:
++ case AV_CODEC_ID_VC1:
++ case AV_CODEC_ID_WMV3:
++ p_context->thread_type &= ~FF_THREAD_FRAME;
++# endif
++ default:
++ break;
++ }
++
++ if( p_context->thread_type & FF_THREAD_FRAME )
++ p_dec->i_extra_picture_buffers = 2 * p_context->thread_count;
++
++ /* ***** misc init ***** */
++ date_Init(&p_sys->pts, 1, 30001);
++ date_Set(&p_sys->pts, VLC_TS_INVALID);
++ p_sys->b_first_frame = true;
++ p_sys->i_late_frames = 0;
++ p_sys->b_from_preroll = false;
++
++ /* Set output properties */
++ if( ZcGetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS )
++ {
++ /* we are doomed. but not really, because most codecs set their pix_fmt later on */
++// p_dec->fmt_out.i_codec = VLC_CODEC_I420;
++ p_dec->fmt_out.i_codec = VLC_CODEC_MMAL_ZC_I420;
++ }
++ p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma;
++
++ p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation;
++
++ if( p_dec->fmt_in.video.p_palette ) {
++ p_sys->palette_sent = false;
++ p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) );
++ if( p_dec->fmt_out.video.p_palette )
++ *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette;
++ } else
++ p_sys->palette_sent = true;
++
++ if ((p_sys->cma_pool = cma_buf_pool_new(p_sys->cma_in_flight_max, p_sys->cma_in_flight_max, false, "mmal_avcodec")) == NULL)
++ {
++ msg_Err(p_dec, "CMA pool alloc failure");
++ goto fail;
++ }
++
++ /* ***** init this codec with special data ***** */
++ ffmpeg_InitCodec( p_dec );
++
++ /* ***** Open the codec ***** */
++ if( OpenVideoCodec( p_dec ) < 0 )
++ {
++ vlc_sem_destroy( &p_sys->sem_mt );
++ free( p_sys );
++ avcodec_free_context( &p_context );
++ return VLC_EGENERIC;
++ }
++
++ p_dec->pf_decode = DecodeVideo;
++ p_dec->pf_flush = Flush;
++
++ /* XXX: Writing input format makes little sense. */
++ if( p_context->profile != FF_PROFILE_UNKNOWN )
++ p_dec->fmt_in.i_profile = p_context->profile;
++ if( p_context->level != FF_LEVEL_UNKNOWN )
++ p_dec->fmt_in.i_level = p_context->level;
++
++#if 1
++ // Most of the time we have nothing useful by way of a format here
++ // wait till we've decoded something
++#else
++ // Update output format
++ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
++ p_context->pix_fmt) != 0)
++ {
++ msg_Err(p_dec, "Unable to update format: pix_fmt=%d", p_context->pix_fmt);
++// goto fail;
+ }
+#endif
-+ return 0;
-+}
+
++#if TRACE_ALL
++ msg_Dbg(p_dec, "<<< %s: OK", __func__);
++#endif
++ return VLC_SUCCESS;
+
++fail:
++ MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec));
+
-+static int OpenVideoCodec( decoder_t *p_dec )
++#if TRACE_ALL
++ msg_Dbg(p_dec, "<<< %s: FAIL", __func__);
++#endif
++
++ return VLC_EGENERIC;
++}
++
++/*****************************************************************************
++ * Flush:
++ *****************************************************************************/
++static void Flush( decoder_t *p_dec )
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
-+ AVCodecContext *ctx = p_sys->p_context;
-+ const AVCodec *codec = p_sys->p_codec;
-+ int ret;
++ AVCodecContext *p_context = p_sys->p_context;
+
+#if TRACE_ALL
+ msg_Dbg(p_dec, "<<< %s", __func__);
+#endif
+
-+ if( ctx->extradata_size <= 0 )
-+ {
-+ if( codec->id == AV_CODEC_ID_VC1 ||
-+ codec->id == AV_CODEC_ID_THEORA )
-+ {
-+ msg_Warn( p_dec, "waiting for extra data for codec %s",
-+ codec->name );
-+ return 1;
-+ }
-+ }
++ date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */
++ p_sys->i_late_frames = 0;
++ cc_Flush( &p_sys->cc );
+
-+ ctx->width = p_dec->fmt_in.video.i_visible_width;
-+ ctx->height = p_dec->fmt_in.video.i_visible_height;
++ /* Abort pictures in order to unblock all avcodec workers threads waiting
++ * for a picture. This will avoid a deadlock between avcodec_flush_buffers
++ * and workers threads */
++// It would probably be good to use AbortPicture but that often deadlocks on close
++// and given that we wait for pics in the main thread it should be unneeded (whereas
++// cma is alloced in the depths of ffmpeg on its own threads)
++// decoder_AbortPictures( p_dec, true );
++ cma_buf_pool_cancel(p_sys->cma_pool);
+
-+ ctx->coded_width = p_dec->fmt_in.video.i_width;
-+ ctx->coded_height = p_dec->fmt_in.video.i_height;
++ post_mt( p_sys );
++ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
++ if( avcodec_is_open( p_context ) )
++ avcodec_flush_buffers( p_context );
++ wait_mt( p_sys );
+
-+ ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel;
-+ p_sys->pix_fmt = AV_PIX_FMT_NONE;
-+ p_sys->profile = -1;
-+ p_sys->level = -1;
-+// cc_Init( &p_sys->cc );
++ /* Reset cancel state to false */
++ cma_buf_pool_uncancel(p_sys->cma_pool);
++// decoder_AbortPictures( p_dec, false );
+
-+ set_video_color_settings( &p_dec->fmt_in.video, ctx );
++#if TRACE_ALL
++ msg_Dbg(p_dec, ">>> %s", __func__);
++#endif
+
-+ ret = ffmpeg_OpenCodec( p_dec, ctx, codec );
-+ if( ret < 0 )
-+ return ret;
++}
+
-+ switch( ctx->active_thread_type )
++static bool check_block_validity( decoder_sys_t *p_sys, block_t *block )
++{
++ if( !block)
++ return true;
++
++ if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )
+ {
-+ case FF_THREAD_FRAME:
-+ msg_Dbg( p_dec, "using frame thread mode with %d threads",
-+ ctx->thread_count );
-+ break;
-+ case FF_THREAD_SLICE:
-+ msg_Dbg( p_dec, "using slice thread mode with %d threads",
-+ ctx->thread_count );
-+ break;
-+ case 0:
-+ if( ctx->thread_count > 1 )
-+ msg_Warn( p_dec, "failed to enable threaded decoding" );
-+ break;
-+ default:
-+ msg_Warn( p_dec, "using unknown thread mode with %d threads",
-+ ctx->thread_count );
-+ break;
++ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
++ cc_Flush( &p_sys->cc );
++
++ p_sys->i_late_frames = 0;
++ if( block->i_flags & BLOCK_FLAG_CORRUPTED )
++ {
++ block_Release( block );
++ return false;
++ }
+ }
-+ return 0;
++ return true;
+}
+
-+static MMAL_BOOL_T
-+zc_buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata)
++static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time)
+{
-+ const AVRpiZcRefPtr fr_ref = userdata;
-+ VLC_UNUSED(buf);
++ if( !block )
++ return false;
++ if( block->i_flags & BLOCK_FLAG_PREROLL )
++ {
++ /* Do not care about late frames when prerolling
++ * TODO avoid decoding of non reference frame
++ * (ie all B except for H264 where it depends only on nal_ref_idc) */
++ p_sys->i_late_frames = 0;
++ p_sys->b_from_preroll = true;
++ p_sys->i_last_late_delay = INT64_MAX;
++ }
+
-+ av_rpi_zc_unref(fr_ref);
++ if( p_sys->i_late_frames <= 0 )
++ return false;
+
-+ return MMAL_FALSE;
++ if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ))
++ {
++ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
++ block_Release( block );
++ p_sys->i_late_frames--;
++ return true;
++ }
++ return false;
+}
+
-+static MMAL_FOURCC_T
-+avfmt_to_mmal(const int avfmt)
++static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture )
+{
-+ switch( avfmt )
++ if( p_sys->i_late_frames <= 4)
++ return false;
++
++ *b_need_output_picture = false;
++ if( p_sys->i_late_frames < 12 )
+ {
-+ case AV_PIX_FMT_SAND128:
-+ return MMAL_ENCODING_YUVUV128;
-+ case AV_PIX_FMT_SAND64_10:
-+ return MMAL_ENCODING_YUVUV64_10;
-+ default:
-+ break;
++ p_context->skip_frame =
++ (p_sys->i_skip_frame <= AVDISCARD_NONREF) ?
++ AVDISCARD_NONREF : p_sys->i_skip_frame;
+ }
-+ return MMAL_ENCODING_UNKNOWN;
++ else
++ {
++ /* picture too late, won't decode
++ * but break picture until a new I, and for mpeg4 ...*/
++ p_sys->i_late_frames--; /* needed else it will never be decrease */
++ return true;
++ }
++ return false;
+}
+
-+/*****************************************************************************
-+ * DecodeBlock: Called to decode one or more frames
-+ *****************************************************************************/
++static mtime_t interpolate_next_pts( decoder_t *p_dec, AVFrame *frame )
++{
++ decoder_sys_t *p_sys = p_dec->p_sys;
++ AVCodecContext *p_context = p_sys->p_context;
+
++ if( date_Get( &p_sys->pts ) == VLC_TS_INVALID ||
++ p_sys->pts.i_divider_num == 0 )
++ return VLC_TS_INVALID;
+
-+// Returns
-+// -ve error
-+// 0 Need more input (EAGAIN)
-+// 1 Frame decoded (dropped or Qed)
-+// 2 Decode err
-+// 3 EOF
++ int i_tick = p_context->ticks_per_frame;
++ if( i_tick <= 0 )
++ i_tick = 1;
+
-+static int rx_frame(decoder_t * const p_dec, decoder_sys_t * const p_sys, AVCodecContext * const p_context)
++ /* interpolate the next PTS */
++ return date_Increment( &p_sys->pts, i_tick + frame->repeat_pict );
++}
++
++static void update_late_frame_count( decoder_t *p_dec, block_t *p_block,
++ mtime_t current_time, mtime_t i_pts,
++ mtime_t i_next_pts )
+{
-+ AVFrame * frame = av_frame_alloc();
-+ picture_t * p_pic = NULL;
-+ int ret;
++ decoder_sys_t *p_sys = p_dec->p_sys;
++ /* Update frame late count (except when doing preroll) */
++ mtime_t i_display_date = VLC_TS_INVALID;
++ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
++ i_display_date = decoder_GetDisplayDate( p_dec, i_pts );
+
-+ if (frame == NULL)
-+ return VLC_ENOMEM;
++ mtime_t i_threshold = i_next_pts != VLC_TS_INVALID ? (i_next_pts - i_pts) / 2 : 20000;
++
++ if( i_display_date > VLC_TS_INVALID && i_display_date + i_threshold <= current_time )
++ {
++ /* Out of preroll, consider only late frames on rising delay */
++ if( p_sys->b_from_preroll )
++ {
++ if( p_sys->i_last_late_delay > current_time - i_display_date )
++ {
++ p_sys->i_last_late_delay = current_time - i_display_date;
++ return;
++ }
++ p_sys->b_from_preroll = false;
++ }
++
++ p_sys->i_late_frames++;
++ if( p_sys->i_late_frames == 1 )
++ p_sys->i_late_frames_start = current_time;
+
-+ ret = avcodec_receive_frame(p_context, frame);
++ }
++ else
++ {
++ p_sys->i_late_frames = 0;
++ }
++}
++
++
++static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic )
++{
++ decoder_sys_t *p_sys = p_dec->p_sys;
++ bool format_changed = false;
++
++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
++#define FROM_AVRAT(default_factor, avrat) \
++(uint64_t)(default_factor) * (avrat).num / (avrat).den
++ const AVFrameSideData *metadata =
++ av_frame_get_side_data( frame,
++ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA );
++ if ( metadata )
++ {
++ const AVMasteringDisplayMetadata *hdr_meta =
++ (const AVMasteringDisplayMetadata *) metadata->data;
++ if ( hdr_meta->has_luminance )
++ {
++#define ST2086_LUMA_FACTOR 10000
++ p_pic->format.mastering.max_luminance =
++ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance);
++ p_pic->format.mastering.min_luminance =
++ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance);
++ }
++ if ( hdr_meta->has_primaries )
++ {
++#define ST2086_RED 2
++#define ST2086_GREEN 0
++#define ST2086_BLUE 1
++#define LAV_RED 0
++#define LAV_GREEN 1
++#define LAV_BLUE 2
++#define ST2086_PRIM_FACTOR 50000
++ p_pic->format.mastering.primaries[ST2086_RED*2 + 0] =
++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]);
++ p_pic->format.mastering.primaries[ST2086_RED*2 + 1] =
++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]);
++ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] =
++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]);
++ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] =
++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]);
++ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] =
++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]);
++ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] =
++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]);
++ p_pic->format.mastering.white_point[0] =
++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]);
++ p_pic->format.mastering.white_point[1] =
++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]);
++ }
+
-+ if (ret != 0)
-+ {
-+ av_frame_free(&frame);
-+ switch (ret)
++ if ( memcmp( &p_dec->fmt_out.video.mastering,
++ &p_pic->format.mastering,
++ sizeof(p_pic->format.mastering) ) )
+ {
-+ case AVERROR(EAGAIN):
-+ return 0;
-+
-+ case AVERROR(ENOMEM):
-+ case AVERROR(EINVAL):
-+ msg_Err(p_dec, "avcodec_receive_frame critical error");
-+ return VLC_EGENERIC;
-+
-+ case AVERROR_EOF:
-+ msg_Dbg(p_dec, "Rx EOF");
-+ avcodec_flush_buffers(p_context);
-+ return 2;
-+
-+ default:
-+ msg_Warn(p_dec, "Decode error: %d", ret);
-+ return 1;
++ p_dec->fmt_out.video.mastering = p_pic->format.mastering;
++ format_changed = true;
+ }
++#undef FROM_AVRAT
+ }
-+
-+ /* Compute the PTS */
-+#ifdef FF_API_PKT_PTS
-+ mtime_t i_pts = frame->pts;
-+#else
-+ mtime_t i_pts = frame->pkt_pts;
+#endif
-+ if (i_pts == AV_NOPTS_VALUE )
-+ i_pts = frame->pkt_dts;
-+
-+ if( i_pts == AV_NOPTS_VALUE )
-+ i_pts = date_Get( &p_sys->pts );
-+
-+ /* Interpolate the next PTS */
-+ if( i_pts > VLC_TS_INVALID )
-+ date_Set( &p_sys->pts, i_pts );
-+
-+ interpolate_next_pts( p_dec, frame );
-+
-+// update_late_frame_count( p_dec, p_block, current_time, i_pts); //*********************
-+
-+ if( ( /* !p_sys->p_va && */ !frame->linesize[0] ) ||
-+ ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) &&
-+ !p_sys->b_show_corrupted ) )
-+ {
-+ msg_Dbg(p_dec, "Frame drop");
-+ av_frame_free(&frame);
-+ return 1;
-+ }
-+
-+ lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
-+ p_context->pix_fmt);
-+
++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) )
++ const AVFrameSideData *metadata_lt =
++ av_frame_get_side_data( frame,
++ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL );
++ if ( metadata_lt )
+ {
-+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(p_sys->out_pool->queue);
-+// MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(p_sys->out_pool->queue);
-+ if (buf == NULL) {
-+ msg_Err(p_dec, "MMAL buffer alloc failure");
-+ av_frame_free(&frame);
-+ return 1;
-+ }
-+
-+ mmal_buffer_header_reset(buf); // length, offset, flags, pts, dts
-+ buf->cmd = 0;
-+ buf->user_data = NULL;
-+
-+ {
-+ const AVRpiZcRefPtr fr_buf = av_rpi_zc_ref(p_context, frame, frame->format, 0);
-+
-+ if (fr_buf == NULL) {
-+ mmal_buffer_header_release(buf);
-+ av_frame_free(&frame);
-+ return VLC_ENOMEM;
-+ }
-+
-+ const intptr_t vc_handle = (intptr_t)av_rpi_zc_vc_handle(fr_buf);
-+
-+ buf->data = (uint8_t *)vc_handle; // Cast our handle to a pointer for mmal - 2 steps to avoid gcc warnings
-+ buf->offset = av_rpi_zc_offset(fr_buf);
-+ buf->length = av_rpi_zc_length(fr_buf);
-+ buf->alloc_size = av_rpi_zc_numbytes(fr_buf);
-+ buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
-+
-+ mmal_buffer_header_pre_release_cb_set(buf, zc_buf_pre_release_cb, fr_buf);
-+ }
-+
-+ p_pic = decoder_NewPicture(p_dec); // *** Really want an empy pic
-+ if (p_pic == NULL)
++ const AVContentLightMetadata *light_meta =
++ (const AVContentLightMetadata *) metadata_lt->data;
++ p_pic->format.lighting.MaxCLL = light_meta->MaxCLL;
++ p_pic->format.lighting.MaxFALL = light_meta->MaxFALL;
++ if ( memcmp( &p_dec->fmt_out.video.lighting,
++ &p_pic->format.lighting,
++ sizeof(p_pic->format.lighting) ) )
+ {
-+ msg_Err(p_dec, "Picture alloc failure");
-+ mmal_buffer_header_release(buf);
-+ av_frame_free(&frame);
-+ return VLC_ENOMEM;
++ p_dec->fmt_out.video.lighting = p_pic->format.lighting;
++ format_changed = true;
+ }
-+
-+ p_pic->context = hw_mmal_gen_context(avfmt_to_mmal(frame->format), buf, NULL);
+ }
++#endif
+
++ if (format_changed && decoder_UpdateVideoFormat( p_dec ))
++ return -1;
+
-+ if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den )
++ const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC );
++ if( p_avcc )
+ {
-+ /* Fetch again the aspect ratio in case it changed */
-+ p_dec->fmt_out.video.i_sar_num
-+ = p_context->sample_aspect_ratio.num;
-+ p_dec->fmt_out.video.i_sar_den
-+ = p_context->sample_aspect_ratio.den;
-+
-+ if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den )
++ cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size );
++ if( p_sys->cc.b_reorder || p_sys->cc.i_data )
+ {
-+ p_dec->fmt_out.video.i_sar_num = 1;
-+ p_dec->fmt_out.video.i_sar_den = 1;
++ block_t *p_cc = block_Alloc( p_sys->cc.i_data );
++ if( p_cc )
++ {
++ memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
++ if( p_sys->cc.b_reorder )
++ p_cc->i_dts = p_cc->i_pts = p_pic->date;
++ else
++ p_cc->i_pts = p_cc->i_dts;
++ decoder_cc_desc_t desc;
++ desc.i_608_channels = p_sys->cc.i_608channels;
++ desc.i_708_channels = p_sys->cc.i_708channels;
++ desc.i_reorder_depth = 4;
++ decoder_QueueCc( p_dec, p_cc, &desc );
++ }
++ cc_Flush( &p_sys->cc );
+ }
+ }
-+
-+ p_pic->date = i_pts;
-+ /* Hack to force display of still pictures */
-+ p_pic->b_force = p_sys->b_first_frame;
-+ p_pic->i_nb_fields = 2 + frame->repeat_pict;
-+ p_pic->b_progressive = !frame->interlaced_frame;
-+ p_pic->b_top_field_first = frame->top_field_first;
-+
-+ if (DecodeSidedata(p_dec, frame, p_pic))
-+ i_pts = VLC_TS_INVALID;
-+
-+ av_frame_free(&frame);
-+
-+ p_sys->b_first_frame = false;
-+ decoder_QueueVideo(p_dec, p_pic);
-+
-+ return 1;
++ return 0;
+}
+
++/*****************************************************************************
++ * DecodeBlock: Called to decode one or more frames
++ *****************************************************************************/
+
-+
-+
-+
-+static int DecodeVideo( decoder_t *p_dec, block_t * p_block)
++static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error )
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ AVCodecContext *p_context = p_sys->p_context;
@@ -6115,8 +6544,18 @@
+
+ /* Boolean for END_OF_SEQUENCE */
+ bool eos_spotted = false;
++
++#if TRACE_ALL
++ msg_Dbg(p_dec, "<<< %s: (buf_size=%d)", __func__, pp_block == NULL || *pp_block == NULL ? 0 : (*pp_block)->i_buffer);
++#endif
++
++ block_t *p_block;
+ mtime_t current_time;
-+ int rv = VLCDEC_SUCCESS;
++ picture_t *p_pic = NULL;
++ AVFrame *frame = NULL;
++
++ // By default we are OK
++ *error = false;
+
+ if( !p_context->extradata_size && p_dec->fmt_in.i_extra )
+ {
@@ -6125,30 +6564,30 @@
+ OpenVideoCodec( p_dec );
+ }
+
++ p_block = pp_block ? *pp_block : NULL;
+ if(!p_block && !(p_sys->p_codec->capabilities & AV_CODEC_CAP_DELAY) )
-+ return VLCDEC_SUCCESS;
++ return NULL;
+
+ if( !avcodec_is_open( p_context ) )
+ {
+ if( p_block )
+ block_Release( p_block );
-+ return VLCDEC_ECRITICAL;
++ return NULL;
+ }
+
+ if( !check_block_validity( p_sys, p_block ) )
-+ return VLCDEC_SUCCESS;
++ return NULL;
+
+ current_time = mdate();
+ if( p_dec->b_frame_drop_allowed && check_block_being_late( p_sys, p_block, current_time) )
+ {
+ msg_Err( p_dec, "more than 5 seconds of late video -> "
+ "dropping frame (computer too slow ?)" );
-+ return VLCDEC_SUCCESS;
++ return NULL;
+ }
+
+
+ /* A good idea could be to decode all I pictures and see for the other */
-+ b_need_output_picture = true;
+
+ /* Defaults that if we aren't in prerolling, we want output picture
+ same for if we are flushing (p_block==NULL) */
@@ -6170,7 +6609,7 @@
+ if( p_block )
+ block_Release( p_block );
+ msg_Warn( p_dec, "More than 11 late frames, dropping frame" );
-+ return VLCDEC_SUCCESS;
++ return NULL;
+ }
+ }
+ if( !b_need_output_picture )
@@ -6191,74 +6630,318 @@
+ p_block = block_Realloc( p_block, 0,
+ p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE );
+ if( !p_block )
-+ return VLCDEC_ECRITICAL;
-+
++ return NULL;
+ p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
++ *pp_block = p_block;
+ memset( p_block->p_buffer + p_block->i_buffer, 0,
+ FF_INPUT_BUFFER_PADDING_SIZE );
+ }
+
-+ AVPacket pkt = {.data = NULL, .size = 0};
-+
-+ av_init_packet( &pkt );
-+ if( p_block && p_block->i_buffer > 0 )
++ while( !p_block || p_block->i_buffer > 0 || eos_spotted )
+ {
-+ pkt.data = p_block->p_buffer;
-+ pkt.size = p_block->i_buffer;
-+ pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE;
-+ pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE;
-+ }
++ int i_used;
++ AVPacket pkt;
+
-+ if( !p_sys->palette_sent )
-+ {
-+ uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
-+ if (pal) {
-+ memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE);
-+ p_sys->palette_sent = true;
++ post_mt( p_sys );
++
++ av_init_packet( &pkt );
++ if( p_block && p_block->i_buffer > 0 )
++ {
++ pkt.data = p_block->p_buffer;
++ pkt.size = p_block->i_buffer;
++ pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE;
++ pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE;
++ }
++ else
++ {
++ /* Return delayed frames if codec has CODEC_CAP_DELAY */
++ pkt.data = NULL;
++ pkt.size = 0;
+ }
-+ }
+
-+#if LIBAVCODEC_VERSION_CHECK( 57, 0, 0xFFFFFFFFU, 64, 101 )
-+ if( !b_need_output_picture )
-+ pkt.flags |= AV_PKT_FLAG_DISCARD;
++ if( !p_sys->palette_sent )
++ {
++ uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
++ if (pal) {
++ memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE);
++ p_sys->palette_sent = true;
++ }
++ }
++
++ /* Make sure we don't reuse the same timestamps twice */
++ if( p_block )
++ {
++ p_block->i_pts =
++ p_block->i_dts = VLC_TS_INVALID;
++ }
++
++ int ret = avcodec_send_packet(p_context, &pkt);
++ if( ret != 0 && ret != AVERROR(EAGAIN) )
++ {
++ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
++ {
++ msg_Err(p_dec, "avcodec_send_packet critical error");
++ *error = true;
++ }
++ av_packet_unref( &pkt );
++ break;
++ }
++ i_used = ret != AVERROR(EAGAIN) ? pkt.size : 0;
++ av_packet_unref( &pkt );
++
++ frame = av_frame_alloc();
++ if (unlikely(frame == NULL))
++ {
++ *error = true;
++ break;
++ }
++
++ ret = avcodec_receive_frame(p_context, frame);
++ if( ret != 0 && ret != AVERROR(EAGAIN) )
++ {
++ msg_Dbg(p_dec, "No receive");
++ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
++ {
++ msg_Err(p_dec, "avcodec_receive_frame critical error");
++ *error = true;
++ }
++ av_frame_free(&frame);
++ /* After draining, we need to reset decoder with a flush */
++ if( ret == AVERROR_EOF )
++ avcodec_flush_buffers( p_sys->p_context );
++ break;
++ }
++ bool not_received_frame = ret;
++
++ wait_mt( p_sys );
++
++ if( eos_spotted )
++ p_sys->b_first_frame = true;
++
++ if( p_block )
++ {
++ if( p_block->i_buffer <= 0 )
++ eos_spotted = false;
++
++ /* Consumed bytes */
++ p_block->p_buffer += i_used;
++ p_block->i_buffer -= i_used;
++ }
++
++ /* Nothing to display */
++ if( not_received_frame )
++ {
++// msg_Dbg(p_dec, "No rx: used=%d", i_used);
++ av_frame_free(&frame);
++ if( i_used == 0 ) break;
++ continue;
++ }
++
++ /* Compute the PTS */
++#ifdef FF_API_PKT_PTS
++ mtime_t i_pts = frame->pts;
++#else
++ mtime_t i_pts = frame->pkt_pts;
+#endif
++ if (i_pts == AV_NOPTS_VALUE )
++ i_pts = frame->pkt_dts;
+
-+ int ret = avcodec_send_packet(p_context, &pkt);
++ if( i_pts == AV_NOPTS_VALUE )
++ i_pts = date_Get( &p_sys->pts );
+
-+ if (ret == AVERROR(EAGAIN))
-+ {
-+ // Cannot send more data until output drained - so do drain
-+ while (rx_frame(p_dec, p_sys, p_context) == 1)
-+ /* Loop */;
++ /* Interpolate the next PTS */
++ if( i_pts > VLC_TS_INVALID )
++ date_Set( &p_sys->pts, i_pts );
+
-+ // And try again - should not fail the same way
-+ ret = avcodec_send_packet(p_context, &pkt);
-+ }
++ const mtime_t i_next_pts = interpolate_next_pts(p_dec, frame);
+
-+ // Now done with pkt & block
-+ av_packet_unref(&pkt);
-+ if (p_block != NULL)
-+ {
-+ block_Release( p_block );
-+ p_block = NULL;
-+ }
++ update_late_frame_count( p_dec, p_block, current_time, i_pts, i_next_pts);
+
-+ if (ret != 0)
-+ {
-+ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
++ if( !b_need_output_picture ||
++// ( !p_sys->p_va && !frame->linesize[0] ) ||
++ ( !frame->linesize[0] ) ||
++ ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) &&
++ !p_sys->b_show_corrupted ) )
++ {
++ av_frame_free(&frame);
++// msg_Dbg(p_dec, "Bad frame");
++ continue;
++ }
++
++ if( p_context->pix_fmt == AV_PIX_FMT_PAL8
++ && !p_dec->fmt_out.video.p_palette )
++ {
++ /* See AV_PIX_FMT_PAL8 comment in avc_GetVideoFormat(): update the
++ * fmt_out palette and change the fmt_out chroma to request a new
++ * vout */
++ assert( p_dec->fmt_out.video.i_chroma != VLC_CODEC_RGBP );
++
++ video_palette_t *p_palette;
++ p_palette = p_dec->fmt_out.video.p_palette
++ = malloc( sizeof(video_palette_t) );
++ if( !p_palette )
++ {
++ *error = true;
++ av_frame_free(&frame);
++ break;
++ }
++ static_assert( sizeof(p_palette->palette) == AVPALETTE_SIZE,
++ "Palette size mismatch between vlc and libavutil" );
++ assert( frame->data[1] != NULL );
++ memcpy( p_palette->palette, frame->data[1], AVPALETTE_SIZE );
++ p_palette->i_entries = AVPALETTE_COUNT;
++ p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBP;
++ if( decoder_UpdateVideoFormat( p_dec ) )
++ {
++ av_frame_free(&frame);
++ continue;
++ }
++ }
++
++#if 1
++ {
++ cma_buf_t * const cb = av_rpi_zc_buf_v(frame->buf[0]);
++
++ if (cb == NULL)
++ {
++ msg_Err(p_dec, "Frame has no attached CMA buffer");
++ goto fail;
++ }
++
++ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
++ p_context->pix_fmt) != 0)
++ {
++ msg_Err(p_dec, "Failed to update format");
++ goto fail;
++ }
++
++ if ((p_pic = decoder_NewPicture(p_dec)) == NULL)
++ {
++ msg_Err(p_dec, "Failed to allocate pic");
++ goto fail;
++ }
++
++ if (cma_buf_pic_attach(cma_buf_ref(cb), p_pic) != 0)
++ {
++ cma_buf_unref(cb); // Undo the in_flight
++ char dbuf0[5];
++ msg_Err(p_dec, "Failed to attach bufs to pic: fmt=%s", str_fourcc(dbuf0, p_pic->format.i_chroma));
++ goto fail;
++ }
++
++ // ****** Set planes etc.
++ set_pic_from_frame(p_pic, frame);
++ }
++#else
++ picture_t *p_pic = frame->opaque;
++ if( p_pic == NULL )
++ { /* When direct rendering is not used, get_format() and get_buffer()
++ * might not be called. The output video format must be set here
++ * then picture buffer can be allocated. */
++ if (p_sys->p_va == NULL
++ && lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
++ p_context->pix_fmt) == 0)
++ p_pic = decoder_NewPicture(p_dec);
++
++ if( !p_pic )
++ {
++ av_frame_free(&frame);
++ break;
++ }
++
++ /* Fill picture_t from AVFrame */
++ if( lavc_CopyPicture( p_dec, p_pic, frame ) != VLC_SUCCESS )
++ {
++ av_frame_free(&frame);
++ picture_Release( p_pic );
++ break;
++ }
++ }
++ else
++ {
++ /* Some codecs can return the same frame multiple times. By the
++ * time that the same frame is returned a second time, it will be
++ * too late to clone the underlying picture. So clone proactively.
++ * A single picture CANNOT be queued multiple times.
++ */
++ p_pic = picture_Clone( p_pic );
++ if( unlikely(p_pic == NULL) )
++ {
++ av_frame_free(&frame);
++ break;
++ }
++ }
++#endif
++
++ if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den )
++ {
++ /* Fetch again the aspect ratio in case it changed */
++ p_dec->fmt_out.video.i_sar_num
++ = p_context->sample_aspect_ratio.num;
++ p_dec->fmt_out.video.i_sar_den
++ = p_context->sample_aspect_ratio.den;
++
++ if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den )
++ {
++ p_dec->fmt_out.video.i_sar_num = 1;
++ p_dec->fmt_out.video.i_sar_den = 1;
++ }
++ }
++
++ p_pic->date = i_pts;
++ /* Hack to force display of still pictures */
++ p_pic->b_force = p_sys->b_first_frame;
++ p_pic->i_nb_fields = 2 + frame->repeat_pict;
++ p_pic->b_progressive = !frame->interlaced_frame;
++ p_pic->b_top_field_first = frame->top_field_first;
++
++ if (DecodeSidedata(p_dec, frame, p_pic))
++ i_pts = VLC_TS_INVALID;
++
++ av_frame_free(&frame);
++
++ /* Send decoded frame to vout */
++ if (i_pts > VLC_TS_INVALID)
+ {
-+ msg_Err(p_dec, "avcodec_send_packet critical error");
-+ rv = VLCDEC_ECRITICAL;
++ p_sys->b_first_frame = false;
++#if TRACE_ALL
++ msg_Dbg(p_dec, ">>> %s: Got pic", __func__);
++#endif
++ return p_pic;
+ }
++ else
++ picture_Release( p_pic );
+ }
+
-+ while (rx_frame(p_dec, p_sys, p_context) == 1)
-+ /* Loop */;
++ if( p_block )
++ block_Release( p_block );
++
++#if TRACE_ALL
++ msg_Dbg(p_dec, ">>> %s: NULL", __func__);
++#endif
++ return NULL;
+
-+ if (eos_spotted)
-+ p_sys->b_first_frame = true;
++fail:
++#if TRACE_ALL
++ msg_Dbg(p_dec, ">>> %s: FAIL", __func__);
++#endif
++ av_frame_free(&frame);
++ if (p_pic != NULL)
++ picture_Release(p_pic);
++ if (p_block != NULL)
++ block_Release(p_block);
++ *error = true;
++ return NULL;
++}
+
-+ return rv;
++static int DecodeVideo( decoder_t *p_dec, block_t *p_block )
++{
++ block_t **pp_block = p_block ? &p_block : NULL;
++ picture_t *p_pic;
++ bool error = false;
++ while( ( p_pic = DecodeBlock( p_dec, pp_block, &error ) ) != NULL )
++ decoder_QueueVideo( p_dec, p_pic );
++ return error ? VLCDEC_ECRITICAL : VLCDEC_SUCCESS;
+}
+
+/*****************************************************************************
@@ -6267,31 +6950,38 @@
+ * This function is called when the thread ends after a successful
+ * initialization.
+ *****************************************************************************/
-+static void MmalAvcodecCloseDecoder(vlc_object_t *obj)
++static void MmalAvcodecCloseDecoder( vlc_object_t *obj )
+{
+ decoder_t *p_dec = (decoder_t *)obj;
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ AVCodecContext *ctx = p_sys->p_context;
+// void *hwaccel_context;
+
++ msg_Dbg(obj, "<<< %s", __func__);
++
++ post_mt( p_sys );
++
++ cma_buf_pool_cancel(p_sys->cma_pool); // Abort any pending frame allocs
++
+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
+ if( avcodec_is_open( ctx ) )
+ avcodec_flush_buffers( ctx );
+
-+// cc_Flush( &p_sys->cc );
++ av_rpi_zc_uninit2(ctx);
++
++ wait_mt( p_sys );
+
-+ avcodec_close(ctx);
-+ av_rpi_zc_uninit(ctx);
++ cc_Flush( &p_sys->cc );
+
+// hwaccel_context = ctx->hwaccel_context;
+ avcodec_free_context( &ctx );
+
-+ if( p_sys->out_pool != NULL )
-+ mmal_pool_destroy(p_sys->out_pool);
-+
+// if( p_sys->p_va )
+// vlc_va_Delete( p_sys->p_va, &hwaccel_context );
+
++ cma_vcsm_exit(p_sys->vcsm_init_type);
++
++ vlc_sem_destroy( &p_sys->sem_mt );
+ free( p_sys );
+}
+
@@ -6316,396 +7006,66 @@
+ if( !p )
+ return;
+
-+ memcpy( &p[0], "SVQ3", 4 );
-+ memset( &p[4], 0, 8 );
-+ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size );
-+
-+ /* Now remove all atoms before the SMI one */
-+ if( p_sys->p_context->extradata_size > 0x5a &&
-+ strncmp( (char*)&p[0x56], "SMI ", 4 ) )
-+ {
-+ uint8_t *psz = &p[0x52];
-+
-+ while( psz < &p[p_sys->p_context->extradata_size - 8] )
-+ {
-+ uint_fast32_t atom_size = GetDWBE( psz );
-+ if( atom_size <= 1 )
-+ {
-+ /* FIXME handle 1 as long size */
-+ break;
-+ }
-+ if( !strncmp( (char*)&psz[4], "SMI ", 4 ) )
-+ {
-+ memmove( &p[0x52], psz,
-+ &p[p_sys->p_context->extradata_size] - psz );
-+ break;
-+ }
-+
-+ psz += atom_size;
-+ }
-+ }
-+ }
-+ else
-+ {
-+ p_sys->p_context->extradata_size = i_size;
-+ p_sys->p_context->extradata =
-+ av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );
-+ if( p_sys->p_context->extradata )
-+ {
-+ memcpy( p_sys->p_context->extradata,
-+ p_dec->fmt_in.p_extra, i_size );
-+ memset( p_sys->p_context->extradata + i_size,
-+ 0, FF_INPUT_BUFFER_PADDING_SIZE );
-+ }
-+ }
-+}
-+
-+
-+static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
-+ const enum PixelFormat *pi_fmt )
-+{
-+ decoder_t *p_dec = p_context->opaque;
-+ decoder_sys_t *p_sys = p_dec->p_sys;
-+ video_format_t fmt;
-+
-+ /* Enumerate available formats */
-+ enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt);
-+// bool can_hwaccel = false;
-+
-+ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++)
-+ {
-+ const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(pi_fmt[i]);
-+ if (dsc == NULL)
-+ continue;
-+ bool hwaccel = (dsc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0;
-+
-+ msg_Dbg( p_dec, "available %sware decoder output format %d (%s)",
-+ hwaccel ? "hard" : "soft", pi_fmt[i], dsc->name );
-+// if (hwaccel)
-+// can_hwaccel = true;
-+ }
-+
-+ /* If the format did not actually change (e.g. seeking), try to reuse the
-+ * existing output format, and if present, hardware acceleration back-end.
-+ * This avoids resetting the pipeline downstream. This also avoids
-+ * needlessly probing for hardware acceleration support. */
-+ if (p_sys->pix_fmt != AV_PIX_FMT_NONE
-+ && lavc_GetVideoFormat(p_dec, &fmt, p_context, p_sys->pix_fmt, swfmt) == 0
-+ && fmt.i_width == p_dec->fmt_out.video.i_width
-+ && fmt.i_height == p_dec->fmt_out.video.i_height
-+ && p_context->profile == p_sys->profile
-+ && p_context->level <= p_sys->level)
-+ {
-+ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++)
-+ if (pi_fmt[i] == p_sys->pix_fmt)
-+ {
-+ msg_Dbg(p_dec, "reusing decoder output format %d", pi_fmt[i]);
-+ return p_sys->pix_fmt;
-+ }
-+ }
-+
-+// if (p_sys->p_va != NULL)
-+// {
-+// msg_Err(p_dec, "existing hardware acceleration cannot be reused");
-+// vlc_va_Delete(p_sys->p_va, &p_context->hwaccel_context);
-+// p_sys->p_va = NULL;
-+// }
-+
-+ p_sys->profile = p_context->profile;
-+ p_sys->level = p_context->level;
-+
-+#if 1
-+ return swfmt;
-+#else
-+ if (!can_hwaccel)
-+ return swfmt;
-+
-+#if (LIBAVCODEC_VERSION_MICRO >= 100) \
-+ && (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 83, 101))
-+ if (p_context->active_thread_type)
-+ {
-+ msg_Warn(p_dec, "thread type %d: disabling hardware acceleration",
-+ p_context->active_thread_type);
-+ return swfmt;
-+ }
-+#endif
-+
-+ wait_mt(p_sys);
-+
-+ static const enum PixelFormat hwfmts[] =
-+ {
-+#ifdef _WIN32
-+#if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100)
-+ AV_PIX_FMT_D3D11VA_VLD,
-+#endif
-+ AV_PIX_FMT_DXVA2_VLD,
-+#endif
-+ AV_PIX_FMT_VAAPI_VLD,
-+#if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 4, 0))
-+ AV_PIX_FMT_VDPAU,
-+#endif
-+ AV_PIX_FMT_NONE,
-+ };
-+
-+ for( size_t i = 0; hwfmts[i] != AV_PIX_FMT_NONE; i++ )
-+ {
-+ enum PixelFormat hwfmt = AV_PIX_FMT_NONE;
-+ for( size_t j = 0; hwfmt == AV_PIX_FMT_NONE && pi_fmt[j] != AV_PIX_FMT_NONE; j++ )
-+ if( hwfmts[i] == pi_fmt[j] )
-+ hwfmt = hwfmts[i];
-+
-+ if( hwfmt == AV_PIX_FMT_NONE )
-+ continue;
-+
-+ p_dec->fmt_out.video.i_chroma = vlc_va_GetChroma(hwfmt, swfmt);
-+ if (p_dec->fmt_out.video.i_chroma == 0)
-+ continue; /* Unknown brand of hardware acceleration */
-+ if (p_context->width == 0 || p_context->height == 0)
-+ { /* should never happen */
-+ msg_Err(p_dec, "unspecified video dimensions");
-+ continue;
-+ }
-+ const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(hwfmt);
-+ msg_Dbg(p_dec, "trying format %s", dsc ? dsc->name : "unknown");
-+ if (lavc_UpdateVideoFormat(p_dec, p_context, hwfmt, swfmt))
-+ continue; /* Unsupported brand of hardware acceleration */
-+ post_mt(p_sys);
-+
-+ picture_t *test_pic = decoder_NewPicture(p_dec);
-+ assert(!test_pic || test_pic->format.i_chroma == p_dec->fmt_out.video.i_chroma);
-+ vlc_va_t *va = vlc_va_New(VLC_OBJECT(p_dec), p_context, hwfmt,
-+ &p_dec->fmt_in,
-+ test_pic ? test_pic->p_sys : NULL);
-+ if (test_pic)
-+ picture_Release(test_pic);
-+ if (va == NULL)
-+ {
-+ wait_mt(p_sys);
-+ continue; /* Unsupported codec profile or such */
-+ }
-+
-+ if (va->description != NULL)
-+ msg_Info(p_dec, "Using %s for hardware decoding", va->description);
-+
-+ p_sys->p_va = va;
-+ p_sys->pix_fmt = hwfmt;
-+ p_context->draw_horiz_band = NULL;
-+ return hwfmt;
-+ }
-+
-+ post_mt(p_sys);
-+ /* Fallback to default behaviour */
-+ p_sys->pix_fmt = swfmt;
-+ return swfmt;
-+#endif
-+}
-+
-+/*****************************************************************************
-+ * InitVideo: initialize the video decoder
-+ *****************************************************************************
-+ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet
-+ * opened (done after the first decoded frame).
-+ *****************************************************************************/
-+
-+/*****************************************************************************
-+ * ffmpeg_OpenCodec:
-+ *****************************************************************************/
-+
-+static int MmalAvcodecOpenDecoder( vlc_object_t *obj )
-+{
-+ decoder_t *p_dec = (decoder_t *)obj;
-+ const AVCodec *p_codec;
-+
-+ if (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC)
-+ return VLC_EGENERIC;
-+
-+ AVCodecContext *p_context = ffmpeg_AllocContext( p_dec, &p_codec );
-+ if( p_context == NULL )
-+ return VLC_EGENERIC;
-+
-+ int i_val;
-+
-+ /* Allocate the memory needed to store the decoder's structure */
-+ decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) );
-+ if( unlikely(p_sys == NULL) )
-+ {
-+ avcodec_free_context( &p_context );
-+ return VLC_ENOMEM;
-+ }
-+
-+ p_dec->p_sys = p_sys;
-+ p_sys->p_context = p_context;
-+ p_sys->p_codec = p_codec;
-+// p_sys->p_va = NULL;
-+
-+ /* ***** Fill p_context with init values ***** */
-+ p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ?
-+ p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec );
-+
-+ /* ***** Get configuration of ffmpeg plugin ***** */
-+ p_context->workaround_bugs =
-+ var_InheritInteger( p_dec, "avcodec-workaround-bugs" );
-+ p_context->err_recognition =
-+ var_InheritInteger( p_dec, "avcodec-error-resilience" );
-+
-+ if( var_CreateGetBool( p_dec, "grayscale" ) )
-+ p_context->flags |= AV_CODEC_FLAG_GRAY;
-+
-+ /* ***** Output always the frames ***** */
-+ p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
-+
-+ i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" );
-+ if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL;
-+ else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY;
-+ else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR;
-+ else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF;
-+ else p_context->skip_loop_filter = AVDISCARD_DEFAULT;
-+
-+ if( var_CreateGetBool( p_dec, "avcodec-fast" ) )
-+ p_context->flags2 |= AV_CODEC_FLAG2_FAST;
-+
-+ /* ***** libavcodec frame skipping ***** */
-+ p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" );
-+ p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" );
-+
-+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" );
-+ if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL;
-+ else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY;
-+ else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR;
-+ else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF;
-+ else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE;
-+ else p_sys->i_skip_frame = AVDISCARD_DEFAULT;
-+ p_context->skip_frame = p_sys->i_skip_frame;
-+
-+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" );
-+ if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL;
-+ else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY;
-+ else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR;
-+ else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF;
-+ else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE;
-+ else p_context->skip_idct = AVDISCARD_DEFAULT;
-+
-+ /* ***** libavcodec direct rendering ***** */
-+ p_sys->b_direct_rendering = false;
-+ atomic_init(&p_sys->b_dr_failure, false);
-+ if( var_CreateGetBool( p_dec, "avcodec-dr" ) &&
-+ (p_codec->capabilities & AV_CODEC_CAP_DR1) &&
-+ /* No idea why ... but this fixes flickering on some TSCC streams */
-+ p_sys->p_codec->id != AV_CODEC_ID_TSCC &&
-+ p_sys->p_codec->id != AV_CODEC_ID_CSCD &&
-+ p_sys->p_codec->id != AV_CODEC_ID_CINEPAK )
-+ {
-+ /* Some codecs set pix_fmt only after the 1st frame has been decoded,
-+ * so we need to do another check in ffmpeg_GetFrameBuf() */
-+ p_sys->b_direct_rendering = true;
-+ }
-+
-+ p_context->get_format = ffmpeg_GetFormat;
-+ /* Always use our get_buffer wrapper so we can calculate the
-+ * PTS correctly */
-+// p_context->get_buffer2 = lavc_GetFrame;
-+// p_context->opaque = p_dec;
-+
-+ int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" );
-+ if( i_thread_count <= 0 )
-+ i_thread_count = 6;
-+#if 0
-+ if( i_thread_count <= 0 )
-+ {
-+ i_thread_count = vlc_GetCPUCount();
-+ if( i_thread_count > 1 )
-+ i_thread_count++;
-+
-+ //FIXME: take in count the decoding time
-+#if VLC_WINSTORE_APP
-+ i_thread_count = __MIN( i_thread_count, 6 );
-+#else
-+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 );
-+#endif
-+ }
-+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 );
-+#endif
-+ msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count );
-+ p_context->thread_count = i_thread_count;
-+ p_context->thread_safe_callbacks = true;
-+
-+ p_context->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
-+
-+ if( p_context->thread_type & FF_THREAD_FRAME )
-+ p_dec->i_extra_picture_buffers = 2 * p_context->thread_count;
-+
-+ /* ***** misc init ***** */
-+ date_Init(&p_sys->pts, 1, 30001);
-+ date_Set(&p_sys->pts, VLC_TS_INVALID);
-+ p_sys->b_first_frame = true;
-+ p_sys->i_late_frames = 0;
-+ p_sys->b_from_preroll = false;
-+
-+ /* Set output properties */
-+ if( GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS )
-+ {
-+ /* we are doomed. but not really, because most codecs set their pix_fmt later on */
-+ p_dec->fmt_out.i_codec = VLC_CODEC_I420;
-+ }
-+ p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma;
-+
-+ p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation;
-+
-+ if( p_dec->fmt_in.video.p_palette ) {
-+ p_sys->palette_sent = false;
-+ p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) );
-+ if( p_dec->fmt_out.video.p_palette )
-+ *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette;
-+ } else
-+ p_sys->palette_sent = true;
++ memcpy( &p[0], "SVQ3", 4 );
++ memset( &p[4], 0, 8 );
++ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size );
+
-+ /* ***** init this codec with special data ***** */
-+ ffmpeg_InitCodec( p_dec );
++ /* Now remove all atoms before the SMI one */
++ if( p_sys->p_context->extradata_size > 0x5a &&
++ strncmp( (char*)&p[0x56], "SMI ", 4 ) )
++ {
++ uint8_t *psz = &p[0x52];
+
-+ /* ***** Open the codec ***** */
-+ if( OpenVideoCodec( p_dec ) < 0 )
-+ {
-+ free( p_sys );
-+ avcodec_free_context( &p_context );
-+ return VLC_EGENERIC;
-+ }
++ while( psz < &p[p_sys->p_context->extradata_size - 8] )
++ {
++ uint_fast32_t atom_size = GetDWBE( psz );
++ if( atom_size <= 1 )
++ {
++ /* FIXME handle 1 as long size */
++ break;
++ }
++ if( !strncmp( (char*)&psz[4], "SMI ", 4 ) )
++ {
++ memmove( &p[0x52], psz,
++ &p[p_sys->p_context->extradata_size] - psz );
++ break;
++ }
+
-+ if ((p_sys->out_pool = mmal_pool_create(5, 0)) == NULL)
++ psz += atom_size;
++ }
++ }
++ }
++ else
+ {
-+ msg_Err(p_dec, "Failed to create mmal buffer pool");
-+ goto fail;
++ p_sys->p_context->extradata_size = i_size;
++ p_sys->p_context->extradata =
++ av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );
++ if( p_sys->p_context->extradata )
++ {
++ memcpy( p_sys->p_context->extradata,
++ p_dec->fmt_in.p_extra, i_size );
++ memset( p_sys->p_context->extradata + i_size,
++ 0, FF_INPUT_BUFFER_PADDING_SIZE );
++ }
+ }
-+
-+ p_dec->pf_decode = DecodeVideo;
-+ p_dec->pf_flush = Flush;
-+
-+ /* XXX: Writing input format makes little sense. */
-+ if( p_context->profile != FF_PROFILE_UNKNOWN )
-+ p_dec->fmt_in.i_profile = p_context->profile;
-+ if( p_context->level != FF_LEVEL_UNKNOWN )
-+ p_dec->fmt_in.i_level = p_context->level;
-+ return VLC_SUCCESS;
-+
-+fail:
-+ MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec));
-+ return VLC_EGENERIC;
+}
+
+
-+
+vlc_module_begin()
+ set_category( CAT_INPUT )
+ set_subcategory( SUBCAT_INPUT_VCODEC )
+ set_shortname(N_("MMAL avcodec"))
+ set_description(N_("MMAL buffered avcodec "))
-+ set_capability("video decoder", 800)
++ set_capability("video decoder", 80)
+ add_shortcut("mmal_avcodec")
++ add_integer(MMAL_AVCODEC_BUFFERS, -1, MMAL_AVCODEC_BUFFERS_TEXT,
++ MMAL_AVCODEC_BUFFERS_LONGTEXT, true)
+ set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder)
+vlc_module_end()
+
--- /dev/null
+++ b/modules/hw/mmal/mmal_cma.c
-@@ -0,0 +1,377 @@
+@@ -0,0 +1,668 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
@@ -6722,13 +7082,24 @@
+#include <vlc_picture.h>
+
+#include "mmal_cma.h"
++#include "mmal_picture.h"
+
+#include <assert.h>
+
++#define TRACE_ALL 0
++
++//-----------------------------------------------------------------------------
++//
++// Generic pool functions
++// Knows nothing about pool entries
+
+typedef void * cma_pool_alloc_fn(void * v, size_t size);
+typedef void cma_pool_free_fn(void * v, void * el, size_t size);
+
++#if TRACE_ALL
++static atomic_int pool_seq;
++#endif
++
+// Pool structure
+// Ref count is held by pool owner and pool els that have been got
+// Els in the pool do not count towards its ref count
@@ -6740,95 +7111,145 @@
+ unsigned int n_in;
+ unsigned int n_out;
+ unsigned int pool_size;
++ int flight_size;
+ size_t el_size;
+ void ** pool;
+
++ bool cancel;
++ int in_flight;
++ vlc_cond_t flight_cond;
++
+ void * alloc_v;
+ cma_pool_alloc_fn * el_alloc_fn;
+ cma_pool_free_fn * el_free_fn;
++ cma_pool_on_delete_fn * on_delete_fn;
++
++ const char * name;
++#if TRACE_ALL
++ int seq;
++#endif
+};
+
-+typedef struct cma_buf_s {
-+ size_t size;
-+ unsigned int vcsm_h; // VCSM handle from initial alloc
-+ unsigned int vc_h; // VC handle for ZC mmal buffers
-+ int fd; // dmabuf handle for GL
-+ void * mmap; // ARM mapped address
-+ picture_context_t *ctx2;
-+} cma_buf_t;
++static inline unsigned int inc_mod(const unsigned int n, const unsigned int m)
++{
++ return n + 1 >= m ? 0 : n + 1;
++}
+
-+static int free_pool(const cma_pool_fixed_t * const p, void ** pool, unsigned int n, size_t el_size)
++static void free_pool(const cma_pool_fixed_t * const p, void ** const pool,
++ const unsigned int pool_size, const size_t el_size)
+{
-+ int i = 0;
-+ assert(pool != NULL);
++ if (pool == NULL)
++ return;
+
-+ while (pool[n] != NULL)
-+ {
-+ p->el_free_fn(p->alloc_v, pool[n], el_size);
-+ pool[n] = NULL;
-+ n = n + 1 < p->pool_size ? n + 1 : 0;
-+ ++i;
-+ }
++ for (unsigned int n = 0; n != pool_size; ++n)
++ if (pool[n] != NULL)
++ p->el_free_fn(p->alloc_v, pool[n], el_size);
+ free(pool);
-+ return i;
+}
+
+// Just kill this - no checks
+static void cma_pool_fixed_delete(cma_pool_fixed_t * const p)
+{
-+ if (p->pool != NULL)
-+ free_pool(p, p->pool, p->n_in, p->el_size);
++ cma_pool_on_delete_fn *const on_delete_fn = p->on_delete_fn;
++ void *const v = p->alloc_v;
++
++ free_pool(p, p->pool, p->pool_size, p->el_size);
++
++ if (p->name != NULL)
++ free((void *)p->name); // Discard const
+
++ vlc_cond_destroy(&p->flight_cond);
+ vlc_mutex_destroy(&p->lock);
+ free(p);
++
++ // Inform our container that we are dead (if it cares)
++ if (on_delete_fn)
++ on_delete_fn(v);
+}
+
-+void cma_pool_fixed_unref(cma_pool_fixed_t * const p)
++static void cma_pool_fixed_unref(cma_pool_fixed_t * const p)
+{
+ if (atomic_fetch_sub(&p->ref_count, 1) <= 1)
+ cma_pool_fixed_delete(p);
+}
+
-+void cma_pool_fixed_ref(cma_pool_fixed_t * const p)
++static void cma_pool_fixed_ref(cma_pool_fixed_t * const p)
+{
+ atomic_fetch_add(&p->ref_count, 1);
+}
+
-+void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size)
++static void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p)
++{
++ vlc_mutex_lock(&p->lock);
++ ++p->in_flight;
++ vlc_mutex_unlock(&p->lock);
++}
++
++static void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p)
++{
++ vlc_mutex_lock(&p->lock);
++ if (--p->in_flight == 0)
++ vlc_cond_signal(&p->flight_cond);
++ vlc_mutex_unlock(&p->lock);
++}
++
++static void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool inc_flight, const bool no_pool)
+{
+ void * v = NULL;
-+ void ** deadpool = NULL;
-+ size_t dead_size = 0;
-+ unsigned int dead_n = 0;
+
+ vlc_mutex_lock(&p->lock);
+
-+ if (req_el_size != p->el_size)
++ for (;;)
+ {
-+ deadpool = p->pool;
-+ dead_n = p->n_in;
-+ dead_size = p->el_size;
++ if (req_el_size != p->el_size)
++ {
++ void ** const deadpool = p->pool;
++ const size_t dead_size = p->el_size;
++ const unsigned int dead_n = p->pool_size;
+
-+ p->pool = NULL;
-+ p->n_in = 0;
-+ p->n_out = 0;
-+ p->el_size = req_el_size;
-+ }
-+ else if (p->pool != NULL)
-+ {
-+ v = p->pool[p->n_in];
-+ if (v != NULL)
++ p->pool = NULL;
++ p->n_in = 0;
++ p->n_out = 0;
++ p->el_size = req_el_size;
++
++ if (deadpool != NULL)
++ {
++ vlc_mutex_unlock(&p->lock);
++ // Do the free old op outside the mutex in case the free is slow
++ free_pool(p, deadpool, dead_n, dead_size);
++ vlc_mutex_lock(&p->lock);
++ continue;
++ }
++ }
++
++ // Late abort if flush or cancel so we can still kill the pool
++ if (req_el_size == 0 || p->cancel)
++ {
++ vlc_mutex_unlock(&p->lock);
++ return NULL;
++ }
++
++ if (p->pool != NULL && !no_pool)
+ {
-+ p->pool[p->n_in] = NULL;
-+ p->n_in = p->n_in + 1 < p->pool_size ? p->n_in + 1 : 0;
++ v = p->pool[p->n_in];
++ if (v != NULL)
++ {
++ p->pool[p->n_in] = NULL;
++ p->n_in = inc_mod(p->n_in, p->pool_size);
++ break;
++ }
+ }
++
++ if (p->in_flight <= 0)
++ break;
++
++ vlc_cond_wait(&p->flight_cond, &p->lock);
+ }
+
-+ vlc_mutex_unlock(&p->lock);
++ if (inc_flight)
++ ++p->in_flight;
+
-+ // Do the free old op outside the mutex in case the free is slow
-+ if (deadpool != NULL)
-+ free_pool(p, deadpool, dead_n, dead_size);
++ vlc_mutex_unlock(&p->lock);
+
+ if (v == NULL && req_el_size != 0)
+ v = p->el_alloc_fn(p->alloc_v, req_el_size);
@@ -6836,11 +7257,14 @@
+ // Tag ref
+ if (v != NULL)
+ cma_pool_fixed_ref(p);
++ // Remove flight if we set it and error
++ else if (inc_flight)
++ cma_pool_fixed_dec_in_flight(p);
+
+ return v;
+}
+
-+void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size)
++static void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight)
+{
+ vlc_mutex_lock(&p->lock);
+
@@ -6850,29 +7274,123 @@
+ p->pool = calloc(p->pool_size, sizeof(void*));
+
+ p->pool[p->n_out] = v;
-+ p->n_out = p->n_out + 1 < p->pool_size ? p->n_out + 1 : 0;
++ p->n_out = inc_mod(p->n_out, p->pool_size);
+ v = NULL;
+ }
+
++ if (was_in_flight)
++ --p->in_flight;
++
+ vlc_mutex_unlock(&p->lock);
+
++ vlc_cond_signal(&p->flight_cond);
++
+ if (v != NULL)
+ p->el_free_fn(p->alloc_v, v, el_size);
+
+ cma_pool_fixed_unref(p);
+}
+
++static int cma_pool_fixed_resize(cma_pool_fixed_t * const p,
++ const unsigned int new_pool_size, const int new_flight_size)
++{
++ void ** dead_pool = NULL;
++ size_t dead_size = 0;
++ unsigned int dead_n = 0;
++
++ // This makes this non-reentrant but saves us a lot of time in the normal
++ // "nothing happens" case
++ if (p->pool_size == new_pool_size && p->flight_size == new_flight_size)
++ return 0;
++
++ vlc_mutex_lock(&p->lock);
++
++ if (p->pool != NULL && new_pool_size != p->pool_size)
++ {
++ void ** const new_pool = calloc(new_pool_size, sizeof(void*));
++ unsigned int d, s;
++ dead_pool = p->pool;
++ dead_size = p->el_size;
++ dead_n = p->pool_size;
++
++ if (new_pool == NULL)
++ {
++ vlc_mutex_unlock(&p->lock);
++ return -1;
++ }
++
++ for (d = 0, s = p->n_in; d != new_pool_size && (new_pool[d] = dead_pool[s]) != NULL; ++d, s = inc_mod(s, dead_n))
++ dead_pool[s] = NULL;
++
++ p->n_out = 0;
++ p->n_in = (d != new_pool_size) ? d : 0;
++ p->pool = new_pool;
++ }
++
++ p->pool_size = new_pool_size;
++ if (new_flight_size > p->flight_size)
++ vlc_cond_broadcast(&p->flight_cond); // Lock still active so nothing happens till we release it
++ p->in_flight += p->flight_size - new_flight_size;
++ p->flight_size = new_flight_size;
++
++ vlc_mutex_unlock(&p->lock);
++
++ free_pool(p, dead_pool, dead_n, dead_size);
++ return 0;
++}
++
++static int cma_pool_fixed_fill(cma_pool_fixed_t * const p, const size_t el_size)
++{
++ for (;;)
++ {
++ vlc_mutex_lock(&p->lock);
++ bool done = el_size == p->el_size && p->pool != NULL && p->pool[p->n_out] != NULL;
++ vlc_mutex_unlock(&p->lock);
++ if (done)
++ break;
++ void * buf = cma_pool_fixed_get(p, el_size, false, true);
++ if (buf == NULL)
++ return -ENOMEM;
++ cma_pool_fixed_put(p, buf, el_size, false);
++ }
++ return 0;
++}
++
++static void cma_pool_fixed_cancel(cma_pool_fixed_t * const p)
++{
++ vlc_mutex_lock(&p->lock);
++ p->cancel = true;
++ vlc_cond_broadcast(&p->flight_cond);
++ vlc_mutex_unlock(&p->lock);
++}
++
++static void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p)
++{
++ vlc_mutex_lock(&p->lock);
++ p->cancel = false;
++ vlc_mutex_unlock(&p->lock);
++}
++
++
+// Purge pool & unref
-+void cma_pool_fixed_kill(cma_pool_fixed_t * const p)
++static void cma_pool_fixed_kill(cma_pool_fixed_t * const p)
+{
++ if (p == NULL)
++ return;
++
+ // This flush is not strictly needed but it reclaims what memory we can reclaim asap
-+ cma_pool_fixed_get(p, 0);
++ cma_pool_fixed_get(p, 0, false, false);
+ cma_pool_fixed_unref(p);
+}
+
-+cma_pool_fixed_t*
-+cma_pool_fixed_new(const unsigned int pool_size, void * const alloc_v,
-+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn)
++// Create a new pool
++static cma_pool_fixed_t*
++cma_pool_fixed_new(const unsigned int pool_size,
++ const int flight_size,
++ void * const alloc_v,
++ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn,
++ cma_pool_on_delete_fn * const on_delete_fn,
++ const char * const name)
+{
+ cma_pool_fixed_t* const p = calloc(1, sizeof(cma_pool_fixed_t));
+ if (p == NULL)
@@ -6880,24 +7398,71 @@
+
+ atomic_store(&p->ref_count, 1);
+ vlc_mutex_init(&p->lock);
++ vlc_cond_init(&p->flight_cond);
+
+ p->pool_size = pool_size;
++ p->flight_size = flight_size;
++ p->in_flight = -flight_size;
+
+ p->alloc_v = alloc_v;
+ p->el_alloc_fn = alloc_fn;
+ p->el_free_fn = free_fn;
++ p->on_delete_fn = on_delete_fn;
++ p->name = name == NULL ? NULL : strdup(name);
++#if TRACE_ALL
++ p->seq = atomic_fetch_add(&pool_seq, 1);
++#endif
+
+ return p;
+}
+
++// ---------------------------------------------------------------------------
++//
++// CMA buffer functions - uses cma_pool_fixed for pooling
++
++struct cma_buf_pool_s {
++ cma_pool_fixed_t * pool;
++ vcsm_init_type_t init_type;
++
++ bool all_in_flight;
++#if TRACE_ALL
++ size_t alloc_n;
++ size_t alloc_size;
++#endif
++};
++
++typedef struct cma_buf_s {
++ atomic_int ref_count;
++ cma_buf_pool_t * cbp;
++ bool in_flight;
++ size_t size;
++ unsigned int vcsm_h; // VCSM handle from initial alloc
++ unsigned int vc_h; // VC handle for ZC mmal buffers
++ unsigned int vc_addr; // VC addr - unused by us but wanted by FFmpeg
++ int fd; // dmabuf handle for GL
++ void * mmap; // ARM mapped address
++ picture_context_t *ctx2;
++} cma_buf_t;
+
+static void cma_pool_delete(cma_buf_t * const cb)
+{
++ assert(atomic_load(&cb->ref_count) == 0);
++#if TRACE_ALL
++ cb->cbp->alloc_size -= cb->size;
++ --cb->cbp->alloc_n;
++ fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cb->cbp->pool->seq, cb->cbp->pool->name, cb->cbp->alloc_n, cb->cbp->alloc_size);
++#endif
++
+ if (cb->ctx2 != NULL)
+ cb->ctx2->destroy(cb->ctx2);
+
+ if (cb->mmap != MAP_FAILED)
-+ munmap(cb->mmap, cb->size);
++ {
++ if (cb->cbp->init_type == VCSM_INIT_CMA)
++ munmap(cb->mmap, cb->size);
++ else
++ vcsm_unlock_hdl(cb->vcsm_h);
++ }
+ if (cb->fd != -1)
+ close(cb->fd);
+ if (cb->vcsm_h != 0)
@@ -6915,13 +7480,16 @@
+
+static void * cma_pool_alloc_cb(void * v, size_t size)
+{
-+ VLC_UNUSED(v);
++ cma_buf_pool_t * const cbp = v;
+
+ cma_buf_t * const cb = malloc(sizeof(cma_buf_t));
+ if (cb == NULL)
+ return NULL;
+
+ *cb = (cma_buf_t){
++ .ref_count = ATOMIC_VAR_INIT(0),
++ .cbp = cbp,
++ .in_flight = 0,
+ .size = size,
+ .vcsm_h = 0,
+ .vc_h = 0,
@@ -6929,18 +7497,57 @@
+ .mmap = MAP_FAILED,
+ .ctx2 = NULL
+ };
++#if TRACE_ALL
++ cb->cbp->alloc_size += cb->size;
++ ++cb->cbp->alloc_n;
++ fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cbp->pool->seq, cbp->pool->name, cbp->alloc_n, cbp->alloc_size);
++#endif
+
-+ if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST, (char*)"VLC frame")) == 0)
++ // 0x80 is magic value to force full ARM-side mapping - otherwise
++ // cache requests can cause kernel crashes
++ if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST | 0x80, "VLC frame")) == 0)
++ {
++#if TRACE_ALL
++ fprintf(stderr, "vcsm_malloc_cache fail\n");
++#endif
+ goto fail;
++ }
+
+ if ((cb->vc_h = vcsm_vc_hdl_from_hdl(cb->vcsm_h)) == 0)
++ {
++#if TRACE_ALL
++ fprintf(stderr, "vcsm_vc_hdl_from_hdl fail\n");
++#endif
+ goto fail;
++ }
+
-+ if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1)
-+ goto fail;
++ if (cbp->init_type == VCSM_INIT_CMA)
++ {
++ if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1)
++ {
++#if TRACE_ALL
++ fprintf(stderr, "vcsm_export_dmabuf fail\n");
++#endif
++ goto fail;
++ }
+
-+ if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED)
-+ goto fail;
++ if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED)
++ goto fail;
++ }
++ else
++ {
++ void * arm_addr;
++ if ((arm_addr = vcsm_lock(cb->vcsm_h)) == NULL)
++ {
++#if TRACE_ALL
++ fprintf(stderr, "vcsm_lock fail\n");
++#endif
++ goto fail;
++ }
++ cb->mmap = arm_addr;
++ }
++
++ cb->vc_addr = vcsm_vc_addr_from_hdl(cb->vcsm_h);
+
+ return cb;
+
@@ -6949,188 +7556,258 @@
+ return NULL;
+}
+
-+void cma_buf_pool_delete(cma_pool_fixed_t * const p)
++// Pool has died - safe now to exit vcsm
++static void cma_buf_pool_on_delete_cb(void * v)
+{
-+ assert(p != NULL);
++ cma_buf_pool_t * const cbp = v;
+
-+ cma_pool_fixed_kill(p);
++ cma_vcsm_exit(cbp->init_type);
++ free(cbp);
+}
+
-+cma_pool_fixed_t * cma_buf_pool_new(void)
++void cma_buf_pool_cancel(cma_buf_pool_t * const cbp)
+{
-+ return cma_pool_fixed_new(5, NULL, cma_pool_alloc_cb, cma_pool_free_cb);
++ if (cbp == NULL || cbp->pool == NULL)
++ return;
++
++ cma_pool_fixed_cancel(cbp->pool);
+}
+
++void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp)
++{
++ if (cbp == NULL || cbp->pool == NULL)
++ return;
+
-+typedef struct cma_pic_context_s {
-+ picture_context_t cmn;
++ cma_pool_fixed_uncancel(cbp->pool);
++}
+
-+ atomic_int ref_count;
-+ cma_pool_fixed_t * p;
-+ cma_buf_t * cb;
-+} cma_pic_context_t;
++// User finished with pool
++void cma_buf_pool_delete(cma_buf_pool_t * const cbp)
++{
++ if (cbp == NULL)
++ return;
++
++ if (cbp->pool != NULL)
++ {
++ // We will call cma_buf_pool_on_delete_cb when the pool finally dies
++ // (might be now) which will free up our env.
++ cma_pool_fixed_kill(cbp->pool);
++ }
++ else
++ {
++ // Had no pool for some reason (error) but must still finish cleanup
++ cma_buf_pool_on_delete_cb(cbp);
++ }
++}
+
++int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size)
++{
++ return cma_pool_fixed_fill(cbp->pool, el_size);
++}
+
-+static void cma_buf_pic_ctx_ref(cma_pic_context_t * const ctx)
++int cma_buf_pool_resize(cma_buf_pool_t * const cbp,
++ const unsigned int new_pool_size, const int new_flight_size)
+{
-+ atomic_fetch_add(&ctx->ref_count, 1);
++ return cma_pool_fixed_resize(cbp->pool, new_pool_size, new_flight_size);
+}
+
-+static void cma_buf_pic_ctx_unref(cma_pic_context_t * const ctx)
++cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size, const bool all_in_flight, const char * const name)
+{
-+ if (atomic_fetch_sub(&ctx->ref_count, 1) > 0)
-+ return;
++ vcsm_init_type_t const init_type = cma_vcsm_init();
++ if (init_type == VCSM_INIT_NONE)
++ return NULL;
++
++ cma_buf_pool_t * const cbp = calloc(1, sizeof(cma_buf_pool_t));
++ if (cbp == NULL)
++ return NULL;
+
-+ if (ctx->cb != NULL)
-+ cma_pool_fixed_put(ctx->p, ctx->cb, ctx->cb->size);
++ cbp->init_type = init_type;
++ cbp->all_in_flight = all_in_flight;
+
-+ free(ctx);
-+}
++ if ((cbp->pool = cma_pool_fixed_new(pool_size, flight_size, cbp, cma_pool_alloc_cb, cma_pool_free_cb, cma_buf_pool_on_delete_cb, name)) == NULL)
++ goto fail;
++ return cbp;
+
-+static picture_context_t * cma_buf_pic_ctx_copy(picture_context_t * pic_ctx)
-+{
-+ cma_buf_pic_ctx_ref((cma_pic_context_t *)pic_ctx);
-+ return pic_ctx;
++fail:
++ cma_buf_pool_delete(cbp);
++ return NULL;
+}
+
-+static void cma_buf_pic_ctx_destroy(picture_context_t * pic_ctx)
++
++void cma_buf_in_flight(cma_buf_t * const cb)
+{
-+ cma_buf_pic_ctx_unref((cma_pic_context_t *)pic_ctx);
++ if (!cb->cbp->all_in_flight)
++ {
++ assert(!cb->in_flight);
++ cb->in_flight = true;
++ cma_pool_fixed_inc_in_flight(cb->cbp->pool);
++ }
+}
+
-+int cma_buf_pic_attach(cma_pool_fixed_t * const p, picture_t * const pic, const size_t size)
++void cma_buf_end_flight(cma_buf_t * const cb)
+{
-+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma))
-+ return VLC_EGENERIC;
-+ if (pic->context != NULL)
-+ return VLC_EBADVAR;
-+
-+ cma_buf_t * const cb = cma_pool_fixed_get(p, size);
-+ if (cb == NULL)
-+ return VLC_ENOMEM;
-+
-+ cma_pic_context_t * const ctx = malloc(sizeof(cma_pic_context_t));
-+ if (ctx == NULL)
-+ goto fail;
++ if (cb != NULL && !cb->cbp->all_in_flight && cb->in_flight)
++ {
++ cb->in_flight = false;
++ cma_pool_fixed_dec_in_flight(cb->cbp->pool);
++ }
++}
+
-+ *ctx = (cma_pic_context_t){
-+ .cmn = {
-+ .destroy = cma_buf_pic_ctx_destroy,
-+ .copy = cma_buf_pic_ctx_copy
-+ },
-+ .ref_count = 0,
-+ .p = p,
-+ .cb = cb
-+ };
+
-+ pic->context = &ctx->cmn;
-+ return VLC_SUCCESS;
++// Return vcsm handle
++unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb)
++{
++ return cb->vcsm_h;
++}
+
-+fail:
-+ cma_pool_fixed_put(p, cb, size);
-+ return VLC_EGENERIC;
++size_t cma_buf_size(const cma_buf_t * const cb)
++{
++ return cb->size;
+}
+
-+int cma_buf_pic_add_context2(picture_t *const pic, picture_context_t * const ctx2)
++int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2)
+{
-+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context;
-+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL || ctx->cb == NULL || ctx->cb->ctx2 != NULL)
++ if (cb->ctx2 != NULL)
+ return VLC_EGENERIC;
+
-+ ctx->cb->ctx2 = ctx2;
++ cb->ctx2 = ctx2;
+ return VLC_SUCCESS;
+}
+
-+unsigned int cma_buf_pic_vc_handle(const picture_t * const pic)
++unsigned int cma_buf_vc_handle(const cma_buf_t *const cb)
+{
-+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context;
-+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? 0 : ctx->cb == NULL ? 0 : ctx->cb->vc_h;
++ return cb->vc_h;
+}
+
-+int cma_buf_pic_fd(const picture_t * const pic)
++int cma_buf_fd(const cma_buf_t *const cb)
+{
-+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context;
-+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? -1 : ctx->cb == NULL ? -1 : ctx->cb->fd;
++ return cb->fd;
+}
+
-+void * cma_buf_pic_addr(const picture_t * const pic)
++void * cma_buf_addr(const cma_buf_t *const cb)
+{
-+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context;
-+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? NULL : ctx->cb == NULL ? NULL : ctx->cb->mmap;
++ return cb->mmap;
+}
+
-+picture_context_t * cma_buf_pic_context2(const picture_t * const pic)
++unsigned int cma_buf_vc_addr(const cma_buf_t *const cb)
+{
-+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context;
-+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? NULL : ctx->cb == NULL ? NULL : ctx->cb->ctx2;
++ return cb->vc_addr;
+}
+
-+cma_pic_context_t * cma_buf_pic_context_ref(const picture_t * const pic)
++
++picture_context_t * cma_buf_context2(const cma_buf_t *const cb)
+{
-+ cma_pic_context_t *const ctx = (cma_pic_context_t *)pic->context;
++ return cb->ctx2;
++}
+
-+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL || ctx->cb == NULL)
-+ return NULL;
+
-+ cma_buf_pic_ctx_ref(ctx);
-+ return ctx;
++void cma_buf_unref(cma_buf_t * const cb)
++{
++ if (cb == NULL)
++ return;
++ if (atomic_fetch_sub(&cb->ref_count, 1) <= 1)
++ {
++ const bool was_in_flight = cb->in_flight;
++ cb->in_flight = false;
++ cma_pool_fixed_put(cb->cbp->pool, cb, cb->size, was_in_flight);
++ }
+}
+
-+void cma_buf_pic_context_unref(cma_pic_context_t * const ctx)
++cma_buf_t * cma_buf_ref(cma_buf_t * const cb)
+{
-+ if (ctx != NULL)
-+ cma_buf_pic_ctx_unref(ctx);
++ if (cb == NULL)
++ return NULL;
++ atomic_fetch_add(&cb->ref_count, 1);
++ return cb;
+}
+
++cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const cbp, const size_t size)
++{
++ cma_buf_t *const cb = cma_pool_fixed_get(cbp->pool, size, cbp->all_in_flight, false);
++
++ if (cb == NULL)
++ return NULL;
++
++ cb->in_flight = cbp->all_in_flight;
++ // When 1st allocated or retrieved from the pool the block will have a
++ // ref count of 0 so ref here
++ return cma_buf_ref(cb);
++}
+
--- /dev/null
+++ b/modules/hw/mmal/mmal_cma.h
-@@ -0,0 +1,45 @@
+@@ -0,0 +1,71 @@
++#ifndef VLC_MMAL_MMAL_CMA_H_
++#define VLC_MMAL_MMAL_CMA_H_
++
+
+struct cma_pool_fixed_s;
+typedef struct cma_pool_fixed_s cma_pool_fixed_t;
+
+typedef void * cma_pool_alloc_fn(void * v, size_t size);
+typedef void cma_pool_free_fn(void * v, void * el, size_t size);
++typedef void cma_pool_on_delete_fn(void * v);
+
++#if 0
+void cma_pool_fixed_unref(cma_pool_fixed_t * const p);
+void cma_pool_fixed_ref(cma_pool_fixed_t * const p);
-+void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size);
-+void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size);
++void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool in_flight);
++void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight);
++void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p);
++void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p);
++void cma_pool_fixed_cancel(cma_pool_fixed_t * const p);
++void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p);
+void cma_pool_fixed_kill(cma_pool_fixed_t * const p);
-+cma_pool_fixed_t* cma_pool_fixed_new(const unsigned int pool_size, void * const alloc_v,
-+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn);
-+
-+void cma_buf_pool_delete(cma_pool_fixed_t * const p);
-+cma_pool_fixed_t * cma_buf_pool_new(void);
-+
-+int cma_buf_pic_attach(cma_pool_fixed_t * const p, picture_t * const pic, const size_t size);
-+int cma_buf_pic_add_context2(picture_t *const pic, picture_context_t * const ctx2);
-+unsigned int cma_buf_pic_vc_handle(const picture_t * const pic);
-+int cma_buf_pic_fd(const picture_t * const pic);
-+void * cma_buf_pic_addr(const picture_t * const pic);
-+picture_context_t * cma_buf_pic_context2(const picture_t * const pic);
-+struct cma_pic_context_s;
-+struct cma_pic_context_s * cma_buf_pic_context_ref(const picture_t * const pic);
-+void cma_buf_pic_context_unref(struct cma_pic_context_s * const ctx);
-+
-+#include <vlc_fourcc.h>
-+
-+static inline bool is_cma_buf_pic_chroma(const uint32_t chroma)
-+{
-+ return chroma == VLC_CODEC_MMAL_ZC_RGB32 || chroma == VLC_CODEC_MMAL_ZC_SAND8 || chroma == VLC_CODEC_MMAL_ZC_I420;
-+}
-+
-+static inline void cma_buf_pool_deletez(cma_pool_fixed_t ** const pp)
-+{
-+ cma_pool_fixed_t * const p = *pp;
++int cma_pool_fixed_resize(cma_pool_fixed_t * const p,
++ const unsigned int new_pool_size, const int new_flight_size);
++cma_pool_fixed_t * cma_pool_fixed_new(const unsigned int pool_size,
++ const int flight_size,
++ void * const alloc_v,
++ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn,
++ cma_pool_on_delete_fn * const on_delete_fn,
++ const char * const name);
++#endif
++
++struct cma_buf_s;
++typedef struct cma_buf_s cma_buf_t;
++
++void cma_buf_in_flight(cma_buf_t * const cb);
++void cma_buf_end_flight(cma_buf_t * const cb);
++unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb);
++size_t cma_buf_size(const cma_buf_t * const cb);
++int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2);
++unsigned int cma_buf_vc_handle(const cma_buf_t *const cb);
++int cma_buf_fd(const cma_buf_t *const cb);
++void * cma_buf_addr(const cma_buf_t *const cb);
++unsigned int cma_buf_vc_addr(const cma_buf_t *const cb);
++picture_context_t * cma_buf_context2(const cma_buf_t *const cb);
++
++void cma_buf_unref(cma_buf_t * const cb);
++cma_buf_t * cma_buf_ref(cma_buf_t * const cb);
++
++struct cma_buf_pool_s;
++typedef struct cma_buf_pool_s cma_buf_pool_t;
++
++cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const p, const size_t size);
++void cma_buf_pool_cancel(cma_buf_pool_t * const cbp);
++void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp);
++void cma_buf_pool_delete(cma_buf_pool_t * const p);
++int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size);
++int cma_buf_pool_resize(cma_buf_pool_t * const cbp,
++ const unsigned int new_pool_size, const int new_flight_size);
++cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size,
++ const bool all_in_flight, const char * const name);
++
++static inline void cma_buf_pool_deletez(cma_buf_pool_t ** const pp)
++{
++ cma_buf_pool_t * const p = *pp;
+ if (p != NULL) {
+ *pp = NULL;
+ cma_buf_pool_delete(p);
+ }
+}
+
-+
++#endif // VLC_MMAL_MMAL_CMA_H_
--- /dev/null
+++ b/modules/hw/mmal/mmal_gl.h
@@ -0,0 +1,45 @@
@@ -7179,9 +7856,117 @@
+ return pic_sys->cmabuf->h_vcsm;
+}
+
+--- /dev/null
++++ b/modules/hw/mmal/mmal_piccpy_neon.S
+@@ -0,0 +1,105 @@
++// Copy pix
++
++ .syntax unified
++ .arm
++// .thumb
++ .text
++ .align 16
++ .arch armv7-a
++ .fpu neon-vfpv4
++
++
++.macro function name
++ .global \name
++#ifdef __ELF__
++ .type \name, %function
++#endif
++\name:
++.endm
++
++
++.macro piccpy_to_8, bit_depth
++ subs r2, #128
++ vpush {q4-q7}
++ blt 2f
++1:
++ vldm r1!, {q0-q7}
++ subs r2, #128
++ vqrshrn.u16 d0, q0, #\bit_depth - 8
++ vqrshrn.u16 d1, q1, #\bit_depth - 8
++ vqrshrn.u16 d2, q2, #\bit_depth - 8
++ vqrshrn.u16 d3, q3, #\bit_depth - 8
++ vldm r1!, {q8-q15}
++ vqrshrn.u16 d4, q4, #\bit_depth - 8
++ vqrshrn.u16 d5, q5, #\bit_depth - 8
++ vqrshrn.u16 d6, q6, #\bit_depth - 8
++ vqrshrn.u16 d7, q7, #\bit_depth - 8
++ vqrshrn.u16 d8, q8, #\bit_depth - 8
++ vqrshrn.u16 d9, q9, #\bit_depth - 8
++ vqrshrn.u16 d10, q10, #\bit_depth - 8
++ vqrshrn.u16 d11, q11, #\bit_depth - 8
++ vqrshrn.u16 d12, q12, #\bit_depth - 8
++ vqrshrn.u16 d13, q13, #\bit_depth - 8
++ vqrshrn.u16 d14, q14, #\bit_depth - 8
++ vqrshrn.u16 d15, q15, #\bit_depth - 8
++ vstm r0!, {q0-q7}
++ bge 1b
++2:
++ adds r2, #64
++ blt 1f
++
++ vldm r1!, {q0-q7}
++ vqrshrn.u16 d0, q0, #\bit_depth - 8
++ vqrshrn.u16 d1, q1, #\bit_depth - 8
++ vqrshrn.u16 d2, q2, #\bit_depth - 8
++ vqrshrn.u16 d3, q3, #\bit_depth - 8
++ vqrshrn.u16 d4, q4, #\bit_depth - 8
++ vqrshrn.u16 d5, q5, #\bit_depth - 8
++ vqrshrn.u16 d6, q6, #\bit_depth - 8
++ vqrshrn.u16 d7, q7, #\bit_depth - 8
++ vstm r0!, {q0-q3}
++1:
++ adds r2, #32
++ blt 1f
++
++ vldm r1!, {q0-q3}
++ vqrshrn.u16 d0, q0, #\bit_depth - 8
++ vqrshrn.u16 d1, q1, #\bit_depth - 8
++ vqrshrn.u16 d2, q2, #\bit_depth - 8
++ vqrshrn.u16 d3, q3, #\bit_depth - 8
++ vstm r0!, {q0-q1}
++1:
++ adds r2, #16
++ blt 1f
++
++ vldm r1!, {q0-q1}
++ vqrshrn.u16 d0, q0, #\bit_depth - 8
++ vqrshrn.u16 d1, q1, #\bit_depth - 8
++ vstm r0!, {q0}
++1:
++ adds r2, #8
++ blt 1f
++
++ vldm r1!, {q0}
++ vqrshrn.u16 d0, q0, #\bit_depth - 8
++ vstr d0, [r0]
++ add r0, #8
++1:
++ adds r2, #4
++ blt 1f
++
++ vldr d0, [r1]
++ vqrshrn.u16 d0, q0, #\bit_depth - 8
++ vstr s0, [r0]
++1:
++ vpop {q4-q7}
++ bx lr
++.endm
++
++
++@ [r0] Dest
++@ [r1] Src
++@ r2 Pels
++function mmal_piccpy_10_to_8_neon
++ piccpy_to_8 10
++
--- a/modules/hw/mmal/mmal_picture.c
+++ b/modules/hw/mmal/mmal_picture.c
-@@ -21,25 +21,1034 @@
+@@ -21,25 +21,1509 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
@@ -7194,15 +7979,20 @@
+#include <fcntl.h>
+
#include <vlc_common.h>
++#include <vlc_cpu.h>
#include <vlc_picture.h>
++
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wbad-function-cast"
++#include <bcm_host.h>
++#pragma GCC diagnostic pop
#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_cma.h" // **************
++#include "mmal_cma.h"
#include "mmal_picture.h"
-int mmal_picture_lock(picture_t *picture)
@@ -7281,6 +8071,17 @@
+ return MMAL_ENCODING_ARGB;
+ break;
+ }
++ case VLC_CODEC_RGB16:
++ {
++ // VLC RGB16 aka RV16 means we have to look at the mask values
++ const uint32_t r = vf_vlc->i_rmask;
++ const uint32_t g = vf_vlc->i_gmask;
++ const uint32_t b = vf_vlc->i_bmask;
++ if (r == 0xf800 && g == 0x7e0 && b == 0x1f)
++ return MMAL_ENCODING_RGB16;
++ break;
++ }
++ case VLC_CODEC_I420:
+ case VLC_CODEC_MMAL_ZC_I420:
+ return MMAL_ENCODING_I420;
+ case VLC_CODEC_RGBA:
@@ -7296,18 +8097,20 @@
+ return MMAL_ENCODING_YUVUV128;
+ case VLC_CODEC_MMAL_ZC_SAND10:
+ return MMAL_ENCODING_YUVUV64_10;
++ case VLC_CODEC_MMAL_ZC_SAND30:
++ return MMAL_ENCODING_YUV10_COL;
+ default:
+ break;
+ }
+ return 0;
+}
+
-+
-+void vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc)
++static void vlc_fmt_to_video_format(MMAL_VIDEO_FORMAT_T *const vf_mmal, const video_frame_format_t * const vf_vlc)
+{
-+ MMAL_VIDEO_FORMAT_T * const vf_mmal = &es_fmt->es->video;
++ const unsigned int wmask = (vf_vlc->i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
++ vf_vlc->i_chroma == VLC_CODEC_I420) ? 31 : 15;
+
-+ vf_mmal->width = (vf_vlc->i_width + 31) & ~31;
++ vf_mmal->width = (vf_vlc->i_width + wmask) & ~wmask;
+ 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;
@@ -7325,6 +8128,73 @@
+ vf_mmal->color_space = vlc_to_mmal_color_space(vf_vlc->space);
+}
+
++
++void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc)
++{
++ vlc_fmt_to_video_format(&es_fmt->es->video, vf_vlc);
++}
++
++bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic)
++{
++ MMAL_VIDEO_FORMAT_T vf_new_ss;
++ MMAL_VIDEO_FORMAT_T *const vf_old = &es_fmt->es->video;
++ MMAL_VIDEO_FORMAT_T *const vf_new = &vf_new_ss;
++
++ vlc_fmt_to_video_format(vf_new, &pic->format);
++
++ // If we have a format that might have come from ffmpeg then rework for
++ // a better guess as to layout. All sand stuff is "special" with regards to
++ // width/height vs real layout so leave as is if that
++ if ((pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
++ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) &&
++ pic->p[0].i_pixel_pitch != 0)
++ {
++ // Now overwrite width/height with a better guess as to actual layout info
++ vf_new->height = pic->p[0].i_lines;
++ vf_new->width = pic->p[0].i_pitch / pic->p[0].i_pixel_pitch;
++ }
++
++ if (
++ vf_new->width != vf_old->width ||
++ vf_new->height != vf_old->height ||
++ vf_new->crop.x != vf_old->crop.x ||
++ vf_new->crop.y != vf_old->crop.y ||
++ vf_new->crop.width != vf_old->crop.width ||
++ vf_new->crop.height != vf_old->crop.height ||
++ vf_new->par.num != vf_old->par.num ||
++ vf_new->par.den != vf_old->par.den ||
++ // Frame rate ignored
++ vf_new->color_space != vf_old->color_space)
++ {
++#if 0
++ char dbuf0[5], dbuf1[5];
++ printf("%dx%d (%d,%d %dx%d) par:%d/%d %s -> %dx%d (%d,%d %dx%d) par:%d/%d %s\n",
++ vf_old->width ,
++ vf_old->height ,
++ vf_old->crop.x ,
++ vf_old->crop.y ,
++ vf_old->crop.width ,
++ vf_old->crop.height ,
++ vf_old->par.num ,
++ vf_old->par.den ,
++ str_fourcc(dbuf0, vf_old->color_space) ,
++ vf_new->width ,
++ vf_new->height ,
++ vf_new->crop.x ,
++ vf_new->crop.y ,
++ vf_new->crop.width ,
++ vf_new->crop.height ,
++ vf_new->par.num ,
++ vf_new->par.den ,
++ str_fourcc(dbuf1, vf_new->color_space) );
++#endif
++ *vf_old = *vf_new;
++ return true;
++ }
++ return false;
++}
++
++
+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)
+{
@@ -7407,9 +8277,7 @@
+ hw_mmal_port_pool_ref_t ** pppr,
+ MMAL_PORT_T * const port,
+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback)
- {
-- picture_sys_t *pic_sys = picture->p_sys;
-- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
++{
+ MMAL_STATUS_T status;
+
+ port->userdata = (struct MMAL_PORT_USERDATA_T *)obj;
@@ -7444,20 +8312,16 @@
+ msg_Err(obj, "Failed to create output pool");
+ return status;
+ }
-
-- 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;
++
+ status = mmal_port_enable(port, callback);
+ if (status != MMAL_SUCCESS) {
++ hw_mmal_port_pool_ref_release(*pppr, false);
++ *pppr = NULL;
+ msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)",
+ port->name, status, mmal_status_to_string(status));
+ return status;
- }
-
-- pic_sys->displayed = false;
++ }
++
+ return MMAL_SUCCESS;
+}
+
@@ -7468,78 +8332,317 @@
+ unsigned int i;
+
+ for (i = 0; i != ctx->buf_count; ++i) {
-+ mmal_buffer_header_release(ctx->bufs[i]);
++ if (ctx->bufs[i] != NULL)
++ mmal_buffer_header_release(ctx->bufs[i]);
+ }
++
++ cma_buf_end_flight(ctx->cb);
++ cma_buf_unref(ctx->cb);
++
+ free(ctx);
+}
+
-+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn)
++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->cb = cma_buf_ref(src_ctx->cb);
++
++ 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];
++ if (dst_ctx->bufs[i] != NULL)
++ 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;
++
++ // If we have an associated ppr then ref & set appropriate callbacks
++ if (ppr != NULL) {
++ hw_mmal_port_pool_ref_acquire(ppr);
++ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr);
++ buf->user_data = NULL;
++ }
++
++ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
++ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
++
++ ctx->buf_count = 1;
++ ctx->bufs[0] = buf;
++
++ return &ctx->cmn;
++}
++
++// n is els
++// * Make NEON!
++typedef void piccpy_fn(void * dest, const void * src, size_t n);
++
++extern piccpy_fn mmal_piccpy_10_to_8_neon;
++
++static void piccpy_10_to_8_c(void * dest, const void * src, size_t n)
+ {
+- picture_sys_t *pic_sys = picture->p_sys;
+- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
++ uint8_t * d = dest;
++ const uint16_t * s = src;
++ while (n-- != 0)
++ *d++ = *s++ >> 2;
++}
++
++// Do a stride converting copy - if the strides are the same and line_len is
++// close then do a single block copy - we don't expect to have to preserve
++// pixels in the output frame
++static void mem_copy_2d(uint8_t * d_ptr, const size_t d_stride,
++ const uint8_t * s_ptr, const size_t s_stride,
++ size_t lines, const size_t line_len)
++{
++ if (s_stride == d_stride && d_stride < line_len + 32)
++ {
++ memcpy(d_ptr, s_ptr, d_stride * lines);
++ }
++ else
++ {
++ while (lines-- != 0) {
++ memcpy(d_ptr, s_ptr, line_len);
++ d_ptr += d_stride;
++ s_ptr += s_stride;
++ }
++ }
++}
++
++// line_len in D units
++static void mem_copy_2d_10_to_8(uint8_t * d_ptr, const size_t d_stride,
++ const uint8_t * s_ptr, const size_t s_stride,
++ size_t lines, const size_t line_len)
++{
++ piccpy_fn * const docpy = vlc_CPU_ARM_NEON() ? mmal_piccpy_10_to_8_neon : piccpy_10_to_8_c;
++ if (s_stride == d_stride * 2 && d_stride < line_len + 32)
++ {
++ docpy(d_ptr, s_ptr, d_stride * lines);
++ }
++ else
++ {
++ while (lines-- != 0) {
++ docpy(d_ptr, s_ptr, line_len);
++ d_ptr += d_stride;
++ s_ptr += s_stride;
++ }
++ }
++}
++
++
++int hw_mmal_copy_pic_to_buf(void * const buf_data,
++ uint32_t * const pLength,
++ const MMAL_ES_FORMAT_T * const fmt,
++ const picture_t * const pic)
++{
++ const MMAL_VIDEO_FORMAT_T *const video = &fmt->es->video;
++ uint8_t * const dest = buf_data;
++ size_t length = 0;
++
++ //**** Worry about x/y_offsets
+
+- 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;
++ assert(fmt->encoding == MMAL_ENCODING_I420);
++
++ switch (pic->format.i_chroma) {
++ case VLC_CODEC_I420:
++ {
++ const size_t y_size = video->width * video->height;
++ mem_copy_2d(dest, video->width,
++ pic->p[0].p_pixels, pic->p[0].i_pitch,
++ video->crop.height,
++ video->crop.width);
++
++ mem_copy_2d(dest + y_size, video->width / 2,
++ pic->p[1].p_pixels, pic->p[1].i_pitch,
++ video->crop.height / 2,
++ video->crop.width / 2);
++
++ mem_copy_2d(dest + y_size + y_size / 4, video->width / 2,
++ pic->p[2].p_pixels, pic->p[2].i_pitch,
++ video->crop.height / 2,
++ video->crop.width / 2);
++
++ // And make sure it is actually in memory
++ length = y_size + y_size / 2;
++ break;
++ }
++
++ case VLC_CODEC_I420_10L:
++ {
++ const size_t y_size = video->width * video->height;
++ mem_copy_2d_10_to_8(dest, video->width,
++ pic->p[0].p_pixels, pic->p[0].i_pitch,
++ video->crop.height,
++ video->crop.width);
++
++ mem_copy_2d_10_to_8(dest + y_size, video->width / 2,
++ pic->p[1].p_pixels, pic->p[1].i_pitch,
++ video->crop.height / 2,
++ video->crop.width / 2);
++
++ mem_copy_2d_10_to_8(dest + y_size + y_size / 4, video->width / 2,
++ pic->p[2].p_pixels, pic->p[2].i_pitch,
++ video->crop.height / 2,
++ video->crop.width / 2);
++
++ // And make sure it is actually in memory
++ length = y_size + y_size / 2;
++ break;
++ }
++
++ default:
++ if (pLength != NULL)
++ *pLength = 0;
++ return VLC_EBADVAR;
++ }
++
++ if (cma_vcsm_type() == VCSM_INIT_LEGACY) { // ** CMA is currently always uncached
++ flush_range(dest, length);
+ }
+
+- pic_sys->displayed = false;
++ if (pLength != NULL)
++ *pLength = (uint32_t)length;
+
+ return VLC_SUCCESS;
+ }
++
++
++static MMAL_BOOL_T rep_buf_free_cb(MMAL_BUFFER_HEADER_T *header, void *userdata)
+{
-+ 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;
++ cma_buf_t * const cb = userdata;
++ VLC_UNUSED(header);
+
-+ if (dst_ctx == NULL)
-+ return NULL;
-+
-+ // Copy
-+ dst_ctx->cmn = src_ctx->cmn;
++ cma_buf_unref(cb);
++ return MMAL_FALSE;
++}
+
-+ 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]);
-+ }
++static int cma_buf_buf_attach(MMAL_BUFFER_HEADER_T * const buf, cma_buf_t * const cb)
++{
++ // Just a CMA buffer - fill in new buffer
++ const uintptr_t vc_h = cma_buf_vc_handle(cb);
++ if (vc_h == 0)
++ return VLC_EGENERIC;
+
-+ return &dst_ctx->cmn;
++ mmal_buffer_header_reset(buf);
++ buf->data = (uint8_t *)vc_h;
++ buf->alloc_size = cma_buf_size(cb);
++ buf->length = buf->alloc_size;
++ // Ensure cb remains valid for the duration of this buffer
++ mmal_buffer_header_pre_release_cb_set(buf, rep_buf_free_cb, cma_buf_ref(cb));
++ return VLC_SUCCESS;
+}
+
-+static MMAL_BOOL_T
-+buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata)
++MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic,
++ MMAL_POOL_T * const rep_pool,
++ MMAL_PORT_T * const port,
++ cma_buf_pool_t * const cbp)
+{
-+ hw_mmal_port_pool_ref_t * const ppr = userdata;
++ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(rep_pool->queue);
++ if (buf == NULL)
++ goto fail0;
+
-+ // 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
++ cma_buf_t * const cb = cma_buf_pool_alloc_buf(cbp, port->buffer_size);
++ if (cb == NULL)
++ goto fail1;
+
-+ // 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
++ if (cma_buf_buf_attach(buf, cb) != VLC_SUCCESS)
++ goto fail2;
+
-+ return MMAL_TRUE;
++ pic_to_buf_copy_props(buf, pic);
++
++ if (hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), &buf->length, port->format, pic) != VLC_SUCCESS)
++ goto fail2;
++ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
++
++ cma_buf_unref(cb);
++ return buf;
++
++fail2:
++ cma_buf_unref(cb);
++fail1:
++ mmal_buffer_header_release(buf);
++fail0:
++ return NULL;
+}
-
-- return VLC_SUCCESS;
-+// Buffer belongs to context on successful return from this fn
-+// is still valid on failure
-+picture_context_t *
-+hw_mmal_gen_context(const MMAL_FOURCC_T fmt, MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr)
++
++MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool)
+{
-+ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
++ pic_ctx_mmal_t *const ctx = (pic_ctx_mmal_t *)pic->context;
++ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
+
-+ if (ctx == NULL)
++ if (rep_buf == NULL)
+ return NULL;
+
-+ // If we have an associated ppr then ref & set appropriate callbacks
-+ if (ppr != NULL) {
-+ hw_mmal_port_pool_ref_acquire(ppr);
-+ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr);
-+ buf->user_data = NULL;
++ if (ctx->bufs[0] != NULL)
++ {
++ // Existing buffer - replicate it
++ if (mmal_buffer_header_replicate(rep_buf, ctx->bufs[0]) != MMAL_SUCCESS)
++ goto fail;
+ }
++ else if (ctx->cb != NULL)
++ {
++ // Just a CMA buffer - fill in new buffer
++ if (cma_buf_buf_attach(rep_buf, ctx->cb) != 0)
++ goto fail;
++ }
++ else
++ goto fail;
+
-+ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
-+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
-+
-+ ctx->fmt = fmt;
-+ ctx->buf_count = 1;
-+ ctx->bufs[0] = buf;
++ pic_to_buf_copy_props(rep_buf, pic);
++ return rep_buf;
+
-+ return &ctx->cmn;
++fail:
++ mmal_buffer_header_release(rep_buf);
++ return NULL;
+}
+
++
++
++
+int hw_mmal_get_gpu_mem(void) {
+ static int stashed_val = -2;
+ VCHI_INSTANCE_T vchi_instance;
@@ -7642,6 +8745,8 @@
+ vlc_mutex_t lock;
+
+ MMAL_POOL_T * buf_pool;
++
++ vcsm_init_type_t vcsm_init_type;
+};
+
+typedef struct vzc_subbuf_ent_s
@@ -7752,7 +8857,7 @@
+ ent = ent->prev;
+ }
+ return NULL;
- }
++}
+
+#define POOL_ENT_ALLOC_BLOCK 0x10000
+
@@ -7919,10 +9024,17 @@
+
+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);
++ d->x = rescale_x(s->x - div_rect->x, mul_rect->width, div_rect->width) + mul_rect->x;
++ d->y = rescale_x(s->y - div_rect->y, mul_rect->height, div_rect->height) + mul_rect->y;
++ d->width = rescale_x(s->width, mul_rect->width, div_rect->width);
++ d->height = rescale_x(s->height, mul_rect->height, div_rect->height);
++#if 0
++ fprintf(stderr, "(%d,%d %dx%d) * (%d,%d %dx%d) / (%d,%d %dx%d) -> (%d,%d %dx%d)\n",
++ s->x, s->y, s->width, s->height,
++ mul_rect->x, mul_rect->y, mul_rect->width, mul_rect->height,
++ div_rect->x, div_rect->y, div_rect->width, div_rect->height,
++ d->x, d->y, d->width, d->height);
++#endif
+}
+
+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect)
@@ -8070,17 +9182,15 @@
+
+ // 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;
-+ }
++
++ mem_copy_2d(d, dst_stride, s, pic->p[0].i_pitch, fmt->i_visible_height, dst_stride);
+
+ // And make sure it is actually in memory
-+ flush_range(ent->buf, d - (uint8_t *)ent->buf);
++ if (pc->vcsm_init_type != VCSM_INIT_CMA) { // ** CMA is currently always uncached
++ flush_range(ent->buf, dst_stride * fmt->i_visible_height);
++ }
+ }
+ }
+ }
@@ -8114,13 +9224,12 @@
+
+ vlc_mutex_destroy(&pc->lock);
+
-+// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash
++ cma_vcsm_exit(pc->vcsm_init_type);
+
++// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash
+ free (pc);
+
-+ vcsm_exit();
-+
-+// printf(">>> %s\n", __func__);
++ // printf(">>> %s\n", __func__);
+}
+
+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc)
@@ -8171,7 +9280,11 @@
+ if (pc == NULL)
+ return NULL;
+
-+ vcsm_init();
++ if ((pc->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
++ {
++ free(pc);
++ return NULL;
++ }
+
+ pc->max_n = 8;
+ vlc_mutex_init(&pc->lock); // Must init before potential destruction
@@ -8189,24 +9302,170 @@
+ return pc;
+}
+
++//----------------------------------------------------------------------------
++
++
++static const uint8_t shift_00[] = {0,0,0,0};
++static const uint8_t shift_01[] = {0,1,1,1};
++
++int cma_pic_set_data(picture_t * const pic,
++ const MMAL_ES_FORMAT_T * const mm_esfmt,
++ const MMAL_BUFFER_HEADER_T * const buf)
++{
++ const MMAL_VIDEO_FORMAT_T * const mm_fmt = &mm_esfmt->es->video;
++ const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = (buf == NULL) ? NULL : &buf->type->video;
++ cma_buf_t *const cb = cma_buf_pic_get(pic);
++ unsigned int planes = 1;
++
++ uint8_t * const data = cma_buf_addr(cb);
++ if (data == NULL) {
++ return VLC_ENOMEM;
++ }
++
++ const uint8_t * ws = shift_00;
++ const uint8_t * hs = shift_00;
++ int pb = 1;
++
++ switch (mm_esfmt->encoding)
++ {
++ case MMAL_ENCODING_ARGB:
++ case MMAL_ENCODING_ABGR:
++ case MMAL_ENCODING_RGBA:
++ case MMAL_ENCODING_BGRA:
++ case MMAL_ENCODING_RGB32:
++ case MMAL_ENCODING_BGR32:
++ pb = 4;
++ break;
++ case MMAL_ENCODING_RGB16:
++ pb = 2;
++ break;
++
++ case MMAL_ENCODING_I420:
++ ws = shift_01;
++ hs = shift_01;
++ planes = 3;
++ break;
++
++ case MMAL_ENCODING_YUVUV128:
++ hs = shift_01;
++ planes = 2;
++ break;
++
++ default:
++// msg_Err(p_filter, "%s: Unexpected format", __func__);
++ return VLC_EGENERIC;
++ }
++
++ // Fix up SAR if unset
++ if (pic->format.i_sar_den == 0 || pic->format.i_sar_num == 0) {
++ pic->format.i_sar_den = mm_fmt->par.den;
++ pic->format.i_sar_num = mm_fmt->par.num;
++ }
++
++ pic->i_planes = planes;
++ unsigned int offset = 0;
++ for (unsigned int i = 0; i != planes; ++i) {
++ pic->p[i] = (plane_t){
++ .p_pixels = data + (buf_vid != NULL ? buf_vid->offset[i] : offset),
++ .i_lines = mm_fmt->height >> hs[i],
++ .i_pitch = buf_vid != NULL ? buf_vid->pitch[i] : mm_fmt->width * pb,
++ .i_pixel_pitch = pb,
++ .i_visible_lines = mm_fmt->crop.height >> hs[i],
++ .i_visible_pitch = mm_fmt->crop.width >> ws[i]
++ };
++ offset += pic->p[i].i_pitch * pic->p[i].i_lines;
++ }
++ return VLC_SUCCESS;
++}
++
++int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic)
++{
++ if (!is_cma_buf_pic_chroma(pic->format.i_chroma))
++ return VLC_EGENERIC;
++ if (pic->context != NULL)
++ return VLC_EBADVAR;
++
++ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
++
++ if (ctx == NULL)
++ return VLC_ENOMEM;
++
++ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
++ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
++ ctx->buf_count = 1; // cb takes the place of the 1st buf
++ ctx->cb = cb;
++
++ cma_buf_in_flight(cb);
++
++ pic->context = &ctx->cmn;
++ return VLC_SUCCESS;
++}
++
++cma_buf_t * cma_buf_pic_get(picture_t * const pic)
++{
++ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
++ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? 0 : ctx->cb;
++}
++
+
+//----------------------------------------------------------------------------
+
++/* Returns the type of the Pi being used
++*/
++bool rpi_is_model_pi4(void) {
++ return bcm_host_is_model_pi4();
++}
++
++// Preferred mode - none->cma on Pi4 otherwise legacy
++static volatile vcsm_init_type_t last_vcsm_type = VCSM_INIT_NONE;
++
++vcsm_init_type_t cma_vcsm_type(void)
++{
++ return last_vcsm_type;
++}
++
+vcsm_init_type_t cma_vcsm_init(void)
+{
-+ if (vcsm_init_ex(1, -1) == 0) {
-+ return VCSM_INIT_CMA;
++ vcsm_init_type_t rv = VCSM_INIT_NONE;
++ // We don't bother locking - taking a copy here should be good enough
++ vcsm_init_type_t try_type = last_vcsm_type;
++
++ if (try_type == VCSM_INIT_NONE) {
++ if (bcm_host_is_fkms_active())
++ try_type = VCSM_INIT_CMA;
++ else
++ try_type = VCSM_INIT_LEGACY;
++ }
++
++ if (try_type == VCSM_INIT_CMA) {
++ if (vcsm_init_ex(1, -1) == 0)
++ rv = VCSM_INIT_CMA;
++ else if (vcsm_init_ex(0, -1) == 0)
++ rv = VCSM_INIT_LEGACY;
+ }
-+ else if (vcsm_init_ex(0, -1) == 0) {
-+ return VCSM_INIT_LEGACY;
++ else
++ {
++ if (vcsm_init_ex(0, -1) == 0)
++ rv = VCSM_INIT_LEGACY;
++ else if (vcsm_init_ex(1, -1) == 0)
++ rv = VCSM_INIT_CMA;
+ }
-+ return VCSM_INIT_NONE;
++
++ // Just in case this affects vcsm init do after that
++ if (rv != VCSM_INIT_NONE)
++ bcm_host_init();
++
++ last_vcsm_type = rv;
++ return rv;
+}
+
+void cma_vcsm_exit(const vcsm_init_type_t init_mode)
+{
+ if (init_mode != VCSM_INIT_NONE)
++ {
+ vcsm_exit();
++ bcm_host_deinit(); // Does nothing but add in case it ever does
++ }
+}
+
+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode)
@@ -8228,7 +9487,7 @@
+
--- a/modules/hw/mmal/mmal_picture.h
+++ b/modules/hw/mmal/mmal_picture.h
-@@ -24,19 +24,275 @@
+@@ -24,19 +24,298 @@
#ifndef VLC_MMAL_MMAL_PICTURE_H_
#define VLC_MMAL_MMAL_PICTURE_H_
@@ -8237,6 +9496,8 @@
#include <vlc_common.h>
#include <interface/mmal/mmal.h>
++#include "mmal_cma.h"
++
/* Think twice before changing this. Incorrect values cause havoc. */
#define NUM_ACTUAL_OPAQUE_BUFFERS 30
@@ -8264,31 +9525,28 @@
+
+
+#define CTX_BUFS_MAX 4
-+
+typedef struct pic_ctx_mmal_s {
+ picture_context_t cmn; // PARENT: Common els at start
+
-+ MMAL_FOURCC_T fmt;
++ cma_buf_t * cb;
+
+ 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;
+
+const char * str_fourcc(char * const buf, const unsigned int fcc);
+
+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc);
+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);
++void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc);
++// Returns true if fmt_changed
++// frame_rate ignored for compare, but is set if something else is updated
++bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic);
++
++// Copy pic contents into an existing buffer
++int hw_mmal_copy_pic_to_buf(void * const buf_data, uint32_t * const pLength,
++ const MMAL_ES_FORMAT_T * const fmt, const picture_t * const pic);
+
+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);
@@ -8329,24 +9587,25 @@
+ return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1];
+}
+
-+static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic)
++static inline bool hw_mmal_chroma_is_mmal(const vlc_fourcc_t chroma)
+{
-+ return pic->format.i_chroma == VLC_CODEC_MMAL_OPAQUE ||
-+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
-+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
-+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
-+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32;
++ return
++ chroma == VLC_CODEC_MMAL_OPAQUE ||
++ chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
++ chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
++ chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
++ chroma == VLC_CODEC_MMAL_ZC_I420 ||
++ chroma == VLC_CODEC_MMAL_ZC_RGB32;
+}
+
-+static inline MMAL_FOURCC_T hw_mmal_pic_format(const picture_t *const pic)
++static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic)
+{
-+ const pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
-+ return ctx->fmt;
++ return hw_mmal_chroma_is_mmal(pic->format.i_chroma);
+}
+
+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(const MMAL_FOURCC_T fmt,
++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);
@@ -8394,6 +9653,7 @@
+ 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)
@@ -8432,16 +9692,12 @@
+ VLC_TICK_INVALID;
+}
+
-+// Retrieve buf from pic & update with pic props
-+// Note that this is a weak pointer - replicate before putting in a Q
-+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);
++MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic,
++ MMAL_POOL_T * const rep_pool,
++ MMAL_PORT_T * const port,
++ cma_buf_pool_t * const cbp);
+
-+ return buf;
-+}
++MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool);
+
+struct vzc_pool_ctl_s;
+typedef struct vzc_pool_ctl_s vzc_pool_ctl_t;
@@ -8487,6 +9743,31 @@
+ };
+}
+
++int cma_pic_set_data(picture_t * const pic,
++ const MMAL_ES_FORMAT_T * const mm_esfmt,
++ const MMAL_BUFFER_HEADER_T * const buf);
++
++// Attaches cma buf to pic
++// Marks in_flight if not all_in_flight anyway
++int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic);
++// Returns a pointer to the cma_buf attached to the pic
++// Just a pointer - doesn't add a ref
++cma_buf_t * cma_buf_pic_get(picture_t * const pic);
++
++static inline bool is_cma_buf_pic_chroma(const uint32_t chroma)
++{
++ return chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
++ chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
++ chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
++ chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
++ chroma == VLC_CODEC_MMAL_ZC_I420;
++}
++
++
++int rpi_get_model_type(void);
++bool rpi_is_model_pi4(void);
++bool rpi_is_fkms_active(void);
++
+typedef enum vcsm_init_type_e {
+ VCSM_INIT_NONE = 0,
+ VCSM_INIT_LEGACY,
@@ -8495,6 +9776,7 @@
+
+vcsm_init_type_t cma_vcsm_init(void);
+void cma_vcsm_exit(const vcsm_init_type_t init_mode);
++vcsm_init_type_t cma_vcsm_type(void);
+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode);
+
@@ -8625,7 +9907,7 @@
+
--- /dev/null
+++ b/modules/hw/mmal/subpic.c
-@@ -0,0 +1,227 @@
+@@ -0,0 +1,234 @@
+/*****************************************************************************
+ * mmal.c: MMAL-based decoder plugin for Raspberry Pi
+ *****************************************************************************
@@ -8693,7 +9975,8 @@
+ *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 hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port,
++ const int display_id, const unsigned int layer)
+{
+ MMAL_STATUS_T err;
+
@@ -8714,6 +9997,7 @@
+
+ port->userdata = (void *)p_filter;
+ spe->port = port;
++ spe->display_id = display_id;
+ spe->layer = layer;
+
+ return MMAL_SUCCESS;
@@ -8752,7 +10036,7 @@
+ return -1;
+ }
+#if TRACE_ALL
-+ msg_Dbg(p_filter, "Remove pic for sub %d", sub_no);
++ msg_Dbg(p_filter, "Remove pic for sub %d", spe->seq);
+#endif
+ buf->cmd = 0;
+ buf->data = NULL;
@@ -8797,6 +10081,11 @@
+ spe->dest_rect = dreg->dest_rect;
+ needs_update = true;
+
++ if (spe->display_id >= 0)
++ {
++ dreg->display_num = spe->display_id;
++ dreg->set |= MMAL_DISPLAY_SET_NUM;
++ }
+ dreg->layer = spe->layer;
+ dreg->set |= MMAL_DISPLAY_SET_LAYER;
+
@@ -8837,7 +10126,7 @@
+ if (needs_update)
+ {
+#if TRACE_ALL
-+ msg_Dbg(p_filter, "Update pic for sub %d", sub_no);
++ msg_Dbg(p_filter, "Update pic for sub %d", spe->seq);
+#endif
+ if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS)
+ {
@@ -8855,7 +10144,7 @@
+
--- /dev/null
+++ b/modules/hw/mmal/subpic.h
-@@ -0,0 +1,29 @@
+@@ -0,0 +1,32 @@
+#ifndef VLC_HW_MMAL_SUBPIC_H_
+#define VLC_HW_MMAL_SUBPIC_H_
+
@@ -8863,6 +10152,7 @@
+{
+ MMAL_PORT_T * port;
+ MMAL_POOL_T * pool;
++ int display_id; // -1 => do not set
+ unsigned int layer;
+ // Shadow vars so we can tell if stuff has changed
+ MMAL_RECT_T dest_rect;
@@ -8881,7 +10171,9 @@
+
+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);
++// If display id is -1 it will be unset
++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 int display_id, const unsigned int layer);
+
+#endif
+
@@ -9269,7 +10561,7 @@
+
--- a/modules/hw/mmal/vout.c
+++ b/modules/hw/mmal/vout.c
-@@ -27,21 +27,24 @@
+@@ -27,21 +27,27 @@
#endif
#include <math.h>
@@ -9282,21 +10574,26 @@
#include <vlc_vout_display.h>
+#include <vlc_modules.h>
- #include "mmal_picture.h"
-+#include "subpic.h"
-
+-#include "mmal_picture.h"
+-
++#pragma GCC diagnostic push
++#pragma GCC diagnostic ignored "-Wbad-function-cast"
#include <bcm_host.h>
++#pragma GCC diagnostic pop
#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>
+
++#include "mmal_picture.h"
++#include "subpic.h"
++
+#define TRACE_ALL 0
#define MAX_BUFFERS_IN_TRANSIT 1
#define VC_TV_MAX_MODE_IDS 127
-@@ -50,11 +53,6 @@
+@@ -50,10 +56,12 @@
#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.")
@@ -9304,11 +10601,16 @@
-#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_DISPLAY_NAME "mmal-display"
++#define MMAL_DISPLAY_TEXT N_("Output device for Rpi fullscreen.")
++#define MMAL_DISPLAY_LONGTEXT N_("Output device for Rpi fullscreen. " \
++"Valid values are HDMI-1,HDMI-2. By default if qt-fullscreen-screennumber " \
++"is specified (or set by Fullscreen Output Device in Preferences) " \
++"HDMI-<qt-fullscreen-screennumber+1> will be used, otherwise HDMI-1.")
+
#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 @@
+@@ -68,64 +76,33 @@
#define PHASE_OFFSET_TARGET ((double)0.25)
#define PHASE_CHECK_INTERVAL 100
@@ -9371,17 +10673,20 @@
- DISPMANX_DISPLAY_HANDLE_T dmx_handle;
- DISPMANX_ELEMENT_HANDLE_T bkg_element;
- DISPMANX_RESOURCE_HANDLE_T bkg_resource;
++ int display_id;
unsigned display_width;
unsigned display_height;
- int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
- int i_frame_rate;
++ MMAL_RECT_T dest_rect; // Output rectangle in display coords
++
+ 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,264 +100,451 @@
+@@ -136,264 +113,485 @@
bool native_interlaced;
bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */
bool b_progressive;
@@ -9471,36 +10776,22 @@
- return VLC_EGENERIC;
+static inline bool want_copy(const vout_display_t * const vd)
+{
-+ return (vd->fmt.i_chroma == VLC_CODEC_I420);
++ return (vd->fmt.i_chroma == VLC_CODEC_I420 || vd->fmt.i_chroma == VLC_CODEC_I420_10L);
+}
- sys = calloc(1, sizeof(struct vout_display_sys_t));
- if (!sys)
- return VLC_ENOMEM;
- vd->sys = sys;
-+// Do a stride converting copy - if the strides are the same and line_len is
-+// close then do a single block copy - we don't expect to have to preserve
-+// pixels in the output frame
-+static inline void mem_copy_2d(uint8_t * d_ptr, const size_t d_stride, const uint8_t * s_ptr, const size_t s_stride, size_t lines, const size_t line_len)
++static inline vlc_fourcc_t req_chroma(const vout_display_t * const vd)
+{
-+ if (s_stride == d_stride && s_stride < line_len + 32)
-+ {
-+ memcpy(d_ptr, s_ptr, s_stride * lines);
-+ }
-+ else
-+ {
-+ while (lines-- != 0) {
-+ memcpy(d_ptr, s_ptr, line_len);
-+ d_ptr += d_stride;
-+ s_ptr += s_stride;
-+ }
-+ }
++ return !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma) && !want_copy(vd) ?
++ VLC_CODEC_I420 :
++ vd->fmt.i_chroma;
+}
- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
- bcm_host_init();
-
-- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE;
+static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc)
+{
+ switch (fcc){
@@ -9509,29 +10800,33 @@
+ case VLC_CODEC_MMAL_ZC_SAND8:
+ return MMAL_ENCODING_YUVUV128;
+ case VLC_CODEC_MMAL_ZC_SAND10:
-+ return MMAL_ENCODING_YUVUV64_10; // It will be after we've converted it...
++ return MMAL_ENCODING_YUVUV64_10;
++ case VLC_CODEC_MMAL_ZC_SAND30:
++ return MMAL_ENCODING_YUV10_COL;
++ case VLC_CODEC_MMAL_ZC_I420:
+ case VLC_CODEC_I420:
+ return MMAL_ENCODING_I420;
+ default:
+ break;
+ }
-+ return 0;
++ return MMAL_ENCODING_I420;
+}
+- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE;
++static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate)
++{
++ const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ;
++ const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height;
++ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
+
- 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;
-+static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate)
-+{
-+ const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ;
-+ const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height;
-+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
-+
+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
-+ es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);;
++ es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
+ es_fmt->encoding_variant = 0;
+
+ v_fmt->width = (w + 31) & ~31;
@@ -9550,7 +10845,6 @@
+ v_fmt->frame_rate.num = vd->fmt.i_frame_rate;
+ v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base;
+ v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space);
-+}
- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
- status = mmal_port_enable(sys->component->control, control_port_cb);
@@ -9559,6 +10853,9 @@
- sys->component->control->name, status, mmal_status_to_string(status));
- ret = VLC_EGENERIC;
- goto out;
++ msg_Dbg(vd, "WxH: %dx%d, Crop: %dx%d", v_fmt->width, v_fmt->height, v_fmt->crop.width, v_fmt->crop.height);
++}
++
+static void display_src_rect(const vout_display_t * const vd, MMAL_RECT_T *const rect)
+{
+ const bool wants_isp = want_isp(vd);
@@ -9899,14 +11196,14 @@
+ isp->output->buffer_size = isp->output->buffer_size_recommended;
+ isp->output->buffer_num = 2;
+ isp->output->userdata = (void *)vd;
-
-- bcm_host_deinit();
++
+ if ((isp->out_pool = mmal_port_pool_create(isp->output, isp->output->buffer_num, isp->output->buffer_size)) == NULL)
+ {
+ msg_Err(vd, "Failed to make ISP port pool");
+ goto fail;
+ }
-+
+
+- bcm_host_deinit();
+ mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp);
+
+ if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS)
@@ -9981,12 +11278,12 @@
+#endif
+}
+
-+static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
++static int query_resolution(vout_display_t *vd, const int display_id, unsigned *width, unsigned *height)
+{
-+ TV_DISPLAY_STATE_T display_state;
++ TV_DISPLAY_STATE_T display_state = {0};
+ int ret = 0;
+
-+ if (vc_tv_get_display_state(&display_state) == 0) {
++ if (vc_tv_get_display_state_id(display_id, &display_state) == 0) {
+ msg_Dbg(vd, "State=%#x", display_state.state);
+ if (display_state.state & 0xFF) {
+ msg_Dbg(vd, "HDMI: %dx%d", display_state.display.hdmi.width, display_state.display.hdmi.height);
@@ -10005,15 +11302,58 @@
+ ret = -1;
+ }
+
-+ return ret;
- }
-
++ return ret;
++}
++
++static inline MMAL_RECT_T
++place_to_mmal_rect(const vout_display_place_t place)
++{
++ return (MMAL_RECT_T){
++ .x = place.x,
++ .y = place.y,
++ .width = place.width,
++ .height = place.height
++ };
+ }
+
++static void
++place_dest(vout_display_t *vd, vout_display_sys_t * const sys,
++ const vout_display_cfg_t * const cfg, const video_format_t * fmt)
++{
++ video_format_t tfmt;
++
++ // Fix SAR if unknown
++ if (fmt->i_sar_den == 0 || fmt->i_sar_num == 0) {
++ tfmt = *fmt;
++ tfmt.i_sar_den = 1;
++ tfmt.i_sar_num = 1;
++ fmt = &tfmt;
++ }
++
++ // Ignore what VLC thinks might be going on with display size
++ vout_display_cfg_t tcfg = *cfg;
++ vout_display_place_t place;
++ tcfg.display.width = sys->display_width;
++ tcfg.display.height = sys->display_height;
++ tcfg.is_display_filled = true;
++ vout_display_PlacePicture(&place, fmt, &tcfg, false);
++
++ sys->dest_rect = place_to_mmal_rect(place);
++#if TRACE_ALL
++ msg_Dbg(vd, "%s: %dx%d -> %dx%d @ %d,%d", __func__,
++ tcfg.display.width, tcfg.display.height,
++ place.width, place.height, place.x, place.y);
++#endif
++}
++
++
++
static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
const video_format_t *fmt)
{
- vout_display_sys_t *sys = vd->sys;
+- vout_display_place_t place;
+ vout_display_sys_t * const sys = vd->sys;
- vout_display_place_t place;
MMAL_DISPLAYREGION_T display_region;
MMAL_STATUS_T status;
@@ -10027,21 +11367,12 @@
if (fmt) {
sys->input->format->es->video.par.num = fmt->i_sar_num;
-@@ -412,22 +563,29 @@
+@@ -412,22 +610,17 @@
if (!cfg)
cfg = vd->cfg;
- vout_display_PlacePicture(&place, fmt, cfg, false);
-+ {
-+ // Ignore what VLC thinks might be going on with display size
-+ vout_display_cfg_t tcfg = *cfg;
-+ tcfg.display.width = sys->display_width;
-+ tcfg.display.height = sys->display_height;
-+ tcfg.is_display_filled = true;
-+ vout_display_PlacePicture(&place, fmt, &tcfg, false);
-+
-+ msg_Dbg(vd, "%dx%d -> %dx%d @ %d,%d", tcfg.display.width, tcfg.display.height, place.width, place.height, place.x, place.y);
-+ }
++ place_dest(vd, sys, cfg, fmt);
display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
@@ -10050,11 +11381,12 @@
- display_region.src_rect.y = fmt->i_y_offset;
- display_region.src_rect.width = fmt->i_visible_width;
- display_region.src_rect.height = 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_src_rect(vd, &display_region.src_rect);
- 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.dest_rect = sys->dest_rect;
display_region.layer = sys->layer;
+ display_region.alpha = 0xff | (1 << 29);
display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
@@ -10063,7 +11395,7 @@
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 +593,6 @@
+@@ -435,7 +628,6 @@
return -EINVAL;
}
@@ -10071,7 +11403,7 @@
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,192 +603,153 @@
+@@ -446,192 +638,161 @@
return 0;
}
@@ -10135,7 +11467,14 @@
-#ifndef NDEBUG
- msg_Dbg(vd, "Creating picture pool with %u pictures", count);
+#if TRACE_ALL
-+ msg_Dbg(vd, "<<< %s", __func__);
++ {
++ char dbuf0[5];
++ msg_Dbg(vd, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
++ str_fourcc(dbuf0, p_pic->format.i_chroma), p_pic->format.i_width, p_pic->format.i_height,
++ p_pic->format.i_x_offset, p_pic->format.i_y_offset,
++ p_pic->format.i_visible_width, p_pic->format.i_visible_height,
++ p_pic->format.i_sar_num, p_pic->format.i_sar_den);
++ }
#endif
- sys->input->buffer_num = count;
@@ -10144,40 +11483,17 @@
- 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;
+ // If we had subpics then we have attached them to the main pic in prepare
+ // so all we have to do here is delete the refs
+ 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);
+- 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;
-+ 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);
-+ }
-+
+ if (!sys->input->is_enabled &&
+ (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS)
+ {
@@ -10200,6 +11516,27 @@
+ }
}
-
+- 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;
++ else if (sys->isp.pending) {
++ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q);
++ sys->isp.pending = false;
++#if TRACE_ALL
++ msg_Dbg(vd, "--- %s: ISP stuff", __func__);
++#endif
++ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
++ {
++ mmal_buffer_header_release(buf);
++ msg_Err(vd, "Send ISP buffer to render input failed");
++ goto fail;
++ }
+ }
+-
- 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) {
@@ -10212,40 +11549,40 @@
- msg_Err(vd, "Failed to create picture");
- free(picture_res.p_sys);
- goto out;
-+ else if (sys->isp.pending) {
-+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q);
-+ sys->isp.pending = false;
-+#if TRACE_ALL
-+ msg_Dbg(vd, "--- %s: ISP stuff", __func__);
-+#endif
-+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
++ else
++ {
++ MMAL_BUFFER_HEADER_T *const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->pool);
++ if (pic_buf == NULL)
+ {
-+ mmal_buffer_header_release(buf);
-+ msg_Err(vd, "Send ISP buffer to render input failed");
++ msg_Err(vd, "Replicated buffer get fail");
+ 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;
--
++ // If dimensions have chnaged then fix that
++ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic))
++ {
++ msg_Dbg(vd, "Reset port format");
++
++ // HVS can deal with on-line dimension changes
++ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS)
++ msg_Warn(vd, "Input format commit failed");
++ }
+
- sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
- if (!sys->picture_pool) {
- msg_Err(vd, "Failed to create picture pool");
- goto out;
-+ else
-+ {
-+ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic);
-+#if TRACE_ALL
-+ msg_Dbg(vd, "--- %s: Buf stuff", __func__);
-+#endif
-+ if ((err = port_send_replicated(sys->input, sys->pool, pic_buf, pic_buf->pts)) != MMAL_SUCCESS)
++ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS)
+ {
++ mmal_buffer_header_release(pic_buf);
+ msg_Err(vd, "Send buffer to input failed");
+ goto fail;
+ }
@@ -10314,7 +11651,7 @@
+ sub_buf != NULL ? sub_buf : *psub_bufs2++,
+ &sys->subs[sub_no].sub,
+ &p_pic->format,
-+ &(MMAL_RECT_T){.width = sys->display_width, .height = sys->display_height},
++ &sys->dest_rect,
+ p_pic->date)) == 0)
+ break;
+ else if (rv < 0)
@@ -10377,14 +11714,15 @@
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
-@@ -640,11 +758,38 @@
+@@ -640,11 +801,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
++ vd->fmt = vd->source; // Take (nearly) whatever source wants to give us
++ vd->fmt.i_chroma = req_chroma(vd); // Adjust chroma to something we can actaully deal with
+ ret = VLC_SUCCESS;
+ break;
+
@@ -10417,14 +11755,15 @@
default:
msg_Warn(vd, "Unknown control query %d", query);
break;
-@@ -661,13 +806,11 @@
+@@ -661,13 +850,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) {
+- if (query_resolution(vd, &width, &height) >= 0) {
++ if (query_resolution(vd, sys->display_id, &width, &height) >= 0) {
sys->display_width = width;
sys->display_height = height;
- vout_display_SendEventDisplaySize(vd, width, height);
@@ -10433,7 +11772,7 @@
}
sys->need_configure_display = false;
-@@ -676,56 +819,164 @@
+@@ -676,56 +863,171 @@
vlc_mutex_unlock(&sys->manage_mutex);
}
@@ -10466,16 +11805,21 @@
+
+#if 0
+ char dbuf0[5];
-+ msg_Dbg(vd, " [%p:%p] Pos=%d,%d src=%dx%d/%dx%d, vd=%dx%d/%dx%d, Alpha=%d, Fmt=%s", src, src->p[0].p_pixels,
++ msg_Dbg(vd, " [%p:%p] Pos=%d,%d src=%dx%d/%dx%d, vd->fmt=%dx%d/%dx%d, vd->source=%dx%d/%dx%d, cfg=%dx%d, Alpha=%d, Fmt=%s", src, src->p[0].p_pixels,
+ sreg->i_x, sreg->i_y,
+ src->format.i_visible_width, src->format.i_visible_height,
+ src->format.i_width, src->format.i_height,
+ vd->fmt.i_visible_width, vd->fmt.i_visible_height,
+ vd->fmt.i_width, vd->fmt.i_height,
++ vd->source.i_visible_width, vd->source.i_visible_height,
++ vd->source.i_width, vd->source.i_height,
++ vd->cfg->display.width, vd->cfg->display.height,
+ sreg->i_alpha,
+ str_fourcc(dbuf0, src->format.i_chroma));
+#endif
+
++ // At this point I think the subtitles are being placed in the
++ // coord space of the cfg rectangle
+ if ((sys->subpic_bufs[n] = hw_mmal_vzc_buf_from_pic(sys->vzc,
+ src,
+ (MMAL_RECT_T){.width = vd->cfg->display.width, .height=vd->cfg->display.height},
@@ -10510,8 +11854,22 @@
+
+ vd_manage(vd);
+
++ 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);
++ }
++
+ // Subpics can either turn up attached to the main pic or in the
-+ // subpic list here - if they turn up here then process inrto temp
++ // subpic list here - if they turn up here then process into temp
+ // buffers
+ if (subpicture != NULL) {
+ attach_subpics(vd, sys, subpicture);
@@ -10527,25 +11885,8 @@
+
+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->copy_pool->queue);
+ // Copy 2d
-+ unsigned int y_size = sys->input->format->es->video.width * sys->input->format->es->video.height;
-+
++ hw_mmal_copy_pic_to_buf(buf->data, &buf->length, sys->input->format, p_pic);
+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
-+ buf->length = sys->input->buffer_size;
-+
-+ mem_copy_2d(buf->data, sys->input->format->es->video.width,
-+ p_pic->p[0].p_pixels, p_pic->p[0].i_pitch,
-+ sys->input->format->es->video.crop.height,
-+ sys->input->format->es->video.crop.width);
-+
-+ mem_copy_2d(buf->data + y_size, sys->input->format->es->video.width / 2,
-+ p_pic->p[1].p_pixels, p_pic->p[1].i_pitch,
-+ sys->input->format->es->video.crop.height / 2,
-+ sys->input->format->es->video.crop.width / 2);
-+
-+ mem_copy_2d(buf->data + y_size + y_size / 4, sys->input->format->es->video.width / 2,
-+ p_pic->p[2].p_pixels, p_pic->p[2].i_pitch,
-+ sys->input->format->es->video.crop.height / 2,
-+ sys->input->format->es->video.crop.width / 2);
+
+ sys->copy_buf = buf;
+ }
@@ -10567,11 +11908,16 @@
+ if (isp_prepare(vd, isp) != MMAL_SUCCESS)
+ return;
+
-+ buf = pic_mmal_buffer(p_pic);
-+ if ((err = port_send_replicated(isp->input, isp->in_pool,
-+ buf, buf->pts)) != MMAL_SUCCESS)
++ if ((buf = hw_mmal_pic_buf_replicated(p_pic, isp->in_pool)) == NULL)
++ {
++ msg_Err(vd, "Pic has no attached buffer");
++ return;
++ }
++
++ if ((err = mmal_port_send_buffer(isp->input, buf)) != MMAL_SUCCESS)
+ {
+ msg_Err(vd, "Send buffer to input failed");
++ mmal_buffer_header_release(buf);
+ return;
+ }
+
@@ -10632,7 +11978,28 @@
}
static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
-@@ -828,148 +1079,12 @@
+@@ -780,9 +1082,9 @@
+ double best_score, score;
+ int i;
+
+- vc_tv_get_display_state(&display_state);
++ vc_tv_get_display_state_id(sys->display_id, &display_state);
+ if(display_state.display.hdmi.mode != HDMI_MODE_OFF) {
+- num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group,
++ num_modes = vc_tv_hdmi_get_supported_modes_new_id(sys->display_id, display_state.display.hdmi.group,
+ supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL);
+
+ for (i = 0; i < num_modes; ++i) {
+@@ -810,7 +1112,7 @@
+ if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) {
+ msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32,
+ supported_modes[best_id].frame_rate);
+- vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI,
++ vc_tv_hdmi_power_on_explicit_new_id(sys->display_id, HDMI_MODE_HDMI,
+ supported_modes[best_id].group,
+ supported_modes[best_id].code);
+ }
+@@ -828,148 +1130,12 @@
}
}
@@ -10782,7 +12149,7 @@
((double)vd->sys->i_frame_rate /
vd->sys->i_frame_rate_base);
vout_display_sys_t *sys = vd->sys;
-@@ -1012,32 +1127,286 @@
+@@ -1012,32 +1178,317 @@
}
}
@@ -10886,41 +12253,72 @@
+#endif
+}
+
++
++static const struct {
++ const char * name;
++ int num;
++} display_name_to_num[] = {
++ {"auto", -1},
++ {"hdmi-1", DISPMANX_ID_HDMI0},
++ {"hdmi-2", DISPMANX_ID_HDMI1},
++ {NULL, -2}
++};
++
++static int find_display_num(const char * name)
++{
++ unsigned int i;
++ for (i = 0; display_name_to_num[i].name != NULL && strcasecmp(display_name_to_num[i].name, name) != 0; ++i)
++ /* Loop */;
++ return display_name_to_num[i].num;
++}
++
+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;
-+ const MMAL_FOURCC_T enc_in = vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
++ // At the moment all copy is via I420
++ const bool needs_copy = !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma);
++ const MMAL_FOURCC_T enc_in = needs_copy ? MMAL_ENCODING_I420 :
++ vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
+
+#if TRACE_ALL
+ msg_Dbg(vd, "<<< %s", __func__);
+#endif
-+ if (enc_in == 0)
-+ {
-+#if TRACE_ALL
-+ char dbuf0[5];
-+ msg_Dbg(vd, ">>> %s: Format %s not MMAL", __func__, str_fourcc(dbuf0, vd->fmt.i_chroma));
-+#endif
-+ return VLC_EGENERIC;
-+ }
+
+ sys = calloc(1, sizeof(struct vout_display_sys_t));
+ if (!sys)
+ return VLC_ENOMEM;
+ vd->sys = sys;
+
++ vlc_mutex_init(&sys->manage_mutex);
++
+ if ((sys->init_type = cma_vcsm_init()) == VCSM_INIT_NONE)
+ {
+ msg_Err(vd, "VCSM init fail");
+ goto fail;
+ }
+
++ vc_tv_register_callback(tvservice_cb, vd);
++
+ sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
+
++ {
++ const char *display_name = var_InheritString(vd, MMAL_DISPLAY_NAME);
++ int qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber" );
++ int display_id = find_display_num(display_name);
++// sys->display_id = display_id < 0 ? vc_tv_get_default_display_id() : display_id;
++ sys->display_id = display_id >= 0 ? display_id :
++ qt_num == 1 ? DISPMANX_ID_HDMI1 : DISPMANX_ID_HDMI;
++ if (display_id < -1)
++ msg_Warn(vd, "Unknown display device: '%s'", display_name);
++ else
++ msg_Dbg(vd, "Display device: %s, qt=%d id=%d display=%d", display_name,
++ qt_num, display_id, sys->display_id);
++ }
++
+ 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)",
@@ -10961,7 +12359,7 @@
+
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
+
-+ if (!want_copy(vd)) {
++ if (!needs_copy) {
+ sys->input->buffer_num = 30;
+ }
+ else {
@@ -10973,18 +12371,25 @@
+ }
+ }
+
-+ vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
++ if (query_resolution(vd, sys->display_id, &sys->display_width, &sys->display_height) < 0)
++ {
++ sys->display_width = vd->cfg->display.width;
++ sys->display_height = vd->cfg->display.height;
++ }
++
++ place_dest(vd, sys, vd->cfg, &vd->source); // Sets sys->dest_rect
++
+ display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
+ display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
++ display_region.display_num = sys->display_id;
+ display_region.fullscreen = MMAL_FALSE;
+ display_src_rect(vd, &display_region.src_rect);
-+ 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.dest_rect = sys->dest_rect;
+ 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;
++ display_region.set =
++ MMAL_DISPLAY_SET_NUM |
++ 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)",
@@ -11012,35 +12417,33 @@
+ 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;
-+ }
++ for (unsigned int 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->display_id, 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);
++ // If we can't deal with it directly ask for I420
++ vd->fmt.i_chroma = req_chroma(vd);
+
+ vd->info = (vout_display_info_t){
+ .is_slow = false,
@@ -11055,13 +12458,6 @@
+ 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)
-+ {
-+ sys->display_width = vd->cfg->display.width;
-+ sys->display_height = vd->cfg->display.height;
-+ }
+
+ msg_Dbg(vd, ">>> %s: ok", __func__);
+ return VLC_SUCCESS;
@@ -11090,6 +12486,8 @@
+ MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
+ add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
+ MMAL_NATIVE_INTERLACE_LONGTEXT, false)
++ add_string(MMAL_DISPLAY_NAME, "auto", MMAL_DISPLAY_TEXT,
++ MMAL_DISPLAY_LONGTEXT, false)
+ set_callbacks(OpenMmalVout, CloseMmalVout)
+
+vlc_module_end()
@@ -11097,7 +12495,7 @@
+
--- /dev/null
+++ b/modules/hw/mmal/xsplitter.c
-@@ -0,0 +1,437 @@
+@@ -0,0 +1,560 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
@@ -11119,16 +12517,97 @@
+
+#define TRACE_ALL 0
+
++typedef struct display_desc_s
++{
++ vout_display_t * vout;
++ unsigned int max_pels;
++} display_desc_t;
++
+typedef struct mmal_x11_sys_s
+{
+ bool use_mmal;
-+ vout_display_t * cur_vout;
-+ vout_display_t * mmal_vout;
-+ vout_display_t * x_vout;
++ display_desc_t * cur_desc;
++ display_desc_t mmal_desc;
++ display_desc_t x_desc;
+ uint32_t changed;
+ vlc_fourcc_t subpicture_chromas[16];
+} mmal_x11_sys_t;
+
++#define MAX_GL_PELS (1920*1080)
++#define MAX_MMAL_PELS (4096*4096) // Should never be hit
++
++#if 0
++// Gen prog for the following table
++// Not done inline in case we end up pulling in FP libs we don't want
++#include <math.h>
++#include <stdio.h>
++
++int main(int argc, char *argv[])
++{
++ unsigned int i;
++ for (i = 0; i != 64; ++i)
++ {
++ printf(" [%2u]=%5u,", i, (unsigned int)(0.5 + (1/sqrt((i + 5)/4.0) * 65536.0)));
++ if (i % 4 == 3)
++ printf("\n");
++ }
++}
++#endif
++
++static const uint16_t sqrt_tab[64] = {
++ [ 0]=58617, [ 1]=53510, [ 2]=49541, [ 3]=46341,
++ [ 4]=43691, [ 5]=41449, [ 6]=39520, [ 7]=37837,
++ [ 8]=36353, [ 9]=35030, [10]=33843, [11]=32768,
++ [12]=31790, [13]=30894, [14]=30070, [15]=29309,
++ [16]=28602, [17]=27945, [18]=27330, [19]=26755,
++ [20]=26214, [21]=25705, [22]=25225, [23]=24770,
++ [24]=24339, [25]=23930, [26]=23541, [27]=23170,
++ [28]=22817, [29]=22479, [30]=22155, [31]=21845,
++ [32]=21548, [33]=21263, [34]=20988, [35]=20724,
++ [36]=20470, [37]=20225, [38]=19988, [39]=19760,
++ [40]=19539, [41]=19326, [42]=19119, [43]=18919,
++ [44]=18725, [45]=18536, [46]=18354, [47]=18176,
++ [48]=18004, [49]=17837, [50]=17674, [51]=17515,
++ [52]=17361, [53]=17211, [54]=17064, [55]=16921,
++ [56]=16782, [57]=16646, [58]=16514, [59]=16384,
++ [60]=16257, [61]=16134, [62]=16013, [63]=15895
++};
++#define SQRT_MAX (sizeof(sqrt_tab)/sizeof(sqrt_tab[0]) - 1)
++
++static bool cpy_fmt_limit_size(const display_desc_t * const dd,
++ video_format_t * const dst,
++ const video_format_t * const src)
++{
++ const unsigned int src_pel = src->i_visible_width * src->i_visible_height;
++
++ *dst = *src;
++
++ if (src_pel <= dd->max_pels)
++ return false;
++
++ // scaling factor sqrt(max_pel/cur_pel)
++ // sqrt done by lookup & 16 bit fixed-point maths - not exactly accurate but
++ // easily good enough & avoids floating point (which may be slow)
++ // src_pel > max_pel so n >= 0
++ // Rounding should be such that exact sqrts work and everything else rounds
++ // down
++ unsigned int n = ((src_pel * 4 - 1) / dd->max_pels) - 4;
++ unsigned int scale = sqrt_tab[n >= SQRT_MAX ? SQRT_MAX : n];
++
++ // Rescale width - rounding up to 16
++ unsigned int width = ((src->i_visible_width * scale + (16 << 16) - 1) >> 16) & ~15;
++ // Rescale height based on new width
++ unsigned int height = (src->i_visible_height * width + src->i_visible_width/2) / src->i_visible_width;
++
++// fprintf(stderr, "%dx%d -> %dx%d\n", src->i_visible_width, src->i_visible_height, width, height);
++
++ dst->i_width = width;
++ dst->i_visible_width = width;
++ dst->i_height = height;
++ dst->i_visible_height = height;
++ return true;
++}
++
+static void unload_display_module(vout_display_t * const x_vout)
+{
+ if (x_vout != NULL) {
@@ -11149,9 +12628,9 @@
+ if (sys == NULL)
+ return;
+
-+ unload_display_module(sys->x_vout);
++ unload_display_module(sys->x_desc.vout);
+
-+ unload_display_module(sys->mmal_vout);
++ unload_display_module(sys->mmal_desc.vout);
+
+ free(sys);
+
@@ -11192,13 +12671,16 @@
+}
+
+
-+static vout_display_t * load_display_module(vout_display_t * const vd,
-+ const char * const cap, const char * const module_name)
++static int load_display_module(vout_display_t * const vd,
++ display_desc_t * const dd,
++ const char * const cap,
++ const char * const module_name)
+{
+ vout_display_t * const x_vout = vlc_object_create(vd, sizeof(*x_vout));
+
++ dd->vout = NULL;
+ if (!x_vout)
-+ return NULL;
++ return -1;
+
+ x_vout->owner.sys = vd;
+ x_vout->owner.event = mmal_x11_event;
@@ -11206,10 +12688,9 @@
+ 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;
++ cpy_fmt_limit_size(dd, &x_vout->source, &vd->source);
++ cpy_fmt_limit_size(dd, &x_vout->fmt, &vd->fmt);
+
+ if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL)
+ {
@@ -11219,11 +12700,12 @@
+
+ msg_Dbg(vd, "R/G/B: %08x/%08x/%08x", x_vout->fmt.i_rmask, x_vout->fmt.i_gmask, x_vout->fmt.i_bmask);
+
-+ return x_vout;
++ dd->vout = x_vout;
++ return 0;
+
+fail:
+ vlc_object_release(x_vout);
-+ return NULL;
++ return -1;
+}
+
+
@@ -11239,7 +12721,7 @@
+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;
++ vout_display_t * const x_vd = sys->cur_desc->vout;
+#if TRACE_ALL
+ char buf0[5];
+ msg_Dbg(vd, "<<< %s (count=%d) %s:%dx%d->%s:%dx%d", __func__, count,
@@ -11268,7 +12750,7 @@
+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;
++ vout_display_t * const x_vd = sys->cur_desc->vout;
+#if TRACE_ALL
+ msg_Dbg(vd, "<<< %s", __func__);
+#endif
@@ -11289,10 +12771,10 @@
+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 = hw_mmal_pic_is_mmal(pic);
++ vout_display_t * const x_vd = sys->cur_desc->vout;
+
+#if TRACE_ALL
++ const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic);
+ msg_Dbg(vd, "<<< %s: fmt: %dx%d/%dx%d, pic:%dx%d, pts=%lld, mmal=%d/%d", __func__, vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height, pic->format.i_width, pic->format.i_height, (long long)pic->date,
+ is_mmal_pic, sys->use_mmal);
+#endif
@@ -11312,28 +12794,29 @@
+}
+
+
-+static int vout_display_Control(vout_display_t *vd, int query, ...)
++static int vout_display_Control(display_desc_t * const dd, int query, ...)
+{
+ va_list args;
+ int result;
+
+ va_start(args, query);
-+ result = vd->control(vd, query, args);
++ result = dd->vout->control(dd->vout, query, args);
+ va_end(args);
+
+ return result;
+}
+
-+static bool want_mmal_vout(vout_display_t * vd, const mmal_x11_sys_t * const sys)
++static bool want_mmal_vout(vout_display_t * const vd, const mmal_x11_sys_t * const sys)
+{
-+ return sys->mmal_vout != NULL && (sys->x_vout == NULL || var_InheritBool(vd, "fullscreen"));
++ return sys->mmal_desc.vout != NULL &&
++ (sys->x_desc.vout == NULL || var_InheritBool(vd, "fullscreen"));
+}
+
+/* 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;
++ display_desc_t *x_desc = sys->cur_desc;
+ int rv;
+#if TRACE_ALL
+ msg_Dbg(vd, "<<< %s[%d] (ctl=%d)", __func__, sys->use_mmal, ctl);
@@ -11348,23 +12831,23 @@
+ const vout_display_cfg_t * const cfg = va_arg(va, const vout_display_cfg_t *);
+ const bool want_mmal = want_mmal_vout(vd, sys);
+ const bool swap_vout = (sys->use_mmal != want_mmal);
-+ vout_display_t * const new_vd = want_mmal ? sys->mmal_vout : sys->x_vout;
++ display_desc_t * const new_desc = want_mmal ? &sys->mmal_desc : &sys->x_desc;
+
+ msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d",
-+ cfg->display.width, cfg->display.height, sys->mmal_vout, want_mmal,
++ cfg->display.width, cfg->display.height, sys->mmal_desc.vout, want_mmal,
+ var_InheritBool(vd, "fullscreen"));
+
+ if (swap_vout) {
+ if (sys->use_mmal) {
-+ vout_display_Control(x_vd, VOUT_DISPLAY_CHANGE_MMAL_HIDE);
++ vout_display_Control(x_desc, VOUT_DISPLAY_CHANGE_MMAL_HIDE);
+ }
+ vout_display_SendEventPicturesInvalid(vd);
+ }
+
-+ rv = vout_display_Control(new_vd, ctl, cfg);
++ rv = vout_display_Control(new_desc, ctl, cfg);
+ if (rv == VLC_SUCCESS) {
-+ vd->fmt = new_vd->fmt;
-+ sys->cur_vout = new_vd;
++ vd->fmt = new_desc->vout->fmt;
++ sys->cur_desc = new_desc;
+ sys->use_mmal = want_mmal;
+ }
+
@@ -11373,43 +12856,50 @@
+ 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);
++ vout_display_Control(new_desc, 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;
++ vout_display_Control(new_desc, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg);
++ if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) |
++ (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0)
++ cpy_fmt_limit_size(new_desc, &new_desc->vout->source, &vd->source);
+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT)) != 0)
-+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT);
++ vout_display_Control(new_desc, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT);
+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP)) != 0)
-+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP);
++ vout_display_Control(new_desc, VOUT_DISPLAY_CHANGE_SOURCE_CROP);
+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_VIEWPOINT)) != 0)
-+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg);
++ vout_display_Control(new_desc, VOUT_DISPLAY_CHANGE_VIEWPOINT, vd->cfg);
+ }
+
+ break;
+ }
+
+ case VOUT_DISPLAY_RESET_PICTURES:
-+ 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);
++ {
++ char dbuf0[5], dbuf1[5], dbuf2[5];
++ msg_Dbg(vd, "<<< %s: Pic reset: fmt: %s,%dx%d<-%s,%dx%d, source: %s,%dx%d/%dx%d", __func__,
++ str_fourcc(dbuf0, vd->fmt.i_chroma), vd->fmt.i_width, vd->fmt.i_height,
++ str_fourcc(dbuf1, x_desc->vout->fmt.i_chroma), x_desc->vout->fmt.i_width, x_desc->vout->fmt.i_height,
++ str_fourcc(dbuf2, vd->source.i_chroma), vd->source.i_width, vd->source.i_height, x_desc->vout->source.i_width,
++ x_desc->vout->source.i_height);
++ }
+ // If the display doesn't have has_pictures_invalid then it doesn't
+ // expect RESET_PICTURES
-+ if (sys->x_vout->info.has_pictures_invalid) {
-+ rv = sys->x_vout->control(sys->x_vout, ctl, va);
++ if (sys->x_desc.vout->info.has_pictures_invalid) {
++ rv = sys->x_desc.vout->control(sys->x_desc.vout, ctl, va);
+ }
-+ if (sys->mmal_vout && sys->mmal_vout->info.has_pictures_invalid) {
-+ rv = sys->mmal_vout->control(sys->mmal_vout, ctl, va);
++ if (sys->mmal_desc.vout && sys->mmal_desc.vout->info.has_pictures_invalid) {
++ rv = sys->mmal_desc.vout->control(sys->mmal_desc.vout, ctl, va);
+ }
-+ vd->fmt = x_vd->fmt;
++ vd->fmt = x_desc->vout->fmt;
+ break;
+
+ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
+ case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
-+ x_vd->source = vd->source;
++ cpy_fmt_limit_size(x_desc, &x_desc->vout->source, &vd->source);
++
+ /* FALLTHRU */
+ default:
-+ rv = x_vd->control(x_vd, ctl, va);
++ rv = x_desc->vout->control(x_desc->vout, ctl, va);
+// vd->fmt = x_vd->fmt;
+ break;
+ }
@@ -11426,7 +12916,7 @@
+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;
++ vout_display_t * const x_vd = sys->cur_desc->vout;
+#if TRACE_ALL
+ msg_Dbg(vd, "<<< %s", __func__);
+#endif
@@ -11453,15 +12943,34 @@
+ .subpicture_chromas = NULL
+ };
+
-+ if ((sys->x_vout = load_display_module(vd, "vout display", "opengles2")) != NULL)
++ {
++ char dbuf0[5];
++ msg_Dbg(vd, ">>> %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
++ str_fourcc(dbuf0, vd->fmt.i_chroma),
++ vd->fmt.i_width, vd->fmt.i_height,
++ vd->fmt.i_x_offset, vd->fmt.i_y_offset,
++ vd->fmt.i_visible_width, vd->fmt.i_visible_height,
++ vd->fmt.i_sar_num, vd->fmt.i_sar_den);
++ }
++
++ sys->x_desc.max_pels = MAX_GL_PELS;
++ sys->mmal_desc.max_pels = MAX_MMAL_PELS;
++
++ if (load_display_module(vd, &sys->x_desc, "vout display", "opengles2") == 0)
++ {
+ msg_Dbg(vd, "Opengles2 output found");
-+ else if ((sys->x_vout = load_display_module(vd, "vout display", "xcb_x11")) != NULL)
-+ msg_Dbg(vd, "X11 XCB output found");
++ }
++ else
++ {
++ sys->x_desc.max_pels = MAX_MMAL_PELS;
++ if (load_display_module(vd, &sys->x_desc, "vout display", "xcb_x11") == 0)
++ msg_Dbg(vd, "X11 XCB output found");
++ }
+
-+ if ((sys->mmal_vout = load_display_module(vd, "vout display", "mmal_vout")) != NULL)
++ if ((load_display_module(vd, &sys->mmal_desc, "vout display", "mmal_vout")) == 0)
+ msg_Dbg(vd, "MMAL output found");
+
-+ if (sys->mmal_vout == NULL && sys->x_vout == NULL) {
++ if (sys->mmal_desc.vout == NULL && sys->x_desc.vout == NULL) {
+ char dbuf0[5], dbuf1[5];
+ msg_Info(vd, "No valid output found for vout (%s/%s)", str_fourcc(dbuf0, vd->fmt.i_chroma), str_fourcc(dbuf1, vd->source.i_chroma));
+ goto fail;
@@ -11476,33 +12985,33 @@
+#endif
+
+ if (want_mmal_vout(vd, sys)) {
-+ sys->cur_vout = sys->mmal_vout;
++ sys->cur_desc = &sys->mmal_desc;
+ sys->use_mmal = true;
+ }
+ else {
-+ sys->cur_vout = sys->x_vout;
++ sys->cur_desc = &sys->x_desc;
+ sys->use_mmal = false;
+ }
+
-+ if (sys->mmal_vout == NULL || sys->x_vout == NULL) {
-+ vd->info = sys->cur_vout->info;
++ if (sys->mmal_desc.vout == NULL || sys->x_desc.vout == NULL) {
++ vd->info = sys->cur_desc->vout->info;
+ vd->info.has_pictures_invalid = true; // Should make this unwanted
+ }
+ else {
+ // We have both - construct a combination
+ vd->info = (vout_display_info_t){
+ .is_slow = false,
-+ .has_double_click = sys->mmal_vout->info.has_double_click || sys->x_vout->info.has_double_click,
-+ .needs_hide_mouse = sys->mmal_vout->info.needs_hide_mouse || sys->x_vout->info.needs_hide_mouse,
++ .has_double_click = sys->mmal_desc.vout->info.has_double_click || sys->x_desc.vout->info.has_double_click,
++ .needs_hide_mouse = sys->mmal_desc.vout->info.needs_hide_mouse || sys->x_desc.vout->info.needs_hide_mouse,
+ .has_pictures_invalid = true,
+ };
+ // Construct intersection of subpicture chromas
+ // sys calloced so no need to add the terminating zero
-+ if (sys->mmal_vout->info.subpicture_chromas != NULL && sys->x_vout->info.subpicture_chromas != NULL) {
++ if (sys->mmal_desc.vout->info.subpicture_chromas != NULL && sys->x_desc.vout->info.subpicture_chromas != NULL) {
+ unsigned int n = 0;
+ // N^2 - fix if we ever care
-+ for (const vlc_fourcc_t * p1 = sys->mmal_vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) {
-+ for (const vlc_fourcc_t * p2 = sys->x_vout->info.subpicture_chromas; *p2 != 0; ++p2) {
++ for (const vlc_fourcc_t * p1 = sys->mmal_desc.vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) {
++ for (const vlc_fourcc_t * p2 = sys->x_desc.vout->info.subpicture_chromas; *p2 != 0; ++p2) {
+ if (*p1 == *p2) {
+ sys->subpicture_chromas[n++] = *p1;
+ break;
@@ -11513,8 +13022,20 @@
+ vd->info.subpicture_chromas = sys->subpicture_chromas;
+ }
+ }
-+ vd->fmt = sys->cur_vout->fmt;
++ vd->fmt = sys->cur_desc->vout->fmt;
+
++#if TRACE_ALL
++ {
++ char dbuf0[5];
++ msg_Dbg(vd, ">>> %s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__,
++ module_get_name(sys->cur_desc->vout->module, false),
++ str_fourcc(dbuf0, vd->fmt.i_chroma),
++ vd->fmt.i_width, vd->fmt.i_height,
++ vd->fmt.i_x_offset, vd->fmt.i_y_offset,
++ vd->fmt.i_visible_width, vd->fmt.i_visible_height,
++ vd->fmt.i_sar_num, vd->fmt.i_sar_den);
++ }
++#endif
+ return VLC_SUCCESS;
+
+fail:
@@ -11561,15 +13082,34 @@
const EGLint conf_attr[] = {
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 5,
+--- a/src/input/decoder.c
++++ b/src/input/decoder.c
+@@ -1995,6 +1995,7 @@
+ vlc_mutex_lock( &p_owner->lock );
+ p_owner->b_waiting = false;
+ vlc_cond_signal( &p_owner->wait_request );
++ vlc_mutex_unlock( &p_owner->lock );
+
+ /* If the video output is paused or slow, or if the picture pool size was
+ * under-estimated (e.g. greedy video filter, buggy decoder...), the
+@@ -2005,7 +2006,6 @@
+ * worker threads (if any) and the decoder thread to terminate. */
+ if( p_owner->p_vout != NULL )
+ vout_Cancel( p_owner->p_vout, true );
+- vlc_mutex_unlock( &p_owner->lock );
+
+ vlc_join( p_owner->thread, NULL );
+
--- a/src/misc/fourcc.c
+++ b/src/misc/fourcc.c
-@@ -755,8 +755,12 @@
+@@ -755,8 +755,13 @@
{ { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422,
VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT },
FAKE_FMT() },
- { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE,
- VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE },
-+ { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE },
++ { { VLC_CODEC_ANDROID_OPAQUE }, FAKE_FMT() },
++ { { VLC_CODEC_MMAL_OPAQUE, VLC_CODEC_MMAL_ZC_SAND30 },
+ FAKE_FMT() },
+ { { VLC_CODEC_MMAL_ZC_I420, VLC_CODEC_MMAL_ZC_SAND8,
+ VLC_CODEC_MMAL_ZC_SAND10, VLC_CODEC_MMAL_ZC_RGB32 },
@@ -11578,3 +13118,67 @@
FAKE_FMT() },
{ { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B },
FAKE_FMT() },
+--- a/src/misc/picture.c
++++ b/src/misc/picture.c
+@@ -365,10 +365,30 @@
+ p_dst->b_top_field_first = p_src->b_top_field_first;
+ }
+
++static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma)
++{
++ return i_chroma == VLC_CODEC_MMAL_OPAQUE ||
++ i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
++ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
++ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
++ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
++ i_chroma == VLC_CODEC_MMAL_ZC_SAND8;
++}
++
+ void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src )
+ {
+- for( int i = 0; i < p_src->i_planes ; i++ )
+- plane_CopyPixels( p_dst->p+i, p_src->p+i );
++ if( is_zc_chroma(p_src->format.i_chroma) )
++ {
++ assert(p_dst->i_planes == 0);
++ p_dst->i_planes = p_src->i_planes;
++ for( int i = 0; i < p_src->i_planes; i++ )
++ p_dst->p[i] = p_src->p[i];
++ }
++ else
++ {
++ for( int i = 0; i < p_src->i_planes; i++ )
++ plane_CopyPixels( p_dst->p+i, p_src->p+i );
++ }
+
+ assert( p_dst->context == NULL );
+
+--- a/src/video_output/video_output.c
++++ b/src/video_output/video_output.c
+@@ -964,6 +964,17 @@
+ return NULL;
+ }
+
++
++static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma)
++{
++ return i_chroma == VLC_CODEC_MMAL_OPAQUE ||
++ i_chroma == VLC_CODEC_MMAL_ZC_I420 ||
++ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 ||
++ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
++ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 ||
++ i_chroma == VLC_CODEC_MMAL_ZC_SAND8;
++}
++
+ static int ThreadDisplayRenderPicture(vout_thread_t *vout, bool is_forced)
+ {
+ vout_thread_sys_t *sys = vout->p;
+@@ -1098,7 +1109,7 @@
+ }
+
+ assert(vout_IsDisplayFiltered(vd) == !sys->display.use_dr);
+- if (sys->display.use_dr && !is_direct) {
++ if (sys->display.use_dr && !is_direct && !is_zc_chroma(todisplay->format.i_chroma)) {
+ picture_t *direct = NULL;
+ if (likely(vout->p->display_pool != NULL))
+ direct = picture_pool_Get(vout->p->display_pool);
diff --git a/sources b/sources
index cc99bb4..3bf1f0a 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-SHA512 (vlc-3.0.9-20191014-0223.tar.xz) = c4585121d67d426d4cc1441f3b7f11be864b2a9e4da100e5b5f3a7116a4bce8e96c809ee9048b9566cf8c1893d2326ca9afad5913696ecfd8b502f80cdfd1a78
+SHA512 (vlc-3.0.9-20200118-0223.tar.xz) = 3ad1e02a4603953fc08e4be7b3a4bb2fb05fe987b26559467e6b19b437dfc01c76ce4d03185fe3bda7d3d6c07cca9b99607a2cc530f266f4eb0f37d05e0f32c9
diff --git a/vlc.spec b/vlc.spec
index 2b1ccf9..8e4ec8e 100644
--- a/vlc.spec
+++ b/vlc.spec
@@ -1,4 +1,4 @@
-%global vlc_date 20191014
+%global vlc_date 20200118
#global vlc_rc -rc9
%global vlc_tag -%{?vlc_date}-0223
%if 0%{?vlc_tag:1}
@@ -54,11 +54,11 @@ Summary: The cross-platform open-source multimedia framework, player and server
Epoch: 1
Name: vlc
Version: 3.0.9
-Release: 27%{?dist}
+Release: 28%{?dist}
License: GPLv2+
URL: https://www.videolan.org
Source0: %{vlc_url}/%{?!vlc_tag:%{version}/}vlc-%{version}%{?vlc_tag}.tar.xz
-Patch0: https://github.com/RPi-Distro/vlc/raw/buster-rpt/debian/patches/mmal_10.p...
+Patch0: https://github.com/RPi-Distro/vlc/raw/buster-rpt/debian/patches/mmal_16.p...
Patch1: libplacebo_patch_1.patch
Patch2: Fix_aom_abi_break.patch
Patch3: 0001-Use-SYSTEM-wide-ciphers-for-gnutls.patch
@@ -129,7 +129,9 @@ BuildRequires: libmtp-devel >= 1.0.0
%{?_with_projectm:BuildRequires: libprojectM-devel}
BuildRequires: libproxy-devel
BuildRequires: librsvg2-devel >= 2.9.0
+%if ! 0%{?el8}
BuildRequires: libssh2-devel
+%endif
BuildRequires: libsysfs-devel
BuildRequires: libshout-devel
BuildRequires: libsmbclient-devel
@@ -560,6 +562,11 @@ fi || :
%changelog
+* Sat Jan 18 2020 Nicolas Chauvet <kwizart(a)gmail.com> - 1:3.0.9-28
+- Update to current snapshot
+- Drop libssh2 from el8 - rfbz#5519
+- Update mmal patch
+
* Sun Dec 22 2019 Leigh Scott <leigh123linux(a)googlemail.com> - 1:3.0.9-27
- Rebuild for new protobuf version
4 years, 10 months
[buildsys-build-rpmfusion/el8] rebuild for kernel 4.18.0-147.el8
by Nicolas Chauvet
commit c98f839e02058e9104aaeb4508e426337dbbfccf
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Sat Jan 18 08:35:46 2020 +0100
rebuild for kernel 4.18.0-147.el8
buildsys-build-rpmfusion-kerneldevpkgs-current | 8 ++++----
buildsys-build-rpmfusion.spec | 5 ++++-
2 files changed, 8 insertions(+), 5 deletions(-)
---
diff --git a/buildsys-build-rpmfusion-kerneldevpkgs-current b/buildsys-build-rpmfusion-kerneldevpkgs-current
index 457a42c..35debdc 100644
--- a/buildsys-build-rpmfusion-kerneldevpkgs-current
+++ b/buildsys-build-rpmfusion-kerneldevpkgs-current
@@ -1,4 +1,4 @@
-4.18.0-80.el8
-4.18.0-80.el8smp
-4.18.0-80.el8PAE
-4.18.0-80.el8lpae
+4.18.0-147.el8
+4.18.0-147.el8smp
+4.18.0-147.el8PAE
+4.18.0-147.el8lpae
diff --git a/buildsys-build-rpmfusion.spec b/buildsys-build-rpmfusion.spec
index 9e60ccb..4f60f03 100644
--- a/buildsys-build-rpmfusion.spec
+++ b/buildsys-build-rpmfusion.spec
@@ -3,7 +3,7 @@
Name: buildsys-build-%{repo}
Epoch: 11
Version: 30
-Release: 1%{?dist}
+Release: 2%{?dist}
Summary: Tools and files used by the %{repo} buildsys
License: MIT
@@ -77,6 +77,9 @@ sed -i 's|^default_prefix=.*|default_prefix=%{_datadir}/%{name}/|' \
%changelog
+* Sat Jan 18 2020 Nicolas Chauvet <kwizart(a)gmail.com> - 11:30-2
+- rebuild for kernel 4.18.0-147.el8
+
* Fri May 17 2019 Nicolas Chauvet <kwizart(a)gmail.com> - 10:30-1
- rebuild for kernel 4.18.0-80.el8
4 years, 10 months
[chromium-browser-privacy/f30] Update to 79.0.3945.130
by qvint
commit ff2f8e550edbbe339c58160926bcc105d422a529
Author: qvint <dotqvint(a)gmail.com>
Date: Fri Jan 17 18:53:19 2020 +0300
Update to 79.0.3945.130
- Update Chromium to 79.0.3945.130
- Update ungoogled-chromium to 79.0.3945.130-1
chromium-browser-privacy.spec | 8 ++++++--
sources | 4 ++--
2 files changed, 8 insertions(+), 4 deletions(-)
---
diff --git a/chromium-browser-privacy.spec b/chromium-browser-privacy.spec
index 509daf0..7ad8df1 100644
--- a/chromium-browser-privacy.spec
+++ b/chromium-browser-privacy.spec
@@ -69,7 +69,7 @@
%global ozone 0
##############################Package Definitions######################################
Name: chromium-browser-privacy
-Version: 79.0.3945.117
+Version: 79.0.3945.130
Release: 1%{?dist}
Summary: Chromium, sans integration with Google
License: BSD and LGPLv2+ and ASL 2.0 and IJG and MIT and GPLv2+ and ISC and OpenSSL and (MPLv1.1 or GPLv2 or LGPLv2)
@@ -91,7 +91,7 @@ Source0: https://commondatastorage.googleapis.com/chromium-browser-official/c
# ./chromium-latest.py --stable --ffmpegclean --ffmpegarm --deleteunrar
Source0: chromium-%{version}-clean.tar.xz
%endif
-%global ungoogled_chromium_revision 79.0.3945.117-1
+%global ungoogled_chromium_revision 79.0.3945.130-1
Source300: https://github.com/Eloston/ungoogled-chromium/archive/%{ungoogled_chromiu...
# The following two source files are copied and modified from the chromium source
Source10: %{name}.sh
@@ -797,6 +797,10 @@ appstream-util validate-relax --nonet "%{buildroot}%{_metainfodir}/%{name}.appda
%{chromiumdir}/swiftshader/libGLESv2.so
#########################################changelogs#################################################
%changelog
+* Fri Jan 17 2020 qvint <dotqvint(a)gmail.com> - 79.0.3945.130-1
+- Update Chromium to 79.0.3945.130
+- Update ungoogled-chromium to 79.0.3945.130-1
+
* Wed Jan 08 2020 qvint <dotqvint(a)gmail.com> - 79.0.3945.117-1
- Update Chromium to 79.0.3945.117
- Update ungoogled-chromium to 79.0.3945.117-1
diff --git a/sources b/sources
index 55f6fae..b202d7c 100644
--- a/sources
+++ b/sources
@@ -1,2 +1,2 @@
-SHA512 (chromium-79.0.3945.117.tar.xz) = bba4f52eec8011f92e3098d29269d978b6e2571593b66c93bf6767bab00f7984e733e6df5fde883b9f643e86b1ad527c3ec7d43ba381b48935512ad82e931e26
-SHA512 (ungoogled-chromium-79.0.3945.117-1.tar.gz) = ad74112a0160c7703467d82c4a16924c030e2615f524ce9352bfa172a9a043c2ae34d12ee84d1d230101434b0377649a4d06ff6542bd49335e7e88b09f6678af
+SHA512 (chromium-79.0.3945.130.tar.xz) = 4b5d4769b068f20b8028085a148cbaf31311eeffad0a22c5c0909af273a8e57e077b33cc93331bbeff9165477262eed2e345b2224ac4f76d8bdb79f983596a81
+SHA512 (ungoogled-chromium-79.0.3945.130-1.tar.gz) = a5e18fe599e1d00ba3cd755168c73a4e96edecf581e747aae12f4426fc57ee4d17073505848790ead921855923915ecb10ee7cd8ec6f2a9bd54bee52973e2765
4 years, 10 months
[telegram-desktop/f31] Updated to version 1.9.4.
by Vitaly Zaitsev
commit 28e9b0405abdb1c23279ed3c82195dbaaa8f6362
Author: Vitaly Zaitsev <vitaly(a)easycoding.org>
Date: Fri Jan 17 20:33:04 2020 +0100
Updated to version 1.9.4.
.gitignore | 5 +
cmake_helpers-system-expected.patch | 4 +-
cmake_helpers-system-gsl.patch | 4 +-
cmake_helpers-system-libraries.patch | 1233 ----------------------
cmake_helpers-system-qrcode.patch | 4 +-
cmake_helpers-system-variant.patch | 4 +-
lib_ui-remove-configs.patch | 25 -
sources | 10 +-
telegram-desktop-commit-100fed3.patch | 30 -
telegram-desktop-commit-322367c.patch | 104 --
telegram-desktop-fix-appdata.patch | 4 +-
telegram-desktop-fix-desktop.patch | 4 +-
telegram-desktop-pr6956.patch | 1819 ---------------------------------
telegram-desktop-pr6985.patch | 30 -
telegram-desktop.spec | 36 +-
15 files changed, 32 insertions(+), 3284 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index d2b5eab..0519238 100644
--- a/.gitignore
+++ b/.gitignore
@@ -119,3 +119,8 @@
/lib_ui-4ec9e32.tar.gz
/rlottie-c490c7a.tar.gz
/tdesktop-1.9.3.tar.gz
+/cmake_helpers-458fec9.tar.gz
+/lib_base-8ba8607.tar.gz
+/lib_spellcheck-4784796.tar.gz
+/lib_ui-c0b0745.tar.gz
+/tdesktop-1.9.4.tar.gz
diff --git a/cmake_helpers-system-expected.patch b/cmake_helpers-system-expected.patch
index f6833c0..bb7ab96 100644
--- a/cmake_helpers-system-expected.patch
+++ b/cmake_helpers-system-expected.patch
@@ -1,7 +1,7 @@
-From 506b42ad9db309231e21e33bcc35995a135261c0 Mon Sep 17 00:00:00 2001
+From 03300739a87eba33e28f0ee5299745f9100bb0c9 Mon Sep 17 00:00:00 2001
From: Vitaly Zaitsev <vitaly(a)easycoding.org>
Date: Thu, 9 Jan 2020 14:42:45 +0100
-Subject: [PATCH 2/5] Use packaged version of expected library.
+Subject: [PATCH 1/4] Use packaged version of expected library.
---
external/expected/CMakeLists.txt | 12 ++++++++----
diff --git a/cmake_helpers-system-gsl.patch b/cmake_helpers-system-gsl.patch
index 923cf57..eff43f1 100644
--- a/cmake_helpers-system-gsl.patch
+++ b/cmake_helpers-system-gsl.patch
@@ -1,7 +1,7 @@
-From 5a25232dbf1b69e7497602e1d439115b76d0f9ff Mon Sep 17 00:00:00 2001
+From 5365f0e2a6228873b4ac625d113d9d1a733abe2c Mon Sep 17 00:00:00 2001
From: Vitaly Zaitsev <vitaly(a)easycoding.org>
Date: Thu, 9 Jan 2020 14:43:15 +0100
-Subject: [PATCH 3/5] Use packaged version of guidelines-support-library.
+Subject: [PATCH 2/4] Use packaged version of guidelines-support-library.
---
external/gsl/CMakeLists.txt | 12 ++++++++----
diff --git a/cmake_helpers-system-qrcode.patch b/cmake_helpers-system-qrcode.patch
index d3cd399..ac5c631 100644
--- a/cmake_helpers-system-qrcode.patch
+++ b/cmake_helpers-system-qrcode.patch
@@ -1,7 +1,7 @@
-From 82de89301519043e392c4475f7aee5c24e94fd69 Mon Sep 17 00:00:00 2001
+From fee202d3b1f86a2a1d63c8f7eb828deb439a3d85 Mon Sep 17 00:00:00 2001
From: Vitaly Zaitsev <vitaly(a)easycoding.org>
Date: Thu, 9 Jan 2020 14:44:11 +0100
-Subject: [PATCH 4/5] Use packaged version of qr-code-generator library.
+Subject: [PATCH 3/4] Use packaged version of qr-code-generator library.
---
external/qr_code_generator/CMakeLists.txt | 41 +++++++++++++----------
diff --git a/cmake_helpers-system-variant.patch b/cmake_helpers-system-variant.patch
index d8a5d3d..03fa271 100644
--- a/cmake_helpers-system-variant.patch
+++ b/cmake_helpers-system-variant.patch
@@ -1,7 +1,7 @@
-From 08551b891e74445c6d7093edc1db54a819d7268b Mon Sep 17 00:00:00 2001
+From 539fe57aa89160ef775fe6995dd7173bd040a8e3 Mon Sep 17 00:00:00 2001
From: Vitaly Zaitsev <vitaly(a)easycoding.org>
Date: Thu, 9 Jan 2020 14:44:54 +0100
-Subject: [PATCH 5/5] Use packaged version of mapbox-variant library.
+Subject: [PATCH 4/4] Use packaged version of mapbox-variant library.
---
external/variant/CMakeLists.txt | 13 +++++++++----
diff --git a/sources b/sources
index c8c9f89..e78ebce 100644
--- a/sources
+++ b/sources
@@ -1,13 +1,13 @@
-SHA512 (cmake_helpers-652bbaf.tar.gz) = 609ad8196eb3a7dbbe3b2f3daf9edf993bff9e1e9a1015ecf64842baabd05f951f5b2fa67a3cce7857943d1d2b7599ffddb9ab6a994f6550086cdd5bdda876d7
+SHA512 (cmake_helpers-458fec9.tar.gz) = a8e423165a149a17f5f58441d460e3157c9d21af76f29aeef93e273e093df90788782edde5b8183313570d333b95398ca05bbbb9325757d05d02a2b4c4bdcfbc
SHA512 (codegen-d14ae77.tar.gz) = 721139f88b468e87109869bab7f6685ff1753d7fec22a9f57adf10ebbeb915f82cf5bfda8766aabd782c0b6c6d1f638be2d9388bd87503fcb322bbe0919f0f74
-SHA512 (lib_base-baae6cd.tar.gz) = 28c4020f17414b216d9071169ec0942345ef407a9e8d1fda84296f2b77f2b970d64022a47f2bd46dd139f1beee33d0b28756151f857a62d905ce82485235e3f7
+SHA512 (lib_base-8ba8607.tar.gz) = 75c9712a3e074120e41cdd6d00158bc08561926c046dec797e9227a2438feed90f6d94cb49c0fc50d2fce4e8129f32a90eff87576b1c094ce43de5a8617f2e15
SHA512 (lib_crl-5a740bf.tar.gz) = 279306cd55a3a48881cd14263aa7c63872934f1fe9ae92d397d39b5b75cfef318e54c204bbf172e7ee08bef576a00990bfdfd603c18baa442de4a53fe9ceec65
SHA512 (lib_lottie-a0a0269.tar.gz) = c7aebf681ad18465e8db6e0e17a6b946fa0d848a3a1f97ca4df2fa646a4dbd6483383dcb93b486d22a209d82c4412ba9974fcfd5b6230b79e5966a0d049be4f2
SHA512 (lib_qr-9877397.tar.gz) = 9b6da26294b25e9e215e036a11e14203f98c2c1b099f312bdabf4267af042d90a89892f3672f04a2823fcc3fd048259da306e3b6f277fa65df4ac7843b398e7b
SHA512 (lib_rpl-2888aab.tar.gz) = fa239f0fa2854747ae67a6cd13f02720ac483e9768c46a12e5942ee7ffb5c826f7f7ed516b4d59d7d4c914368f02eed2d86ce7bee0fb3ebd88bc98ff7766e704
-SHA512 (lib_spellcheck-d305de6.tar.gz) = 9470177e1ce3b507ae155695c75bc69db997913940001fc09846d0eeae9a1be9f565b6278549aa1158ecb8d5761aa7d2eb17f34db85b72c24f0e3898ed9d2c4a
+SHA512 (lib_spellcheck-4784796.tar.gz) = 0b5a6fb05c5cfcb23ad2576f040ff87be60419b1f449b21bcf5c1c8fe2c346853032220a9a383dbfda22ac9a17c0ce8f2c34772d5b79531e463c93c6b29752a1
SHA512 (lib_storage-b0388a1.tar.gz) = 0cab9532c60709a1906dba833fbef9f4d3d08e69200f482648ea81454770eda5bebc36aaeecb905ee9eab8e21fbb18afa1be2eecc72eea253ca6058502de92a3
SHA512 (lib_storage-cb56ad4.tar.gz) = dc617272ef3963582c8738358cda56ae7dee201bb99bbf8e123a49c3838d2ea690aec8a0cae71b1498fcb96df615e714a5c425949ba9910a7a6e8ce14e324040
-SHA512 (lib_ui-4ec9e32.tar.gz) = 3e214b59c509a0e4c6468a78ccaed82ad48708aab0db6e79f11aa45c660040c80f25cc31943da62993b0d32c63bbee4b74eca7452ad9ca024cf803d2c3258eac
+SHA512 (lib_ui-c0b0745.tar.gz) = 519a4277a63e4346e6bfaeeb68aecf534e781d00eab6d42bbf60cd7509d18feeac7fd40ff45ac8b274bbf40fb5997e0a0696e378a9a7debf04bce816c9442e41
SHA512 (rlottie-c490c7a.tar.gz) = 30891b6550c29b71506e5a476844acc23bcfb1a3f749aeec02917a6b62f8d60f7c88edcfc7a38b249eb4a4d32dd89f914c6ae3cc70154d1a2e178bf2176adef4
-SHA512 (tdesktop-1.9.3.tar.gz) = 94f5e3cade2b8ebe8b03cd106d84d588fc8d60877292ddbc3a106a5e6621baf4341d1386cdf829e32af175f16ca417050ac160019651b4e02f023b29dfbf2e9e
+SHA512 (tdesktop-1.9.4.tar.gz) = 54947a7557bce97654914170f302949b9bc8a2be8dc8d5835cbc2377e0aa41fe4b0d119910824ea5333c33dcb2531b6e04e7698d6d784add626a57c5f81741de
diff --git a/telegram-desktop-fix-appdata.patch b/telegram-desktop-fix-appdata.patch
index d024b30..d085ed3 100644
--- a/telegram-desktop-fix-appdata.patch
+++ b/telegram-desktop-fix-appdata.patch
@@ -1,6 +1,6 @@
-From aa1bf7309fad9d556551756e5501cf0306955dd2 Mon Sep 17 00:00:00 2001
+From 0b4699365b6a4818aa46ee00d43c0e8249c14876 Mon Sep 17 00:00:00 2001
From: Vitaly Zaitsev <vitaly(a)easycoding.org>
-Date: Thu, 9 Jan 2020 15:06:32 +0100
+Date: Fri, 17 Jan 2020 19:35:45 +0100
Subject: [PATCH 2/2] Updated AppData file.
---
diff --git a/telegram-desktop-fix-desktop.patch b/telegram-desktop-fix-desktop.patch
index 3e665d3..7370df4 100644
--- a/telegram-desktop-fix-desktop.patch
+++ b/telegram-desktop-fix-desktop.patch
@@ -1,6 +1,6 @@
-From 6503d4a1943f0a43ab31e20563d1ce5d2b0eaf18 Mon Sep 17 00:00:00 2001
+From 444234974cb7aae6b7d85002f382a8d0635956d4 Mon Sep 17 00:00:00 2001
From: Vitaly Zaitsev <vitaly(a)easycoding.org>
-Date: Thu, 9 Jan 2020 15:05:52 +0100
+Date: Fri, 17 Jan 2020 19:35:22 +0100
Subject: [PATCH 1/2] Updated desktop file.
---
diff --git a/telegram-desktop.spec b/telegram-desktop.spec
index e1e5d1d..07e49e9 100644
--- a/telegram-desktop.spec
+++ b/telegram-desktop.spec
@@ -9,7 +9,7 @@
%global apihash dfbe1bc42dc9d20507e17d1814cc2f0a
# Git revision of cmake_helpers...
-%global commit1 652bbaf002546ae1822fff4474ffe95091bfc2e4
+%global commit1 458fec94999b82145bffaaab114ee9baa8708dd3
%global shortcommit1 %(c=%{commit1}; echo ${c:0:7})
# Git revision of patched rlottie...
@@ -21,7 +21,7 @@
%global shortcommit3 %(c=%{commit3}; echo ${c:0:7})
# Git revision of lib_base...
-%global commit4 baae6cdd9ba5216732222e7dec9a76b9ea3a7c83
+%global commit4 8ba86078fbe71ab857ee1b6e35acc4def4cc0d1c
%global shortcommit4 %(c=%{commit4}; echo ${c:0:7})
# Git revision of lib_lottie...
@@ -37,7 +37,7 @@
%global shortcommit7 %(c=%{commit7}; echo ${c:0:7})
# Git revision of lib_spellcheck...
-%global commit8 d305de6d67ca4f3891bad96a0e49e40f5c904189
+%global commit8 47847963bf491dfd266da916478de5cc479342f6
%global shortcommit8 %(c=%{commit8}; echo ${c:0:7})
# Git revision of lib_storage...
@@ -49,7 +49,7 @@
%global shortcommit10 %(c=%{commit10}; echo ${c:0:7})
# Git revision of lib_ui...
-%global commit11 4ec9e32f8f5f6b8192fd60d73ae0b575e82c1c60
+%global commit11 c0b07457fa5df905f7926025302f66065dc4d52b
%global shortcommit11 %(c=%{commit11}; echo ${c:0:7})
# Git revision of codegen...
@@ -62,7 +62,7 @@
%endif
Name: telegram-desktop
-Version: 1.9.3
+Version: 1.9.4
Release: 1%{?dist}
# Application and 3rd-party modules licensing:
@@ -97,14 +97,6 @@ Patch10: cmake_helpers-system-expected.patch
Patch11: cmake_helpers-system-gsl.patch
Patch12: cmake_helpers-system-qrcode.patch
Patch13: cmake_helpers-system-variant.patch
-Patch20: lib_ui-remove-configs.patch
-
-# Temporary upstream and proposed to upstream patches...
-Patch100: %{name}-pr6956.patch
-Patch101: cmake_helpers-system-libraries.patch
-Patch102: %{name}-commit-100fed3.patch
-Patch103: %{name}-commit-322367c.patch
-Patch104: %{name}-pr6985.patch
%{?_qt5:Requires: %{_qt5}%{?_isa} = %{_qt5_version}}
Requires: qt5-qtimageformats%{?_isa}
@@ -127,7 +119,7 @@ BuildRequires: guidelines-support-library-devel >= 1.0.0
BuildRequires: mapbox-variant-devel >= 0.3.6
BuildRequires: qt5-qtbase-private-devel
BuildRequires: libtgvoip-devel >= 2.4.4
-BuildRequires: range-v3-devel >= 0.9.1
+BuildRequires: range-v3-devel >= 0.10.0
BuildRequires: libqrcodegencpp-devel
BuildRequires: ffmpeg-devel >= 3.1
BuildRequires: openal-soft-devel
@@ -260,6 +252,7 @@ pushd Telegram
rm -rf lib_ui
tar -xf %{SOURCE11}
mv lib_ui-%{commit11} lib_ui
+ rm -f lib_ui/qt_conf/linux.qrc
popd
# Unpacking codegen...
@@ -272,21 +265,13 @@ popd
# Applying patches for core application...
%patch0 -p1 -b .desktop
%patch1 -p1 -b .appdata
-%patch102 -p1 -b .commit-100fed3
-%patch103 -p1 -b .commit-322367c
-%patch100 -p1 -b .pr6956
-%patch104 -p1 -b .pr6985
# Applying patches for build system...
-%patch101 -d cmake -p1 -b .system-libraries
%patch10 -d cmake -p1 -b .system-expected
%patch11 -d cmake -p1 -b .system-gsl
%patch12 -d cmake -p1 -b .system-qrcode
%patch13 -d cmake -p1 -b .system-variant
-# Applying patches for lib_ui...
-%patch20 -d Telegram/lib_ui -p1 -b .remove-configs
-
%build
# Building Telegram Desktop using cmake...
pushd %{_target_platform}
@@ -358,12 +343,11 @@ appstream-util validate-relax --nonet %{buildroot}%{_metainfodir}/%{name}.appdat
%{_metainfodir}/%{name}.appdata.xml
%changelog
+* Fri Jan 17 2020 Vitaly Zaitsev <vitaly(a)easycoding.org> - 1.9.4-1
+- Updated to version 1.9.4.
+
* Thu Jan 09 2020 Vitaly Zaitsev <vitaly(a)easycoding.org> - 1.9.3-1
- Updated to version 1.9.3.
* Tue Dec 24 2019 Vitaly Zaitsev <vitaly(a)easycoding.org> - 1.8.15-3
- Removed GTK2 from build requirements.
-
-* Tue Dec 17 2019 Vitaly Zaitsev <vitaly(a)easycoding.org> - 1.8.15-2
-- Fixed issue with menu bar on Gnome.
-- Rebuilt due to Qt 5.13.2 update on Rawhide.
4 years, 10 months
[mpd/el8] Update to 0.21.19
by Leigh Scott
Summary of changes:
67ff8cf... Update to 0.21.19 (*)
(*) This commit already existed in another branch; no separate mail sent
4 years, 10 months