diff --git a/mmal_1.patch b/mmal_8.patch similarity index 58% rename from mmal_1.patch rename to mmal_8.patch index 35bdeb2..8744fcb 100644 --- a/mmal_1.patch +++ b/mmal_8.patch @@ -1,6 +1,16 @@ --- a/configure.ac +++ b/configure.ac -@@ -3406,7 +3406,7 @@ +@@ -3419,6 +3419,9 @@ + AC_ARG_ENABLE(mmal, + AS_HELP_STRING([--enable-mmal], + [Multi-Media Abstraction Layer (MMAL) hardware plugin (default enable)])) ++AC_ARG_ENABLE(mmal_avcodec, ++ AS_HELP_STRING([--enable-mmal-avcodec], ++ [Use MMAL enabled avcodec libs (default disable)])) + if test "${enable_mmal}" != "no"; then + VLC_SAVE_FLAGS + LDFLAGS="${LDFLAGS} -L/opt/vc/lib -lvchostif" +@@ -3429,7 +3432,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 ]) @@ -9,9 +19,29 @@ AS_IF([test "${enable_mmal}" = "yes"], [ AC_MSG_ERROR([Cannot find bcm library...]) ], [ AC_MSG_WARN([Cannot find bcm library...]) ]) +@@ -3441,6 +3444,7 @@ + VLC_RESTORE_FLAGS + fi + AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"]) ++AM_CONDITIONAL([HAVE_MMAL_AVCODEC], [test "${enable_mmal_avcodec}" = "yes"]) + + dnl + dnl evas plugin +--- a/include/vlc_fourcc.h ++++ b/include/vlc_fourcc.h +@@ -365,6 +365,9 @@ + + /* Broadcom MMAL opaque buffer type */ + #define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L') ++#define VLC_CODEC_MMAL_ZC_SAND8 VLC_FOURCC('Z','S','D','8') ++#define VLC_CODEC_MMAL_ZC_SAND10 VLC_FOURCC('Z','S','D','0') ++#define VLC_CODEC_MMAL_ZC_I420 VLC_FOURCC('Z','4','2','0') + + /* DXVA2 opaque video surface for use with D3D9 */ + #define VLC_CODEC_D3D9_OPAQUE VLC_FOURCC('D','X','A','9') /* 4:2:0 8 bpc */ --- a/modules/hw/mmal/Makefile.am +++ b/modules/hw/mmal/Makefile.am -@@ -1,23 +1,30 @@ +@@ -1,23 +1,39 @@ include $(top_srcdir)/modules/common.am mmaldir = $(pluginsdir)/mmal @@ -47,197 +77,221 @@ +libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal) +mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la + ++if HAVE_MMAL_AVCODEC ++libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_picture.c mmal_picture.h ++libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS) ++libmmal_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS) ++libmmal_avcodec_plugin_la_LIBADD = $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(LIBS_mmal) ++mmal_LTLIBRARIES += libmmal_avcodec_plugin.la ++endif ++ ++ --- /dev/null +++ b/modules/hw/mmal/blend_rgba_neon.S -@@ -0,0 +1,185 @@ +@@ -0,0 +1,200 @@ + .syntax unified + .arm +// .thumb + .text + .align 16 ++ .arch armv7-a ++ .fpu neon-vfpv4 + -+@ 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.) ++@ blend_rgbx_rgba_neon + -+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 ++@ Implements /255 as ((x * 257) + 0x8000) >> 16 ++@ This generates something in the range [(x+126)/255, (x+127)/255] which is good enough + -+ lsls r2, r3, #30 @ b2 -> C, b1 -> N -+ vstmcs r12!, {d6,d7} -+ vstmmi r12!, {d5} -+ tst r3, #1 -+ vstrne s9, [r12] -+ bx lr ++@ There is advantage to aligning src and/or dest - dest gives a bit more due to being used twice + + -+@ blend_rgbx_rgba_neon + -+@ [r0] RGBx dest (Byte order: R, G, B, x) -+@ [r1] RGBA src merge (Byte order: R, G, B, A) ++@ [r0] RGBx dest loaded into d20-d23 ++@ [r1] RGBA src merge loaded into d16-d19 +@ 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 ++.macro blend_main sR, sG, sB, sA, dR, dG, dB, dA + -+ .align 16 -+ .global blend_rgbx_rgba_neon -+#ifdef __ELF__ -+ .type blend_rgbx_rgba_neon, %function -+#endif ++ push { r4, lr } + -+blend_rgbx_rgba_neon: ++ vdup.u8 d7, r2 + -+ 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 ++ vmov.u8 d6, #0xff ++ + 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 ++ mov r4, r3 + 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 ++ blne 10f + + sub r3, r4, r3 + +1: -+ vld1.32 { q0,q1 }, [r1] ++ vld4.8 {d16, d17, d18, d19}, [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 ++ @ Alpha ends up in d19 ++ vmull.u8 q15, \sA, d7 + -+ vmull.u8 q1, d1, d17 -+ vmull.u8 q0, d0, d16 -+ vsub.u8 q8, q11, q8 -+ addge r1, #32 ++ vld4.8 {d20, d21, d22, d23}, [r0] + -+ vmlal.u8 q3, d31, d19 -+ vmlal.u8 q2, d30, d18 -+ vmlal.u8 q1, d29, d17 -+ vmlal.u8 q0, d28, d16 ++ vsra.u16 q15, q15, #8 ++ subs r3, #8 ++ vrshrn.u16 d31, q15, #8 ++ vsub.u8 d30, d6, d31 + -+ vsra.u16 q3, q3, #8 -+ vsra.u16 q2, q2, #8 -+ vsra.u16 q1, q1, #8 -+ vsra.u16 q0, q0, #8 ++ vmull.u8 q12, \sR, d31 ++ vmull.u8 q13, \sG, d31 ++ vmull.u8 q14, \sB, d31 ++ addge r1, #32 + -+ vrshrn.u16 d7, q3, #8 -+ vrshrn.u16 d6, q2, #8 -+ vrshrn.u16 d5, q1, #8 -+ vrshrn.u16 d4, q0, #8 ++ vmlal.u8 q12, \dR, d30 ++ vmlal.u8 q13, \dG, d30 ++ vmlal.u8 q14, \dB, d30 ++ vld4.8 {d16, d17, d18, d19}, [r1] + -+ vld1.32 { q0,q1 }, [r1] ++ vsra.u16 q12, q12, #8 @ * 257/256 ++ vsra.u16 q13, q13, #8 ++ vsra.u16 q14, q14, #8 + -+ vbit q3, q12, q12 @ Set alpha to #255 -+ vbit q2, q12, q12 ++ vrshrn.u16 \dR, q12, #8 ++ vrshrn.u16 \dG, q13, #8 ++ vrshrn.u16 \dB, q14, #8 ++ vmov.u8 \dA, #0xff + -+ vst1.32 { q2,q3 }, [r0]! ++ vst4.8 {d20, d21, d22, d23}, [r0]! + bge 1b + add r1, #32 + +2: -+ cmp r3, #-8 -+ blgt tail_rgbx_rgba ++ cmp r3, #-8 ++ blgt 10f + -+ vpop { q4 } + pop { r4, pc } + + ++// Partial version ++// Align @ start & deal with tail ++10: ++ lsls r2, r3, #30 @ b2 -> C, b1 -> N ++ mov r2, r0 ++ bcc 1f ++ vld4.8 {d16[0], d17[0], d18[0], d19[0]}, [r1]! ++ vld4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]! ++ vld4.8 {d16[1], d17[1], d18[1], d19[1]}, [r1]! ++ vld4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]! ++ vld4.8 {d16[2], d17[2], d18[2], d19[2]}, [r1]! ++ vld4.8 {d20[2], d21[2], d22[2], d23[2]}, [r0]! ++ vld4.8 {d16[3], d17[3], d18[3], d19[3]}, [r1]! ++ vld4.8 {d20[3], d21[3], d22[3], d23[3]}, [r0]! ++1: ++ bpl 1f ++ vld4.8 {d16[4], d17[4], d18[4], d19[4]}, [r1]! ++ vld4.8 {d20[4], d21[4], d22[4], d23[4]}, [r0]! ++ vld4.8 {d16[5], d17[5], d18[5], d19[5]}, [r1]! ++ vld4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]! ++1: ++ tst r3, #1 ++ beq 1f ++ vld4.8 {d16[6], d17[6], d18[6], d19[6]}, [r1]! ++ vld4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]! ++1: ++ @ Alpha ends up in d19 ++ ++ vmull.u8 q15, \sA, d7 ++ vsra.u16 q15, q15, #8 ++ vrshrn.u16 d31, q15, #8 ++ vsub.u8 d30, d6, d31 ++ ++ vmull.u8 q12, \sR, d31 ++ vmull.u8 q13, \sG, d31 ++ vmull.u8 q14, \sB, d31 ++ ++ vmlal.u8 q12, \dR, d30 ++ vmlal.u8 q13, \dG, d30 ++ vmlal.u8 q14, \dB, d30 ++ ++ vsra.u16 q12, q12, #8 ++ vsra.u16 q13, q13, #8 ++ vsra.u16 q14, q14, #8 ++ ++ vrshrn.u16 \dR, q12, #8 ++ vrshrn.u16 \dG, q13, #8 ++ vrshrn.u16 \dB, q14, #8 ++ vmov.u8 \dA, #0xff ++ ++ mov r0, r2 ++ lsls r2, r3, #30 @ b2 -> C, b1 -> N ++ mov r2, r0 ++ bcc 1f ++ vst4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]! ++ vst4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]! ++ vst4.8 {d20[2], d21[2], d22[2], d23[2]}, [r0]! ++ vst4.8 {d20[3], d21[3], d22[3], d23[3]}, [r0]! ++1: ++ bpl 1f ++ vst4.8 {d20[4], d21[4], d22[4], d23[4]}, [r0]! ++ vst4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]! ++1: ++ tst r3, #1 ++ beq 1f ++ vst4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]! ++1: ++ bx lr ++ ++.endm ++ ++ ++@ [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 ++ ++@ [r0] RGBx dest (Byte order: R, G, B, x) ++@ [r1] RGBA src merge (Byte order: R, G, B, A) - same as above ++@ r2 plane alpha ++@ r3 count (pixels) ++ .align 16 ++ .global blend_rgbx_rgba_neon ++#ifdef __ELF__ ++ .type blend_rgbx_rgba_neon, %function ++#endif ++blend_rgbx_rgba_neon: ++ blend_main d16, d17, d18, d19, d20, d21, d22, d23 ++ ++ ++@ [r0] RGBx dest (Byte order: R, G, B, x) ++@ [r1] RGBA src merge (Byte order: B, G, R, A) - B / R swapped ++@ r2 plane alpha ++@ r3 count (pixels) ++ .align 16 ++ .global blend_bgrx_rgba_neon ++#ifdef __ELF__ ++ .type blend_bgrx_rgba_neon, %function ++#endif ++blend_bgrx_rgba_neon: ++ blend_main d18, d17, d16, d19, d20, d21, d22, d23 ++ ++ + --- /dev/null +++ b/modules/hw/mmal/blend_rgba_neon.h -@@ -0,0 +1,15 @@ +@@ -0,0 +1,17 @@ +#ifndef HW_MMAL_BLEND_RGBA_NEON_H +#define HW_MMAL_BLEND_RGBA_NEON_H + @@ -245,7 +299,9 @@ +extern "C" { +#endif + -+extern void blend_rgbx_rgba_neon(void * dest, const void * src, int alpha, unsigned int n); ++typedef void blend_neon_fn(void * dest, const void * src, int alpha, unsigned int n); ++extern blend_neon_fn blend_rgbx_rgba_neon; ++extern blend_neon_fn blend_bgrx_rgba_neon; + +#ifdef __cplusplus +} @@ -255,7 +311,7 @@ + --- /dev/null +++ b/modules/hw/mmal/blend_test.c -@@ -0,0 +1,116 @@ +@@ -0,0 +1,180 @@ +#include +#include +#include @@ -296,6 +352,25 @@ + } +} + ++ ++// Merge RGBA with BGRA ++static void merge_line2(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 >> 0) & 0xff, (s_pel >> 16) & 0xff, a) << 0 ) | ++ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) | ++ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 0) & 0xff, a) << 16); ++ } ++} ++ +#define BUF_SIZE 256 +#define BUF_SLACK 16 +#define BUF_ALIGN 64 @@ -329,6 +404,35 @@ + } +} + ++static void test_line2(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_line2(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len); ++ ++ PROFILE_START(); ++ blend_bgrx_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[]) +{ @@ -351,8 +455,9 @@ + + for (i = 0; i != BUF_ALLOC; ++i) { + d0_buf[i] = 0xff00 | i; -+ s0_buf[i] = (i << 24) | 0xffffff; ++ s0_buf[i] = (i << 24) | 0x40ffc0; + } ++ + for (i = 0; i != 256; ++i) { + test_line(d0, 0, s0, 0, i, 256, -1); + } @@ -365,9 +470,24 @@ + test_line(d0, j & 3, s0, j >> 2, i, 256, j); + } + PROFILE_PRINTF_N(j); ++ PROFILE_CLEAR_N(j); + } ++ printf("Done 1\n"); + -+ printf("Done\n"); ++ for (i = 0; i != 256; ++i) { ++ test_line2(d0, 0, s0, 0, i, 256, -1); ++ } ++ for (i = 0; i != 256; ++i) { ++ test_line2(d0, 0, s0, 0, 128, i, -1); ++ } ++ ++ for (j = 0; j != 16; ++j) { ++ for (i = 0; i != 256; ++i) { ++ test_line2(d0, j & 3, s0, j >> 2, i, 256, j); ++ } ++ PROFILE_PRINTF_N(j); ++ } ++ printf("Done 2\n"); + + return 0; +} @@ -388,7 +508,7 @@ #include #include -@@ -38,255 +40,380 @@ +@@ -38,255 +40,393 @@ #include #include "mmal_picture.h" @@ -408,10 +528,6 @@ #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 @@ -457,13 +573,32 @@ bool b_progressive; + bool b_flushed; ++ ++ // Lock to avoid pic update & allocate happenening simultainiously ++ // * We should be able to arrange life s.t. this isn't needed ++ // but while we are confused apply belt & braces ++ vlc_mutex_t pic_lock; + /* statistics */ - int output_in_transit; - int input_in_transit; atomic_bool started; --}; +} decoder_sys_t; ++ ++ ++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 = ++{ ++ {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, ++ -1 + }; -/* Utilities */ -static int change_output_format(decoder_t *dec); @@ -478,51 +613,40 @@ -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 inline char safe_char(const unsigned int c0) ++{ ++ const unsigned int c = c0 & 0xff; ++ return c > ' ' && c < 0x7f ? c : '.'; ++} -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 = ++static const char * str_fourcc(char * buf, unsigned int fcc) { - 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[0] = safe_char(fcc >> 0); ++ buf[1] = safe_char(fcc >> 8); ++ buf[2] = safe_char(fcc >> 16); ++ buf[3] = safe_char(fcc >> 24); + buf[4] = 0; + return buf; +} -+#endif + +- if (dec->fmt_in.i_codec != VLC_CODEC_MPGV && +- dec->fmt_in.i_codec != VLC_CODEC_H264) +- return VLC_EGENERIC; ++static bool is_enc_supported(const MMAL_FOURCC_T fcc) ++{ ++ int i; - 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) @@ -606,42 +730,6 @@ - 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){ @@ -671,15 +759,41 @@ + 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 (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)); +- } ++#define DEBUG_SQUARES 0 ++#if DEBUG_SQUARES ++static void draw_square(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t val) ++{ ++ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x; ++ unsigned int i; ++ for (i = 0; i != h; ++i) { ++ unsigned int j; ++ for (j = 0; j != w; ++j) { ++ p[j] = val; + } ++ p += pic_stride; + } ++} ++#endif + +- status = mmal_port_format_commit(sys->input); - if (status != MMAL_SUCCESS) { -- msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)", +- 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; @@ -691,22 +805,20 @@ + *p = ~0U; + p += inc; } +- 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)); +- ret = VLC_EGENERIC; +- goto out; +- } + - 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; @@ -721,16 +833,30 @@ +} +#endif +- 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; +- } ++// 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; + - 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; ++ vlc_mutex_lock(&dec_sys->pic_lock); + picture_t * const pic = decoder_NewPicture(dec); ++ vlc_mutex_unlock(&dec_sys->pic_lock); - status = mmal_port_parameter_set(sys->output, &zero_copy.hdr); - if (status != MMAL_SUCCESS) { @@ -760,7 +886,7 @@ - ret = VLC_EGENERIC; - goto out; - } -+ if ((pic->context = hw_mmal_gen_context(buf, dec_sys->ppr)) == NULL) ++ if ((pic->context = hw_mmal_gen_context(MMAL_ENCODING_OPAQUE, buf, dec_sys->ppr)) == NULL) + goto fail2; - sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0); @@ -781,8 +907,8 @@ +fail2: + picture_Release(pic); +fail1: -+ // * maybe should recycle rather than release? -+ mmal_buffer_header_release(buf); ++ // Recycle rather than release to avoid buffer starvation if NewPic fails ++ hw_mmal_port_pool_ref_recycle(dec_sys->ppr, buf); + return NULL; +} + @@ -806,23 +932,23 @@ - dec->pf_flush = flush_decoder; + mmal_buffer_header_release(buffer); +} -+ + +- vlc_sem_init(&sys->sem, 0); +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); ++ (void)port; // Unused + +- return ret; +#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); + @@ -831,19 +957,17 @@ } -static void CloseDecoder(decoder_t *dec) -+static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) ++static void decoder_output_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 ++ decoder_t * const dec = (decoder_t *)port->userdata; - 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__, ++ msg_Dbg(dec, "<<< %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 + @@ -858,15 +982,17 @@ + // Buffer released or attached to pic - do not release again return; + } -+ else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) + +- if (sys->component && sys->component->control->is_enabled) +- mmal_port_disable(sys->component->control); ++ 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 (sys->input && sys->input->is_enabled) +- mmal_port_disable(sys->input); + if (format == NULL) + msg_Err(dec, "Failed to allocate new format"); + else @@ -874,38 +1000,45 @@ + 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 && sys->output->is_enabled) +- mmal_port_disable(sys->output); + if (sys->output_format != NULL) + mmal_format_free(sys->output_format); -- if (sys->output && sys->output->is_enabled) -- mmal_port_disable(sys->output); +- if (sys->component && sys->component->is_enabled) +- mmal_component_disable(sys->component); + sys->output_format = format; + } + } ++ else if (buffer->cmd != 0) { ++ char buf0[5]; ++ msg_Warn(dec, "Unexpected output cb event: %s", str_fourcc(buf0, buffer->cmd)); ++ } -- if (sys->component && sys->component->is_enabled) -- mmal_component_disable(sys->component); +- if (sys->input_pool) +- mmal_pool_destroy(sys->input_pool); ++ // If we get here then we were flushing (cmd == 0 && len == 0) or ++ // that was an EVENT - in either case we want to release the buffer ++ // back to its pool rather than recycle it. + 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); + +- if (sys->component) +- mmal_component_release(sys->component); +static void fill_output_port(decoder_t *dec) +{ + decoder_sys_t *sys = dec->p_sys; -- if (sys->component) -- mmal_component_release(sys->component); +- vlc_sem_destroy(&sys->sem); +- free(sys); + if (decoder_UpdateVideoFormat(dec) != 0) + { + // If we have a new format don't bother stuffing the buffer @@ -914,12 +1047,10 @@ + msg_Dbg(dec, "%s: Updated", __func__); +#endif -- vlc_sem_destroy(&sys->sem); -- free(sys); +- bcm_host_deinit(); + return; + } - -- bcm_host_deinit(); ++ + hw_mmal_port_pool_ref_fill(sys->ppr); + return; } @@ -940,7 +1071,7 @@ 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 @@ +@@ -300,7 +440,9 @@ } port_reset: @@ -950,7 +1081,7 @@ 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 @@ +@@ -318,18 +460,10 @@ goto out; } @@ -966,8 +1097,12 @@ + 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 @@ +- status = mmal_port_enable(sys->output, output_port_cb); ++ status = mmal_port_enable(sys->output, decoder_output_cb); + if (status != MMAL_SUCCESS) { + msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)", + status, mmal_status_to_string(status)); +@@ -338,25 +472,14 @@ } if (!atomic_load(&sys->started)) { @@ -996,7 +1131,7 @@ } apply_fmt: -@@ -382,12 +492,17 @@ +@@ -382,12 +505,19 @@ sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive); sys->b_top_field_first = sys->b_progressive ? true : (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst); @@ -1008,13 +1143,15 @@ +#endif } -+ // Tell the reset of the world we have changed format ++ // Tell the rest of the world we have changed format ++ vlc_mutex_lock(&sys->pic_lock); + ret = decoder_UpdateVideoFormat(dec); ++ vlc_mutex_unlock(&sys->pic_lock); + out: mmal_format_free(sys->output_format); sys->output_format = NULL; -@@ -395,144 +510,85 @@ +@@ -395,144 +525,85 @@ return ret; } @@ -1029,7 +1166,7 @@ MMAL_STATUS_T status; - unsigned buffer_size = 0; - int ret = 0; - +- - if (!sys->output->is_enabled) - return VLC_EGENERIC; - @@ -1078,7 +1215,7 @@ - } - buffer->user_data = picture; - buffer->cmd = 0; -- + - status = mmal_port_send_buffer(sys->output, buffer); + status = mmal_port_format_commit(sys->input); if (status != MMAL_SUCCESS) { @@ -1182,7 +1319,7 @@ + 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); ++ mmal_port_enable(sys->output, decoder_output_cb); + sys->b_flushed = true; + } +#if TRACE_ALL @@ -1212,7 +1349,7 @@ /* * Configure output port if necessary */ -@@ -541,18 +597,50 @@ +@@ -541,18 +612,50 @@ msg_Err(dec, "Failed to change output port format"); } @@ -1240,7 +1377,7 @@ + // 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) ++ (status = mmal_port_enable(sys->output, decoder_output_cb)) != MMAL_SUCCESS) + { + msg_Err(dec, "Output port enable failed"); + goto fail; @@ -1266,7 +1403,7 @@ if (atomic_load(&sys->started)) fill_output_port(dec); -@@ -563,18 +651,21 @@ +@@ -563,18 +666,21 @@ if (block->i_flags & BLOCK_FLAG_CORRUPTED) flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED; @@ -1293,7 +1430,7 @@ len = block->i_buffer; if (len > buffer->alloc_size) -@@ -590,89 +681,1304 @@ +@@ -590,89 +696,1443 @@ } buffer->flags = flags; @@ -1362,6 +1499,7 @@ + + hw_mmal_port_pool_ref_release(sys->ppr, false); + ++ vlc_mutex_destroy(&sys->pic_lock); + free(sys); + + bcm_host_deinit(); @@ -1398,6 +1536,7 @@ + goto fail; + } + dec->p_sys = sys; ++ vlc_mutex_init(&sys->pic_lock); + + bcm_host_init(); + @@ -1445,45 +1584,9 @@ + 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)); ++ if ((status = hw_mmal_opaque_output(VLC_OBJECT(dec), &sys->ppr, ++ sys->output, NUM_EXTRA_BUFFERS, decoder_output_cb)) != MMAL_SUCCESS) + goto fail; -+ } + + status = mmal_component_enable(sys->component); + if (status != MMAL_SUCCESS) { @@ -1573,7 +1676,20 @@ + +#define SUBS_MAX 3 + ++typedef enum filter_resizer_e { ++ FILTER_RESIZER_RESIZER, ++ FILTER_RESIZER_ISP, ++ FILTER_RESIZER_HVS ++} filter_resizer_t; ++ ++typedef struct conv_frame_stash_s ++{ ++ mtime_t pts; ++ MMAL_BUFFER_HEADER_T * sub_bufs[SUBS_MAX]; ++} conv_frame_stash_t; ++ +typedef struct filter_sys_t { ++ filter_resizer_t resizer_type; + MMAL_COMPONENT_T *component; + MMAL_PORT_T *input; + MMAL_PORT_T *output; @@ -1593,13 +1709,15 @@ + MMAL_STATUS_T err_stream; + int in_count; + -+ bool zero_copy; ++ bool is_sliced; ++ bool out_fmt_set; ++ bool latency_set; + const char * component_name; + MMAL_PORT_BH_CB_T in_port_cb_fn; + MMAL_PORT_BH_CB_T out_port_cb_fn; + + uint64_t frame_seq; -+ mtime_t pts_stash[16]; ++ conv_frame_stash_t stash[16]; + + // Slice specific tracking stuff + struct { @@ -1610,20 +1728,24 @@ +} filter_sys_t; + + -+static void pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic) ++static MMAL_STATUS_T 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; + ++ if (bpp < 1 || bpp > 4) ++ return MMAL_EINVAL; ++ + es_fmt->type = MMAL_ES_TYPE_VIDEO; -+ es_fmt->encoding_variant = es_fmt->encoding = -+ vlc_to_mmal_pic_fourcc(pic->format.i_chroma); ++ es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format); ++ es_fmt->encoding_variant = 0; + + // 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; ++ return MMAL_SUCCESS; +} + + @@ -1743,7 +1865,7 @@ +#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)]); ++ (long long)buf->pts, (long long)sys->stash[(unsigned int)(buf->pts & 0xf)].pts); +#endif + if (buf->cmd == 0) { + picture_t * const pic = (picture_t *)buf->user_data; @@ -1761,10 +1883,13 @@ + 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); -+ ++#if DEBUG_SQUARES ++ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, 32, 32, 0xffff0000); ++ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 32, 0, 32, 32, 0xff00ff00); ++ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 64, 0, 32, 32, 0xff0000ff); ++#endif + conv_out_q_pic(sys, pic); + } + } @@ -1824,7 +1949,7 @@ + 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) { @@ -1832,7 +1957,7 @@ + dst += dst_stride; + src += src_stride; + } - } ++ } + sys->slice.line += scale_n; } - atomic_fetch_sub(&sys->output_in_transit, 1); @@ -1888,8 +2013,11 @@ + 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->resizer_type == FILTER_RESIZER_HVS) ++ { ++ 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) @@ -1902,6 +2030,19 @@ + // Don't need lock as the above disables should have prevented anything + // happening in the background + ++ for (i = 0; i != 16; ++i) { ++ conv_frame_stash_t *const stash = sys->stash + i; ++ unsigned int sub_no; ++ ++ stash->pts = MMAL_TIME_UNKNOWN; ++ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { ++ if (stash->sub_bufs[sub_no] != NULL) { ++ mmal_buffer_header_release(stash->sub_bufs[sub_no]); ++ stash->sub_bufs[sub_no] = NULL; ++ } ++ } ++ } ++ + pic_fifo_release_all(&sys->slice.pics); + pic_fifo_release_all(&sys->ret_pics); + @@ -1921,12 +2062,79 @@ +#endif +} + ++static void conv_flush_passthrough(filter_t * p_filter) ++{ ++ VLC_UNUSED(p_filter); ++} ++ ++static void conv_stash_fixup(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const p_pic) ++{ ++ conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf); ++ unsigned int sub_no; ++ VLC_UNUSED(p_filter); ++ ++ p_pic->date = stash->pts; ++ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { ++ if (stash->sub_bufs[sub_no] != NULL) { ++ // **** Do stashed blend ++ // **** Aaargh, bother... need to rescale subs too ++ ++ mmal_buffer_header_release(stash->sub_bufs[sub_no]); ++ stash->sub_bufs[sub_no] = NULL; ++ } ++ } ++} ++ ++static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const pic) ++{ ++ MMAL_STATUS_T status; ++ ++ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter; ++ sys->output->format->type = MMAL_ES_TYPE_VIDEO; ++ sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video); ++ sys->output->format->encoding_variant = 0; ++ vlc_to_mmal_video_fmt(sys->output->format, &p_filter->fmt_out.video); ++ ++ // Override default format width/height if we have a pic we need to match ++ if (pic != NULL) ++ { ++ if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS) ++ { ++ char cbuf[5]; ++ msg_Err(p_filter, "Bad format desc: %s, bits=%d", str_fourcc(cbuf, pic->format.i_chroma), pic->format.i_bits_per_pixel); ++ return status; ++ } ++ ++ 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 status; ++ } ++ ++ sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended); ++ sys->output->buffer_size = sys->output->buffer_size_recommended; ++ ++ if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS) ++ return status; + +- sys->output_format = format; ++ return MMAL_SUCCESS; ++} ++ +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; ++ conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf); + +#if TRACE_ALL + msg_Dbg(p_filter, "<<< %s", __func__); @@ -1953,7 +2161,7 @@ + if (p_pic->context == NULL) { + msg_Dbg(p_filter, "%s: No context", __func__); + } -+ else ++ else if (sys->resizer_type == FILTER_RESIZER_HVS) + { + unsigned int sub_no = 0; + @@ -1966,6 +2174,43 @@ + goto fail; + } + } ++ else ++ { ++ unsigned int sub_no = 0; ++ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { ++ if ((stash->sub_bufs[sub_no] = hw_mmal_pic_sub_buf_get(p_pic, sub_no)) != NULL) { ++ mmal_buffer_header_acquire(stash->sub_bufs[sub_no]); ++ } ++ } ++ } ++ ++ if (!sys->out_fmt_set) { ++ sys->out_fmt_set = true; ++ ++ if (sys->is_sliced) { ++ // 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 ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS) ++ goto fail; ++ } ++ else { ++ picture_t *pic = filter_NewPicture(p_filter); ++ err = conv_set_output(p_filter, sys, pic); ++ picture_Release(pic); ++ if (err != MMAL_SUCCESS) ++ goto fail; ++ } ++ ++ msg_Dbg(p_filter, "Outpool: zc=%d, num=%d, size=%d", sys->is_sliced, sys->output->buffer_num, sys->output->buffer_size); ++ sys->out_pool = sys->is_sliced ? ++ mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size) : ++ mmal_pool_create(sys->output->buffer_num, 0); ++ ++ if (sys->out_pool == NULL) { ++ msg_Err(p_filter, "Failed to create output pool"); ++ goto fail; ++ } ++ } + + // Reenable stuff if the last thing we did was flush + if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS || @@ -1973,7 +2218,7 @@ + goto fail; + + // If ZC then we need to allocate the out pic before we stuff the input -+ if (sys->zero_copy) { ++ if (sys->is_sliced) { + MMAL_BUFFER_HEADER_T * out_buf; + picture_t * const out_pic = filter_NewPicture(p_filter); + @@ -2008,7 +2253,7 @@ + goto fail; + } + -+ sys->pts_stash[(frame_seq & 0xf)] = p_pic->date; ++ stash->pts = p_pic->date; + if ((err = port_send_replicated(sys->input, sys->in_pool, pic_buf, frame_seq)) != MMAL_SUCCESS) + { + msg_Err(p_filter, "Send buffer to input failed"); @@ -2020,14 +2265,13 @@ + --sys->in_count; + } + -+ if (!sys->zero_copy) { ++ if (!sys->is_sliced) { + MMAL_BUFFER_HEADER_T * out_buf; + + while ((out_buf = sys->in_count < 0 ? + mmal_queue_wait(sys->out_pool->queue) : mmal_queue_get(sys->out_pool->queue)) != NULL) + { + 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"); @@ -2036,6 +2280,7 @@ + } + +#if 0 ++ char dbuf0[5]; + msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", + str_fourcc(dbuf0, out_pic->format.i_chroma), + out_pic->format.i_width, out_pic->format.i_height, @@ -2076,6 +2321,15 @@ + // Avoid being more than 1 pic behind + vlc_sem_wait(&sys->sem); + ++ // Set sem for delayed scale if not already set ++ if (!sys->latency_set) { ++ unsigned int i; ++ sys->latency_set = true; ++ for (i = 0; i != CONV_MAX_LATENCY; ++i) { ++ vlc_sem_post(&sys->sem); ++ } ++ } ++ + // Return all pending buffers + vlc_mutex_lock(&sys->lock); + ret_pics = pic_fifo_get_all(&sys->ret_pics); @@ -2089,6 +2343,8 @@ + if (ret_pics != NULL) + { + picture_t *next_pic = ret_pics->p_next; ++ ++ conv_stash_fixup(p_filter, sys, ret_pics); +#if 0 + char dbuf0[5]; + @@ -2101,6 +2357,7 @@ +#endif + while (next_pic != NULL) { + vlc_sem_wait(&sys->sem); ++ conv_stash_fixup(p_filter, sys, next_pic); + next_pic = next_pic->p_next; + } + } @@ -2108,7 +2365,8 @@ +#if TRACE_ALL + msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics); +#endif -+ + +- mmal_buffer_header_release(buffer); + return ret_pics; + +stream_fail: @@ -2120,14 +2378,21 @@ + return NULL; +} + ++static picture_t *conv_filter_passthrough(filter_t *p_filter, picture_t *p_pic) ++{ ++ VLC_UNUSED(p_filter); ++#if TRACE_ALL ++ msg_Dbg(p_filter, "<<< %s", __func__); ++#endif ++ return p_pic; ++} + +static void CloseConverter(vlc_object_t * obj) +{ + filter_t * const p_filter = (filter_t *)obj; + 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 @@ -2144,13 +2409,16 @@ + 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->resizer_type == FILTER_RESIZER_HVS) ++ { ++ 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) ++ if (sys->is_sliced) + mmal_port_pool_destroy(sys->output, sys->out_pool); + else + mmal_pool_destroy(sys->out_pool); @@ -2169,41 +2437,30 @@ +} + + -+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) ++static int open_converter_passthrough(filter_t * const p_filter) +{ -+ 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); ++ char dbuf0[5], dbuf1[5]; ++ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d", __func__, ++ "passthrough", ++ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), ++ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, ++ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, ++ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, ++ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den, ++ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), ++ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, ++ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, ++ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, ++ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask, ++ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den); + } + -+ 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; -+} ++ p_filter->pf_video_filter = conv_filter_passthrough; ++ p_filter->pf_flush = conv_flush_passthrough; ++ return VLC_SUCCESS; ++} + +static int OpenConverter(vlc_object_t * obj) +{ @@ -2212,19 +2469,34 @@ + filter_sys_t *sys; + MMAL_STATUS_T status; + MMAL_FOURCC_T enc_out; -+ const MMAL_FOURCC_T enc_in = MMAL_ENCODING_OPAQUE; ++ const MMAL_FOURCC_T enc_in = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video); + 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) ++ if ((enc_in != MMAL_ENCODING_OPAQUE && ++ enc_in != MMAL_ENCODING_YUVUV128 && ++ enc_in != MMAL_ENCODING_YUVUV64_10) || ++ (enc_out = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video)) == 0) + return VLC_EGENERIC; - -- mmal_buffer_header_release(buffer); ++ ++ if (enc_in == enc_out) { ++ return open_converter_passthrough(p_filter); ++ } ++ + use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME); + use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME); + ++retry: ++ // Must use ISP - HVS can't do this, nor can resizer ++ if (enc_in == MMAL_ENCODING_YUVUV64_10) { ++ // If resizer selected then just give up ++ if (use_resizer) ++ return VLC_EGENERIC; ++ // otherwise downgrade HVS to ISP ++ use_isp = true; ++ } ++ + if (use_resizer) { + // use resizer overrides use_isp + use_isp = false; @@ -2232,22 +2504,26 @@ + + // 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) ++ (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__, ++ char dbuf0[5], dbuf1[5], dbuf2[5], dbuf3[5]; ++ msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s/%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d (gpu=%d)", __func__, + 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, ++ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), str_fourcc(dbuf2, enc_in), ++ 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, ++ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), str_fourcc(dbuf3, enc_out), ++ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, + p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, + p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, ++ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask, + p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den, + gpu_mem); + } @@ -2261,34 +2537,43 @@ + + // Init stuff the we destroy unconditionaly in Close first + vlc_mutex_init(&sys->lock); -+ vlc_sem_init(&sys->sem, CONV_MAX_LATENCY); ++ vlc_sem_init(&sys->sem, 0); + 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->resizer_type = FILTER_RESIZER_RESIZER; ++ sys->is_sliced = 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->resizer_type = FILTER_RESIZER_ISP; ++ sys->is_sliced = 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->resizer_type = FILTER_RESIZER_HVS; ++ sys->is_sliced = 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) { ++ if (!use_isp && !use_resizer) { ++ msg_Warn(p_filter, "Failed to rcreate HVS resizer - retrying with ISP"); ++ CloseConverter(obj); ++ use_isp = true; ++ goto retry; ++ } + 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]; + @@ -2318,25 +2603,10 @@ + 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) ++ if ((status = 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; -+ } -+ } ++ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced); + + status = mmal_component_enable(sys->component); + if (status != MMAL_SUCCESS) { @@ -2345,21 +2615,13 @@ + 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; + } + ++ if (sys->resizer_type == FILTER_RESIZER_HVS) + { + unsigned int i; + for (i = 0; i != SUBS_MAX; ++i) { @@ -2383,8 +2645,15 @@ + +fail: + CloseConverter(obj); ++ ++ if (!use_resizer && status == MMAL_ENOMEM) { ++ use_resizer = true; ++ msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer"); ++ goto retry; ++ } ++ +#if TRACE_ALL -+msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret); ++ msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret); +#endif + return ret; +} @@ -2438,8 +2707,8 @@ + hw_mmal_pic_sub_buf_add(dst, buf); + + sys->last_dst = dst; - } - } ++ } ++} + +static void FlushBlendMmal(filter_t * p_filter) +{ @@ -2451,9 +2720,16 @@ +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; + ++ if ((vfcc_dst != VLC_CODEC_MMAL_OPAQUE && ++ vfcc_dst != VLC_CODEC_MMAL_ZC_SAND8 && ++ vfcc_dst != VLC_CODEC_MMAL_ZC_SAND10) || ++ !hw_mmal_vzc_subpic_fmt_valid(&p_filter->fmt_in.video)) ++ { ++ return VLC_EGENERIC; ++ } ++ + { + 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__, @@ -2466,10 +2742,6 @@ + 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) @@ -2499,20 +2771,6 @@ + +// --------------------------------------------------------------------------- + -+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) @@ -2521,11 +2779,10 @@ + uint8_t * d_data; + int width = src_pic->format.i_visible_width; + int height = src_pic->format.i_visible_height; ++ blend_neon_fn *const blend_fn = (blend_neon_fn * )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_pic, src_pic->date, src_pic->b_force); -+#else -+ VLC_UNUSED(p_filter); +#endif + + if (alpha == 0 || @@ -2559,20 +2816,7 @@ + + + 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 ++ blend_fn(d_data, s_data, alpha, width); + s_data += src_pic->p[0].i_pitch; + d_data += dst_pic->p[0].i_pitch; + } while (--height > 0); @@ -2581,31 +2825,63 @@ +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; ++ MMAL_FOURCC_T mfcc_src = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video); ++ MMAL_FOURCC_T mfcc_dst = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video); ++ blend_neon_fn * blend_fn = (blend_neon_fn *)0; ++ ++ // Non-alpha RGB only for dest ++ if (vfcc_dst != VLC_CODEC_RGB32) ++ return VLC_EGENERIC; ++ ++ // Check we have appropriate blend fn (mmal doesn't have a non-alpha RGB32) ++ switch (mfcc_src) { ++ case MMAL_ENCODING_RGBA: ++ if (mfcc_dst == MMAL_ENCODING_RGBA) ++ blend_fn = blend_rgbx_rgba_neon; ++ else if (mfcc_dst == MMAL_ENCODING_BGRA) ++ blend_fn = blend_bgrx_rgba_neon; ++ break; ++ ++ case MMAL_ENCODING_BGRA: ++ if (mfcc_dst == MMAL_ENCODING_BGRA) ++ blend_fn = blend_rgbx_rgba_neon; ++ else if (mfcc_dst == MMAL_ENCODING_RGBA) ++ blend_fn = blend_bgrx_rgba_neon; ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (blend_fn == (blend_neon_fn *)0) ++ return VLC_EGENERIC; ++ ++ p_filter->p_sys = (void *)blend_fn; ++ p_filter->pf_video_blend = FilterBlendNeon; + + { + 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__, ++ char dbuf0a[5], dbuf1a[5]; ++ msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %dx%d]->%s/%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, ++ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), ++ str_fourcc(dbuf0a, mfcc_src), ++ 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, ++ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), ++ str_fourcc(dbuf1a, mfcc_dst), ++ 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; +} + @@ -2666,7 +2942,7 @@ #include "mmal_picture.h" -@@ -41,466 +42,602 @@ +@@ -41,466 +42,569 @@ #define MIN_NUM_BUFFERS_IN_TRANSIT 2 @@ -2682,7 +2958,18 @@ +#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") -+ + +-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() +#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") @@ -2700,19 +2987,8 @@ +#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; @@ -2790,7 +3066,7 @@ - if (!sys) - return VLC_ENOMEM; - filter->p_sys = sys; -+ if ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL) ++ if ((pic->context = hw_mmal_gen_context(MMAL_ENCODING_OPAQUE, buf, filter_sys->out_ppr)) == NULL) + goto fail2; - bcm_host_init(); @@ -3033,6 +3309,7 @@ - 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; @@ -3051,56 +3328,77 @@ + mmal_queue_put_back(sys->out_q, out_buf); + break; + } -+ + +- status = mmal_component_enable(sys->component); +- if (status != MMAL_SUCCESS) { +- msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)", +- sys->component->name, status, mmal_status_to_string(status)); +- ret = VLC_EGENERIC; +- goto out; +- } +#if TRACE_ALL + msg_Dbg(p_filter, "-- %s: Q pic=%p: seq_in=%d, seq_out=%d, delta=%d", __func__, out_pic, sys->seq_in, seq_out, seq_delta(sys->seq_in, seq_out)); +#endif -+ + +- sys->filtered_pictures = mmal_queue_create(); + *pp_pic = out_pic; + pp_pic = &out_pic->p_next; -+ + +- filter->pf_video_filter = deinterlace; +- filter->pf_flush = flush; + // Ignore 0 seqs + // 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; +- vlc_sem_init(&sys->sem, 0); +#if TRACE_ALL + msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics); +#endif -+ + +-out: +- if (ret != VLC_SUCCESS) +- Close(filter); + return ret_pics; -+ + +- return ret; +fail: + picture_Release(p_pic); + return NULL; -+} -+ + } + +-static void Close(filter_t *filter) +static void di_flush(filter_t *p_filter) -+{ + { +- filter_sys_t *sys = filter->p_sys; +- MMAL_BUFFER_HEADER_T *buffer; + filter_sys_t * const sys = p_filter->p_sys; -+ + +- if (!sys) +- return; +- +- if (sys->component && sys->component->control->is_enabled) +- mmal_port_disable(sys->component->control); +#if TRACE_ALL + msg_Dbg(p_filter, "<<< %s", __func__); +#endif -+ + +- if (sys->input && sys->input->is_enabled) + if (sys->input != NULL && sys->input->is_enabled) -+ mmal_port_disable(sys->input); -+ + mmal_port_disable(sys->input); + +- if (sys->output && sys->output->is_enabled) + 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); -+ + mmal_port_disable(sys->output); + +- if (sys->component && sys->component->is_enabled) +- mmal_component_disable(sys->component); + // If that dumped anything real into the out_q then have another go + if (mmal_queue_length(sys->out_q) != 0) + { @@ -3110,34 +3408,36 @@ + // Out q should now be empty & should remain so until the input is reenabled + } + mmal_port_enable(sys->output, di_output_port_cb); -+ + +- while ((buffer = mmal_queue_get(sys->filtered_pictures))) { +- picture_t *pic = (picture_t *)buffer->user_data; +- picture_Release(pic); + // Leaving the input disabled is fine - but we want to leave the output enabled + // so we can retrieve buffers that are still bound to pictures } -- sys->filtered_pictures = mmal_queue_create(); +- if (sys->filtered_pictures) +- mmal_queue_destroy(sys->filtered_pictures); + sys->seq_in = 1; + sys->seq_out = 1; -- filter->pf_video_filter = deinterlace; -- filter->pf_flush = flush; +- if (sys->component) +- mmal_component_release(sys->component); +#if TRACE_ALL + msg_Dbg(p_filter, ">>> %s", __func__); +#endif +} -- vlc_sem_init(&sys->sem, 0); +- vlc_sem_destroy(&sys->sem); +- free(sys); --out: -- if (ret != VLC_SUCCESS) -- Close(filter); +- bcm_host_deinit(); +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); @@ -3146,22 +3446,34 @@ + return p_pic; } --static void Close(filter_t *filter) +-static int send_output_buffer(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; + MMAL_STATUS_T status; +- picture_t *picture; +- int ret = 0; -- if (!sys) +- if (!sys->output->is_enabled) { +- ret = VLC_EGENERIC; +- goto out; + if (buffer->cmd == MMAL_EVENT_ERROR) { + status = *(uint32_t *)buffer->data; + msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status, + mmal_status_to_string(status)); -+ } -+ + } + +- picture = filter_NewPicture(filter); +- if (!picture) { +- msg_Warn(filter, "Failed to get new picture"); +- ret = -1; +- goto out; +- } +- picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate; +- picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base; + mmal_buffer_header_reset(buffer); + mmal_buffer_header_release(buffer); +} @@ -3169,87 +3481,101 @@ +static void CloseMmalDeinterlace(filter_t *filter) +{ + filter_sys_t * const sys = filter->p_sys; -+ + +- buffer = picture->p_sys->buffer; +- buffer->user_data = picture; +- buffer->cmd = 0; +#if TRACE_ALL + msg_Dbg(filter, "<<< %s", __func__); +#endif -+ + +- mmal_picture_lock(picture); + if (sys == NULL) - return; ++ return; -- if (sys->component && sys->component->control->is_enabled) -- mmal_port_disable(sys->component->control); +- status = mmal_port_send_buffer(sys->output, buffer); +- if (status != MMAL_SUCCESS) { +- msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)", +- status, mmal_status_to_string(status)); +- mmal_buffer_header_release(buffer); +- picture_Release(picture); +- ret = -1; +- } else { +- atomic_fetch_add(&sys->output_in_transit, 1); +- vlc_sem_post(&sys->sem); + if (sys->use_passthrough) + { + free(sys); + return; -+ } + } -- if (sys->input && sys->input->is_enabled) -- mmal_port_disable(sys->input); +-out: +- return ret; + 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->component && sys->component->is_enabled) ++ mmal_component_disable(sys->component); ++ + 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(); ++ ++ if (sys->component) ++ mmal_component_release(sys->component); ++ ++ free(sys); ++ ++ bcm_host_deinit(); } --static int send_output_buffer(filter_t *filter) +-static void fill_output_port(filter_t *filter) + +static int OpenMmalDeinterlace(filter_t *filter) { - filter_sys_t *sys = filter->p_sys; -- MMAL_BUFFER_HEADER_T *buffer; +- /* 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; + 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; ++ MMAL_STATUS_T status; + filter_sys_t *sys; ++ ++ msg_Dbg(filter, "<<< %s", __func__); -- if (!sys->output->is_enabled) { -- ret = VLC_EGENERIC; -- goto out; -- } +- if (buffers_to_send > buffers_available) +- buffers_to_send = buffers_available; + if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE || + filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE) + return VLC_EGENERIC; -+ + +-#ifndef NDEBUG +- msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)", +- buffers_to_send, buffers_available, sys->output_in_transit, +- sys->output->buffer_num); +#if TRACE_ALL + msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!", + frame_duration, use_qpu ? "used" : "unused"); -+#endif + #endif +- for (i = 0; i < buffers_to_send; ++i) { +- if (send_output_buffer(filter) < 0) +- break; + + sys = calloc(1, sizeof(filter_sys_t)); + if (!sys) @@ -3270,19 +3596,18 @@ + // 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; +-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; + if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU)) + sys->use_qpu = false; + if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV)) @@ -3308,40 +3633,21 @@ + filter->pf_flush = pass_flush; + return 0; + } - -- mmal_picture_lock(picture); ++ + bcm_host_init(); -- status = mmal_port_send_buffer(sys->output, buffer); +- fill_output_port(filter); + 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); ++ 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)); + 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; +- buffer = picture->p_sys->buffer; +- buffer->user_data = picture; +- buffer->pts = picture->date; +- buffer->cmd = 0; + { + const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = { + { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) }, @@ -3352,69 +3658,10 @@ + { 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) { ++ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr); + 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); @@ -3422,14 +3669,12 @@ - picture->p_sys->displayed = true; - atomic_fetch_add(&sys->input_in_transit, 1); - vlc_sem_post(&sys->sem); -- } ++ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)", ++ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); ++ goto fail; + } - } else { - picture_Release(picture); -+ 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; } - /* @@ -3450,10 +3695,11 @@ - msg_Dbg(filter, "Failed waiting for filtered picture"); - break; - } -+ status = mmal_port_enable(sys->input, di_input_port_cb); ++ 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 input port %s (status=%"PRIx32" %s)", -+ sys->input->name, status, mmal_status_to_string(status)); ++ msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)", ++ sys->component->control->name, status, mmal_status_to_string(status)); + goto fail; } - if (out_picture) @@ -3461,28 +3707,30 @@ - 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); ++ sys->input = sys->component->input[0]; ++ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter; ++ if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) ++ sys->input->format->encoding = MMAL_ENCODING_OPAQUE; ++ vlc_to_mmal_video_fmt(sys->input->format, &filter->fmt_in.video); -static void flush(filter_t *filter) -{ - filter_sys_t *sys = filter->p_sys; - MMAL_BUFFER_HEADER_T *buffer; -+ 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; -+ } ++ es_format_Copy(&filter->fmt_out, &filter->fmt_in); ++ if (!sys->half_rate) ++ filter->fmt_out.video.i_frame_rate *= 2; - msg_Dbg(filter, "flush deinterlace filter"); -+ status = port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, true); ++ status = mmal_port_format_commit(sys->input); + 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_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; - msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)", - sys->input_in_transit, sys->output_in_transit); @@ -3499,10 +3747,9 @@ - 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)); ++ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) ++ { ++ msg_Err(filter, "Failed to create input pool"); + goto fail; } - atomic_store(&sys->started, false); @@ -3513,36 +3760,35 @@ -{ - 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; ++ 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; ++ } - 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"); ++ 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; } - 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"); ++ if ((sys->out_q = mmal_queue_create()) == NULL) ++ { ++ msg_Err(filter, "Failed to create out Q"); + goto fail; + } @@ -3551,22 +3797,21 @@ - } 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)); ++ sys->output = sys->component->output[0]; ++ mmal_format_full_copy(sys->output->format, sys->input->format); ++ ++ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS) + goto fail; - } - -- 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; -+ } -+ + } + +- atomic_fetch_sub(&sys->input_in_transit, 1); +- vlc_sem_post(&sys->sem); + filter->pf_video_filter = deinterlace; + filter->pf_flush = di_flush; + return 0; @@ -3623,9 +3868,2287 @@ - mmal_buffer_header_release(buffer); - } -} +--- /dev/null ++++ b/modules/hw/mmal/mmal_avcodec.c +@@ -0,0 +1,2275 @@ ++/***************************************************************************** ++ * video.c: video decoder using the libavcodec library ++ ***************************************************************************** ++ * Copyright (C) 1999-2001 VLC authors and VideoLAN ++ * $Id$ ++ * ++ * Authors: Laurent Aimar ++ * Gildas Bazin ++ * ++ * 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. ++ *****************************************************************************/ ++ ++/***************************************************************************** ++ * Preamble ++ *****************************************************************************/ ++#ifdef HAVE_CONFIG_H ++# include "config.h" ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../../codec/avcodec/avcommon.h" ++ ++#include ++ ++#include ++#include ++#include ++#include ++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) ) ++#include ++#endif ++ ++#include "mmal_picture.h" ++ ++#define TRACE_ALL 0 ++ ++#define AVPROVIDER(lib) ((lib##_VERSION_MICRO < 100) ? "libav" : "ffmpeg") ++ ++#ifdef HAVE_LIBAVCODEC_AVCODEC_H ++#include ++ ++/* LIBAVCODEC_VERSION_CHECK checks for the right version of libav and FFmpeg ++ * a is the major version ++ * b and c the minor and micro versions of libav ++ * d and e the minor and micro versions of FFmpeg */ ++#define LIBAVCODEC_VERSION_CHECK( a, b, c, d, e ) \ ++ ( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \ ++ (LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) ) ++ ++#ifndef AV_CODEC_FLAG_OUTPUT_CORRUPT ++# define AV_CODEC_FLAG_OUTPUT_CORRUPT CODEC_FLAG_OUTPUT_CORRUPT ++#endif ++#ifndef AV_CODEC_FLAG_GRAY ++# define AV_CODEC_FLAG_GRAY CODEC_FLAG_GRAY ++#endif ++#ifndef AV_CODEC_FLAG_DR1 ++# define AV_CODEC_FLAG_DR1 CODEC_FLAG_DR1 ++#endif ++#ifndef AV_CODEC_FLAG_DELAY ++# define AV_CODEC_FLAG_DELAY CODEC_FLAG_DELAY ++#endif ++#ifndef AV_CODEC_FLAG2_FAST ++# define AV_CODEC_FLAG2_FAST CODEC_FLAG2_FAST ++#endif ++#ifndef FF_INPUT_BUFFER_PADDING_SIZE ++# define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE ++#endif ++#ifndef AV_CODEC_FLAG_INTERLACED_DCT ++# define AV_CODEC_FLAG_INTERLACED_DCT CODEC_FLAG_INTERLACED_DCT ++#endif ++#ifndef AV_CODEC_FLAG_INTERLACED_ME ++# define AV_CODEC_FLAG_INTERLACED_ME CODEC_FLAG_INTERLACED_ME ++#endif ++#ifndef AV_CODEC_FLAG_GLOBAL_HEADER ++# define AV_CODEC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER ++#endif ++#ifndef AV_CODEC_FLAG_LOW_DELAY ++# define AV_CODEC_FLAG_LOW_DELAY CODEC_FLAG_LOW_DELAY ++#endif ++#ifndef AV_CODEC_CAP_SMALL_LAST_FRAME ++# define AV_CODEC_CAP_SMALL_LAST_FRAME CODEC_CAP_SMALL_LAST_FRAME ++#endif ++#ifndef AV_INPUT_BUFFER_MIN_SIZE ++# define AV_INPUT_BUFFER_MIN_SIZE FF_MIN_BUFFER_SIZE ++#endif ++#ifndef FF_MAX_B_FRAMES ++# define FF_MAX_B_FRAMES 16 // FIXME: remove this ++#endif ++ ++#endif /* HAVE_LIBAVCODEC_AVCODEC_H */ ++ ++#ifdef HAVE_LIBAVUTIL_AVUTIL_H ++# include ++ ++/* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg ++ * a is the major version ++ * b and c the minor and micro versions of libav ++ * d and e the minor and micro versions of FFmpeg */ ++#define LIBAVUTIL_VERSION_CHECK( a, b, c, d, e ) \ ++ ( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \ ++ (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) ) ++ ++#if !LIBAVUTIL_VERSION_CHECK( 52, 11, 0, 32, 100 ) ++# define AV_PIX_FMT_FLAG_HWACCEL PIX_FMT_HWACCEL ++#endif ++ ++#endif /* HAVE_LIBAVUTIL_AVUTIL_H */ ++ ++#if LIBAVUTIL_VERSION_MAJOR >= 55 ++# define FF_API_AUDIOCONVERT 1 ++#endif ++ ++/* libavutil/pixfmt.h */ ++#ifndef PixelFormat ++# define PixelFormat AVPixelFormat ++#endif ++ ++#ifdef HAVE_LIBAVFORMAT_AVFORMAT_H ++# include ++ ++#define LIBAVFORMAT_VERSION_CHECK( a, b, c, d, e ) \ ++ ( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \ ++ (LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) ) ++ ++#endif ++ ++/***************************************************************************** ++ * Codec fourcc -> libavcodec Codec_id mapping ++ * Sorted by AVCodecID enumeration order ++ *****************************************************************************/ ++struct vlc_avcodec_fourcc ++{ ++ vlc_fourcc_t i_fourcc; ++ unsigned i_codec; ++}; ++ ++/* ++ * Video Codecs ++ */ ++static const struct vlc_avcodec_fourcc video_codecs[] = ++{ ++ { VLC_CODEC_MP1V, AV_CODEC_ID_MPEG1VIDEO }, ++ { VLC_CODEC_MP2V, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */ ++ { VLC_CODEC_MPGV, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */ ++ /* AV_CODEC_ID_MPEG2VIDEO_XVMC */ ++ { VLC_CODEC_H261, AV_CODEC_ID_H261 }, ++ { VLC_CODEC_H263, AV_CODEC_ID_H263 }, ++ { VLC_CODEC_RV10, AV_CODEC_ID_RV10 }, ++ { VLC_CODEC_RV13, AV_CODEC_ID_RV10 }, ++ { VLC_CODEC_RV20, AV_CODEC_ID_RV20 }, ++ { VLC_CODEC_MJPG, AV_CODEC_ID_MJPEG }, ++ { VLC_CODEC_MJPGB, AV_CODEC_ID_MJPEGB }, ++ { VLC_CODEC_LJPG, AV_CODEC_ID_LJPEG }, ++ { VLC_CODEC_SP5X, AV_CODEC_ID_SP5X }, ++ { VLC_CODEC_JPEGLS, AV_CODEC_ID_JPEGLS }, ++ { VLC_CODEC_MP4V, AV_CODEC_ID_MPEG4 }, ++ /* AV_CODEC_ID_RAWVIDEO */ ++ { VLC_CODEC_DIV1, AV_CODEC_ID_MSMPEG4V1 }, ++ { VLC_CODEC_DIV2, AV_CODEC_ID_MSMPEG4V2 }, ++ { VLC_CODEC_DIV3, AV_CODEC_ID_MSMPEG4V3 }, ++ { VLC_CODEC_WMV1, AV_CODEC_ID_WMV1 }, ++ { VLC_CODEC_WMV2, AV_CODEC_ID_WMV2 }, ++ { VLC_CODEC_H263P, AV_CODEC_ID_H263P }, ++ { VLC_CODEC_H263I, AV_CODEC_ID_H263I }, ++ { VLC_CODEC_FLV1, AV_CODEC_ID_FLV1 }, ++ { VLC_CODEC_SVQ1, AV_CODEC_ID_SVQ1 }, ++ { VLC_CODEC_SVQ3, AV_CODEC_ID_SVQ3 }, ++ { VLC_CODEC_DV, AV_CODEC_ID_DVVIDEO }, ++ { VLC_CODEC_HUFFYUV, AV_CODEC_ID_HUFFYUV }, ++ { VLC_CODEC_CYUV, AV_CODEC_ID_CYUV }, ++ { VLC_CODEC_H264, AV_CODEC_ID_H264 }, ++ { VLC_CODEC_INDEO3, AV_CODEC_ID_INDEO3 }, ++ { VLC_CODEC_VP3, AV_CODEC_ID_VP3 }, ++ { VLC_CODEC_THEORA, AV_CODEC_ID_THEORA }, ++#if ( !defined( WORDS_BIGENDIAN ) ) ++ /* Asus Video (Another thing that doesn't work on PPC) */ ++ { VLC_CODEC_ASV1, AV_CODEC_ID_ASV1 }, ++ { VLC_CODEC_ASV2, AV_CODEC_ID_ASV2 }, ++#endif ++ { VLC_CODEC_FFV1, AV_CODEC_ID_FFV1 }, ++ { VLC_CODEC_4XM, AV_CODEC_ID_4XM }, ++ { VLC_CODEC_VCR1, AV_CODEC_ID_VCR1 }, ++ { VLC_CODEC_CLJR, AV_CODEC_ID_CLJR }, ++ { VLC_CODEC_MDEC, AV_CODEC_ID_MDEC }, ++ { VLC_CODEC_ROQ, AV_CODEC_ID_ROQ }, ++ { VLC_CODEC_INTERPLAY, AV_CODEC_ID_INTERPLAY_VIDEO }, ++ { VLC_CODEC_XAN_WC3, AV_CODEC_ID_XAN_WC3 }, ++ { VLC_CODEC_XAN_WC4, AV_CODEC_ID_XAN_WC4 }, ++ { VLC_CODEC_RPZA, AV_CODEC_ID_RPZA }, ++ { VLC_CODEC_CINEPAK, AV_CODEC_ID_CINEPAK }, ++ { VLC_CODEC_WS_VQA, AV_CODEC_ID_WS_VQA }, ++ { VLC_CODEC_MSRLE, AV_CODEC_ID_MSRLE }, ++ { VLC_CODEC_MSVIDEO1, AV_CODEC_ID_MSVIDEO1 }, ++ { VLC_CODEC_IDCIN, AV_CODEC_ID_IDCIN }, ++ { VLC_CODEC_8BPS, AV_CODEC_ID_8BPS }, ++ { VLC_CODEC_SMC, AV_CODEC_ID_SMC }, ++ { VLC_CODEC_FLIC, AV_CODEC_ID_FLIC }, ++ { VLC_CODEC_TRUEMOTION1, AV_CODEC_ID_TRUEMOTION1 }, ++ { VLC_CODEC_VMDVIDEO, AV_CODEC_ID_VMDVIDEO }, ++ { VLC_CODEC_LCL_MSZH, AV_CODEC_ID_MSZH }, ++ { VLC_CODEC_LCL_ZLIB, AV_CODEC_ID_ZLIB }, ++ { VLC_CODEC_QTRLE, AV_CODEC_ID_QTRLE }, ++ { VLC_CODEC_TSCC, AV_CODEC_ID_TSCC }, ++ { VLC_CODEC_ULTI, AV_CODEC_ID_ULTI }, ++ { VLC_CODEC_QDRAW, AV_CODEC_ID_QDRAW }, ++ { VLC_CODEC_VIXL, AV_CODEC_ID_VIXL }, ++ { VLC_CODEC_QPEG, AV_CODEC_ID_QPEG }, ++ { VLC_CODEC_PNG, AV_CODEC_ID_PNG }, ++ { VLC_CODEC_PPM, AV_CODEC_ID_PPM }, ++ /* AV_CODEC_ID_PBM */ ++ { VLC_CODEC_PGM, AV_CODEC_ID_PGM }, ++ { VLC_CODEC_PGMYUV, AV_CODEC_ID_PGMYUV }, ++ { VLC_CODEC_PAM, AV_CODEC_ID_PAM }, ++ { VLC_CODEC_FFVHUFF, AV_CODEC_ID_FFVHUFF }, ++ { VLC_CODEC_RV30, AV_CODEC_ID_RV30 }, ++ { VLC_CODEC_RV40, AV_CODEC_ID_RV40 }, ++ { VLC_CODEC_VC1, AV_CODEC_ID_VC1 }, ++ { VLC_CODEC_WMVA, AV_CODEC_ID_VC1 }, ++ { VLC_CODEC_WMV3, AV_CODEC_ID_WMV3 }, ++ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3 }, ++ { VLC_CODEC_LOCO, AV_CODEC_ID_LOCO }, ++ { VLC_CODEC_WNV1, AV_CODEC_ID_WNV1 }, ++ { VLC_CODEC_AASC, AV_CODEC_ID_AASC }, ++ { VLC_CODEC_INDEO2, AV_CODEC_ID_INDEO2 }, ++ { VLC_CODEC_FRAPS, AV_CODEC_ID_FRAPS }, ++ { VLC_CODEC_TRUEMOTION2, AV_CODEC_ID_TRUEMOTION2 }, ++ { VLC_CODEC_BMP, AV_CODEC_ID_BMP }, ++ { VLC_CODEC_CSCD, AV_CODEC_ID_CSCD }, ++ { VLC_CODEC_MMVIDEO, AV_CODEC_ID_MMVIDEO }, ++ { VLC_CODEC_ZMBV, AV_CODEC_ID_ZMBV }, ++ { VLC_CODEC_AVS, AV_CODEC_ID_AVS }, ++ { VLC_CODEC_SMACKVIDEO, AV_CODEC_ID_SMACKVIDEO }, ++ { VLC_CODEC_NUV, AV_CODEC_ID_NUV }, ++ { VLC_CODEC_KMVC, AV_CODEC_ID_KMVC }, ++ { VLC_CODEC_FLASHSV, AV_CODEC_ID_FLASHSV }, ++ { VLC_CODEC_CAVS, AV_CODEC_ID_CAVS }, ++ { VLC_CODEC_JPEG2000, AV_CODEC_ID_JPEG2000 }, ++ { VLC_CODEC_VMNC, AV_CODEC_ID_VMNC }, ++ { VLC_CODEC_VP5, AV_CODEC_ID_VP5 }, ++ { VLC_CODEC_VP6, AV_CODEC_ID_VP6 }, ++ { VLC_CODEC_VP6F, AV_CODEC_ID_VP6F }, ++ { VLC_CODEC_TARGA, AV_CODEC_ID_TARGA }, ++ { VLC_CODEC_DSICINVIDEO, AV_CODEC_ID_DSICINVIDEO }, ++ { VLC_CODEC_TIERTEXSEQVIDEO, AV_CODEC_ID_TIERTEXSEQVIDEO }, ++ { VLC_CODEC_TIFF, AV_CODEC_ID_TIFF }, ++ { VLC_CODEC_GIF, AV_CODEC_ID_GIF }, ++ { VLC_CODEC_DXA, AV_CODEC_ID_DXA }, ++ { VLC_CODEC_DNXHD, AV_CODEC_ID_DNXHD }, ++ { VLC_CODEC_THP, AV_CODEC_ID_THP }, ++ { VLC_CODEC_SGI, AV_CODEC_ID_SGI }, ++ { VLC_CODEC_C93, AV_CODEC_ID_C93 }, ++ { VLC_CODEC_BETHSOFTVID, AV_CODEC_ID_BETHSOFTVID }, ++ /* AV_CODEC_ID_PTX */ ++ { VLC_CODEC_TXD, AV_CODEC_ID_TXD }, ++ { VLC_CODEC_VP6A, AV_CODEC_ID_VP6A }, ++ { VLC_CODEC_AMV, AV_CODEC_ID_AMV }, ++ { VLC_CODEC_VB, AV_CODEC_ID_VB }, ++ { VLC_CODEC_PCX, AV_CODEC_ID_PCX }, ++ /* AV_CODEC_ID_SUNRAST */ ++ { VLC_CODEC_INDEO4, AV_CODEC_ID_INDEO4 }, ++ { VLC_CODEC_INDEO5, AV_CODEC_ID_INDEO5 }, ++ { VLC_CODEC_MIMIC, AV_CODEC_ID_MIMIC }, ++ { VLC_CODEC_RL2, AV_CODEC_ID_RL2 }, ++ { VLC_CODEC_ESCAPE124, AV_CODEC_ID_ESCAPE124 }, ++ { VLC_CODEC_DIRAC, AV_CODEC_ID_DIRAC }, ++ { VLC_CODEC_BFI, AV_CODEC_ID_BFI }, ++ { VLC_CODEC_CMV, AV_CODEC_ID_CMV }, ++ { VLC_CODEC_MOTIONPIXELS, AV_CODEC_ID_MOTIONPIXELS }, ++ { VLC_CODEC_TGV, AV_CODEC_ID_TGV }, ++ { VLC_CODEC_TGQ, AV_CODEC_ID_TGQ }, ++ { VLC_CODEC_TQI, AV_CODEC_ID_TQI }, ++ { VLC_CODEC_AURA, AV_CODEC_ID_AURA }, ++ /* AV_CODEC_ID_AURA2 */ ++ /* AV_CODEC_ID_V210X */ ++ { VLC_CODEC_TMV, AV_CODEC_ID_TMV }, ++ { VLC_CODEC_V210, AV_CODEC_ID_V210 }, ++ /* AV_CODEC_ID_DPX */ ++ { VLC_CODEC_MAD, AV_CODEC_ID_MAD }, ++ { VLC_CODEC_FRWU, AV_CODEC_ID_FRWU }, ++ { VLC_CODEC_FLASHSV2, AV_CODEC_ID_FLASHSV2 }, ++ /* AV_CODEC_ID_CDGRAPHICS */ ++ /* AV_CODEC_ID_R210 */ ++ { VLC_CODEC_ANM, AV_CODEC_ID_ANM }, ++ { VLC_CODEC_BINKVIDEO, AV_CODEC_ID_BINKVIDEO }, ++ /* AV_CODEC_ID_IFF_ILBM */ ++ /* AV_CODEC_ID_IFF_BYTERUN1 */ ++ { VLC_CODEC_KGV1, AV_CODEC_ID_KGV1 }, ++ { VLC_CODEC_YOP, AV_CODEC_ID_YOP }, ++ { VLC_CODEC_VP8, AV_CODEC_ID_VP8 }, ++ /* AV_CODEC_ID_PICTOR */ ++ /* AV_CODEC_ID_ANSI */ ++ /* AV_CODEC_ID_A64_MULTI */ ++ /* AV_CODEC_ID_A64_MULTI5 */ ++ /* AV_CODEC_ID_R10K */ ++ { VLC_CODEC_MXPEG, AV_CODEC_ID_MXPEG }, ++ { VLC_CODEC_LAGARITH, AV_CODEC_ID_LAGARITH }, ++ { VLC_CODEC_PRORES, AV_CODEC_ID_PRORES }, ++ { VLC_CODEC_JV, AV_CODEC_ID_JV }, ++ { VLC_CODEC_DFA, AV_CODEC_ID_DFA }, ++ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3IMAGE }, ++ { VLC_CODEC_WMVP2, AV_CODEC_ID_VC1IMAGE }, ++ { VLC_CODEC_UTVIDEO, AV_CODEC_ID_UTVIDEO }, ++ { VLC_CODEC_BMVVIDEO, AV_CODEC_ID_BMV_VIDEO }, ++ { VLC_CODEC_VBLE, AV_CODEC_ID_VBLE }, ++ { VLC_CODEC_DXTORY, AV_CODEC_ID_DXTORY }, ++ /* AV_CODEC_ID_V410 */ ++ /* AV_CODEC_ID_XWD */ ++ { VLC_CODEC_CDXL, AV_CODEC_ID_CDXL }, ++ /* AV_CODEC_ID_XBM */ ++ /* AV_CODEC_ID_ZEROCODEC */ ++ { VLC_CODEC_MSS1, AV_CODEC_ID_MSS1 }, ++ { VLC_CODEC_MSA1, AV_CODEC_ID_MSA1 }, ++ { VLC_CODEC_TSC2, AV_CODEC_ID_TSCC2 }, ++ { VLC_CODEC_MTS2, AV_CODEC_ID_MTS2 }, ++ { VLC_CODEC_CLLC, AV_CODEC_ID_CLLC }, ++ { VLC_CODEC_MSS2, AV_CODEC_ID_MSS2 }, ++ { VLC_CODEC_VP9, AV_CODEC_ID_VP9 }, ++#if LIBAVCODEC_VERSION_CHECK( 57, 26, 0, 83, 101 ) ++ { VLC_CODEC_AV1, AV_CODEC_ID_AV1 }, ++#endif ++ { VLC_CODEC_ICOD, AV_CODEC_ID_AIC }, ++ /* AV_CODEC_ID_ESCAPE130 */ ++ { VLC_CODEC_G2M4, AV_CODEC_ID_G2M }, ++ { VLC_CODEC_G2M2, AV_CODEC_ID_G2M }, ++ { VLC_CODEC_G2M3, AV_CODEC_ID_G2M }, ++ /* AV_CODEC_ID_WEBP */ ++ { VLC_CODEC_HNM4_VIDEO, AV_CODEC_ID_HNM4_VIDEO }, ++ { VLC_CODEC_HEVC, AV_CODEC_ID_HEVC }, ++ ++ { VLC_CODEC_FIC , AV_CODEC_ID_FIC }, ++ /* AV_CODEC_ID_ALIAS_PIX */ ++ /* AV_CODEC_ID_BRENDER_PIX */ ++ /* AV_CODEC_ID_PAF_VIDEO */ ++ /* AV_CODEC_ID_EXR */ ++ ++ { VLC_CODEC_VP7 , AV_CODEC_ID_VP7 }, ++ /* AV_CODEC_ID_SANM */ ++ /* AV_CODEC_ID_SGIRLE */ ++ /* AV_CODEC_ID_MVC1 */ ++ /* AV_CODEC_ID_MVC2 */ ++ { VLC_CODEC_HQX, AV_CODEC_ID_HQX }, ++ ++ { VLC_CODEC_TDSC, AV_CODEC_ID_TDSC }, ++ ++ { VLC_CODEC_HQ_HQA, AV_CODEC_ID_HQ_HQA }, ++ ++ { VLC_CODEC_HAP, AV_CODEC_ID_HAP }, ++ /* AV_CODEC_ID_DDS */ ++ ++ { VLC_CODEC_DXV, AV_CODEC_ID_DXV }, ++ ++ /* ffmpeg only: AV_CODEC_ID_BRENDER_PIX */ ++ /* ffmpeg only: AV_CODEC_ID_Y41P */ ++ /* ffmpeg only: AV_CODEC_ID_EXR */ ++ /* ffmpeg only: AV_CODEC_ID_AVRP */ ++ /* ffmpeg only: AV_CODEC_ID_012V */ ++ /* ffmpeg only: AV_CODEC_ID_AVUI */ ++ /* ffmpeg only: AV_CODEC_ID_AYUV */ ++ /* ffmpeg only: AV_CODEC_ID_TARGA_Y216 */ ++ /* ffmpeg only: AV_CODEC_ID_V308 */ ++ /* ffmpeg only: AV_CODEC_ID_V408 */ ++ /* ffmpeg only: AV_CODEC_ID_YUV4 */ ++ /* ffmpeg only: AV_CODEC_ID_SANM */ ++ /* ffmpeg only: AV_CODEC_ID_PAF_VIDEO */ ++ /* ffmpeg only: AV_CODEC_ID_AVRN */ ++ /* ffmpeg only: AV_CODEC_ID_CPIA */ ++ /* ffmpeg only: AV_CODEC_ID_XFACE */ ++ /* ffmpeg only: AV_CODEC_ID_SGIRLE */ ++ /* ffmpeg only: AV_CODEC_ID_MVC1 */ ++ /* ffmpeg only: AV_CODEC_ID_MVC2 */ ++ /* ffmpeg only: AV_CODEC_ID_SNOW */ ++ /* ffmpeg only: AV_CODEC_ID_SMVJPEG */ ++ ++#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 24, 102 ) ++ { VLC_CODEC_CINEFORM, AV_CODEC_ID_CFHD }, ++#endif ++ ++#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 70, 100 ) ++ { VLC_CODEC_PIXLET, AV_CODEC_ID_PIXLET }, ++#endif ++ ++#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 101 ) ++ { VLC_CODEC_SPEEDHQ, AV_CODEC_ID_SPEEDHQ }, ++#endif ++ ++#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 79, 100 ) ++ { VLC_CODEC_FMVC, AV_CODEC_ID_FMVC }, ++#endif ++}; ++ ++/* ++ * Audio Codecs ++ */ ++static const struct vlc_avcodec_fourcc audio_codecs[] = ++{ ++ /* PCM */ ++ { VLC_CODEC_S16L, AV_CODEC_ID_PCM_S16LE }, ++ { VLC_CODEC_S16B, AV_CODEC_ID_PCM_S16BE }, ++ { VLC_CODEC_U16L, AV_CODEC_ID_PCM_U16LE }, ++ { VLC_CODEC_U16B, AV_CODEC_ID_PCM_U16BE }, ++ { VLC_CODEC_S8, AV_CODEC_ID_PCM_S8 }, ++ { VLC_CODEC_U8, AV_CODEC_ID_PCM_U8 }, ++ { VLC_CODEC_MULAW, AV_CODEC_ID_PCM_MULAW }, ++ { VLC_CODEC_ALAW, AV_CODEC_ID_PCM_ALAW }, ++ { VLC_CODEC_S32L, AV_CODEC_ID_PCM_S32LE }, ++ { VLC_CODEC_S32B, AV_CODEC_ID_PCM_S32BE }, ++ { VLC_CODEC_U32L, AV_CODEC_ID_PCM_U32LE }, ++ { VLC_CODEC_U32B, AV_CODEC_ID_PCM_U32BE }, ++ { VLC_CODEC_S24L, AV_CODEC_ID_PCM_S24LE }, ++ { VLC_CODEC_S24B, AV_CODEC_ID_PCM_S24BE }, ++ { VLC_CODEC_U24L, AV_CODEC_ID_PCM_U24LE }, ++ { VLC_CODEC_U24B, AV_CODEC_ID_PCM_U24BE }, ++ { VLC_CODEC_S24DAUD, AV_CODEC_ID_PCM_S24DAUD }, ++ /* AV_CODEC_ID_PCM_ZORK */ ++ { VLC_CODEC_S16L_PLANAR, AV_CODEC_ID_PCM_S16LE_PLANAR }, ++ /* AV_CODEC_ID_PCM_DVD */ ++ { VLC_CODEC_F32B, AV_CODEC_ID_PCM_F32BE }, ++ { VLC_CODEC_F32L, AV_CODEC_ID_PCM_F32LE }, ++ { VLC_CODEC_F64B, AV_CODEC_ID_PCM_F64BE }, ++ { VLC_CODEC_F64L, AV_CODEC_ID_PCM_F64LE }, ++ { VLC_CODEC_BD_LPCM, AV_CODEC_ID_PCM_BLURAY }, ++ /* AV_CODEC_ID_PCM_LXF */ ++ /* AV_CODEC_ID_S302M */ ++ /* AV_CODEC_ID_PCM_S8_PLANAR */ ++ /* AV_CODEC_ID_PCM_S24LE_PLANAR */ ++ /* AV_CODEC_ID_PCM_S32LE_PLANAR */ ++ /* ffmpeg only: AV_CODEC_ID_PCM_S16BE_PLANAR */ ++ ++ /* ADPCM */ ++ { VLC_CODEC_ADPCM_IMA_QT, AV_CODEC_ID_ADPCM_IMA_QT }, ++ { VLC_CODEC_ADPCM_IMA_WAV, AV_CODEC_ID_ADPCM_IMA_WAV }, ++ /* AV_CODEC_ID_ADPCM_IMA_DK3 */ ++ /* AV_CODEC_ID_ADPCM_IMA_DK4 */ ++ { VLC_CODEC_ADPCM_IMA_WS, AV_CODEC_ID_ADPCM_IMA_WS }, ++ /* AV_CODEC_ID_ADPCM_IMA_SMJPEG */ ++ { VLC_CODEC_ADPCM_MS, AV_CODEC_ID_ADPCM_MS }, ++ { VLC_CODEC_ADPCM_4XM, AV_CODEC_ID_ADPCM_4XM }, ++ { VLC_CODEC_ADPCM_XA, AV_CODEC_ID_ADPCM_XA }, ++ { VLC_CODEC_ADPCM_ADX, AV_CODEC_ID_ADPCM_ADX }, ++ { VLC_CODEC_ADPCM_EA, AV_CODEC_ID_ADPCM_EA }, ++ { VLC_CODEC_ADPCM_G726, AV_CODEC_ID_ADPCM_G726 }, ++ { VLC_CODEC_ADPCM_CREATIVE, AV_CODEC_ID_ADPCM_CT }, ++ { VLC_CODEC_ADPCM_SWF, AV_CODEC_ID_ADPCM_SWF }, ++ { VLC_CODEC_ADPCM_YAMAHA, AV_CODEC_ID_ADPCM_YAMAHA }, ++ { VLC_CODEC_ADPCM_SBPRO_4, AV_CODEC_ID_ADPCM_SBPRO_4 }, ++ { VLC_CODEC_ADPCM_SBPRO_3, AV_CODEC_ID_ADPCM_SBPRO_3 }, ++ { VLC_CODEC_ADPCM_SBPRO_2, AV_CODEC_ID_ADPCM_SBPRO_2 }, ++ { VLC_CODEC_ADPCM_THP, AV_CODEC_ID_ADPCM_THP }, ++ { VLC_CODEC_ADPCM_IMA_AMV, AV_CODEC_ID_ADPCM_IMA_AMV }, ++ { VLC_CODEC_ADPCM_EA_R1, AV_CODEC_ID_ADPCM_EA_R1 }, ++ /* AV_CODEC_ID_ADPCM_EA_R3 */ ++ /* AV_CODEC_ID_ADPCM_EA_R2 */ ++ { VLC_CODEC_ADPCM_IMA_EA_SEAD, AV_CODEC_ID_ADPCM_IMA_EA_SEAD }, ++ /* AV_CODEC_ID_ADPCM_IMA_EA_EACS */ ++ /* AV_CODEC_ID_ADPCM_EA_XAS */ ++ /* AV_CODEC_ID_ADPCM_EA_MAXIS_XA */ ++ /* AV_CODEC_ID_ADPCM_IMA_ISS */ ++ { VLC_CODEC_ADPCM_G722, AV_CODEC_ID_ADPCM_G722 }, ++ { VLC_CODEC_ADPCM_IMA_APC, AV_CODEC_ID_ADPCM_IMA_APC }, ++ /* ffmpeg only: AV_CODEC_ID_VIMA */ ++ /* ffmpeg only: AV_CODEC_ID_ADPCM_AFC */ ++ /* ffmpeg only: AV_CODEC_ID_ADPCM_IMA_OKI */ ++ /* ffmpeg only: AV_CODEC_ID_ADPCM_DTK */ ++ /* ffmpeg only: AV_CODEC_ID_ADPCM_IMA_RAD */ ++ /* ffmpeg only: AV_CODEC_ID_ADPCM_G726LE */ ++ ++ /* AMR */ ++ { VLC_CODEC_AMR_NB, AV_CODEC_ID_AMR_NB }, ++ { VLC_CODEC_AMR_WB, AV_CODEC_ID_AMR_WB }, ++ ++ /* RealAudio */ ++ { VLC_CODEC_RA_144, AV_CODEC_ID_RA_144 }, ++ { VLC_CODEC_RA_288, AV_CODEC_ID_RA_288 }, ++ ++ /* DPCM */ ++ { VLC_CODEC_ROQ_DPCM, AV_CODEC_ID_ROQ_DPCM }, ++ { VLC_CODEC_INTERPLAY_DPCM, AV_CODEC_ID_INTERPLAY_DPCM }, ++ /* AV_CODEC_ID_XAN_DPCM */ ++ /* AV_CODEC_ID_SOL_DPCM */ ++ ++ /* audio codecs */ ++ { VLC_CODEC_MPGA, AV_CODEC_ID_MP2 }, ++ { VLC_CODEC_MP2, AV_CODEC_ID_MP2 }, ++ { VLC_CODEC_MP3, AV_CODEC_ID_MP3 }, ++ { VLC_CODEC_MP4A, AV_CODEC_ID_AAC }, ++ { VLC_CODEC_A52, AV_CODEC_ID_AC3 }, ++ { VLC_CODEC_DTS, AV_CODEC_ID_DTS }, ++ { VLC_CODEC_VORBIS, AV_CODEC_ID_VORBIS }, ++ { VLC_CODEC_DVAUDIO, AV_CODEC_ID_DVAUDIO }, ++ { VLC_CODEC_WMA1, AV_CODEC_ID_WMAV1 }, ++ { VLC_CODEC_WMA2, AV_CODEC_ID_WMAV2 }, ++ { VLC_CODEC_MACE3, AV_CODEC_ID_MACE3 }, ++ { VLC_CODEC_MACE6, AV_CODEC_ID_MACE6 }, ++ { VLC_CODEC_VMDAUDIO, AV_CODEC_ID_VMDAUDIO }, ++ { VLC_CODEC_FLAC, AV_CODEC_ID_FLAC }, ++ /* AV_CODEC_ID_MP3ADU */ ++ /* AV_CODEC_ID_MP3ON4 */ ++ { VLC_CODEC_SHORTEN, AV_CODEC_ID_SHORTEN }, ++ { VLC_CODEC_ALAC, AV_CODEC_ID_ALAC }, ++ /* AV_CODEC_ID_WESTWOOD_SND1 */ ++ { VLC_CODEC_GSM, AV_CODEC_ID_GSM }, ++ { VLC_CODEC_QDM2, AV_CODEC_ID_QDM2 }, ++#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 100 ) ++ { VLC_CODEC_QDMC, AV_CODEC_ID_QDMC }, ++#endif ++ { VLC_CODEC_COOK, AV_CODEC_ID_COOK }, ++ { VLC_CODEC_TRUESPEECH, AV_CODEC_ID_TRUESPEECH }, ++ { VLC_CODEC_TTA, AV_CODEC_ID_TTA }, ++ { VLC_CODEC_SMACKAUDIO, AV_CODEC_ID_SMACKAUDIO }, ++ { VLC_CODEC_QCELP, AV_CODEC_ID_QCELP }, ++ { VLC_CODEC_WAVPACK, AV_CODEC_ID_WAVPACK }, ++ { VLC_CODEC_DSICINAUDIO, AV_CODEC_ID_DSICINAUDIO }, ++ { VLC_CODEC_IMC, AV_CODEC_ID_IMC }, ++ { VLC_CODEC_MUSEPACK7, AV_CODEC_ID_MUSEPACK7 }, ++ { VLC_CODEC_MLP, AV_CODEC_ID_MLP }, ++ { VLC_CODEC_GSM_MS, AV_CODEC_ID_GSM_MS }, ++ { VLC_CODEC_ATRAC3, AV_CODEC_ID_ATRAC3 }, ++ { VLC_CODEC_APE, AV_CODEC_ID_APE }, ++ { VLC_CODEC_NELLYMOSER, AV_CODEC_ID_NELLYMOSER }, ++ { VLC_CODEC_MUSEPACK8, AV_CODEC_ID_MUSEPACK8 }, ++ { VLC_CODEC_SPEEX, AV_CODEC_ID_SPEEX }, ++ { VLC_CODEC_WMAS, AV_CODEC_ID_WMAVOICE }, ++ { VLC_CODEC_WMAP, AV_CODEC_ID_WMAPRO }, ++ { VLC_CODEC_WMAL, AV_CODEC_ID_WMALOSSLESS }, ++ { VLC_CODEC_ATRAC3P, AV_CODEC_ID_ATRAC3P }, ++ { VLC_CODEC_EAC3, AV_CODEC_ID_EAC3 }, ++ { VLC_CODEC_SIPR, AV_CODEC_ID_SIPR }, ++ /* AV_CODEC_ID_MP1 */ ++ { VLC_CODEC_TWINVQ, AV_CODEC_ID_TWINVQ }, ++ { VLC_CODEC_TRUEHD, AV_CODEC_ID_TRUEHD }, ++ { VLC_CODEC_ALS, AV_CODEC_ID_MP4ALS }, ++ { VLC_CODEC_ATRAC1, AV_CODEC_ID_ATRAC1 }, ++ { VLC_CODEC_BINKAUDIO_RDFT, AV_CODEC_ID_BINKAUDIO_RDFT }, ++ { VLC_CODEC_BINKAUDIO_DCT, AV_CODEC_ID_BINKAUDIO_DCT }, ++ { VLC_CODEC_MP4A, AV_CODEC_ID_AAC_LATM }, ++ /* AV_CODEC_ID_QDMC */ ++ /* AV_CODEC_ID_CELT */ ++ { VLC_CODEC_G723_1, AV_CODEC_ID_G723_1 }, ++ /* AV_CODEC_ID_G729 */ ++ /* AV_CODEC_ID_8SVX_EXP */ ++ /* AV_CODEC_ID_8SVX_FIB */ ++ { VLC_CODEC_BMVAUDIO, AV_CODEC_ID_BMV_AUDIO }, ++ { VLC_CODEC_RALF, AV_CODEC_ID_RALF }, ++ { VLC_CODEC_INDEO_AUDIO, AV_CODEC_ID_IAC }, ++ /* AV_CODEC_ID_ILBC */ ++ { VLC_CODEC_OPUS, AV_CODEC_ID_OPUS }, ++ /* AV_CODEC_ID_COMFORT_NOISE */ ++ { VLC_CODEC_TAK, AV_CODEC_ID_TAK }, ++ { VLC_CODEC_METASOUND, AV_CODEC_ID_METASOUND }, ++ /* AV_CODEC_ID_PAF_AUDIO */ ++ { VLC_CODEC_ON2AVC, AV_CODEC_ID_ON2AVC }, ++ ++ /* ffmpeg only: AV_CODEC_ID_FFWAVESYNTH */ ++ /* ffmpeg only: AV_CODEC_ID_SONIC */ ++ /* ffmpeg only: AV_CODEC_ID_SONIC_LS */ ++ /* ffmpeg only: AV_CODEC_ID_PAF_AUDIO */ ++ /* ffmpeg only: AV_CODEC_ID_EVRC */ ++ /* ffmpeg only: AV_CODEC_ID_SMV */ ++}; ++ ++/* Subtitle streams */ ++static const struct vlc_avcodec_fourcc spu_codecs[] = ++{ ++ { VLC_CODEC_SPU, AV_CODEC_ID_DVD_SUBTITLE }, ++ { VLC_CODEC_DVBS, AV_CODEC_ID_DVB_SUBTITLE }, ++ { VLC_CODEC_SUBT, AV_CODEC_ID_TEXT }, ++ { VLC_CODEC_XSUB, AV_CODEC_ID_XSUB }, ++ { VLC_CODEC_SSA, AV_CODEC_ID_SSA }, ++ /* AV_CODEC_ID_MOV_TEXT */ ++ { VLC_CODEC_BD_PG, AV_CODEC_ID_HDMV_PGS_SUBTITLE }, ++#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 100 ) ++ { VLC_CODEC_BD_TEXT, AV_CODEC_ID_HDMV_TEXT_SUBTITLE }, ++#endif ++ { VLC_CODEC_TELETEXT, AV_CODEC_ID_DVB_TELETEXT }, ++ /* AV_CODEC_ID_SRT */ ++ /* ffmpeg only: AV_CODEC_ID_MICRODVD */ ++ /* ffmpeg only: AV_CODEC_ID_EIA_608 */ ++ /* ffmpeg only: AV_CODEC_ID_JACOSUB */ ++ /* ffmpeg only: AV_CODEC_ID_SAMI */ ++ /* ffmpeg only: AV_CODEC_ID_REALTEXT */ ++ /* ffmpeg only: AV_CODEC_ID_SUBVIEWER1 */ ++ /* ffmpeg only: AV_CODEC_ID_SUBVIEWER */ ++ /* ffmpeg only: AV_CODEC_ID_SUBRIP */ ++ /* ffmpeg only: AV_CODEC_ID_WEBVTT */ ++ /* ffmpeg only: AV_CODEC_ID_MPL2 */ ++ /* ffmpeg only: AV_CODEC_ID_VPLAYER */ ++ /* ffmpeg only: AV_CODEC_ID_PJS */ ++ /* ffmpeg only: AV_CODEC_ID_ASS */ ++}; ++ ++static bool GetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc, ++ unsigned *pi_ffmpeg_codec, const char **ppsz_name ) ++{ ++ const struct vlc_avcodec_fourcc *base; ++ size_t count; ++ ++ switch( cat ) ++ { ++ case VIDEO_ES: ++ base = video_codecs; ++ count = ARRAY_SIZE(video_codecs); ++ break; ++ case AUDIO_ES: ++ base = audio_codecs; ++ count = ARRAY_SIZE(audio_codecs); ++ break; ++ case SPU_ES: ++ base = spu_codecs; ++ count = ARRAY_SIZE(spu_codecs); ++ break; ++ default: ++ base = NULL; ++ count = 0; ++ } ++ ++ i_fourcc = vlc_fourcc_GetCodec( cat, i_fourcc ); ++ ++ for( size_t i = 0; i < count; i++ ) ++ { ++ if( base[i].i_fourcc == i_fourcc ) ++ { ++ if( pi_ffmpeg_codec != NULL ) ++ *pi_ffmpeg_codec = base[i].i_codec; ++ if( ppsz_name ) ++ *ppsz_name = vlc_fourcc_GetDescription( cat, i_fourcc ); ++ return true; ++ } ++ } ++ return false; ++} ++ ++/***************************************************************************** ++ * Chroma fourcc -> libavutil pixfmt mapping ++ *****************************************************************************/ ++#if defined(WORDS_BIGENDIAN) ++# define VLC_RGB_ES( fcc, leid, beid ) \ ++ { fcc, beid, 0, 0, 0 }, ++#else ++# define VLC_RGB_ES( fcc, leid, beid ) \ ++ { fcc, leid, 0, 0, 0 }, ++#endif ++ ++#define VLC_RGB( fcc, leid, beid, rmask, gmask, bmask ) \ ++ { fcc, leid, rmask, gmask, bmask }, \ ++ { fcc, beid, bmask, gmask, rmask }, \ ++ VLC_RGB_ES( fcc, leid, beid ) ++ ++ ++static const struct ++{ ++ vlc_fourcc_t i_chroma; ++ int i_chroma_id; ++ uint32_t i_rmask; ++ uint32_t i_gmask; ++ uint32_t i_bmask; ++ ++} chroma_table[] = ++{ ++ // Sand ++// {VLC_CODEC_MMAL_OPAQUE, AV_PIX_FMT_SAND128, 0, 0, 0 }, ++ {VLC_CODEC_MMAL_ZC_SAND8, AV_PIX_FMT_SAND128, 0, 0, 0 }, ++ {VLC_CODEC_MMAL_ZC_SAND10, AV_PIX_FMT_SAND64_10, 0, 0, 0 }, ++ ++ /* Planar YUV formats */ ++ {VLC_CODEC_I444, AV_PIX_FMT_YUV444P, 0, 0, 0 }, ++ {VLC_CODEC_J444, AV_PIX_FMT_YUVJ444P, 0, 0, 0 }, ++ ++ {VLC_CODEC_I440, AV_PIX_FMT_YUV440P, 0, 0, 0 }, ++ {VLC_CODEC_J440, AV_PIX_FMT_YUVJ440P, 0, 0, 0 }, ++ ++ {VLC_CODEC_I422, AV_PIX_FMT_YUV422P, 0, 0, 0 }, ++ {VLC_CODEC_J422, AV_PIX_FMT_YUVJ422P, 0, 0, 0 }, ++ ++ {VLC_CODEC_I420, AV_PIX_FMT_YUV420P, 0, 0, 0 }, ++ {VLC_CODEC_YV12, AV_PIX_FMT_YUV420P, 0, 0, 0 }, ++ {VLC_FOURCC('I','Y','U','V'), AV_PIX_FMT_YUV420P, 0, 0, 0 }, ++ {VLC_CODEC_J420, AV_PIX_FMT_YUVJ420P, 0, 0, 0 }, ++ {VLC_CODEC_I411, AV_PIX_FMT_YUV411P, 0, 0, 0 }, ++ {VLC_CODEC_I410, AV_PIX_FMT_YUV410P, 0, 0, 0 }, ++ {VLC_FOURCC('Y','V','U','9'), AV_PIX_FMT_YUV410P, 0, 0, 0 }, ++ ++ {VLC_CODEC_NV12, AV_PIX_FMT_NV12, 0, 0, 0 }, ++ {VLC_CODEC_NV21, AV_PIX_FMT_NV21, 0, 0, 0 }, ++ ++ {VLC_CODEC_I420_9L, AV_PIX_FMT_YUV420P9LE, 0, 0, 0 }, ++ {VLC_CODEC_I420_9B, AV_PIX_FMT_YUV420P9BE, 0, 0, 0 }, ++ {VLC_CODEC_I420_10L, AV_PIX_FMT_YUV420P10LE, 0, 0, 0 }, ++ {VLC_CODEC_I420_10B, AV_PIX_FMT_YUV420P10BE, 0, 0, 0 }, ++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) ) ++ {VLC_CODEC_I420_12L, AV_PIX_FMT_YUV420P12LE, 0, 0, 0 }, ++ {VLC_CODEC_I420_12B, AV_PIX_FMT_YUV420P12BE, 0, 0, 0 }, ++#endif ++ {VLC_CODEC_I420_16L, AV_PIX_FMT_YUV420P16LE, 0, 0, 0 }, ++ {VLC_CODEC_I420_16B, AV_PIX_FMT_YUV420P16BE, 0, 0, 0 }, ++#ifdef AV_PIX_FMT_P010 ++ {VLC_CODEC_P010, AV_PIX_FMT_P010, 0, 0, 0 }, ++#endif ++ ++ {VLC_CODEC_I422_9L, AV_PIX_FMT_YUV422P9LE, 0, 0, 0 }, ++ {VLC_CODEC_I422_9B, AV_PIX_FMT_YUV422P9BE, 0, 0, 0 }, ++ {VLC_CODEC_I422_10L, AV_PIX_FMT_YUV422P10LE, 0, 0, 0 }, ++ {VLC_CODEC_I422_10B, AV_PIX_FMT_YUV422P10BE, 0, 0, 0 }, ++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) ) ++ {VLC_CODEC_I422_12L, AV_PIX_FMT_YUV422P12LE, 0, 0, 0 }, ++ {VLC_CODEC_I422_12B, AV_PIX_FMT_YUV422P12BE, 0, 0, 0 }, ++#endif ++ ++ {VLC_CODEC_YUV420A, AV_PIX_FMT_YUVA420P, 0, 0, 0 }, ++ {VLC_CODEC_YUV422A, AV_PIX_FMT_YUVA422P, 0, 0, 0 }, ++ {VLC_CODEC_YUVA, AV_PIX_FMT_YUVA444P, 0, 0, 0 }, ++ ++ {VLC_CODEC_YUVA_444_10L, AV_PIX_FMT_YUVA444P10LE, 0, 0, 0 }, ++ {VLC_CODEC_YUVA_444_10B, AV_PIX_FMT_YUVA444P10BE, 0, 0, 0 }, ++ ++ {VLC_CODEC_I444_9L, AV_PIX_FMT_YUV444P9LE, 0, 0, 0 }, ++ {VLC_CODEC_I444_9B, AV_PIX_FMT_YUV444P9BE, 0, 0, 0 }, ++ {VLC_CODEC_I444_10L, AV_PIX_FMT_YUV444P10LE, 0, 0, 0 }, ++ {VLC_CODEC_I444_10B, AV_PIX_FMT_YUV444P10BE, 0, 0, 0 }, ++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) ) ++ {VLC_CODEC_I444_12L, AV_PIX_FMT_YUV444P12LE, 0, 0, 0 }, ++ {VLC_CODEC_I444_12B, AV_PIX_FMT_YUV444P12BE, 0, 0, 0 }, ++#endif ++ {VLC_CODEC_I444_16L, AV_PIX_FMT_YUV444P16LE, 0, 0, 0 }, ++ {VLC_CODEC_I444_16B, AV_PIX_FMT_YUV444P16BE, 0, 0, 0 }, ++ ++ /* Packed YUV formats */ ++ {VLC_CODEC_YUYV, AV_PIX_FMT_YUYV422, 0, 0, 0 }, ++ {VLC_FOURCC('Y','U','Y','V'), AV_PIX_FMT_YUYV422, 0, 0, 0 }, ++ {VLC_CODEC_UYVY, AV_PIX_FMT_UYVY422, 0, 0, 0 }, ++ {VLC_CODEC_YVYU, AV_PIX_FMT_YVYU422, 0, 0, 0 }, ++ {VLC_FOURCC('Y','4','1','1'), AV_PIX_FMT_UYYVYY411, 0, 0, 0 }, ++ ++ /* Packed RGB formats */ ++ VLC_RGB( VLC_FOURCC('R','G','B','4'), AV_PIX_FMT_RGB4, AV_PIX_FMT_BGR4, 0x10, 0x06, 0x01 ) ++ VLC_RGB( VLC_CODEC_RGB8, AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, 0xC0, 0x38, 0x07 ) ++ ++ VLC_RGB( VLC_CODEC_RGB15, AV_PIX_FMT_RGB555, AV_PIX_FMT_BGR555, 0x7c00, 0x03e0, 0x001f ) ++ VLC_RGB( VLC_CODEC_RGB16, AV_PIX_FMT_RGB565, AV_PIX_FMT_BGR565, 0xf800, 0x07e0, 0x001f ) ++ VLC_RGB( VLC_CODEC_RGB24, AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, 0xff0000, 0x00ff00, 0x0000ff ) ++ ++ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_RGB32, AV_PIX_FMT_BGR32, 0x00ff0000, 0x0000ff00, 0x000000ff ) ++ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_RGB32_1, AV_PIX_FMT_BGR32_1, 0xff000000, 0x00ff0000, 0x0000ff00 ) ++ ++#ifdef AV_PIX_FMT_0BGR32 ++ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_0BGR32, AV_PIX_FMT_0RGB32, 0x000000ff, 0x0000ff00, 0x00ff0000 ) ++#endif ++ ++ {VLC_CODEC_RGBA, AV_PIX_FMT_RGBA, 0, 0, 0 }, ++ {VLC_CODEC_ARGB, AV_PIX_FMT_ARGB, 0, 0, 0 }, ++ {VLC_CODEC_BGRA, AV_PIX_FMT_BGRA, 0, 0, 0 }, ++ {VLC_CODEC_GREY, AV_PIX_FMT_GRAY8, 0, 0, 0}, ++ ++ /* Paletized RGB */ ++ {VLC_CODEC_RGBP, AV_PIX_FMT_PAL8, 0, 0, 0}, ++ ++ {VLC_CODEC_GBR_PLANAR, AV_PIX_FMT_GBRP, 0, 0, 0 }, ++ {VLC_CODEC_GBR_PLANAR_9L, AV_PIX_FMT_GBRP9LE, 0, 0, 0 }, ++ {VLC_CODEC_GBR_PLANAR_9B, AV_PIX_FMT_GBRP9BE, 0, 0, 0 }, ++ {VLC_CODEC_GBR_PLANAR_10L, AV_PIX_FMT_GBRP10LE, 0, 0, 0 }, ++ {VLC_CODEC_GBR_PLANAR_10B, AV_PIX_FMT_GBRP10BE, 0, 0, 0 }, ++ ++ /* XYZ */ ++#if LIBAVUTIL_VERSION_CHECK(52, 10, 0, 25, 100) ++ {VLC_CODEC_XYZ12, AV_PIX_FMT_XYZ12, 0xfff0, 0xfff0, 0xfff0}, ++#endif ++ { 0, 0, 0, 0, 0 } ++}; ++ ++static int GetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma ) ++{ ++ /* TODO FIXME for rgb format we HAVE to set rgb mask/shift */ ++ for( int i = 0; chroma_table[i].i_chroma != 0; i++ ) ++ { ++ if( chroma_table[i].i_chroma_id == i_ffmpeg_chroma ) ++ { ++ fmt->i_rmask = chroma_table[i].i_rmask; ++ fmt->i_gmask = chroma_table[i].i_gmask; ++ fmt->i_bmask = chroma_table[i].i_bmask; ++ fmt->i_chroma = chroma_table[i].i_chroma; ++ return VLC_SUCCESS; ++ } ++ } ++ return VLC_EGENERIC; ++} ++ ++//#include "../codec/cc.h" ++ ++static AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec, ++ const AVCodec **restrict codecp ) ++{ ++ unsigned i_codec_id; ++ const char *psz_namecodec; ++ const AVCodec *p_codec = NULL; ++ ++ /* *** determine codec type *** */ ++ if( !GetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec, ++ &i_codec_id, &psz_namecodec ) ) ++ return NULL; ++ ++ msg_Dbg( p_dec, "using %s %s", AVPROVIDER(LIBAVCODEC), LIBAVCODEC_IDENT ); ++ ++ /* Initialization must be done before avcodec_find_decoder() */ ++ vlc_init_avcodec(VLC_OBJECT(p_dec)); ++ ++ /* *** ask ffmpeg for a decoder *** */ ++ char *psz_decoder = var_InheritString( p_dec, "avcodec-codec" ); ++ if( psz_decoder != NULL ) ++ { ++ p_codec = avcodec_find_decoder_by_name( psz_decoder ); ++ if( !p_codec ) ++ msg_Err( p_dec, "Decoder `%s' not found", psz_decoder ); ++ else if( p_codec->id != i_codec_id ) ++ { ++ msg_Err( p_dec, "Decoder `%s' can't handle %4.4s", ++ psz_decoder, (char*)&p_dec->fmt_in.i_codec ); ++ p_codec = NULL; ++ } ++ free( psz_decoder ); ++ } ++ if( !p_codec ) ++ p_codec = avcodec_find_decoder( i_codec_id ); ++ if( !p_codec ) ++ { ++ msg_Dbg( p_dec, "codec not found (%s)", psz_namecodec ); ++ return NULL; ++ } ++ ++ *codecp = p_codec; ++ ++ /* *** get a p_context *** */ ++ AVCodecContext *avctx = avcodec_alloc_context3(p_codec); ++ if( unlikely(avctx == NULL) ) ++ return NULL; ++ ++ avctx->debug = var_InheritInteger( p_dec, "avcodec-debug" ); ++ avctx->opaque = p_dec; ++ return avctx; ++} ++ ++static int ffmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx, ++ const AVCodec *codec ) ++{ ++ char *psz_opts = var_InheritString( p_dec, "avcodec-options" ); ++ AVDictionary *options = NULL; ++ int ret; ++ ++ if (psz_opts) { ++ vlc_av_get_options(psz_opts, &options); ++ free(psz_opts); ++ } ++ ++ if (av_rpi_zc_init(ctx) != 0) ++ { ++ msg_Err(p_dec, "Failed to init AV ZC"); ++ return VLC_EGENERIC; ++ } ++ ++ vlc_avcodec_lock(); ++ ret = avcodec_open2( ctx, codec, options ? &options : NULL ); ++ vlc_avcodec_unlock(); ++ ++ AVDictionaryEntry *t = NULL; ++ while ((t = av_dict_get(options, "", t, AV_DICT_IGNORE_SUFFIX))) { ++ msg_Err( p_dec, "Unknown option \"%s\"", t->key ); ++ } ++ av_dict_free(&options); ++ ++ if( ret < 0 ) ++ { ++ msg_Err( p_dec, "cannot start codec (%s)", codec->name ); ++ return VLC_EGENERIC; ++ } ++ ++ msg_Dbg( p_dec, "codec (%s) started", codec->name ); ++ return VLC_SUCCESS; ++} ++ ++ ++/***************************************************************************** ++ * decoder_sys_t : decoder descriptor ++ *****************************************************************************/ ++struct decoder_sys_t ++{ ++ AVCodecContext *p_context; ++ const AVCodec *p_codec; ++ ++ /* Video decoder specific part */ ++ date_t pts; ++ ++ /* Closed captions for decoders */ ++// cc_data_t cc; ++ ++ /* for frame skipping algo */ ++ bool b_hurry_up; ++ bool b_show_corrupted; ++ bool b_from_preroll; ++ enum AVDiscard i_skip_frame; ++ ++ /* how many decoded frames are late */ ++ int i_late_frames; ++ mtime_t i_late_frames_start; ++ mtime_t i_last_late_delay; ++ ++ /* for direct rendering */ ++ bool b_direct_rendering; ++ atomic_bool b_dr_failure; ++ ++ /* Hack to force display of still pictures */ ++ bool b_first_frame; ++ ++ ++ /* */ ++ bool palette_sent; ++ ++ /* VA API */ ++// vlc_va_t *p_va; ++ enum AVPixelFormat pix_fmt; ++ int profile; ++ int level; ++ ++ MMAL_POOL_T * out_pool; ++}; ++ ++/***************************************************************************** ++ * Local prototypes ++ *****************************************************************************/ ++static void ffmpeg_InitCodec ( decoder_t * ); ++static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *, ++ const enum PixelFormat * ); ++static int DecodeVideo( decoder_t *, block_t * ); ++static void Flush( decoder_t * ); ++ ++static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc ) ++{ ++ uint8_t *p = (uint8_t*)&fcc; ++ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); ++} ++ ++/***************************************************************************** ++ * Local Functions ++ *****************************************************************************/ ++ ++/** ++ * Sets the decoder output format. ++ */ ++static int lavc_GetVideoFormat(decoder_t *dec, video_format_t *restrict fmt, ++ AVCodecContext *ctx, enum AVPixelFormat pix_fmt, ++ enum AVPixelFormat sw_pix_fmt) ++{ ++ int width = ctx->coded_width; ++ int height = ctx->coded_height; ++ ++ video_format_Init(fmt, 0); ++ ++ if (pix_fmt == sw_pix_fmt) ++ { /* software decoding */ ++ int aligns[AV_NUM_DATA_POINTERS]; ++ ++ if (GetVlcChroma(fmt, pix_fmt)) ++ return -1; ++ ++ /* The libavcodec palette can only be fetched when the first output ++ * frame is decoded. Assume that the current chroma is RGB32 while we ++ * are waiting for a valid palette. Indeed, fmt_out.video.p_palette ++ * doesn't trigger a new vout request, but a new chroma yes. */ ++ if (pix_fmt == AV_PIX_FMT_PAL8 && !dec->fmt_out.video.p_palette) ++ fmt->i_chroma = VLC_CODEC_RGB32; ++ ++ avcodec_align_dimensions2(ctx, &width, &height, aligns); ++ } ++// else /* hardware decoding */ ++// fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt); ++ ++ if( width == 0 || height == 0 || width > 8192 || height > 8192 || ++ width < ctx->width || height < ctx->height ) ++ { ++ msg_Err(dec, "Invalid frame size %dx%d vsz %dx%d", ++ width, height, ctx->width, ctx->height ); ++ return -1; /* invalid display size */ ++ } ++ ++ fmt->i_width = width; ++ fmt->i_height = height; ++ fmt->i_visible_width = ctx->width; ++ fmt->i_visible_height = ctx->height; ++ ++ /* If an aspect-ratio was specified in the input format then force it */ ++ if (dec->fmt_in.video.i_sar_num > 0 && dec->fmt_in.video.i_sar_den > 0) ++ { ++ fmt->i_sar_num = dec->fmt_in.video.i_sar_num; ++ fmt->i_sar_den = dec->fmt_in.video.i_sar_den; ++ } ++ else ++ { ++ fmt->i_sar_num = ctx->sample_aspect_ratio.num; ++ fmt->i_sar_den = ctx->sample_aspect_ratio.den; ++ ++ if (fmt->i_sar_num == 0 || fmt->i_sar_den == 0) ++ fmt->i_sar_num = fmt->i_sar_den = 1; ++ } ++ ++ if (dec->fmt_in.video.i_frame_rate > 0 ++ && dec->fmt_in.video.i_frame_rate_base > 0) ++ { ++ fmt->i_frame_rate = dec->fmt_in.video.i_frame_rate; ++ fmt->i_frame_rate_base = dec->fmt_in.video.i_frame_rate_base; ++ } ++ else if (ctx->framerate.num > 0 && ctx->framerate.den > 0) ++ { ++ fmt->i_frame_rate = ctx->framerate.num; ++ fmt->i_frame_rate_base = ctx->framerate.den; ++# if LIBAVCODEC_VERSION_MICRO < 100 ++ // for some reason libav don't thinkg framerate presents actually same thing as in ffmpeg ++ fmt->i_frame_rate_base *= __MAX(ctx->ticks_per_frame, 1); ++# endif ++ } ++ else if (ctx->time_base.num > 0 && ctx->time_base.den > 0) ++ { ++ fmt->i_frame_rate = ctx->time_base.den; ++ fmt->i_frame_rate_base = ctx->time_base.num ++ * __MAX(ctx->ticks_per_frame, 1); ++ } ++ ++ if( ctx->color_range == AVCOL_RANGE_JPEG ) ++ fmt->b_color_range_full = true; ++ ++ switch( ctx->colorspace ) ++ { ++ case AVCOL_SPC_BT709: ++ fmt->space = COLOR_SPACE_BT709; ++ break; ++ case AVCOL_SPC_SMPTE170M: ++ case AVCOL_SPC_BT470BG: ++ fmt->space = COLOR_SPACE_BT601; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ case AVCOL_SPC_BT2020_CL: ++ fmt->space = COLOR_SPACE_BT2020; ++ break; ++ default: ++ break; ++ } ++ ++ switch( ctx->color_trc ) ++ { ++ case AVCOL_TRC_LINEAR: ++ fmt->transfer = TRANSFER_FUNC_LINEAR; ++ break; ++ case AVCOL_TRC_GAMMA22: ++ fmt->transfer = TRANSFER_FUNC_SRGB; ++ break; ++ case AVCOL_TRC_BT709: ++ fmt->transfer = TRANSFER_FUNC_BT709; ++ break; ++ case AVCOL_TRC_SMPTE170M: ++ case AVCOL_TRC_BT2020_10: ++ case AVCOL_TRC_BT2020_12: ++ fmt->transfer = TRANSFER_FUNC_BT2020; ++ break; ++#if LIBAVUTIL_VERSION_CHECK( 55, 14, 0, 31, 100) ++ case AVCOL_TRC_ARIB_STD_B67: ++ fmt->transfer = TRANSFER_FUNC_ARIB_B67; ++ break; ++#endif ++#if LIBAVUTIL_VERSION_CHECK( 55, 17, 0, 37, 100) ++ case AVCOL_TRC_SMPTE2084: ++ fmt->transfer = TRANSFER_FUNC_SMPTE_ST2084; ++ break; ++ case AVCOL_TRC_SMPTE240M: ++ fmt->transfer = TRANSFER_FUNC_SMPTE_240; ++ break; ++ case AVCOL_TRC_GAMMA28: ++ fmt->transfer = TRANSFER_FUNC_BT470_BG; ++ break; ++#endif ++ default: ++ break; ++ } ++ ++ switch( ctx->color_primaries ) ++ { ++ case AVCOL_PRI_BT709: ++ fmt->primaries = COLOR_PRIMARIES_BT709; ++ break; ++ case AVCOL_PRI_BT470BG: ++ fmt->primaries = COLOR_PRIMARIES_BT601_625; ++ break; ++ case AVCOL_PRI_SMPTE170M: ++ case AVCOL_PRI_SMPTE240M: ++ fmt->primaries = COLOR_PRIMARIES_BT601_525; ++ break; ++ case AVCOL_PRI_BT2020: ++ fmt->primaries = COLOR_PRIMARIES_BT2020; ++ break; ++ default: ++ break; ++ } ++ ++ switch( ctx->chroma_sample_location ) ++ { ++ case AVCHROMA_LOC_LEFT: ++ fmt->chroma_location = CHROMA_LOCATION_LEFT; ++ break; ++ case AVCHROMA_LOC_CENTER: ++ fmt->chroma_location = CHROMA_LOCATION_CENTER; ++ break; ++ case AVCHROMA_LOC_TOPLEFT: ++ fmt->chroma_location = CHROMA_LOCATION_TOP_LEFT; ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int lavc_UpdateVideoFormat(decoder_t *dec, AVCodecContext *ctx, ++ enum AVPixelFormat fmt, ++ enum AVPixelFormat swfmt) ++{ ++ video_format_t fmt_out; ++ int val; ++ ++ val = lavc_GetVideoFormat(dec, &fmt_out, ctx, fmt, swfmt); ++ if (val) ++ return val; ++ ++ /* always have date in fields/ticks units */ ++ if(dec->p_sys->pts.i_divider_num) ++ date_Change(&dec->p_sys->pts, fmt_out.i_frame_rate * ++ __MAX(ctx->ticks_per_frame, 1), ++ fmt_out.i_frame_rate_base); ++ else ++ date_Init(&dec->p_sys->pts, fmt_out.i_frame_rate * ++ __MAX(ctx->ticks_per_frame, 1), ++ fmt_out.i_frame_rate_base); ++ ++ fmt_out.p_palette = dec->fmt_out.video.p_palette; ++ dec->fmt_out.video.p_palette = NULL; ++ ++ es_format_Change(&dec->fmt_out, VIDEO_ES, fmt_out.i_chroma); ++ dec->fmt_out.video = fmt_out; ++ dec->fmt_out.video.orientation = dec->fmt_in.video.orientation; ++ dec->fmt_out.video.projection_mode = dec->fmt_in.video.projection_mode; ++ dec->fmt_out.video.multiview_mode = dec->fmt_in.video.multiview_mode; ++ dec->fmt_out.video.pose = dec->fmt_in.video.pose; ++ if ( dec->fmt_in.video.mastering.max_luminance ) ++ dec->fmt_out.video.mastering = dec->fmt_in.video.mastering; ++ dec->fmt_out.video.lighting = dec->fmt_in.video.lighting; ++ ++ return decoder_UpdateVideoFormat(dec); ++} ++ ++/***************************************************************************** ++ * Flush: ++ *****************************************************************************/ ++static void Flush( decoder_t *p_dec ) ++{ ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ AVCodecContext *p_context = p_sys->p_context; ++ ++ date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */ ++ p_sys->i_late_frames = 0; ++// cc_Flush( &p_sys->cc ); ++ ++ /* Abort pictures in order to unblock all avcodec workers threads waiting ++ * for a picture. This will avoid a deadlock between avcodec_flush_buffers ++ * and workers threads */ ++ decoder_AbortPictures( p_dec, true ); ++ ++ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ ++ if( avcodec_is_open( p_context ) ) ++ avcodec_flush_buffers( p_context ); ++ ++ /* Reset cancel state to false */ ++ decoder_AbortPictures( p_dec, false ); ++} ++ ++static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) ++{ ++ if( !block) ++ return true; ++ ++ if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) ++ { ++ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ ++// cc_Flush( &p_sys->cc ); ++ ++ p_sys->i_late_frames = 0; ++ if( block->i_flags & BLOCK_FLAG_CORRUPTED ) ++ { ++ block_Release( block ); ++ return false; ++ } ++ } ++ return true; ++} ++ ++static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time) ++{ ++ if( !block ) ++ return false; ++ if( block->i_flags & BLOCK_FLAG_PREROLL ) ++ { ++ /* Do not care about late frames when prerolling ++ * TODO avoid decoding of non reference frame ++ * (ie all B except for H264 where it depends only on nal_ref_idc) */ ++ p_sys->i_late_frames = 0; ++ p_sys->b_from_preroll = true; ++ p_sys->i_last_late_delay = INT64_MAX; ++ } ++ ++ if( p_sys->i_late_frames <= 0 ) ++ return false; ++ ++ if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ)) ++ { ++ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ ++ block_Release( block ); ++ p_sys->i_late_frames--; ++ return true; ++ } ++ return false; ++} ++ ++static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture ) ++{ ++ if( p_sys->i_late_frames <= 4) ++ return false; ++ ++ *b_need_output_picture = false; ++ if( p_sys->i_late_frames < 12 ) ++ { ++ p_context->skip_frame = ++ (p_sys->i_skip_frame <= AVDISCARD_NONREF) ? ++ AVDISCARD_NONREF : p_sys->i_skip_frame; ++ } ++ else ++ { ++ /* picture too late, won't decode ++ * but break picture until a new I, and for mpeg4 ...*/ ++ p_sys->i_late_frames--; /* needed else it will never be decrease */ ++ return true; ++ } ++ return false; ++} ++ ++static void interpolate_next_pts( decoder_t *p_dec, AVFrame *frame ) ++{ ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ AVCodecContext *p_context = p_sys->p_context; ++ ++ if( date_Get( &p_sys->pts ) == VLC_TS_INVALID || ++ p_sys->pts.i_divider_num == 0 ) ++ return; ++ ++ int i_tick = p_context->ticks_per_frame; ++ if( i_tick <= 0 ) ++ i_tick = 1; ++ ++ /* interpolate the next PTS */ ++ date_Increment( &p_sys->pts, i_tick + frame->repeat_pict ); ++} ++ ++static void update_late_frame_count( decoder_t *p_dec, block_t *p_block, mtime_t current_time, mtime_t i_pts ) ++{ ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ /* Update frame late count (except when doing preroll) */ ++ mtime_t i_display_date = VLC_TS_INVALID; ++ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) ++ i_display_date = decoder_GetDisplayDate( p_dec, i_pts ); ++ ++ if( i_display_date > VLC_TS_INVALID && i_display_date <= current_time ) ++ { ++ /* Out of preroll, consider only late frames on rising delay */ ++ if( p_sys->b_from_preroll ) ++ { ++ if( p_sys->i_last_late_delay > current_time - i_display_date ) ++ { ++ p_sys->i_last_late_delay = current_time - i_display_date; ++ return; ++ } ++ p_sys->b_from_preroll = false; ++ } ++ ++ p_sys->i_late_frames++; ++ if( p_sys->i_late_frames == 1 ) ++ p_sys->i_late_frames_start = current_time; ++ ++ } ++ else ++ { ++ p_sys->i_late_frames = 0; ++ } ++} ++ ++ ++static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic ) ++{ ++// decoder_sys_t *p_sys = p_dec->p_sys; ++ bool format_changed = false; ++ ++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) ) ++#define FROM_AVRAT(default_factor, avrat) \ ++(uint64_t)(default_factor) * (avrat).num / (avrat).den ++ const AVFrameSideData *metadata = ++ av_frame_get_side_data( frame, ++ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA ); ++ if ( metadata ) ++ { ++ const AVMasteringDisplayMetadata *hdr_meta = ++ (const AVMasteringDisplayMetadata *) metadata->data; ++ if ( hdr_meta->has_luminance ) ++ { ++#define ST2086_LUMA_FACTOR 10000 ++ p_pic->format.mastering.max_luminance = ++ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance); ++ p_pic->format.mastering.min_luminance = ++ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance); ++ } ++ if ( hdr_meta->has_primaries ) ++ { ++#define ST2086_RED 2 ++#define ST2086_GREEN 0 ++#define ST2086_BLUE 1 ++#define LAV_RED 0 ++#define LAV_GREEN 1 ++#define LAV_BLUE 2 ++#define ST2086_PRIM_FACTOR 50000 ++ p_pic->format.mastering.primaries[ST2086_RED*2 + 0] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]); ++ p_pic->format.mastering.primaries[ST2086_RED*2 + 1] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]); ++ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]); ++ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]); ++ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]); ++ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]); ++ p_pic->format.mastering.white_point[0] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]); ++ p_pic->format.mastering.white_point[1] = ++ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]); ++ } ++ ++ if ( memcmp( &p_dec->fmt_out.video.mastering, ++ &p_pic->format.mastering, ++ sizeof(p_pic->format.mastering) ) ) ++ { ++ p_dec->fmt_out.video.mastering = p_pic->format.mastering; ++ format_changed = true; ++ } ++#undef FROM_AVRAT ++ } ++#endif ++#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) ) ++ const AVFrameSideData *metadata_lt = ++ av_frame_get_side_data( frame, ++ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL ); ++ if ( metadata_lt ) ++ { ++ const AVContentLightMetadata *light_meta = ++ (const AVContentLightMetadata *) metadata_lt->data; ++ p_pic->format.lighting.MaxCLL = light_meta->MaxCLL; ++ p_pic->format.lighting.MaxFALL = light_meta->MaxFALL; ++ if ( memcmp( &p_dec->fmt_out.video.lighting, ++ &p_pic->format.lighting, ++ sizeof(p_pic->format.lighting) ) ) ++ { ++ p_dec->fmt_out.video.lighting = p_pic->format.lighting; ++ format_changed = true; ++ } ++ } ++#endif ++ ++ if (format_changed && decoder_UpdateVideoFormat( p_dec )) ++ return -1; ++#if 0 ++ const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC ); ++ if( p_avcc ) ++ { ++ cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size ); ++ if( p_sys->cc.b_reorder || p_sys->cc.i_data ) ++ { ++ block_t *p_cc = block_Alloc( p_sys->cc.i_data ); ++ if( p_cc ) ++ { ++ memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data ); ++ if( p_sys->cc.b_reorder ) ++ p_cc->i_dts = p_cc->i_pts = p_pic->date; ++ else ++ p_cc->i_pts = p_cc->i_dts; ++ decoder_cc_desc_t desc; ++ desc.i_608_channels = p_sys->cc.i_608channels; ++ desc.i_708_channels = p_sys->cc.i_708channels; ++ desc.i_reorder_depth = 4; ++ decoder_QueueCc( p_dec, p_cc, &desc ); ++ } ++ cc_Flush( &p_sys->cc ); ++ } ++ } ++#endif ++ return 0; ++} ++ ++ ++ ++static int OpenVideoCodec( decoder_t *p_dec ) ++{ ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ AVCodecContext *ctx = p_sys->p_context; ++ const AVCodec *codec = p_sys->p_codec; ++ int ret; ++ ++#if TRACE_ALL ++ msg_Dbg(p_dec, "<<< %s", __func__); ++#endif ++ ++ if( ctx->extradata_size <= 0 ) ++ { ++ if( codec->id == AV_CODEC_ID_VC1 || ++ codec->id == AV_CODEC_ID_THEORA ) ++ { ++ msg_Warn( p_dec, "waiting for extra data for codec %s", ++ codec->name ); ++ return 1; ++ } ++ } ++ ++ ctx->width = p_dec->fmt_in.video.i_visible_width; ++ ctx->height = p_dec->fmt_in.video.i_visible_height; ++ ++ ctx->coded_width = p_dec->fmt_in.video.i_width; ++ ctx->coded_height = p_dec->fmt_in.video.i_height; ++ ++ ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel; ++ p_sys->pix_fmt = AV_PIX_FMT_NONE; ++ p_sys->profile = -1; ++ p_sys->level = -1; ++// cc_Init( &p_sys->cc ); ++ ++ set_video_color_settings( &p_dec->fmt_in.video, ctx ); ++ ++ ret = ffmpeg_OpenCodec( p_dec, ctx, codec ); ++ if( ret < 0 ) ++ return ret; ++ ++ switch( ctx->active_thread_type ) ++ { ++ case FF_THREAD_FRAME: ++ msg_Dbg( p_dec, "using frame thread mode with %d threads", ++ ctx->thread_count ); ++ break; ++ case FF_THREAD_SLICE: ++ msg_Dbg( p_dec, "using slice thread mode with %d threads", ++ ctx->thread_count ); ++ break; ++ case 0: ++ if( ctx->thread_count > 1 ) ++ msg_Warn( p_dec, "failed to enable threaded decoding" ); ++ break; ++ default: ++ msg_Warn( p_dec, "using unknown thread mode with %d threads", ++ ctx->thread_count ); ++ break; ++ } ++ return 0; ++} ++ ++static MMAL_BOOL_T ++zc_buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata) ++{ ++ const AVRpiZcRefPtr fr_ref = userdata; ++ VLC_UNUSED(buf); ++ ++ av_rpi_zc_unref(fr_ref); ++ ++ return MMAL_FALSE; ++} ++ ++static MMAL_FOURCC_T ++avfmt_to_mmal(const int avfmt) ++{ ++ switch( avfmt ) ++ { ++ case AV_PIX_FMT_SAND128: ++ return MMAL_ENCODING_YUVUV128; ++ case AV_PIX_FMT_SAND64_10: ++ return MMAL_ENCODING_YUVUV64_10; ++ default: ++ break; ++ } ++ return MMAL_ENCODING_UNKNOWN; ++} ++ ++/***************************************************************************** ++ * DecodeBlock: Called to decode one or more frames ++ *****************************************************************************/ ++ ++ ++// Returns ++// -ve error ++// 0 Need more input (EAGAIN) ++// 1 Frame decoded (dropped or Qed) ++// 2 Decode err ++// 3 EOF ++ ++static int rx_frame(decoder_t * const p_dec, decoder_sys_t * const p_sys, AVCodecContext * const p_context) ++{ ++ AVFrame * frame = av_frame_alloc(); ++ picture_t * p_pic = NULL; ++ int ret; ++ ++ if (frame == NULL) ++ return VLC_ENOMEM; ++ ++ ret = avcodec_receive_frame(p_context, frame); ++ ++ if (ret != 0) ++ { ++ av_frame_free(&frame); ++ switch (ret) ++ { ++ case AVERROR(EAGAIN): ++ return 0; ++ ++ case AVERROR(ENOMEM): ++ case AVERROR(EINVAL): ++ msg_Err(p_dec, "avcodec_receive_frame critical error"); ++ return VLC_EGENERIC; ++ ++ case AVERROR_EOF: ++ msg_Dbg(p_dec, "Rx EOF"); ++ avcodec_flush_buffers(p_context); ++ return 2; ++ ++ default: ++ msg_Warn(p_dec, "Decode error: %d", ret); ++ return 1; ++ } ++ } ++ ++ /* Compute the PTS */ ++#ifdef FF_API_PKT_PTS ++ mtime_t i_pts = frame->pts; ++#else ++ mtime_t i_pts = frame->pkt_pts; ++#endif ++ if (i_pts == AV_NOPTS_VALUE ) ++ i_pts = frame->pkt_dts; ++ ++ if( i_pts == AV_NOPTS_VALUE ) ++ i_pts = date_Get( &p_sys->pts ); ++ ++ /* Interpolate the next PTS */ ++ if( i_pts > VLC_TS_INVALID ) ++ date_Set( &p_sys->pts, i_pts ); ++ ++ interpolate_next_pts( p_dec, frame ); ++ ++// update_late_frame_count( p_dec, p_block, current_time, i_pts); //********************* ++ ++ if( ( /* !p_sys->p_va && */ !frame->linesize[0] ) || ++ ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) && ++ !p_sys->b_show_corrupted ) ) ++ { ++ msg_Dbg(p_dec, "Frame drop"); ++ av_frame_free(&frame); ++ return 1; ++ } ++ ++ lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, ++ p_context->pix_fmt); ++ ++ { ++ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(p_sys->out_pool->queue); ++// MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(p_sys->out_pool->queue); ++ if (buf == NULL) { ++ msg_Err(p_dec, "MMAL buffer alloc failure"); ++ av_frame_free(&frame); ++ return 1; ++ } ++ ++ mmal_buffer_header_reset(buf); // length, offset, flags, pts, dts ++ buf->cmd = 0; ++ buf->user_data = NULL; ++ ++ { ++ const AVRpiZcRefPtr fr_buf = av_rpi_zc_ref(p_context, frame, frame->format, 0); ++ ++ if (fr_buf == NULL) { ++ mmal_buffer_header_release(buf); ++ av_frame_free(&frame); ++ return VLC_ENOMEM; ++ } ++ ++ const intptr_t vc_handle = (intptr_t)av_rpi_zc_vc_handle(fr_buf); ++ ++ buf->data = (uint8_t *)vc_handle; // Cast our handle to a pointer for mmal - 2 steps to avoid gcc warnings ++ buf->offset = av_rpi_zc_offset(fr_buf); ++ buf->length = av_rpi_zc_length(fr_buf); ++ buf->alloc_size = av_rpi_zc_numbytes(fr_buf); ++ buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; ++ ++ mmal_buffer_header_pre_release_cb_set(buf, zc_buf_pre_release_cb, fr_buf); ++ } ++ ++ p_pic = decoder_NewPicture(p_dec); // *** Really want an empy pic ++ if (p_pic == NULL) ++ { ++ msg_Err(p_dec, "Picture alloc failure"); ++ mmal_buffer_header_release(buf); ++ av_frame_free(&frame); ++ return VLC_ENOMEM; ++ } ++ ++ p_pic->context = hw_mmal_gen_context(avfmt_to_mmal(frame->format), buf, NULL); ++ } ++ ++ ++ if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den ) ++ { ++ /* Fetch again the aspect ratio in case it changed */ ++ p_dec->fmt_out.video.i_sar_num ++ = p_context->sample_aspect_ratio.num; ++ p_dec->fmt_out.video.i_sar_den ++ = p_context->sample_aspect_ratio.den; ++ ++ if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den ) ++ { ++ p_dec->fmt_out.video.i_sar_num = 1; ++ p_dec->fmt_out.video.i_sar_den = 1; ++ } ++ } ++ ++ p_pic->date = i_pts; ++ /* Hack to force display of still pictures */ ++ p_pic->b_force = p_sys->b_first_frame; ++ p_pic->i_nb_fields = 2 + frame->repeat_pict; ++ p_pic->b_progressive = !frame->interlaced_frame; ++ p_pic->b_top_field_first = frame->top_field_first; ++ ++ if (DecodeSidedata(p_dec, frame, p_pic)) ++ i_pts = VLC_TS_INVALID; ++ ++ av_frame_free(&frame); ++ ++ p_sys->b_first_frame = false; ++ decoder_QueueVideo(p_dec, p_pic); ++ ++ return 1; ++} ++ ++ ++ ++ ++ ++static int DecodeVideo( decoder_t *p_dec, block_t * p_block) ++{ ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ AVCodecContext *p_context = p_sys->p_context; ++ /* Boolean if we assume that we should get valid pic as result */ ++ bool b_need_output_picture = true; ++ ++ /* Boolean for END_OF_SEQUENCE */ ++ bool eos_spotted = false; ++ mtime_t current_time; ++ int rv = VLCDEC_SUCCESS; ++ ++ if( !p_context->extradata_size && p_dec->fmt_in.i_extra ) ++ { ++ ffmpeg_InitCodec( p_dec ); ++ if( !avcodec_is_open( p_context ) ) ++ OpenVideoCodec( p_dec ); ++ } ++ ++ if(!p_block && !(p_sys->p_codec->capabilities & AV_CODEC_CAP_DELAY) ) ++ return VLCDEC_SUCCESS; ++ ++ if( !avcodec_is_open( p_context ) ) ++ { ++ if( p_block ) ++ block_Release( p_block ); ++ return VLCDEC_ECRITICAL; ++ } ++ ++ if( !check_block_validity( p_sys, p_block ) ) ++ return VLCDEC_SUCCESS; ++ ++ current_time = mdate(); ++ if( p_dec->b_frame_drop_allowed && check_block_being_late( p_sys, p_block, current_time) ) ++ { ++ msg_Err( p_dec, "more than 5 seconds of late video -> " ++ "dropping frame (computer too slow ?)" ); ++ return VLCDEC_SUCCESS; ++ } ++ ++ ++ /* A good idea could be to decode all I pictures and see for the other */ ++ b_need_output_picture = true; ++ ++ /* Defaults that if we aren't in prerolling, we want output picture ++ same for if we are flushing (p_block==NULL) */ ++ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) ++ b_need_output_picture = true; ++ else ++ b_need_output_picture = false; ++ ++ /* Change skip_frame config only if hurry_up is enabled */ ++ if( p_sys->b_hurry_up ) ++ { ++ p_context->skip_frame = p_sys->i_skip_frame; ++ ++ /* Check also if we should/can drop the block and move to next block ++ as trying to catchup the speed*/ ++ if( p_dec->b_frame_drop_allowed && ++ check_frame_should_be_dropped( p_sys, p_context, &b_need_output_picture ) ) ++ { ++ if( p_block ) ++ block_Release( p_block ); ++ msg_Warn( p_dec, "More than 11 late frames, dropping frame" ); ++ return VLCDEC_SUCCESS; ++ } ++ } ++ if( !b_need_output_picture ) ++ { ++ p_context->skip_frame = __MAX( p_context->skip_frame, ++ AVDISCARD_NONREF ); ++ } ++ ++ /* ++ * Do the actual decoding now */ ++ ++ /* Don't forget that libavcodec requires a little more bytes ++ * that the real frame size */ ++ if( p_block && p_block->i_buffer > 0 ) ++ { ++ eos_spotted = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0; ++ ++ p_block = block_Realloc( p_block, 0, ++ p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE ); ++ if( !p_block ) ++ return VLCDEC_ECRITICAL; ++ ++ p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE; ++ memset( p_block->p_buffer + p_block->i_buffer, 0, ++ FF_INPUT_BUFFER_PADDING_SIZE ); ++ } ++ ++ AVPacket pkt = {.data = NULL, .size = 0}; ++ ++ av_init_packet( &pkt ); ++ if( p_block && p_block->i_buffer > 0 ) ++ { ++ pkt.data = p_block->p_buffer; ++ pkt.size = p_block->i_buffer; ++ pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE; ++ pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE; ++ } ++ ++ if( !p_sys->palette_sent ) ++ { ++ uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); ++ if (pal) { ++ memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE); ++ p_sys->palette_sent = true; ++ } ++ } ++ ++#if LIBAVCODEC_VERSION_CHECK( 57, 0, 0xFFFFFFFFU, 64, 101 ) ++ if( !b_need_output_picture ) ++ pkt.flags |= AV_PKT_FLAG_DISCARD; ++#endif ++ ++ int ret = avcodec_send_packet(p_context, &pkt); ++ ++ if (ret == AVERROR(EAGAIN)) ++ { ++ // Cannot send more data until output drained - so do drain ++ while (rx_frame(p_dec, p_sys, p_context) == 1) ++ /* Loop */; ++ ++ // And try again - should not fail the same way ++ ret = avcodec_send_packet(p_context, &pkt); ++ } ++ ++ // Now done with pkt & block ++ av_packet_unref(&pkt); ++ if (p_block != NULL) ++ { ++ block_Release( p_block ); ++ p_block = NULL; ++ } ++ ++ if (ret != 0) ++ { ++ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL)) ++ { ++ msg_Err(p_dec, "avcodec_send_packet critical error"); ++ rv = VLCDEC_ECRITICAL; ++ } ++ } ++ ++ while (rx_frame(p_dec, p_sys, p_context) == 1) ++ /* Loop */; ++ ++ if (eos_spotted) ++ p_sys->b_first_frame = true; ++ ++ return rv; ++} ++ ++/***************************************************************************** ++ * EndVideo: decoder destruction ++ ***************************************************************************** ++ * This function is called when the thread ends after a successful ++ * initialization. ++ *****************************************************************************/ ++static void MmalAvcodecCloseDecoder(vlc_object_t *obj) ++{ ++ decoder_t *p_dec = (decoder_t *)obj; ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ AVCodecContext *ctx = p_sys->p_context; ++// void *hwaccel_context; ++ ++ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ ++ if( avcodec_is_open( ctx ) ) ++ avcodec_flush_buffers( ctx ); ++ ++// cc_Flush( &p_sys->cc ); ++ ++ avcodec_close(ctx); ++ av_rpi_zc_uninit(ctx); ++ ++// hwaccel_context = ctx->hwaccel_context; ++ avcodec_free_context( &ctx ); ++ ++ if( p_sys->out_pool != NULL ) ++ mmal_pool_destroy(p_sys->out_pool); ++ ++// if( p_sys->p_va ) ++// vlc_va_Delete( p_sys->p_va, &hwaccel_context ); ++ ++ free( p_sys ); ++} ++ ++/***************************************************************************** ++ * ffmpeg_InitCodec: setup codec extra initialization data for ffmpeg ++ *****************************************************************************/ ++static void ffmpeg_InitCodec( decoder_t *p_dec ) ++{ ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ size_t i_size = p_dec->fmt_in.i_extra; ++ ++ if( !i_size ) return; ++ ++ if( p_sys->p_codec->id == AV_CODEC_ID_SVQ3 ) ++ { ++ uint8_t *p; ++ ++ p_sys->p_context->extradata_size = i_size + 12; ++ p = p_sys->p_context->extradata = ++ av_malloc( p_sys->p_context->extradata_size + ++ FF_INPUT_BUFFER_PADDING_SIZE ); ++ if( !p ) ++ return; ++ ++ memcpy( &p[0], "SVQ3", 4 ); ++ memset( &p[4], 0, 8 ); ++ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size ); ++ ++ /* Now remove all atoms before the SMI one */ ++ if( p_sys->p_context->extradata_size > 0x5a && ++ strncmp( (char*)&p[0x56], "SMI ", 4 ) ) ++ { ++ uint8_t *psz = &p[0x52]; ++ ++ while( psz < &p[p_sys->p_context->extradata_size - 8] ) ++ { ++ uint_fast32_t atom_size = GetDWBE( psz ); ++ if( atom_size <= 1 ) ++ { ++ /* FIXME handle 1 as long size */ ++ break; ++ } ++ if( !strncmp( (char*)&psz[4], "SMI ", 4 ) ) ++ { ++ memmove( &p[0x52], psz, ++ &p[p_sys->p_context->extradata_size] - psz ); ++ break; ++ } ++ ++ psz += atom_size; ++ } ++ } ++ } ++ else ++ { ++ p_sys->p_context->extradata_size = i_size; ++ p_sys->p_context->extradata = ++ av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE ); ++ if( p_sys->p_context->extradata ) ++ { ++ memcpy( p_sys->p_context->extradata, ++ p_dec->fmt_in.p_extra, i_size ); ++ memset( p_sys->p_context->extradata + i_size, ++ 0, FF_INPUT_BUFFER_PADDING_SIZE ); ++ } ++ } ++} ++ ++ ++static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context, ++ const enum PixelFormat *pi_fmt ) ++{ ++ decoder_t *p_dec = p_context->opaque; ++ decoder_sys_t *p_sys = p_dec->p_sys; ++ video_format_t fmt; ++ ++ /* Enumerate available formats */ ++ enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt); ++// bool can_hwaccel = false; ++ ++ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++) ++ { ++ const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(pi_fmt[i]); ++ if (dsc == NULL) ++ continue; ++ bool hwaccel = (dsc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0; ++ ++ msg_Dbg( p_dec, "available %sware decoder output format %d (%s)", ++ hwaccel ? "hard" : "soft", pi_fmt[i], dsc->name ); ++// if (hwaccel) ++// can_hwaccel = true; ++ } ++ ++ /* If the format did not actually change (e.g. seeking), try to reuse the ++ * existing output format, and if present, hardware acceleration back-end. ++ * This avoids resetting the pipeline downstream. This also avoids ++ * needlessly probing for hardware acceleration support. */ ++ if (p_sys->pix_fmt != AV_PIX_FMT_NONE ++ && lavc_GetVideoFormat(p_dec, &fmt, p_context, p_sys->pix_fmt, swfmt) == 0 ++ && fmt.i_width == p_dec->fmt_out.video.i_width ++ && fmt.i_height == p_dec->fmt_out.video.i_height ++ && p_context->profile == p_sys->profile ++ && p_context->level <= p_sys->level) ++ { ++ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++) ++ if (pi_fmt[i] == p_sys->pix_fmt) ++ { ++ msg_Dbg(p_dec, "reusing decoder output format %d", pi_fmt[i]); ++ return p_sys->pix_fmt; ++ } ++ } ++ ++// if (p_sys->p_va != NULL) ++// { ++// msg_Err(p_dec, "existing hardware acceleration cannot be reused"); ++// vlc_va_Delete(p_sys->p_va, &p_context->hwaccel_context); ++// p_sys->p_va = NULL; ++// } ++ ++ p_sys->profile = p_context->profile; ++ p_sys->level = p_context->level; ++ ++#if 1 ++ return swfmt; ++#else ++ if (!can_hwaccel) ++ return swfmt; ++ ++#if (LIBAVCODEC_VERSION_MICRO >= 100) \ ++ && (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 83, 101)) ++ if (p_context->active_thread_type) ++ { ++ msg_Warn(p_dec, "thread type %d: disabling hardware acceleration", ++ p_context->active_thread_type); ++ return swfmt; ++ } ++#endif ++ ++ wait_mt(p_sys); ++ ++ static const enum PixelFormat hwfmts[] = ++ { ++#ifdef _WIN32 ++#if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100) ++ AV_PIX_FMT_D3D11VA_VLD, ++#endif ++ AV_PIX_FMT_DXVA2_VLD, ++#endif ++ AV_PIX_FMT_VAAPI_VLD, ++#if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 4, 0)) ++ AV_PIX_FMT_VDPAU, ++#endif ++ AV_PIX_FMT_NONE, ++ }; ++ ++ for( size_t i = 0; hwfmts[i] != AV_PIX_FMT_NONE; i++ ) ++ { ++ enum PixelFormat hwfmt = AV_PIX_FMT_NONE; ++ for( size_t j = 0; hwfmt == AV_PIX_FMT_NONE && pi_fmt[j] != AV_PIX_FMT_NONE; j++ ) ++ if( hwfmts[i] == pi_fmt[j] ) ++ hwfmt = hwfmts[i]; ++ ++ if( hwfmt == AV_PIX_FMT_NONE ) ++ continue; ++ ++ p_dec->fmt_out.video.i_chroma = vlc_va_GetChroma(hwfmt, swfmt); ++ if (p_dec->fmt_out.video.i_chroma == 0) ++ continue; /* Unknown brand of hardware acceleration */ ++ if (p_context->width == 0 || p_context->height == 0) ++ { /* should never happen */ ++ msg_Err(p_dec, "unspecified video dimensions"); ++ continue; ++ } ++ const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(hwfmt); ++ msg_Dbg(p_dec, "trying format %s", dsc ? dsc->name : "unknown"); ++ if (lavc_UpdateVideoFormat(p_dec, p_context, hwfmt, swfmt)) ++ continue; /* Unsupported brand of hardware acceleration */ ++ post_mt(p_sys); ++ ++ picture_t *test_pic = decoder_NewPicture(p_dec); ++ assert(!test_pic || test_pic->format.i_chroma == p_dec->fmt_out.video.i_chroma); ++ vlc_va_t *va = vlc_va_New(VLC_OBJECT(p_dec), p_context, hwfmt, ++ &p_dec->fmt_in, ++ test_pic ? test_pic->p_sys : NULL); ++ if (test_pic) ++ picture_Release(test_pic); ++ if (va == NULL) ++ { ++ wait_mt(p_sys); ++ continue; /* Unsupported codec profile or such */ ++ } ++ ++ if (va->description != NULL) ++ msg_Info(p_dec, "Using %s for hardware decoding", va->description); ++ ++ p_sys->p_va = va; ++ p_sys->pix_fmt = hwfmt; ++ p_context->draw_horiz_band = NULL; ++ return hwfmt; ++ } ++ ++ post_mt(p_sys); ++ /* Fallback to default behaviour */ ++ p_sys->pix_fmt = swfmt; ++ return swfmt; ++#endif ++} ++ ++/***************************************************************************** ++ * InitVideo: initialize the video decoder ++ ***************************************************************************** ++ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet ++ * opened (done after the first decoded frame). ++ *****************************************************************************/ ++ ++/***************************************************************************** ++ * ffmpeg_OpenCodec: ++ *****************************************************************************/ ++ ++static int MmalAvcodecOpenDecoder( vlc_object_t *obj ) ++{ ++ decoder_t *p_dec = (decoder_t *)obj; ++ const AVCodec *p_codec; ++ ++ if (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC) ++ return VLC_EGENERIC; ++ ++ AVCodecContext *p_context = ffmpeg_AllocContext( p_dec, &p_codec ); ++ if( p_context == NULL ) ++ return VLC_EGENERIC; ++ ++ int i_val; ++ ++ /* Allocate the memory needed to store the decoder's structure */ ++ decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) ); ++ if( unlikely(p_sys == NULL) ) ++ { ++ avcodec_free_context( &p_context ); ++ return VLC_ENOMEM; ++ } ++ ++ p_dec->p_sys = p_sys; ++ p_sys->p_context = p_context; ++ p_sys->p_codec = p_codec; ++// p_sys->p_va = NULL; ++ ++ /* ***** Fill p_context with init values ***** */ ++ p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ? ++ p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec ); ++ ++ /* ***** Get configuration of ffmpeg plugin ***** */ ++ p_context->workaround_bugs = ++ var_InheritInteger( p_dec, "avcodec-workaround-bugs" ); ++ p_context->err_recognition = ++ var_InheritInteger( p_dec, "avcodec-error-resilience" ); ++ ++ if( var_CreateGetBool( p_dec, "grayscale" ) ) ++ p_context->flags |= AV_CODEC_FLAG_GRAY; ++ ++ /* ***** Output always the frames ***** */ ++ p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT; ++ ++ i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" ); ++ if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL; ++ else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY; ++ else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR; ++ else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF; ++ else p_context->skip_loop_filter = AVDISCARD_DEFAULT; ++ ++ if( var_CreateGetBool( p_dec, "avcodec-fast" ) ) ++ p_context->flags2 |= AV_CODEC_FLAG2_FAST; ++ ++ /* ***** libavcodec frame skipping ***** */ ++ p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" ); ++ p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" ); ++ ++ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" ); ++ if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL; ++ else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY; ++ else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR; ++ else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF; ++ else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE; ++ else p_sys->i_skip_frame = AVDISCARD_DEFAULT; ++ p_context->skip_frame = p_sys->i_skip_frame; ++ ++ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" ); ++ if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL; ++ else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY; ++ else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR; ++ else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF; ++ else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE; ++ else p_context->skip_idct = AVDISCARD_DEFAULT; ++ ++ /* ***** libavcodec direct rendering ***** */ ++ p_sys->b_direct_rendering = false; ++ atomic_init(&p_sys->b_dr_failure, false); ++ if( var_CreateGetBool( p_dec, "avcodec-dr" ) && ++ (p_codec->capabilities & AV_CODEC_CAP_DR1) && ++ /* No idea why ... but this fixes flickering on some TSCC streams */ ++ p_sys->p_codec->id != AV_CODEC_ID_TSCC && ++ p_sys->p_codec->id != AV_CODEC_ID_CSCD && ++ p_sys->p_codec->id != AV_CODEC_ID_CINEPAK ) ++ { ++ /* Some codecs set pix_fmt only after the 1st frame has been decoded, ++ * so we need to do another check in ffmpeg_GetFrameBuf() */ ++ p_sys->b_direct_rendering = true; ++ } ++ ++ p_context->get_format = ffmpeg_GetFormat; ++ /* Always use our get_buffer wrapper so we can calculate the ++ * PTS correctly */ ++// p_context->get_buffer2 = lavc_GetFrame; ++// p_context->opaque = p_dec; ++ ++ int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" ); ++ if( i_thread_count <= 0 ) ++ i_thread_count = 6; ++#if 0 ++ if( i_thread_count <= 0 ) ++ { ++ i_thread_count = vlc_GetCPUCount(); ++ if( i_thread_count > 1 ) ++ i_thread_count++; ++ ++ //FIXME: take in count the decoding time ++#if VLC_WINSTORE_APP ++ i_thread_count = __MIN( i_thread_count, 6 ); ++#else ++ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 ); ++#endif ++ } ++ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 ); ++#endif ++ msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count ); ++ p_context->thread_count = i_thread_count; ++ p_context->thread_safe_callbacks = true; ++ ++ p_context->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME; ++ ++ if( p_context->thread_type & FF_THREAD_FRAME ) ++ p_dec->i_extra_picture_buffers = 2 * p_context->thread_count; ++ ++ /* ***** misc init ***** */ ++ date_Init(&p_sys->pts, 1, 30001); ++ date_Set(&p_sys->pts, VLC_TS_INVALID); ++ p_sys->b_first_frame = true; ++ p_sys->i_late_frames = 0; ++ p_sys->b_from_preroll = false; ++ ++ /* Set output properties */ ++ if( GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS ) ++ { ++ /* we are doomed. but not really, because most codecs set their pix_fmt later on */ ++ p_dec->fmt_out.i_codec = VLC_CODEC_I420; ++ } ++ p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma; ++ ++ p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation; ++ ++ if( p_dec->fmt_in.video.p_palette ) { ++ p_sys->palette_sent = false; ++ p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) ); ++ if( p_dec->fmt_out.video.p_palette ) ++ *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette; ++ } else ++ p_sys->palette_sent = true; ++ ++ /* ***** init this codec with special data ***** */ ++ ffmpeg_InitCodec( p_dec ); ++ ++ /* ***** Open the codec ***** */ ++ if( OpenVideoCodec( p_dec ) < 0 ) ++ { ++ free( p_sys ); ++ avcodec_free_context( &p_context ); ++ return VLC_EGENERIC; ++ } ++ ++ if ((p_sys->out_pool = mmal_pool_create(5, 0)) == NULL) ++ { ++ msg_Err(p_dec, "Failed to create mmal buffer pool"); ++ goto fail; ++ } ++ ++ p_dec->pf_decode = DecodeVideo; ++ p_dec->pf_flush = Flush; ++ ++ /* XXX: Writing input format makes little sense. */ ++ if( p_context->profile != FF_PROFILE_UNKNOWN ) ++ p_dec->fmt_in.i_profile = p_context->profile; ++ if( p_context->level != FF_LEVEL_UNKNOWN ) ++ p_dec->fmt_in.i_level = p_context->level; ++ return VLC_SUCCESS; ++ ++fail: ++ MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec)); ++ return VLC_EGENERIC; ++} ++ ++ ++ ++vlc_module_begin() ++ set_category( CAT_INPUT ) ++ set_subcategory( SUBCAT_INPUT_VCODEC ) ++ set_shortname(N_("MMAL avcodec")) ++ set_description(N_("MMAL buffered avcodec ")) ++ set_capability("video decoder", 800) ++ add_shortcut("mmal_avcodec") ++ set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder) ++vlc_module_end() ++ --- a/modules/hw/mmal/mmal_picture.c +++ b/modules/hw/mmal/mmal_picture.c -@@ -21,25 +21,847 @@ +@@ -21,25 +21,961 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ @@ -3681,12 +6204,51 @@ + return MMAL_COLOR_SPACE_UNKNOWN; +} + ++MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc) ++{ ++ switch (vf_vlc->i_chroma) { ++ case VLC_CODEC_RGB32: ++ { ++ // VLC RGB32 aka RV32 means we have to look at the mask values ++ const uint32_t r = vf_vlc->i_rmask; ++ const uint32_t g = vf_vlc->i_gmask; ++ const uint32_t b = vf_vlc->i_bmask; ++ if (r == 0xff0000 && g == 0xff00 && b == 0xff) ++ return MMAL_ENCODING_BGRA; ++ if (r == 0xff && g == 0xff00 && b == 0xff0000) ++ return MMAL_ENCODING_RGBA; ++ if (r == 0xff000000 && g == 0xff0000 && b == 0xff00) ++ return MMAL_ENCODING_ABGR; ++ if (r == 0xff00 && g == 0xff0000 && b == 0xff000000) ++ return MMAL_ENCODING_ARGB; ++ break; ++ } ++ case VLC_CODEC_RGBA: ++ return MMAL_ENCODING_RGBA; ++ case VLC_CODEC_BGRA: ++ return MMAL_ENCODING_BGRA; ++ case VLC_CODEC_ARGB: ++ return MMAL_ENCODING_ARGB; ++ // VLC_CODEC_ABGR does not exist in VLC ++ case VLC_CODEC_MMAL_OPAQUE: ++ return MMAL_ENCODING_OPAQUE; ++ case VLC_CODEC_MMAL_ZC_SAND8: ++ return MMAL_ENCODING_YUVUV128; ++ case VLC_CODEC_MMAL_ZC_SAND10: ++ return MMAL_ENCODING_YUVUV64_10; ++ default: ++ break; ++ } ++ return 0; ++} ++ ++ +void vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc) +{ + 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->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; @@ -3754,7 +6316,7 @@ + +// 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) ++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; @@ -3780,6 +6342,58 @@ + return err; +} + ++ ++MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj, ++ hw_mmal_port_pool_ref_t ** pppr, ++ MMAL_PORT_T * const port, ++ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback) ++{ ++ MMAL_STATUS_T status; ++ ++ port->userdata = (struct MMAL_PORT_USERDATA_T *)obj; ++ ++ status = port_parameter_set_uint32(port, MMAL_PARAMETER_EXTRA_BUFFERS, extra_buffers); ++ if (status != MMAL_SUCCESS) { ++ msg_Err(obj, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)", ++ status, mmal_status_to_string(status)); ++ return status; ++ } ++ ++ status = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, 1); ++ if (status != MMAL_SUCCESS) { ++ msg_Err(obj, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", ++ port->name, status, mmal_status_to_string(status)); ++ return status; ++ } ++ ++ port->format->encoding = MMAL_ENCODING_OPAQUE; ++ port->format->encoding_variant = 0; ++ if ((status = mmal_port_format_commit(port)) != MMAL_SUCCESS) ++ { ++ msg_Err(obj, "Failed to commit format on port %s (status=%"PRIx32" %s)", ++ port->name, status, mmal_status_to_string(status)); ++ return status; ++ } ++ ++ port->buffer_num = 30; ++ port->buffer_size = port->buffer_size_recommended; ++ ++ if ((*pppr = hw_mmal_port_pool_ref_create(port, port->buffer_num, port->buffer_size)) == NULL) { ++ msg_Err(obj, "Failed to create output pool"); ++ return status; ++ } ++ ++ status = mmal_port_enable(port, callback); ++ if (status != MMAL_SUCCESS) { ++ msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)", ++ port->name, status, mmal_status_to_string(status)); ++ return status; ++ } ++ ++ return MMAL_SUCCESS; ++} ++ ++ +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; @@ -3833,23 +6447,27 @@ +// 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) ++hw_mmal_gen_context(const MMAL_FOURCC_T fmt, 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); ++ // If we have an associated ppr then ref & set appropriate callbacks ++ if (ppr != NULL) { ++ hw_mmal_port_pool_ref_acquire(ppr); ++ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr); ++ buf->user_data = NULL; ++ } + + ctx->cmn.copy = hw_mmal_pic_ctx_copy; + ctx->cmn.destroy = hw_mmal_pic_ctx_destroy; + -+ ctx->bufs[0] = buf; ++ ctx->fmt = fmt; + ctx->buf_count = 1; ++ ctx->bufs[0] = buf; + -+ buf->user_data = NULL; + return &ctx->cmn; +} + @@ -3922,6 +6540,7 @@ + + unsigned int width; + unsigned int height; ++ MMAL_FOURCC_T enc_type; + + picture_t * pic; +} pool_ent_t; @@ -4069,9 +6688,7 @@ +#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); + @@ -4120,21 +6737,14 @@ + + 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) @@ -4145,7 +6755,7 @@ + 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) @@ -4189,14 +6799,22 @@ + return best; +} + ++ ++void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH) ++{ ++ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent; ++ *pW = ent->width; ++ *pH = ent->height; ++} ++ +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; ++ es_fmt->encoding = ent->enc_type; ++ es_fmt->encoding_variant = 0; + + v_fmt->width = ent->width; + v_fmt->height = ent->height; @@ -4246,7 +6864,9 @@ +} + +void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect) -+{ + { +- picture_sys_t *pic_sys = picture->p_sys; +- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; + vzc_subbuf_ent_t * sb = buf->user_data; + if (scale_rect == NULL) { + sb->dreg.dest_rect = sb->orig_dest_rect; @@ -4306,6 +6926,7 @@ + const size_t dst_size = dst_stride * dst_lines; + + pool_ent_t * ent = ent_list_extract_pic_ent(&pc->ents_prev, pic); ++ bool needs_copy = false; + + // If we didn't find ent in last then look in cur in case is_first + // isn't working @@ -4316,8 +6937,14 @@ + + if (ent == NULL) + { ++ // Need a new ent ++ needs_copy = true; ++ + if ((ent = pool_best_fit(pc, dst_size)) == NULL) + goto fail2; ++ if ((ent->enc_type = vlc_to_mmal_video_fourcc(&pic->format)) == 0) ++ goto fail2; ++ + ent->pic = picture_Hold(pic); + } + @@ -4358,22 +6985,25 @@ + .height = dst_pic->format.i_visible_height + }; + -+ ent->width = dst_stride / bpp; -+ ent->height = dst_lines; -+ -+ // 2D copy ++ if (needs_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; -+ } ++ ent->width = dst_stride / bpp; ++ ent->height = dst_lines; + -+ // And make sure it is actually in memory -+ flush_range(ent->buf, d - (uint8_t *)ent->buf); ++ // 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); ++ } + } + } + @@ -4391,7 +7021,12 @@ + pool_recycle_list(pc, &pc->ents_prev); + pool_recycle_list(pc, &pc->ents_cur); +} -+ + +- 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; +static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc) +{ + @@ -4472,20 +7107,22 @@ + { + hw_mmal_vzc_pool_delete(pc); + return NULL; -+ } -+ + } + +- pic_sys->displayed = false; + atomic_store(&pc->ref_count, 1); + + mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc); -+ + +- return VLC_SUCCESS; + return pc; -+} + } + + + --- a/modules/hw/mmal/mmal_picture.h +++ b/modules/hw/mmal/mmal_picture.h -@@ -24,19 +24,209 @@ +@@ -24,19 +24,243 @@ #ifndef VLC_MMAL_MMAL_PICTURE_H_ #define VLC_MMAL_MMAL_PICTURE_H_ @@ -4521,10 +7158,15 @@ + + +#define CTX_BUFS_MAX 4 -+ + +- MMAL_BUFFER_HEADER_T *buffer; +- bool displayed; +-}; +typedef struct pic_ctx_mmal_s { + picture_context_t cmn; // PARENT: Common els at start + ++ MMAL_FOURCC_T fmt; ++ + unsigned int buf_count; + MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX]; + @@ -4538,23 +7180,25 @@ + vlc_object_t * obj; +#endif +} pic_ctx_mmal_t; -+ -+ + +-int mmal_picture_lock(picture_t *picture); ++MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc); +MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs); +void vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc); - -- 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); ++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); +} ++MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj, ++ hw_mmal_port_pool_ref_t ** pppr, ++ MMAL_PORT_T * const port, ++ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback); + +static inline int hw_mmal_pic_has_sub_bufs(picture_t * const pic) +{ @@ -4581,10 +7225,24 @@ + return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1]; +} + ++static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic) ++{ ++ return pic->format.i_chroma == VLC_CODEC_MMAL_OPAQUE || ++ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8 || ++ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND10 || ++ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420; ++} ++ ++static inline MMAL_FOURCC_T hw_mmal_pic_format(const picture_t *const pic) ++{ ++ const pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; ++ return ctx->fmt; ++} ++ +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); ++picture_context_t * hw_mmal_gen_context(const MMAL_FOURCC_T fmt, ++ MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr); + +int hw_mmal_get_gpu_mem(void); + @@ -4598,7 +7256,7 @@ + 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) ++static inline MMAL_STATUS_T port_parameter_set_bool(MMAL_PORT_T * const port, const uint32_t id, const bool val) +{ + const MMAL_PARAMETER_BOOLEAN_T param = { + .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_BOOLEAN_T)}, @@ -4670,6 +7328,7 @@ +} + +// Retrieve buf from pic & update with pic props ++// Note that this is a weak pointer - replicate before putting in a Q +static inline MMAL_BUFFER_HEADER_T * pic_mmal_buffer(const picture_t *const pic) +{ + MMAL_BUFFER_HEADER_T * const buf = ((pic_ctx_mmal_t *)pic->context)->bufs[0]; @@ -4682,10 +7341,19 @@ +struct vzc_pool_ctl_s; +typedef struct vzc_pool_ctl_s vzc_pool_ctl_t; + ++static inline bool hw_mmal_vzc_subpic_fmt_valid(const video_frame_format_t * const vf_vlc) ++{ ++ const vlc_fourcc_t vfcc_src = vf_vlc->i_chroma; ++ // At the moment we cope with any mono-planar RGBA thing ++ // We could cope with many other things but they currently don't occur ++ return vfcc_src == VLC_CODEC_RGBA || vfcc_src == VLC_CODEC_BGRA || vfcc_src == VLC_CODEC_ARGB; ++} ++ +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); ++void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH); +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, @@ -4695,13 +7363,129 @@ +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) ++ ++#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize" ++#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp" ++#define MMAL_COMPONENT_HVS "vc.ril.hvs" #endif --- /dev/null ++++ b/modules/hw/mmal/rpi_prof.h +@@ -0,0 +1,110 @@ ++#ifndef RPI_PROFILE_H ++#define RPI_PROFILE_H ++ ++#include ++#include ++ ++#ifndef RPI_PROFILE ++#define RPI_PROFILE 0 ++#endif ++ ++#if RPI_PROFILE ++ ++#include "v7_pmu.h" ++ ++#ifdef RPI_PROC_ALLOC ++#define X volatile ++#define Z =0 ++#else ++#define X extern volatile ++#define Z ++#endif ++ ++X uint64_t av_rpi_prof0_cycles Z; ++X unsigned int av_rpi_prof0_cnt Z; ++#define RPI_prof0_MAX_DURATION 100000 ++ ++X uint64_t av_rpi_prof1_cycles Z; ++X unsigned int av_rpi_prof1_cnt Z; ++#define RPI_prof1_MAX_DURATION 100000 ++ ++X uint64_t av_rpi_prof2_cycles Z; ++X unsigned int av_rpi_prof2_cnt Z; ++#define RPI_prof2_MAX_DURATION 10000 ++ ++X uint64_t av_rpi_prof_n_cycles[128]; ++X unsigned int av_rpi_prof_n_cnt[128]; ++#define RPI_prof_n_MAX_DURATION 10000 ++ ++ ++#undef X ++#undef Z ++ ++#define PROFILE_INIT()\ ++do {\ ++ enable_pmu();\ ++ enable_ccnt();\ ++} while (0) ++ ++#define PROFILE_START()\ ++do {\ ++ volatile uint32_t perf_1 = read_ccnt();\ ++ volatile uint32_t perf_2 ++ ++ ++#define PROFILE_ACC(x)\ ++ perf_2 = read_ccnt();\ ++ {\ ++ const uint32_t duration = perf_2 - perf_1;\ ++ if (duration < RPI_##x##_MAX_DURATION)\ ++ {\ ++ av_rpi_##x##_cycles += duration;\ ++ av_rpi_##x##_cnt += 1;\ ++ }\ ++ }\ ++} while(0) ++ ++ ++#define PROFILE_ACC_N(n)\ ++ if ((n) >= 0) {\ ++ perf_2 = read_ccnt();\ ++ {\ ++ const uint32_t duration = perf_2 - perf_1;\ ++ if (duration < RPI_prof_n_MAX_DURATION)\ ++ {\ ++ av_rpi_prof_n_cycles[n] += duration;\ ++ av_rpi_prof_n_cnt[n] += 1;\ ++ }\ ++ }\ ++ }\ ++} while(0) ++ ++#define PROFILE_PRINTF(x)\ ++ printf("%-20s cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", #x, av_rpi_##x##_cycles, av_rpi_##x##_cnt,\ ++ av_rpi_##x##_cnt == 0 ? (uint64_t)0 : av_rpi_##x##_cycles / (uint64_t)av_rpi_##x##_cnt) ++ ++#define PROFILE_PRINTF_N(n)\ ++ printf("prof[%d] cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", (n), av_rpi_prof_n_cycles[n], av_rpi_prof_n_cnt[n],\ ++ av_rpi_prof_n_cnt[n] == 0 ? (uint64_t)0 : av_rpi_prof_n_cycles[n] / (uint64_t)av_rpi_prof_n_cnt[n]) ++ ++#define PROFILE_CLEAR_N(n) \ ++do {\ ++ av_rpi_prof_n_cycles[n] = 0;\ ++ av_rpi_prof_n_cnt[n] = 0;\ ++} while(0) ++ ++#else ++ ++// No profile ++#define PROFILE_INIT() ++#define PROFILE_START() ++#define PROFILE_ACC(x) ++#define PROFILE_ACC_N(x) ++#define PROFILE_PRINTF(x) ++#define PROFILE_PRINTF_N(x) ++#define PROFILE_CLEAR_N(n) ++ ++#endif ++ ++#endif ++ +--- /dev/null +++ b/modules/hw/mmal/subpic.c @@ -0,0 +1,222 @@ +/***************************************************************************** @@ -4927,35 +7711,417 @@ + + --- /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 ++++ 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 ++ +--- /dev/null ++++ b/modules/hw/mmal/v7_pmu.S +@@ -0,0 +1,263 @@ ++/*------------------------------------------------------------ ++Performance Monitor Block ++------------------------------------------------------------*/ ++ .arm @ Make sure we are in ARM mode. ++ .text ++ .align 2 ++ .global getPMN @ export this function for the linker ++ ++/* Returns the number of progammable counters uint32_t getPMN(void) */ ++ ++getPMN: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC Register */ ++ MOV r0, r0, LSR #11 /* Shift N field down to bit 0 */ ++ AND r0, r0, #0x1F /* Mask to leave just the 5 N bits */ ++ BX lr ++ ++ ++ ++ .global pmn_config @ export this function for the linker ++ /* Sets the event for a programmable counter to record */ ++ /* void pmn_config(unsigned counter, uint32_t event) */ ++ /* counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1 */ ++ /* event = r1 = The event code */ ++pmn_config: ++ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ ++ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ ++ MCR p15, 0, r1, c9, c13, 1 /* Write EVTSELx Register */ ++ BX lr ++ ++ ++ ++ .global ccnt_divider @ export this function for the linker ++ /* Enables/disables the divider (1/64) on CCNT */ ++ /* void ccnt_divider(int divider) */ ++ /* divider = r0 = If 0 disable divider, else enable dvider */ ++ccnt_divider: ++ MRC p15, 0, r1, c9, c12, 0 /* Read PMNC */ ++ ++ CMP r0, #0x0 /* IF (r0 == 0) */ ++ BICEQ r1, r1, #0x08 /* THEN: Clear the D bit (disables the */ ++ ORRNE r1, r1, #0x08 /* ELSE: Set the D bit (enables the di */ ++ ++ MCR p15, 0, r1, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ /* --------------------------------------------------------------- */ ++ /* Enable/Disable */ ++ /* --------------------------------------------------------------- */ ++ ++ .global enable_pmu @ export this function for the linker ++ /* Global PMU enable */ ++ /* void enable_pmu(void) */ ++enable_pmu: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ ++ ORR r0, r0, #0x01 /* Set E bit */ ++ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ ++ .global disable_pmu @ export this function for the linker ++ /* Global PMU disable */ ++ /* void disable_pmu(void) */ ++disable_pmu: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ ++ BIC r0, r0, #0x01 /* Clear E bit */ ++ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ ++ .global enable_ccnt @ export this function for the linker ++ /* Enable the CCNT */ ++ /* void enable_ccnt(void) */ ++enable_ccnt: ++ MOV r0, #0x80000000 /* Set C bit */ ++ MCR p15, 0, r0, c9, c12, 1 /* Write CNTENS Register */ ++ BX lr ++ ++ ++ ++ .global disable_ccnt @ export this function for the linker ++ /* Disable the CCNT */ ++ /* void disable_ccnt(void) */ ++disable_ccnt: ++ MOV r0, #0x80000000 /* Clear C bit */ ++ MCR p15, 0, r0, c9, c12, 2 /* Write CNTENC Register */ ++ BX lr ++ ++ ++ ++ .global enable_pmn @ export this function for the linker ++ /* Enable PMN{n} */ ++ /* void enable_pmn(uint32_t counter) */ ++ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) ++enable_pmn: */ ++ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ ++ MOV r1, r1, LSL r0 ++ ++ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ ++ BX lr ++ ++ ++ ++ .global disable_pmn @ export this function for the linker ++ /* Enable PMN{n} */ ++ /* void disable_pmn(uint32_t counter) */ ++ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) ++disable_pmn: */ ++ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ ++ MOV r1, r1, LSL r0 ++ ++ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ ++ BX lr ++ ++ ++ ++ .global enable_pmu_user_access @ export this function for the linker ++ /* Enables User mode access to the PMU (must be called in a priviledge */ ++ /* void enable_pmu_user_access(void) */ ++enable_pmu_user_access: ++ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ ++ ORR r0, r0, #0x01 /* Set EN bit (bit 0) */ ++ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ ++ BX lr ++ ++ ++ ++ .global disable_pmu_user_access @ export this function for the linke ++ /* Disables User mode access to the PMU (must be called in a priviledg */ ++ /* void disable_pmu_user_access(void) */ ++disable_pmu_user_access: ++ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ ++ BIC r0, r0, #0x01 /* Clear EN bit (bit 0) */ ++ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ ++ BX lr ++ ++ ++ /* --------------------------------------------------------------- */ ++ /* Counter read registers */ ++ /* --------------------------------------------------------------- */ ++ ++ .global read_ccnt @ export this function for the linker ++ /* Returns the value of CCNT */ ++ /* uint32_t read_ccnt(void) */ ++read_ccnt: ++ MRC p15, 0, r0, c9, c13, 0 /* Read CCNT Register */ ++ BX lr ++ ++ ++ .global read_pmn @ export this function for the linker ++ /* Returns the value of PMN{n} */ ++ /* uint32_t read_pmn(uint32_t counter) */ ++ /* counter = r0 = The counter to read (e.g. 0 for PMN0, 1 for PMN1) * ++read_pmn: */ ++ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ ++ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ ++ MRC p15, 0, r0, c9, c13, 2 /* Read current PMNx Register */ ++ BX lr ++ ++ ++ /* --------------------------------------------------------------- */ ++ /* Software Increment */ ++ /* --------------------------------------------------------------- */ ++ ++ .global pmu_software_increment @ export this function for the linker ++ /* Writes to software increment register */ ++ /* void pmu_software_increment(uint32_t counter) */ ++ /* counter = r0 = The counter to increment (e.g. 0 for PMN0, 1 for PMN ++pmu_software_increment: */ ++ MOV r1, #0x01 ++ MOV r1, r1, LSL r0 ++ MCR p15, 0, r1, c9, c12, 4 /* Write SWINCR Register */ ++ BX lr ++ ++ /* --------------------------------------------------------------- */ ++ /* Overflow & Interrupt Generation */ ++ /* --------------------------------------------------------------- */ ++ ++ .global read_flags @ export this function for the linker ++ /* Returns the value of the overflow flags */ ++ /* uint32_t read_flags(void) */ ++read_flags: ++ MRC p15, 0, r0, c9, c12, 3 /* Read FLAG Register */ ++ BX lr ++ ++ ++ .global write_flags @ export this function for the linker ++ /* Writes the overflow flags */ ++ /* void write_flags(uint32_t flags) */ ++write_flags: ++ MCR p15, 0, r0, c9, c12, 3 /* Write FLAG Register */ ++ BX lr ++ ++ ++ .global enable_ccnt_irq @ export this function for the linker ++ /* Enables interrupt generation on overflow of the CCNT */ ++ /* void enable_ccnt_irq(void) */ ++enable_ccnt_irq: ++ MOV r0, #0x80000000 ++ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ ++ BX lr ++ ++ .global disable_ccnt_irq @ export this function for the linker ++ /* Disables interrupt generation on overflow of the CCNT */ ++ /* void disable_ccnt_irq(void) */ ++disable_ccnt_irq: ++ MOV r0, #0x80000000 ++ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ ++ BX lr ++ ++ ++ .global enable_pmn_irq @ export this function for the linker ++ /* Enables interrupt generation on overflow of PMN{x} */ ++ /* void enable_pmn_irq(uint32_t counter) */ ++ /* counter = r0 = The counter to enable the interrupt for (e.g. 0 for ++enable_pmn_irq: */ ++ MOV r1, #0x1 /* Use arg (r0) to set which counter */ ++ MOV r0, r1, LSL r0 ++ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ ++ BX lr ++ ++ .global disable_pmn_irq @ export this function for the linker ++ /* Disables interrupt generation on overflow of PMN{x} */ ++ /* void disable_pmn_irq(uint32_t counter) */ ++ /* counter = r0 = The counter to disable the interrupt for (e.g. 0 fo ++disable_pmn_irq: */ ++ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ ++ MOV r0, r1, LSL r0 ++ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ ++ BX lr ++ ++ /* --------------------------------------------------------------- */ ++ /* Reset Functions */ ++ /* --------------------------------------------------------------- */ ++ ++ .global reset_pmn @ export this function for the linker ++ /* Resets the programmable counters */ ++ /* void reset_pmn(void) */ ++reset_pmn: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ ++ ORR r0, r0, #0x02 /* Set P bit (Event Counter Reset) */ ++ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ .global reset_ccnt @ export this function for the linker ++ /* Resets the CCNT */ ++ /* void reset_ccnt(void) */ ++reset_ccnt: ++ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ ++ ORR r0, r0, #0x04 /* Set C bit (Event Counter Reset) */ ++ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ ++ BX lr ++ ++ ++ .end @end of code, this line is optional. ++/* ------------------------------------------------------------ */ ++/* End of v7_pmu.s */ ++/* ------------------------------------------------------------ */ ++ ++ +--- /dev/null ++++ b/modules/hw/mmal/v7_pmu.h +@@ -0,0 +1,113 @@ ++// ------------------------------------------------------------ ++// PMU for Cortex-A/R (v7-A/R) ++// ------------------------------------------------------------ ++ ++#ifndef _V7_PMU_H ++#define _V7_PMU_H ++ ++// Returns the number of progammable counters ++unsigned int getPMN(void); ++ ++// Sets the event for a programmable counter to record ++// counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1) ++// event = r1 = The event code (from appropiate TRM or ARM Architecture Reference Manual) ++void pmn_config(unsigned int counter, unsigned int event); ++ ++// Enables/disables the divider (1/64) on CCNT ++// divider = r0 = If 0 disable divider, else enable dvider ++void ccnt_divider(int divider); ++ ++// ++// Enables and disables ++// ++ ++// Global PMU enable ++// On ARM11 this enables the PMU, and the counters start immediately ++// On Cortex this enables the PMU, there are individual enables for the counters ++void enable_pmu(void); ++ ++// Global PMU disable ++// On Cortex, this overrides the enable state of the individual counters ++void disable_pmu(void); ++ ++// Enable the CCNT ++void enable_ccnt(void); ++ ++// Disable the CCNT ++void disable_ccnt(void); ++ ++// Enable PMN{n} ++// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) ++void enable_pmn(unsigned int counter); ++ ++// Enable PMN{n} ++// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) ++void disable_pmn(unsigned int counter); ++ ++// ++// Read counter values ++// ++ ++// Returns the value of CCNT ++unsigned int read_ccnt(void); ++ ++// Returns the value of PMN{n} ++// counter = The counter to read (e.g. 0 for PMN0, 1 for PMN1) ++unsigned int read_pmn(unsigned int counter); ++ ++// ++// Overflow and interrupts ++// ++ ++// Returns the value of the overflow flags ++unsigned int read_flags(void); ++ ++// Writes the overflow flags ++void write_flags(unsigned int flags); ++ ++// Enables interrupt generation on overflow of the CCNT ++void enable_ccnt_irq(void); ++ ++// Disables interrupt generation on overflow of the CCNT ++void disable_ccnt_irq(void); ++ ++// Enables interrupt generation on overflow of PMN{x} ++// counter = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) ++void enable_pmn_irq(unsigned int counter); ++ ++// Disables interrupt generation on overflow of PMN{x} ++// counter = r0 = The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) ++void disable_pmn_irq(unsigned int counter); ++ ++// ++// Counter reset functions ++// ++ ++// Resets the programmable counters ++void reset_pmn(void); ++ ++// Resets the CCNT ++void reset_ccnt(void); ++ ++// ++// Software Increment ++ ++// Writes to software increment register ++// counter = The counter to increment (e.g. 0 for PMN0, 1 for PMN1) ++void pmu_software_increment(unsigned int counter); ++ ++// ++// User mode access ++// ++ ++// Enables User mode access to the PMU (must be called in a priviledged mode) ++void enable_pmu_user_access(void); ++ ++// Disables User mode access to the PMU (must be called in a priviledged mode) ++void disable_pmu_user_access(void); ++ ++#endif ++// ------------------------------------------------------------ ++// End of v7_pmu.h ++// ------------------------------------------------------------ + --- a/modules/hw/mmal/vout.c +++ b/modules/hw/mmal/vout.c @@ -5070,7 +8236,7 @@ int next_phase_check; /* lowpass for phase check frequency */ int phase_offset; /* currently applied offset to presentation time in ns */ -@@ -136,32 +100,18 @@ +@@ -136,264 +100,415 @@ bool native_interlaced; bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */ bool b_progressive; @@ -5084,13 +8250,22 @@ + vout_subpic_t subs[SUBS_MAX]; + + picture_pool_t * pic_pool; ++ ++ struct vout_isp_conf_s { ++ MMAL_COMPONENT_T *component; ++ MMAL_PORT_T * input; ++ MMAL_PORT_T * output; ++ MMAL_QUEUE_T * out_q; ++ MMAL_POOL_T * in_pool; ++ MMAL_POOL_T * out_pool; ++ bool pending; ++ } isp; }; -+ - /* Utility functions */ +-/* 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); +-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); @@ -5104,13 +8279,18 @@ -/* 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); ++// ISP setup + +-/* 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, +- uint32_t param2); +-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); ++static inline bool want_isp(const vout_display_t * const vd) ++{ ++ return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10); ++} -/* DispManX */ -static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture); @@ -5122,11 +8302,25 @@ -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 void maintain_phase_sync(vout_display_t *vd); ++static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc) ++{ ++ switch (fcc){ ++ case VLC_CODEC_MMAL_OPAQUE: ++ return MMAL_ENCODING_OPAQUE; ++ case VLC_CODEC_MMAL_ZC_SAND8: ++ return MMAL_ENCODING_YUVUV128; ++ case VLC_CODEC_MMAL_ZC_SAND10: ++ return MMAL_ENCODING_YUVUV64_10; // It will be after we've converted it... ++ default: ++ break; ++ } ++ return 0; ++} -static int Open(vlc_object_t *object) --{ ++static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate) + { - vout_display_t *vd = (vout_display_t *)object; - vout_display_sys_t *sys; - uint32_t buffer_pitch, buffer_height; @@ -5135,20 +8329,68 @@ - MMAL_STATUS_T status; - int ret = VLC_SUCCESS; - unsigned i; ++ const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ; ++ const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height; ++ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video; ++ ++ es_fmt->type = MMAL_ES_TYPE_VIDEO; ++ es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);; ++ es_fmt->encoding_variant = 0; ++ ++ v_fmt->width = (w + 31) & ~31; ++ v_fmt->height = (h + 15) & ~15; ++ v_fmt->crop.x = 0; ++ v_fmt->crop.y = 0; ++ v_fmt->crop.width = w; ++ v_fmt->crop.height = h; ++ if (vd->fmt.i_sar_num == 0 || vd->fmt.i_sar_den == 0) { ++ v_fmt->par.num = 1; ++ v_fmt->par.den = 1; ++ } else { ++ v_fmt->par.num = vd->fmt.i_sar_num; ++ v_fmt->par.den = vd->fmt.i_sar_den; ++ } ++ v_fmt->frame_rate.num = vd->fmt.i_frame_rate; ++ v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base; ++ v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space); ++} - if (vout_display_IsWindowed(vd)) - return VLC_EGENERIC; -- ++static void display_src_rect(const vout_display_t * const vd, MMAL_RECT_T *const rect) ++{ ++ const bool wants_isp = want_isp(vd); ++ rect->x = wants_isp ? 0 : vd->fmt.i_x_offset; ++ rect->y = wants_isp ? 0 : vd->fmt.i_y_offset; ++ rect->width = vd->fmt.i_visible_width; ++ rect->height = vd->fmt.i_visible_height; ++} + - sys = calloc(1, sizeof(struct vout_display_sys_t)); - if (!sys) - return VLC_ENOMEM; - vd->sys = sys; -- ++static void isp_input_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: 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 + - sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); - bcm_host_init(); -- ++ mmal_buffer_header_release(buf); + - sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; -- ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s", __func__); ++#endif ++} + - 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)", @@ -5156,7 +8398,11 @@ - ret = VLC_EGENERIC; - goto out; - } -- ++static void isp_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; + - sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; - status = mmal_port_enable(sys->component->control, control_port_cb); - if (status != MMAL_SUCCESS) { @@ -5164,11 +8410,16 @@ - sys->component->control->name, status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; -- } -- ++ if (buffer->cmd == MMAL_EVENT_ERROR) { ++ status = *(uint32_t *)buffer->data; ++ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); + } + - sys->input = sys->component->input[0]; - sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd; -- ++ mmal_buffer_header_release(buffer); ++} + - if (sys->opaque) { - sys->input->format->encoding = MMAL_ENCODING_OPAQUE; - sys->i_planes = 1; @@ -5190,16 +8441,14 @@ - 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) ++static void isp_output_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 ++ 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 ++ vout_display_t * const vd = (vout_display_t *)port->userdata; ++ struct vout_isp_conf_s *const isp = &vd->sys->isp; - status = mmal_port_format_commit(sys->input); - if (status != MMAL_SUCCESS) { @@ -5207,9 +8456,21 @@ - sys->input->name, status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; -- } ++#if TRACE_ALL ++ msg_Dbg(vd, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts); ++#endif ++ mmal_queue_put(isp->out_q, buf); ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: out Q len=%d", __func__, mmal_queue_length(isp->out_q)); ++#endif + } - sys->input->buffer_size = sys->input->buffer_size_recommended; -+ mmal_buffer_header_release(buf); ++ else ++ { ++ mmal_buffer_header_reset(buf); ++ mmal_buffer_header_release(buf); ++ } ++} - vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); - display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; @@ -5232,100 +8493,189 @@ - status, mmal_status_to_string(status)); - ret = VLC_EGENERIC; - goto out; -- } -+#if TRACE_ALL -+ msg_Dbg(vd, ">>> %s", __func__); -+#endif ++static void isp_empty_out_q(struct vout_isp_conf_s * const isp) ++{ ++ MMAL_BUFFER_HEADER_T * buf; ++ // We can be called as part of error recovery so allow for missing Q ++ if (isp->out_q == NULL) ++ return; ++ ++ while ((buf = mmal_queue_get(isp->out_q)) != NULL) ++ mmal_buffer_header_release(buf); ++} ++ ++static void isp_flush(struct vout_isp_conf_s * const isp) ++{ ++ if (!isp->input->is_enabled) ++ mmal_port_disable(isp->input); ++ ++ if (isp->output->is_enabled) ++ mmal_port_disable(isp->output); ++ ++ isp_empty_out_q(isp); ++ isp->pending = false; +} ++ ++static MMAL_STATUS_T isp_prepare(vout_display_t * const vd, struct vout_isp_conf_s * const isp) ++{ ++ MMAL_STATUS_T err; ++ MMAL_BUFFER_HEADER_T * buf; ++ ++ if (!isp->output->is_enabled) { ++ if ((err = mmal_port_enable(isp->output, isp_output_cb)) != MMAL_SUCCESS) ++ { ++ msg_Err(vd, "ISP output port enable failed"); ++ return err; ++ } + } - 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; ++ while ((buf = mmal_queue_get(isp->out_pool->queue)) != NULL) { ++ if ((err = mmal_port_send_buffer(isp->output, buf)) != MMAL_SUCCESS) ++ { ++ msg_Err(vd, "ISP output port stuff failed"); ++ return err; ++ } ++ } - 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; ++ if (!isp->input->is_enabled) { ++ if ((err = mmal_port_enable(isp->input, isp_input_cb)) != MMAL_SUCCESS) ++ { ++ msg_Err(vd, "ISP input port enable failed"); ++ return err; } -- } -- + } ++ return MMAL_SUCCESS; ++} + - vlc_mutex_init(&sys->buffer_mutex); - vlc_cond_init(&sys->buffer_cond); - vlc_mutex_init(&sys->manage_mutex); -- ++static void isp_close(vout_display_t * const vd, vout_display_sys_t * const vd_sys) ++{ ++ struct vout_isp_conf_s * const isp = &vd_sys->isp; ++ VLC_UNUSED(vd); + - vd->pool = vd_pool; - vd->prepare = vd_prepare; - vd->display = vd_display; - vd->control = vd_control; - vd->manage = vd_manage; -- ++ if (isp->component == NULL) ++ return; + - vc_tv_register_callback(tvservice_cb, vd); -- ++ isp_flush(isp); + - if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) { - vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height); - } else { +- } 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; ++ if (isp->component->control->is_enabled) ++ mmal_port_disable(isp->component->control); ++ ++ if (isp->out_q != NULL) { ++ // 1st junk anything lying around ++ isp_empty_out_q(isp); ++ ++ mmal_queue_destroy(isp->out_q); ++ isp->out_q = NULL; } - sys->dmx_handle = vc_dispmanx_display_open(0); - vd->info.subpicture_chromas = subpicture_chromas; -- ++ if (isp->out_pool != NULL) { ++ mmal_port_pool_destroy(isp->output, isp->out_pool); ++ isp->out_pool = NULL; ++ } + - vout_display_DeleteWindow(vd, NULL); -- ++ isp->input = NULL; ++ isp->output = NULL; + -out: - if (ret != VLC_SUCCESS) - Close(object); -- - return ret; ++ mmal_component_release(isp->component); ++ isp->component = NULL; + +- return ret; ++ return; } -static void Close(vlc_object_t *object) --{ ++// Restuff into output rather than return to pool is we can ++static MMAL_BOOL_T isp_out_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata) + { - 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; -- ++ struct vout_isp_conf_s * const isp = userdata; ++ VLC_UNUSED(pool); ++ if (isp->output->is_enabled) { ++ mmal_buffer_header_reset(buffer); ++ if (mmal_port_send_buffer(isp->output, buffer) == MMAL_SUCCESS) ++ return MMAL_FALSE; ++ } ++ return MMAL_TRUE; ++} + - vc_tv_unregister_callback_full(tvservice_cb, vd); -- ++static MMAL_STATUS_T isp_setup(vout_display_t * const vd, vout_display_sys_t * const vd_sys) ++{ ++ struct vout_isp_conf_s * const isp = &vd_sys->isp; ++ MMAL_STATUS_T err; + - if (sys->dmx_handle) - close_dmx(vd); -- ++ if ((err = mmal_component_create(MMAL_COMPONENT_ISP_RESIZER, &isp->component)) != MMAL_SUCCESS) { ++ msg_Err(vd, "Cannot create ISP component"); ++ return err; ++ } ++ isp->input = isp->component->input[0]; ++ isp->output = isp->component->output[0]; + - if (sys->component && sys->component->control->is_enabled) - mmal_port_disable(sys->component->control); -- ++ isp->component->control->userdata = (void *)vd; ++ if ((err = mmal_port_enable(isp->component->control, isp_control_port_cb)) != MMAL_SUCCESS) { ++ msg_Err(vd, "Failed to enable ISP control port"); ++ goto fail; ++ } + - if (sys->input && sys->input->is_enabled) - mmal_port_disable(sys->input); -- ++ isp->input->userdata = (void *)vd; ++ display_set_format(vd, isp->input->format, false); + - if (sys->component && sys->component->is_enabled) - mmal_component_disable(sys->component); -- ++ if ((err = port_parameter_set_bool(isp->input, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) ++ goto fail; + - if (sys->pool) - mmal_port_pool_destroy(sys->input, sys->pool); -- ++ if ((err = mmal_port_format_commit(isp->input)) != MMAL_SUCCESS) { ++ msg_Err(vd, "Failed to set ISP input format"); ++ goto fail; ++ } + - if (sys->component) - mmal_component_release(sys->component); -- ++ isp->input->buffer_size = isp->input->buffer_size_recommended; ++ isp->input->buffer_num = 30; + - if (sys->picture_pool) - picture_pool_Release(sys->picture_pool); - else @@ -5334,35 +8684,194 @@ - mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer); - picture_Release(sys->pictures[i]); - } -- ++ if ((isp->in_pool = mmal_pool_create(isp->input->buffer_num, 0)) == NULL) ++ { ++ msg_Err(vd, "Failed to create input pool"); ++ goto fail; ++ } + - vlc_mutex_destroy(&sys->buffer_mutex); - vlc_cond_destroy(&sys->buffer_cond); - vlc_mutex_destroy(&sys->manage_mutex); -- ++ if ((isp->out_q = mmal_queue_create()) == NULL) ++ { ++ err = MMAL_ENOMEM; ++ goto fail; ++ } + - 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"); -- } -- ++ display_set_format(vd, isp->output->format, true); ++ ++ if ((err = port_parameter_set_bool(isp->output, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) ++ goto fail; ++ ++ if ((err = mmal_port_format_commit(isp->output)) != MMAL_SUCCESS) { ++ msg_Err(vd, "Failed to set ISP input format"); ++ goto fail; + } + - free(sys->pictures); - free(sys); -- ++ isp->output->buffer_size = isp->output->buffer_size_recommended; ++ isp->output->buffer_num = 2; ++ isp->output->userdata = (void *)vd; + - bcm_host_deinit(); --} -- ++ if ((isp->out_pool = mmal_port_pool_create(isp->output, isp->output->buffer_num, isp->output->buffer_size)) == NULL) ++ { ++ msg_Err(vd, "Failed to make ISP port pool"); ++ goto fail; ++ } ++ ++ mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp); ++ ++ if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS) ++ goto fail; ++ ++ return MMAL_SUCCESS; ++ ++fail: ++ isp_close(vd, vd_sys); ++ return err; + } + -static inline uint32_t align(uint32_t x, uint32_t y) { - uint32_t mod = x % y; - if (mod == 0) - return x; -- else ++static MMAL_STATUS_T isp_check(vout_display_t * const vd, vout_display_sys_t * const vd_sys) ++{ ++ struct vout_isp_conf_s *const isp = &vd_sys->isp; ++ const bool has_isp = (isp->component != NULL); ++ const bool wants_isp = want_isp(vd); ++ ++ if (has_isp == wants_isp) ++ { ++ // All OK - do nothing ++ } ++ else if (has_isp) ++ { ++ // ISP active but we don't want it ++ isp_flush(isp); ++ ++ // Check we have everything back and then kill it ++ if (mmal_queue_length(isp->out_pool->queue) == isp->output->buffer_num) ++ isp_close(vd, vd_sys); ++ } + else - return x + y - mod; --} -- ++ { ++ // ISP closed but we want it ++ return isp_setup(vd, vd_sys); ++ } ++ ++ return MMAL_SUCCESS; ++} ++ ++/* TV service */ ++static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, ++ uint32_t param2); ++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); ++ ++// Mmal ++static void maintain_phase_sync(vout_display_t *vd); ++ ++ ++ ++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: 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 ++ ++ mmal_buffer_header_release(buf); ++ ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s", __func__); ++#endif ++} ++ ++static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height) ++{ ++ TV_DISPLAY_STATE_T display_state; ++ int ret = 0; ++ ++ if (vc_tv_get_display_state(&display_state) == 0) { ++ msg_Dbg(vd, "State=%#x", display_state.state); ++ if (display_state.state & 0xFF) { ++ msg_Dbg(vd, "HDMI: %dx%d", display_state.display.hdmi.width, display_state.display.hdmi.height); ++ *width = display_state.display.hdmi.width; ++ *height = display_state.display.hdmi.height; ++ } else if (display_state.state & 0xFF00) { ++ msg_Dbg(vd, "SDTV: %dx%d", display_state.display.sdtv.width, display_state.display.sdtv.height); ++ *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; ++ } ++ ++ return ret; + } + static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, const video_format_t *fmt) { -@@ -426,8 +207,9 @@ +- vout_display_sys_t *sys = vd->sys; ++ vout_display_sys_t * const sys = vd->sys; + vout_display_place_t place; + MMAL_DISPLAYREGION_T display_region; + MMAL_STATUS_T status; + + if (!cfg && !fmt) ++ { ++ msg_Err(vd, "%s: Missing cfg & fmt", __func__); + return -EINVAL; ++ } ++ ++ isp_check(vd, sys); + + if (fmt) { + sys->input->format->es->video.par.num = fmt->i_sar_num; +@@ -412,22 +527,29 @@ + if (!cfg) + cfg = vd->cfg; + +- vout_display_PlacePicture(&place, fmt, cfg, false); ++ { ++ // Ignore what VLC thinks might be going on with display size ++ vout_display_cfg_t tcfg = *cfg; ++ tcfg.display.width = sys->display_width; ++ tcfg.display.height = sys->display_height; ++ tcfg.is_display_filled = true; ++ vout_display_PlacePicture(&place, fmt, &tcfg, false); ++ ++ msg_Dbg(vd, "%dx%d -> %dx%d @ %d,%d", tcfg.display.width, tcfg.display.height, place.width, place.height, place.x, place.y); ++ } + + 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 = fmt->i_x_offset; +- display_region.src_rect.y = fmt->i_y_offset; +- display_region.src_rect.width = fmt->i_visible_width; +- display_region.src_rect.height = fmt->i_visible_height; ++ display_src_rect(vd, &display_region.src_rect); + display_region.dest_rect.x = place.x; + display_region.dest_rect.y = place.y; display_region.dest_rect.width = place.width; display_region.dest_rect.height = place.height; display_region.layer = sys->layer; @@ -5373,7 +8882,7 @@ status = mmal_port_parameter_set(sys->input, &display_region.hdr); if (status != MMAL_SUCCESS) { msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", -@@ -435,7 +217,6 @@ +@@ -435,7 +557,6 @@ return -EINVAL; } @@ -5381,7 +8890,7 @@ sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME); sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED); if (sys->adjust_refresh_rate) { -@@ -446,171 +227,103 @@ +@@ -446,191 +567,130 @@ return 0; } @@ -5486,31 +8995,48 @@ + sys->i_frame_rate = p_pic->format.i_frame_rate; + sys->i_frame_rate_base = p_pic->format.i_frame_rate_base; + configure_display(vd, NULL, &p_pic->format); ++ } ++ ++ if (!sys->input->is_enabled && ++ (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS) ++ { ++ 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. ++ if (sys->isp.pending) { ++ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q); ++ sys->isp.pending = false; ++#if TRACE_ALL ++ msg_Dbg(vd, "--- %s: ISP stuff", __func__); ++#endif ++ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS) ++ { ++ mmal_buffer_header_release(buf); ++ msg_Err(vd, "Send ISP buffer to render input failed"); ++ goto fail; ++ } } - +- - memset(&picture_res, 0, sizeof(picture_resource_t)); - sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *)); - for (i = 0; i < sys->num_buffers; ++i) { - 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. ++ else + { + MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic); ++#if TRACE_ALL ++ msg_Dbg(vd, "--- %s: Buf stuff", __func__); ++#endif + if ((err = port_send_replicated(sys->input, sys->pool, pic_buf, pic_buf->pts)) != MMAL_SUCCESS) + { + msg_Err(vd, "Send buffer to input failed"); @@ -5622,7 +9148,31 @@ } static int vd_control(vout_display_t *vd, int query, va_list args) -@@ -640,11 +353,39 @@ + { +- vout_display_sys_t *sys = vd->sys; +- vout_display_cfg_t cfg; +- const vout_display_cfg_t *tmp_cfg; ++ vout_display_sys_t * const sys = vd->sys; + int ret = VLC_EGENERIC; ++ VLC_UNUSED(args); + + switch (query) { + case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: +- tmp_cfg = va_arg(args, const vout_display_cfg_t *); +- if (tmp_cfg->display.width == sys->display_width && +- tmp_cfg->display.height == sys->display_height) { +- cfg = *vd->cfg; +- cfg.display.width = sys->display_width; +- cfg.display.height = sys->display_height; +- if (configure_display(vd, &cfg, NULL) >= 0) +- ret = VLC_SUCCESS; +- } ++ // Ignore this - we just use full screen anyway ++ ret = VLC_SUCCESS; + break; + + case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: +@@ -640,10 +700,37 @@ break; case VOUT_DISPLAY_RESET_PICTURES: @@ -5635,8 +9185,9 @@ + case VOUT_DISPLAY_CHANGE_ZOOM: msg_Warn(vd, "Unsupported control query %d", query); - break; - ++ ret = VLC_SUCCESS; ++ break; ++ + case VOUT_DISPLAY_CHANGE_MMAL_HIDE: + { + MMAL_STATUS_T err; @@ -5651,19 +9202,16 @@ + (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; + break; + } -+ + default: msg_Warn(vd, "Unknown control query %d", query); - break; -@@ -661,13 +402,11 @@ +@@ -661,13 +748,11 @@ vlc_mutex_lock(&sys->manage_mutex); if (sys->need_configure_display) { @@ -5679,25 +9227,12 @@ } sys->need_configure_display = false; -@@ -676,56 +415,45 @@ +@@ -676,56 +761,76 @@ 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, ++static void vd_prepare(vout_display_t *vd, picture_t *p_pic, +#if VLC_VER_3 + subpicture_t *subpicture +#else @@ -5706,11 +9241,51 @@ + ) { - vout_display_t *vd = (vout_display_t *)port->userdata; -+ VLC_UNUSED(picture); +- MMAL_STATUS_T status; ++ MMAL_STATUS_T err; ++ vout_display_sys_t * const sys = vd->sys; + +- if (buffer->cmd == MMAL_EVENT_ERROR) { +- status = *(uint32_t *)buffer->data; +- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); + VLC_UNUSED(subpicture); +// VLC_UNUSED(date); + + vd_manage(vd); ++ ++ if (isp_check(vd, sys) != MMAL_SUCCESS) { ++ return; + } + +- mmal_buffer_header_release(buffer); +-} ++ if (want_isp(vd)) ++ { ++ struct vout_isp_conf_s * const isp = &sys->isp; ++ MMAL_BUFFER_HEADER_T * buf; ++ ++ // This should be empty - make it so if it isn't ++ isp_empty_out_q(isp); ++ isp->pending = false; + +-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) +-{ +- vout_display_t *vd = (vout_display_t *)port->userdata; ++ // Stuff output ++ if (isp_prepare(vd, isp) != MMAL_SUCCESS) ++ return; ++ ++ buf = pic_mmal_buffer(p_pic); ++ if ((err = port_send_replicated(isp->input, isp->in_pool, ++ buf, buf->pts)) != MMAL_SUCCESS) ++ { ++ msg_Err(vd, "Send buffer to input failed"); ++ return; ++ } ++ ++ isp->pending = true; ++ } ++ +#if 0 + VLC_UNUSED(date); vout_display_sys_t *sys = vd->sys; @@ -5765,7 +9340,7 @@ } static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2) -@@ -828,148 +556,12 @@ +@@ -828,148 +933,12 @@ } } @@ -5915,7 +9490,7 @@ ((double)vd->sys->i_frame_rate / vd->sys->i_frame_rate_base); vout_display_sys_t *sys = vd->sys; -@@ -1012,32 +604,258 @@ +@@ -1012,32 +981,260 @@ } } @@ -5990,6 +9565,8 @@ + if (sys->component) + mmal_component_release(sys->component); + ++ isp_close(vd, sys); ++ + vlc_mutex_destroy(&sys->manage_mutex); + + if (sys->native_interlaced) { @@ -6015,11 +9592,12 @@ + MMAL_DISPLAYREGION_T display_region; + MMAL_STATUS_T status; + int ret = VLC_EGENERIC; ++ const MMAL_FOURCC_T enc_in = vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma); + +#if TRACE_ALL + msg_Dbg(vd, "<<< %s", __func__); +#endif -+ if (vd->fmt.i_chroma != VLC_CODEC_MMAL_OPAQUE) ++ if (enc_in == 0) + { +#if TRACE_ALL + msg_Dbg(vd, ">>> %s: Format not MMAL", __func__); @@ -6032,6 +9610,8 @@ + return VLC_ENOMEM; + vd->sys = sys; + ++ bcm_host_init(); ++ + sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); + + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component); @@ -6052,18 +9632,12 @@ + sys->input = sys->component->input[0]; + sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd; + -+ sys->input->format->encoding = MMAL_ENCODING_OPAQUE; ++ sys->input->format->encoding = enc_in; ++ sys->input->format->encoding_variant = 0; + 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; ++ display_set_format(vd, sys->input->format, want_isp(vd)); + + status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true); + if (status != MMAL_SUCCESS) { @@ -6085,10 +9659,7 @@ + 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_src_rect(vd, &display_region.src_rect); + display_region.dest_rect.x = place.x; + display_region.dest_rect.y = place.y; + display_region.dest_rect.width = place.width; @@ -6153,6 +9724,14 @@ + + vlc_mutex_init(&sys->manage_mutex); + ++ vd->info = (vout_display_info_t){ ++ .is_slow = false, ++ .has_double_click = false, ++ .needs_hide_mouse = false, ++ .has_pictures_invalid = true, ++ .subpicture_chromas = NULL ++ }; ++ + vd->pool = vd_pool; + vd->prepare = vd_prepare; + vd->display = vd_display; @@ -6160,10 +9739,8 @@ + + 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 { ++ if (query_resolution(vd, &sys->display_width, &sys->display_height) < 0) ++ { + sys->display_width = vd->cfg->display.width; + sys->display_height = vd->cfg->display.height; + } @@ -6202,7 +9779,7 @@ + --- /dev/null +++ b/modules/hw/mmal/xsplitter.c -@@ -0,0 +1,365 @@ +@@ -0,0 +1,403 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif @@ -6233,6 +9810,19 @@ + uint32_t changed; +} mmal_x11_sys_t; + ++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 void unload_display_module(vout_display_t * const x_vout) +{ + if (x_vout != NULL) { @@ -6311,10 +9901,12 @@ + + if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL) + { -+ msg_Err(vd, "Failed to find X11 module"); ++ msg_Err(vd, "Failed to open Xsplitter:%s module", module_name); + goto fail; + } + ++ msg_Dbg(vd, "R/G/B: %08x/%08x/%08x", x_vout->fmt.i_rmask, x_vout->fmt.i_gmask, x_vout->fmt.i_bmask); ++ + return x_vout; + +fail: @@ -6337,9 +9929,18 @@ + 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); ++ char buf0[5]; ++ msg_Dbg(vd, "<<< %s (count=%d) %s:%dx%d->%s:%dx%d", __func__, count, ++ str_fourcc(buf0, vd->fmt.i_chroma), ++ vd->fmt.i_width, vd->fmt.i_height, ++ str_fourcc(buf0, x_vd->fmt.i_chroma), ++ x_vd->fmt.i_width, x_vd->fmt.i_height); ++#endif ++ picture_pool_t * pool = x_vd->pool(x_vd, count); ++#if TRACE_ALL ++ msg_Dbg(vd, ">>> %s: %p", __func__, pool); +#endif -+ return x_vd->pool(x_vd, count); ++ return pool; +} + +/* Prepare a picture and an optional subpicture for display (optional). @@ -6377,10 +9978,11 @@ +{ + 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); ++ const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic); + +#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); ++ msg_Dbg(vd, "<<< %s: fmt: %dx%d/%dx%d, pic:%dx%d, pts=%lld, mmal=%d/%d", __func__, vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height, pic->format.i_width, pic->format.i_height, (long long)pic->date, ++ is_mmal_pic, sys->use_mmal); +#endif + + if (sys->use_mmal != is_mmal_pic) { @@ -6407,6 +10009,11 @@ + return result; +} + ++static bool want_mmal_vout(vout_display_t * vd, const mmal_x11_sys_t * const sys) ++{ ++ return sys->mmal_vout != NULL && var_InheritBool(vd, "fullscreen"); ++} ++ +/* Control on the module (mandatory) */ +static int mmal_x11_control(vout_display_t * vd, int ctl, va_list va) +{ @@ -6424,10 +10031,12 @@ + 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"); ++ const bool want_mmal = want_mmal_vout(vd, sys); + 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); ++ msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d", ++ cfg->display.width, cfg->display.height, sys->mmal_vout, want_mmal, ++ var_InheritBool(vd, "fullscreen")); + + if (sys->use_mmal != want_mmal) { + if (sys->use_mmal) { @@ -6466,7 +10075,10 @@ + + case VOUT_DISPLAY_RESET_PICTURES: + msg_Dbg(vd, "Reset pictures"); -+ rv = x_vd->control(x_vd, ctl, va); ++// rv = x_vd->control(x_vd, ctl, va); ++ rv = sys->x_vout->control(sys->x_vout, ctl, va); ++ if (sys->mmal_vout) ++ rv = sys->mmal_vout->control(sys->mmal_vout, 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); @@ -6502,19 +10114,6 @@ +} +#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; @@ -6526,6 +10125,14 @@ + } + vd->sys = (vout_display_sys_t *)sys; + ++ vd->info = (vout_display_info_t){ ++ .is_slow = false, ++ .has_double_click = false, ++ .needs_hide_mouse = false, ++ .has_pictures_invalid = true, ++ .subpicture_chromas = NULL ++ }; ++ + if ((sys->x_vout = load_display_module(vd, "vout display", "xcb_x11")) == NULL) + goto fail; + @@ -6536,10 +10143,6 @@ + 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; @@ -6548,6 +10151,18 @@ + vd->manage = mmal_x11_manage; +#endif + ++ if (want_mmal_vout(vd, sys)) { ++ sys->cur_vout = sys->mmal_vout; ++ sys->use_mmal = true; ++ } ++ else { ++ sys->cur_vout = sys->x_vout; ++ sys->use_mmal = false; ++ } ++ ++ vd->info = sys->cur_vout->info; ++ vd->fmt = sys->cur_vout->fmt; ++ + return VLC_SUCCESS; + +fail: @@ -6568,3 +10183,14 @@ + set_callbacks(OpenMmalX11, CloseMmalX11) +vlc_module_end() + +--- a/src/misc/fourcc.c ++++ b/src/misc/fourcc.c +@@ -756,6 +756,8 @@ + VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT }, + FAKE_FMT() }, + { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, ++ VLC_CODEC_MMAL_ZC_SAND8, VLC_CODEC_MMAL_ZC_SAND10, ++ VLC_CODEC_MMAL_ZC_I420, + VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE }, + FAKE_FMT() }, + { { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B },