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