1Upstream-Status: Inappropriate 2 3RPI-Distro repo forks original vlc and applies patches 4to enable raspiberry pi support. 5 6--- a/configure.ac 7+++ b/configure.ac 8@@ -3478,6 +3478,9 @@ dnl 9 AC_ARG_ENABLE(mmal, 10 AS_HELP_STRING([--enable-mmal], 11 [Multi-Media Abstraction Layer (MMAL) hardware plugin (default enable)])) 12+AC_ARG_ENABLE(mmal_avcodec, 13+ AS_HELP_STRING([--enable-mmal-avcodec], 14+ [Use MMAL enabled avcodec libs (default disable)])) 15 if test "${enable_mmal}" != "no"; then 16 VLC_SAVE_FLAGS 17 LDFLAGS="${LDFLAGS} -L/opt/vc/lib -lvchostif" 18@@ -3488,7 +3491,7 @@ if test "${enable_mmal}" != "no"; then 19 VLC_ADD_PLUGIN([mmal]) 20 VLC_ADD_LDFLAGS([mmal],[ -L/opt/vc/lib ]) 21 VLC_ADD_CFLAGS([mmal],[ -isystem /opt/vc/include -isystem /opt/vc/include/interface/vcos/pthreads -isystem /opt/vc/include/interface/vmcs_host/linux ]) 22- VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif ]) ], [ 23+ VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif -lvchiq_arm -lvcsm ]) ], [ 24 AS_IF([test "${enable_mmal}" = "yes"], 25 [ AC_MSG_ERROR([Cannot find bcm library...]) ], 26 [ AC_MSG_WARN([Cannot find bcm library...]) ]) 27@@ -3500,6 +3503,7 @@ if test "${enable_mmal}" != "no"; then 28 VLC_RESTORE_FLAGS 29 fi 30 AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"]) 31+AM_CONDITIONAL([HAVE_MMAL_AVCODEC], [test "${enable_mmal_avcodec}" = "yes"]) 32 33 dnl 34 dnl evas plugin 35--- a/include/vlc_fourcc.h 36+++ b/include/vlc_fourcc.h 37@@ -365,6 +365,11 @@ 38 39 /* Broadcom MMAL opaque buffer type */ 40 #define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L') 41+#define VLC_CODEC_MMAL_ZC_SAND8 VLC_FOURCC('Z','S','D','8') 42+#define VLC_CODEC_MMAL_ZC_SAND10 VLC_FOURCC('Z','S','D','0') 43+#define VLC_CODEC_MMAL_ZC_SAND30 VLC_FOURCC('Z','S','D','3') 44+#define VLC_CODEC_MMAL_ZC_I420 VLC_FOURCC('Z','4','2','0') 45+#define VLC_CODEC_MMAL_ZC_RGB32 VLC_FOURCC('Z','R','G','B') 46 47 /* DXVA2 opaque video surface for use with D3D9 */ 48 #define VLC_CODEC_D3D9_OPAQUE VLC_FOURCC('D','X','A','9') /* 4:2:0 8 bpc */ 49--- a/modules/hw/mmal/Makefile.am 50+++ b/modules/hw/mmal/Makefile.am 51@@ -1,23 +1,57 @@ 52 include $(top_srcdir)/modules/common.am 53 mmaldir = $(pluginsdir)/mmal 54 55-AM_CFLAGS += $(CFLAGS_mmal) 56-AM_LDFLAGS += -rpath '$(mmaldir)' $(LDFLAGS_mmal) 57+AM_CFLAGS += -pthread $(CFLAGS_mmal) 58+AM_LDFLAGS += -pthread -rpath '$(mmaldir)' $(LDFLAGS_mmal) 59 60-libmmal_vout_plugin_la_SOURCES = vout.c mmal_picture.c mmal_picture.h 61+libmmal_vout_plugin_la_SOURCES = vout.c mmal_cma.c mmal_picture.c subpic.c\ 62+ mmal_cma.h mmal_picture.h subpic.h transform_ops.h\ 63+ mmal_piccpy_neon.S 64 libmmal_vout_plugin_la_CFLAGS = $(AM_CFLAGS) 65-libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm 66+libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm -lX11 -lXrandr 67 libmmal_vout_plugin_la_LIBADD = $(LIBS_mmal) 68 mmal_LTLIBRARIES = libmmal_vout_plugin.la 69 70-libmmal_codec_plugin_la_SOURCES = codec.c 71+libmmal_codec_plugin_la_SOURCES = codec.c mmal_cma.c mmal_picture.c subpic.c\ 72+ mmal_cma.h mmal_picture.h subpic.h transform_ops.h\ 73+ blend_rgba_neon.S mmal_piccpy_neon.S 74 libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS) 75 libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS) 76 libmmal_codec_plugin_la_LIBADD = $(LIBS_mmal) 77 mmal_LTLIBRARIES += libmmal_codec_plugin.la 78 79-libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c 80+libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c mmal_cma.c\ 81+ mmal_cma.h mmal_picture.h transform_ops.h\ 82+ mmal_piccpy_neon.S 83 libmmal_deinterlace_plugin_la_CFLAGS = $(AM_CFLAGS) 84 libmmal_deinterlace_plugin_la_LDFLAGS = $(AM_LDFLAGS) 85 libmmal_deinterlace_plugin_la_LIBADD = $(LIBS_mmal) 86 mmal_LTLIBRARIES += libmmal_deinterlace_plugin.la 87+ 88+libmmal_xsplitter_plugin_la_SOURCES = xsplitter.c mmal_picture.c mmal_cma.c\ 89+ mmal_cma.h mmal_picture.h transform_ops.h\ 90+ mmal_piccpy_neon.S 91+libmmal_xsplitter_plugin_la_CFLAGS = $(AM_CFLAGS) 92+libmmal_xsplitter_plugin_la_LDFLAGS = $(AM_LDFLAGS) 93+libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal) 94+mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la 95+ 96+libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c\ 97+ mmal_cma.h mmal_picture.h transform_ops.h\ 98+ mmal_piccpy_neon.S 99+libmmal_converter_plugin_la_CFLAGS = $(AM_CFLAGS) 100+libmmal_converter_plugin_la_LDFLAGS = $(AM_LDFLAGS) 101+libmmal_converter_plugin_la_LIBADD = $(LIBS_mmal) 102+mmal_LTLIBRARIES += libmmal_converter_plugin.la 103+ 104+if HAVE_MMAL_AVCODEC 105+libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_cma.c mmal_picture.c\ 106+ mmal_cma.h mmal_picture.h transform_ops.h\ 107+ mmal_piccpy_neon.S 108+libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS) 109+libmmal_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS) 110+libmmal_avcodec_plugin_la_LIBADD = $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(LIBS_mmal) 111+mmal_LTLIBRARIES += libmmal_avcodec_plugin.la 112+endif 113+ 114+ 115--- /dev/null 116+++ b/modules/hw/mmal/blend_rgba_neon.S 117@@ -0,0 +1,197 @@ 118+ .syntax unified 119+ .arm 120+// .thumb 121+ .text 122+ .align 16 123+ .arch armv7-a 124+ .fpu neon-vfpv4 125+ 126+@ blend_rgbx_rgba_neon 127+ 128+@ Implements /255 as ((x * 257) + 0x8000) >> 16 129+@ This generates something in the range [(x+126)/255, (x+127)/255] which is good enough 130+ 131+@ There is advantage to aligning src and/or dest - dest gives a bit more due to being used twice 132+ 133+ 134+ 135+@ [r0] RGBx dest loaded into d20-d23 136+@ [r1] RGBA src merge loaded into d16-d19 137+@ r2 plane alpha 138+@ r3 count (pixels) 139+ 140+.macro blend_main sR, sG, sB, sA, dR, dG, dB, dA 141+ 142+ push { r4, lr } 143+ 144+ vdup.u8 d7, r2 145+ 146+ subs r3, #8 147+ vmov.u8 d6, #0xff 148+ 149+ blt 2f 150+ 151+ @ If < 16 bytes to move then don't bother trying to align 152+ @ (a) This means the the align doesn't need to worry about r3 underflow 153+ @ (b) The overhead would be greater than any gain 154+ cmp r3, #8 155+ mov r4, r3 156+ ble 1f 157+ 158+ @ Align r1 on a 32 byte boundary 159+ neg r3, r0 160+ ubfx r3, r3, #2, #3 161+ 162+ cmp r3, #0 163+ blne 10f 164+ 165+ sub r3, r4, r3 166+ 167+1: 168+ vld4.8 {d16, d17, d18, d19}, [r1] 169+ 170+1: 171+ vmull.u8 q15, \sA, d7 172+ 173+ vld4.8 {d20, d21, d22, d23}, [r0] 174+ 175+ vsra.u16 q15, q15, #8 176+ subs r3, #8 177+ vrshrn.u16 d31, q15, #8 178+ vsub.u8 d30, d6, d31 179+ 180+ vmull.u8 q12, \sR, d31 181+ vmull.u8 q13, \sG, d31 182+ vmull.u8 q14, \sB, d31 183+ addge r1, #32 184+ 185+ vmlal.u8 q12, \dR, d30 186+ vmlal.u8 q13, \dG, d30 187+ vmlal.u8 q14, \dB, d30 188+ vld4.8 {d16, d17, d18, d19}, [r1] 189+ 190+ vsra.u16 q12, q12, #8 @ * 257/256 191+ vsra.u16 q13, q13, #8 192+ vsra.u16 q14, q14, #8 193+ 194+ vrshrn.u16 \dR, q12, #8 195+ vrshrn.u16 \dG, q13, #8 196+ vrshrn.u16 \dB, q14, #8 197+ vmov.u8 \dA, #0xff 198+ 199+ vst4.8 {d20, d21, d22, d23}, [r0]! 200+ bge 1b 201+ add r1, #32 202+ 203+2: 204+ cmp r3, #-8 205+ blgt 10f 206+ 207+ pop { r4, pc } 208+ 209+ 210+// Partial version 211+// Align @ start & deal with tail 212+10: 213+ lsls r2, r3, #30 @ b2 -> C, b1 -> N 214+ mov r2, r0 215+ bcc 1f 216+ vld4.8 {d16[0], d17[0], d18[0], d19[0]}, [r1]! 217+ vld4.8 {d20[0], d21[0], d22[0], d23[0]}, [r2]! 218+ vld4.8 {d16[1], d17[1], d18[1], d19[1]}, [r1]! 219+ vld4.8 {d20[1], d21[1], d22[1], d23[1]}, [r2]! 220+ vld4.8 {d16[2], d17[2], d18[2], d19[2]}, [r1]! 221+ vld4.8 {d20[2], d21[2], d22[2], d23[2]}, [r2]! 222+ vld4.8 {d16[3], d17[3], d18[3], d19[3]}, [r1]! 223+ vld4.8 {d20[3], d21[3], d22[3], d23[3]}, [r2]! 224+1: 225+ bpl 1f 226+ vld4.8 {d16[4], d17[4], d18[4], d19[4]}, [r1]! 227+ vld4.8 {d20[4], d21[4], d22[4], d23[4]}, [r2]! 228+ vld4.8 {d16[5], d17[5], d18[5], d19[5]}, [r1]! 229+ vld4.8 {d20[5], d21[5], d22[5], d23[5]}, [r2]! 230+1: 231+ tst r3, #1 232+ beq 1f 233+ vld4.8 {d16[6], d17[6], d18[6], d19[6]}, [r1]! 234+ vld4.8 {d20[6], d21[6], d22[6], d23[6]}, [r2]! 235+1: 236+ @ Set conditions for later 237+ lsls r2, r3, #30 @ b2 -> C, b1 -> N 238+ 239+ vmull.u8 q15, \sA, d7 240+ vsra.u16 q15, q15, #8 241+ vrshrn.u16 d31, q15, #8 242+ vsub.u8 d30, d6, d31 243+ 244+ vmull.u8 q12, \sR, d31 245+ vmull.u8 q13, \sG, d31 246+ vmull.u8 q14, \sB, d31 247+ 248+ vmlal.u8 q12, \dR, d30 249+ vmlal.u8 q13, \dG, d30 250+ vmlal.u8 q14, \dB, d30 251+ 252+ vsra.u16 q12, q12, #8 253+ vsra.u16 q13, q13, #8 254+ vsra.u16 q14, q14, #8 255+ 256+ vrshrn.u16 \dR, q12, #8 257+ vrshrn.u16 \dG, q13, #8 258+ vrshrn.u16 \dB, q14, #8 259+ vmov.u8 \dA, #0xff 260+ 261+ bcc 1f 262+ vst4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]! 263+ vst4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]! 264+ vst4.8 {d20[2], d21[2], d22[2], d23[2]}, [r0]! 265+ vst4.8 {d20[3], d21[3], d22[3], d23[3]}, [r0]! 266+1: 267+ bpl 1f 268+ vst4.8 {d20[4], d21[4], d22[4], d23[4]}, [r0]! 269+ vst4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]! 270+1: 271+ tst r3, #1 272+ bxeq lr 273+ vst4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]! 274+ 275+ bx lr 276+ 277+.endm 278+ 279+ 280+@ [r0] RGBx dest (Byte order: R, G, B, x) 281+@ [r1] RGBA src merge (Byte order: R, G, B, A) 282+@ r2 plane alpha 283+@ r3 count (pixels) 284+ 285+@ Whilst specified as RGBx+RGBA the only important part is the position of 286+@ alpha, the other components are all treated the same 287+ 288+@ [r0] RGBx dest (Byte order: R, G, B, x) 289+@ [r1] RGBA src merge (Byte order: R, G, B, A) - same as above 290+@ r2 plane alpha 291+@ r3 count (pixels) 292+ .align 16 293+ .global blend_rgbx_rgba_neon 294+#ifdef __ELF__ 295+ .type blend_rgbx_rgba_neon, %function 296+#endif 297+blend_rgbx_rgba_neon: 298+ blend_main d16, d17, d18, d19, d20, d21, d22, d23 299+ 300+ 301+@ [r0] RGBx dest (Byte order: R, G, B, x) 302+@ [r1] RGBA src merge (Byte order: B, G, R, A) - B / R swapped 303+@ r2 plane alpha 304+@ r3 count (pixels) 305+ .align 16 306+ .global blend_bgrx_rgba_neon 307+#ifdef __ELF__ 308+ .type blend_bgrx_rgba_neon, %function 309+#endif 310+blend_bgrx_rgba_neon: 311+ blend_main d18, d17, d16, d19, d20, d21, d22, d23 312+ 313+ 314+ 315--- /dev/null 316+++ b/modules/hw/mmal/blend_rgba_neon.h 317@@ -0,0 +1,17 @@ 318+#ifndef HW_MMAL_BLEND_RGBA_NEON_H 319+#define HW_MMAL_BLEND_RGBA_NEON_H 320+ 321+#ifdef __cplusplus 322+extern "C" { 323+#endif 324+ 325+typedef void blend_neon_fn(void * dest, const void * src, int alpha, unsigned int n); 326+extern blend_neon_fn blend_rgbx_rgba_neon; 327+extern blend_neon_fn blend_bgrx_rgba_neon; 328+ 329+#ifdef __cplusplus 330+} 331+#endif 332+ 333+#endif 334+ 335--- /dev/null 336+++ b/modules/hw/mmal/blend_test.c 337@@ -0,0 +1,180 @@ 338+#include <stdio.h> 339+#include <stdint.h> 340+#include <memory.h> 341+ 342+#include "blend_rgba_neon.h" 343+ 344+#define RPI_PROFILE 1 345+#define RPI_PROC_ALLOC 1 346+#include "rpi_prof.h" 347+ 348+static inline unsigned div255(unsigned v) 349+{ 350+ // This models what we we do in the asm for / 255 351+ // It generates something in the range [(i+126)/255, (i+127)/255] which is good enough 352+ return ((v * 257) + 0x8000) >> 16; 353+} 354+ 355+static inline unsigned int a_merge(unsigned int dst, unsigned src, unsigned f) 356+{ 357+ return div255((255 - f) * (dst) + src * f); 358+} 359+ 360+ 361+static void merge_line(void * dest, const void * src, int alpha, unsigned int n) 362+{ 363+ unsigned int i; 364+ const uint8_t * s_data = src; 365+ uint8_t * d_data = dest; 366+ 367+ for (i = 0; i != n; ++i) { 368+ const uint32_t s_pel = ((const uint32_t *)s_data)[i]; 369+ const uint32_t d_pel = ((const uint32_t *)d_data)[i]; 370+ const unsigned int a = div255(alpha * (s_pel >> 24)); 371+ ((uint32_t *)d_data)[i] = 0xff000000 | 372+ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 16) & 0xff, a) << 16) | 373+ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) | 374+ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 0) & 0xff, a) << 0 ); 375+ } 376+} 377+ 378+ 379+// Merge RGBA with BGRA 380+static void merge_line2(void * dest, const void * src, int alpha, unsigned int n) 381+{ 382+ unsigned int i; 383+ const uint8_t * s_data = src; 384+ uint8_t * d_data = dest; 385+ 386+ for (i = 0; i != n; ++i) { 387+ const uint32_t s_pel = ((const uint32_t *)s_data)[i]; 388+ const uint32_t d_pel = ((const uint32_t *)d_data)[i]; 389+ const unsigned int a = div255(alpha * (s_pel >> 24)); 390+ ((uint32_t *)d_data)[i] = 0xff000000 | 391+ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 16) & 0xff, a) << 0 ) | 392+ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) | 393+ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 0) & 0xff, a) << 16); 394+ } 395+} 396+ 397+#define BUF_SIZE 256 398+#define BUF_SLACK 16 399+#define BUF_ALIGN 64 400+#define BUF_ALLOC (BUF_SIZE + 2*BUF_SLACK + BUF_ALIGN) 401+ 402+static void test_line(const uint32_t * const dx, const unsigned int d_off, 403+ const uint32_t * const sx, const unsigned int s_off, 404+ const unsigned int alpha, const unsigned int len, const int prof_no) 405+{ 406+ uint32_t d0_buf[BUF_ALLOC]; 407+ uint32_t d1_buf[BUF_ALLOC]; 408+ const uint32_t * const s0 = sx + s_off; 409+ 410+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off; 411+ uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off; 412+ unsigned int i; 413+ 414+ memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4); 415+ memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4); 416+ 417+ merge_line(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len); 418+ 419+ PROFILE_START(); 420+ blend_rgbx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len); 421+ PROFILE_ACC_N(prof_no); 422+ 423+ for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) { 424+ if (d0[i] != d1[i]) { 425+ printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len); 426+ } 427+ } 428+} 429+ 430+static void test_line2(const uint32_t * const dx, const unsigned int d_off, 431+ const uint32_t * const sx, const unsigned int s_off, 432+ const unsigned int alpha, const unsigned int len, const int prof_no) 433+{ 434+ uint32_t d0_buf[BUF_ALLOC]; 435+ uint32_t d1_buf[BUF_ALLOC]; 436+ const uint32_t * const s0 = sx + s_off; 437+ 438+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off; 439+ uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off; 440+ unsigned int i; 441+ 442+ memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4); 443+ memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4); 444+ 445+ merge_line2(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len); 446+ 447+ PROFILE_START(); 448+ blend_bgrx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len); 449+ PROFILE_ACC_N(prof_no); 450+ 451+ for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) { 452+ if (d0[i] != d1[i]) { 453+ printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len); 454+ } 455+ } 456+} 457+ 458+ 459+ 460+int main(int argc, char *argv[]) 461+{ 462+ unsigned int i, j; 463+ uint32_t d0_buf[BUF_ALLOC]; 464+ uint32_t s0_buf[BUF_ALLOC]; 465+ 466+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + 63) & ~63) + 0; 467+ uint32_t * const s0 = (uint32_t *)(((uintptr_t)s0_buf + 63) & ~63) + 0; 468+ 469+ PROFILE_INIT(); 470+ 471+ for (i = 0; i != 255*255; ++i) { 472+ unsigned int a = div255(i); 473+ unsigned int b = (i + 127)/255; 474+ unsigned int c = (i + 126)/255; 475+ if (a != b && a != c) 476+ printf("%d/255: %d != %d/%d\n", i, a, b, c); 477+ } 478+ 479+ for (i = 0; i != BUF_ALLOC; ++i) { 480+ d0_buf[i] = 0xff00 | i; 481+ s0_buf[i] = (i << 24) | 0x40ffc0; 482+ } 483+ 484+ for (i = 0; i != 256; ++i) { 485+ test_line(d0, 0, s0, 0, i, 256, -1); 486+ } 487+ for (i = 0; i != 256; ++i) { 488+ test_line(d0, 0, s0, 0, 128, i, -1); 489+ } 490+ 491+ for (j = 0; j != 16; ++j) { 492+ for (i = 0; i != 256; ++i) { 493+ test_line(d0, j & 3, s0, j >> 2, i, 256, j); 494+ } 495+ PROFILE_PRINTF_N(j); 496+ PROFILE_CLEAR_N(j); 497+ } 498+ printf("Done 1\n"); 499+ 500+ for (i = 0; i != 256; ++i) { 501+ test_line2(d0, 0, s0, 0, i, 256, -1); 502+ } 503+ for (i = 0; i != 256; ++i) { 504+ test_line2(d0, 0, s0, 0, 128, i, -1); 505+ } 506+ 507+ for (j = 0; j != 16; ++j) { 508+ for (i = 0; i != 256; ++i) { 509+ test_line2(d0, j & 3, s0, j >> 2, i, 256, j); 510+ } 511+ PROFILE_PRINTF_N(j); 512+ } 513+ printf("Done 2\n"); 514+ 515+ return 0; 516+} 517+ 518--- a/modules/hw/mmal/codec.c 519+++ b/modules/hw/mmal/codec.c 520@@ -26,267 +26,443 @@ 521 #include "config.h" 522 #endif 523 524+#include <stdatomic.h> 525+ 526 #include <vlc_common.h> 527-#include <vlc_atomic.h> 528 #include <vlc_plugin.h> 529 #include <vlc_codec.h> 530+#include <vlc_filter.h> 531 #include <vlc_threads.h> 532 533-#include <bcm_host.h> 534 #include <interface/mmal/mmal.h> 535 #include <interface/mmal/util/mmal_util.h> 536 #include <interface/mmal/util/mmal_default_components.h> 537 538+#include <interface/vcsm/user-vcsm.h> 539+ 540+#include "mmal_cma.h" 541 #include "mmal_picture.h" 542 543+#include "subpic.h" 544+#include "blend_rgba_neon.h" 545+ 546+#define TRACE_ALL 0 547+ 548+#define OPT_TO_FROM_ZC 0 549+ 550 /* 551 * This seems to be a bit high, but reducing it causes instabilities 552 */ 553 #define NUM_EXTRA_BUFFERS 5 554+//#define NUM_EXTRA_BUFFERS 10 555 #define NUM_DECODER_BUFFER_HEADERS 30 556 557-#define MIN_NUM_BUFFERS_IN_TRANSIT 2 558+#define CONVERTER_BUFFERS 4 // Buffers on the output of the converter 559+ 560+#define MMAL_SLICE_HEIGHT 16 561+#define MMAL_ALIGN_W 32 562+#define MMAL_ALIGN_H 16 563 564 #define MMAL_OPAQUE_NAME "mmal-opaque" 565 #define MMAL_OPAQUE_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.") 566 #define MMAL_OPAQUE_LONGTEXT N_("Decode frames directly into RPI VideoCore instead of host memory. This option must only be used with the MMAL video output plugin.") 567 568-static int OpenDecoder(decoder_t *dec); 569-static void CloseDecoder(decoder_t *dec); 570- 571-vlc_module_begin() 572- set_shortname(N_("MMAL decoder")) 573- set_description(N_("MMAL-based decoder plugin for Raspberry Pi")) 574- set_capability("video decoder", 90) 575- add_shortcut("mmal_decoder") 576- add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false) 577- set_callbacks(OpenDecoder, CloseDecoder) 578-vlc_module_end() 579+#define MMAL_RESIZE_NAME "mmal-resize" 580+#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.") 581+#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.") 582+ 583+#define MMAL_ISP_NAME "mmal-isp" 584+#define MMAL_ISP_TEXT N_("Use mmal isp rather than hvs.") 585+#define MMAL_ISP_LONGTEXT N_("Use mmal isp rather than hvs. This may be faster but has no blend.") 586 587-struct decoder_sys_t { 588- bool opaque; 589+typedef struct decoder_sys_t 590+{ 591 MMAL_COMPONENT_T *component; 592 MMAL_PORT_T *input; 593 MMAL_POOL_T *input_pool; 594 MMAL_PORT_T *output; 595- MMAL_POOL_T *output_pool; /* only used for non-opaque mode */ 596+ hw_mmal_port_pool_ref_t *ppr; 597 MMAL_ES_FORMAT_T *output_format; 598- vlc_sem_t sem; 599 600+ MMAL_STATUS_T err_stream; 601 bool b_top_field_first; 602 bool b_progressive; 603 604+ bool b_flushed; 605+ 606+ vcsm_init_type_t vcsm_init_type; 607+ 608+ // Lock to avoid pic update & allocate happenening simultainiously 609+ // * We should be able to arrange life s.t. this isn't needed 610+ // but while we are confused apply belt & braces 611+ vlc_mutex_t pic_lock; 612+ 613 /* statistics */ 614- int output_in_transit; 615- int input_in_transit; 616 atomic_bool started; 617-}; 618+} decoder_sys_t; 619 620-/* Utilities */ 621-static int change_output_format(decoder_t *dec); 622-static int send_output_buffer(decoder_t *dec); 623-static void fill_output_port(decoder_t *dec); 624- 625-/* VLC decoder callback */ 626-static int decode(decoder_t *dec, block_t *block); 627-static void flush_decoder(decoder_t *dec); 628- 629-/* MMAL callbacks */ 630-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); 631-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); 632-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); 633 634-static int OpenDecoder(decoder_t *dec) 635-{ 636- int ret = VLC_SUCCESS; 637- decoder_sys_t *sys; 638- MMAL_PARAMETER_UINT32_T extra_buffers; 639- MMAL_STATUS_T status; 640+typedef struct supported_mmal_enc_s { 641+ struct { 642+ MMAL_PARAMETER_HEADER_T header; 643+ MMAL_FOURCC_T encodings[64]; 644+ } supported; 645+ int n; 646+} supported_mmal_enc_t; 647+ 648+#define SUPPORTED_MMAL_ENC_INIT \ 649+{ \ 650+ {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, \ 651+ -1 \ 652+} 653 654- if (dec->fmt_in.i_codec != VLC_CODEC_MPGV && 655- dec->fmt_in.i_codec != VLC_CODEC_H264) 656- return VLC_EGENERIC; 657+static supported_mmal_enc_t supported_decode_in_enc = SUPPORTED_MMAL_ENC_INIT; 658 659- sys = calloc(1, sizeof(decoder_sys_t)); 660- if (!sys) { 661- ret = VLC_ENOMEM; 662- goto out; 663+static bool is_enc_supported(supported_mmal_enc_t * const support, const MMAL_FOURCC_T fcc) 664+{ 665+ int i; 666+ 667+ if (fcc == 0) 668+ return false; 669+ if (support->n == -1) 670+ return true; // Unknown - say OK 671+ for (i = 0; i < support->n; ++i) { 672+ if (support->supported.encodings[i] == fcc) 673+ return true; 674 } 675- dec->p_sys = sys; 676+ return false; 677+} 678 679- sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME); 680- bcm_host_init(); 681+static bool set_and_test_enc_supported(supported_mmal_enc_t * const support, MMAL_PORT_T * port, const MMAL_FOURCC_T fcc) 682+{ 683+ if (support->n >= 0) 684+ /* already done */; 685+ else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&support->supported) != MMAL_SUCCESS) 686+ support->n = 0; 687+ else 688+ support->n = (support->supported.header.size - sizeof(support->supported.header)) / 689+ sizeof(support->supported.encodings[0]); 690 691- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component); 692- if (status != MMAL_SUCCESS) { 693- msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)", 694- MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status)); 695- ret = VLC_EGENERIC; 696- goto out; 697- } 698+ return is_enc_supported(support, fcc); 699+} 700 701- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec; 702- status = mmal_port_enable(sys->component->control, control_port_cb); 703- if (status != MMAL_SUCCESS) { 704- msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)", 705- sys->component->control->name, status, mmal_status_to_string(status)); 706- ret = VLC_EGENERIC; 707- goto out; 708+static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc) 709+{ 710+ switch (fcc){ 711+ case VLC_CODEC_MJPG: 712+ return MMAL_ENCODING_MJPEG; 713+ case VLC_CODEC_MP1V: 714+ return MMAL_ENCODING_MP1V; 715+ case VLC_CODEC_MPGV: 716+ case VLC_CODEC_MP2V: 717+ return MMAL_ENCODING_MP2V; 718+ case VLC_CODEC_H263: 719+ return MMAL_ENCODING_H263; 720+ case VLC_CODEC_MP4V: 721+ return MMAL_ENCODING_MP4V; 722+ case VLC_CODEC_H264: 723+ return MMAL_ENCODING_H264; 724+ case VLC_CODEC_VP6: 725+ return MMAL_ENCODING_VP6; 726+ case VLC_CODEC_VP8: 727+ return MMAL_ENCODING_VP8; 728+ case VLC_CODEC_WMV1: 729+ return MMAL_ENCODING_WMV1; 730+ case VLC_CODEC_WMV2: 731+ return MMAL_ENCODING_WMV2; 732+ case VLC_CODEC_WMV3: 733+ return MMAL_ENCODING_WMV3; 734+ case VLC_CODEC_VC1: 735+ return MMAL_ENCODING_WVC1; 736+ case VLC_CODEC_THEORA: 737+ return MMAL_ENCODING_THEORA; 738+ default: 739+ break; 740 } 741+ return 0; 742+} 743 744- sys->input = sys->component->input[0]; 745- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec; 746- if (dec->fmt_in.i_codec == VLC_CODEC_MPGV) 747- sys->input->format->encoding = MMAL_ENCODING_MP2V; 748- else 749- sys->input->format->encoding = MMAL_ENCODING_H264; 750+static MMAL_FOURCC_T pic_to_slice_mmal_fourcc(const MMAL_FOURCC_T fcc) 751+{ 752+ switch (fcc){ 753+ case MMAL_ENCODING_I420: 754+ return MMAL_ENCODING_I420_SLICE; 755+ case MMAL_ENCODING_I422: 756+ return MMAL_ENCODING_I422_SLICE; 757+ case MMAL_ENCODING_ARGB: 758+ return MMAL_ENCODING_ARGB_SLICE; 759+ case MMAL_ENCODING_RGBA: 760+ return MMAL_ENCODING_RGBA_SLICE; 761+ case MMAL_ENCODING_ABGR: 762+ return MMAL_ENCODING_ABGR_SLICE; 763+ case MMAL_ENCODING_BGRA: 764+ return MMAL_ENCODING_BGRA_SLICE; 765+ case MMAL_ENCODING_RGB16: 766+ return MMAL_ENCODING_RGB16_SLICE; 767+ case MMAL_ENCODING_RGB24: 768+ return MMAL_ENCODING_RGB24_SLICE; 769+ case MMAL_ENCODING_RGB32: 770+ return MMAL_ENCODING_RGB32_SLICE; 771+ case MMAL_ENCODING_BGR16: 772+ return MMAL_ENCODING_BGR16_SLICE; 773+ case MMAL_ENCODING_BGR24: 774+ return MMAL_ENCODING_BGR24_SLICE; 775+ case MMAL_ENCODING_BGR32: 776+ return MMAL_ENCODING_BGR32_SLICE; 777+ default: 778+ break; 779+ } 780+ return 0; 781+} 782 783- if (dec->fmt_in.i_codec == VLC_CODEC_H264) { 784- if (dec->fmt_in.i_extra > 0) { 785- status = mmal_format_extradata_alloc(sys->input->format, 786- dec->fmt_in.i_extra); 787- if (status == MMAL_SUCCESS) { 788- memcpy(sys->input->format->extradata, dec->fmt_in.p_extra, 789- dec->fmt_in.i_extra); 790- sys->input->format->extradata_size = dec->fmt_in.i_extra; 791- } else { 792- msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)", 793- sys->input->name, status, mmal_status_to_string(status)); 794- } 795+#define DEBUG_SQUARES 0 796+#if DEBUG_SQUARES 797+static void draw_square(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t val) 798+{ 799+ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x; 800+ unsigned int i; 801+ for (i = 0; i != h; ++i) { 802+ unsigned int j; 803+ for (j = 0; j != w; ++j) { 804+ p[j] = val; 805 } 806+ p += pic_stride; 807 } 808+} 809+#endif 810 811- status = mmal_port_format_commit(sys->input); 812- if (status != MMAL_SUCCESS) { 813- msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)", 814- sys->input->name, status, mmal_status_to_string(status)); 815- ret = VLC_EGENERIC; 816- goto out; 817+#if 0 818+static inline void draw_line(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int len, int inc) 819+{ 820+ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x; 821+ while (len-- != 0) { 822+ *p = ~0U; 823+ p += inc; 824 } 825- sys->input->buffer_size = sys->input->buffer_size_recommended; 826- sys->input->buffer_num = sys->input->buffer_num_recommended; 827+} 828 829- status = mmal_port_enable(sys->input, input_port_cb); 830- if (status != MMAL_SUCCESS) { 831- msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)", 832- sys->input->name, status, mmal_status_to_string(status)); 833- ret = VLC_EGENERIC; 834- goto out; 835- } 836 837- sys->output = sys->component->output[0]; 838- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec; 839+static void draw_corners(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h) 840+{ 841+ const unsigned int len = 20; 842+ draw_line(pic_buf, pic_stride, x, y, len, 1); 843+ draw_line(pic_buf, pic_stride, x, y, len, pic_stride); 844+ draw_line(pic_buf, pic_stride, x + w - 1, y, len, -1); 845+ draw_line(pic_buf, pic_stride, x + w - 1, y, len, pic_stride); 846+ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -1); 847+ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -(int)pic_stride); 848+ draw_line(pic_buf, pic_stride, x, y + h - 1, len, 1); 849+ draw_line(pic_buf, pic_stride, x, y + h - 1, len, -(int)pic_stride); 850+} 851+#endif 852 853- if (sys->opaque) { 854- extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS; 855- extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T); 856- extra_buffers.value = NUM_EXTRA_BUFFERS; 857- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr); 858- if (status != MMAL_SUCCESS) { 859- msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)", 860- status, mmal_status_to_string(status)); 861- ret = VLC_EGENERIC; 862- goto out; 863- } 864+static MMAL_RATIONAL_T 865+rationalize_sar(unsigned int num, unsigned int den) 866+{ 867+ static const unsigned int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 0}; 868+ const unsigned int * p = primes; 869 870- msg_Dbg(dec, "Activate zero-copy for output port"); 871- MMAL_PARAMETER_BOOLEAN_T zero_copy = { 872- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, 873- 1 874- }; 875+ // If either num or den is 0 then return a well formed "unknown" 876+ if (num == 0 || den == 0) { 877+ return (MMAL_RATIONAL_T){.num = 0, .den = 0}; 878+ } 879 880- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr); 881- if (status != MMAL_SUCCESS) { 882- msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", 883- sys->output->name, status, mmal_status_to_string(status)); 884- goto out; 885+ while (*p != 0 && num >= *p && den >= *p) { 886+ if (num % *p != 0 || den % *p != 0) 887+ ++p; 888+ else { 889+ num /= *p; 890+ den /= *p; 891 } 892 } 893+ return (MMAL_RATIONAL_T){.num = num, .den = den}; 894+} 895 896- status = mmal_port_enable(sys->output, output_port_cb); 897- if (status != MMAL_SUCCESS) { 898- msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)", 899- sys->output->name, status, mmal_status_to_string(status)); 900- ret = VLC_EGENERIC; 901- goto out; 902- } 903+// Buffer either attached to pic or released 904+static picture_t * alloc_opaque_pic(decoder_t * const dec, MMAL_BUFFER_HEADER_T * const buf) 905+{ 906+ decoder_sys_t *const dec_sys = dec->p_sys; 907 908- status = mmal_component_enable(sys->component); 909- if (status != MMAL_SUCCESS) { 910- msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)", 911- sys->component->name, status, mmal_status_to_string(status)); 912- ret = VLC_EGENERIC; 913- goto out; 914+ vlc_mutex_lock(&dec_sys->pic_lock); 915+ picture_t * const pic = decoder_NewPicture(dec); 916+ vlc_mutex_unlock(&dec_sys->pic_lock); 917+ 918+ if (pic == NULL) 919+ goto fail1; 920+ 921+ if (buf->length == 0) { 922+ msg_Err(dec, "%s: Empty buffer", __func__); 923+ goto fail2; 924 } 925 926- sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0); 927+ if ((pic->context = hw_mmal_gen_context(buf, dec_sys->ppr)) == NULL) 928+ goto fail2; 929 930- if (sys->opaque) { 931- dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE; 932- dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE; 933- } else { 934- dec->fmt_out.i_codec = VLC_CODEC_I420; 935- dec->fmt_out.video.i_chroma = VLC_CODEC_I420; 936+ buf_to_pic_copy_props(pic, buf); 937+ 938+#if TRACE_ALL 939+ msg_Dbg(dec, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date); 940+#endif 941+ 942+ return pic; 943+ 944+fail2: 945+ picture_Release(pic); 946+fail1: 947+ // Recycle rather than release to avoid buffer starvation if NewPic fails 948+ hw_mmal_port_pool_ref_recycle(dec_sys->ppr, buf); 949+ return NULL; 950+} 951+ 952+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 953+{ 954+ decoder_t *dec = (decoder_t *)port->userdata; 955+ MMAL_STATUS_T status; 956+ 957+#if TRACE_ALL 958+ msg_Dbg(dec, "<<< %s: cmd=%d, data=%p", __func__, buffer->cmd, buffer->data); 959+#endif 960+ 961+ if (buffer->cmd == MMAL_EVENT_ERROR) { 962+ status = *(uint32_t *)buffer->data; 963+ dec->p_sys->err_stream = status; 964+ msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status, 965+ mmal_status_to_string(status)); 966 } 967 968- dec->pf_decode = decode; 969- dec->pf_flush = flush_decoder; 970+ mmal_buffer_header_release(buffer); 971+} 972 973- vlc_sem_init(&sys->sem, 0); 974+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 975+{ 976+ block_t * const block = (block_t *)buffer->user_data; 977 978-out: 979- if (ret != VLC_SUCCESS) 980- CloseDecoder(dec); 981+ (void)port; // Unused 982 983- return ret; 984+#if TRACE_ALL 985+ msg_Dbg((decoder_t *)port->userdata, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__, 986+ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts); 987+#endif 988+ 989+ mmal_buffer_header_reset(buffer); 990+ mmal_buffer_header_release(buffer); 991+ 992+ if (block != NULL) 993+ block_Release(block); 994 } 995 996-static void CloseDecoder(decoder_t *dec) 997+static void decoder_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 998 { 999- decoder_sys_t *sys = dec->p_sys; 1000- MMAL_BUFFER_HEADER_T *buffer; 1001+ decoder_t * const dec = (decoder_t *)port->userdata; 1002 1003- if (!sys) 1004+ if (buffer->cmd == 0 && buffer->length != 0) 1005+ { 1006+#if TRACE_ALL 1007+ msg_Dbg(dec, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__, 1008+ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts); 1009+#endif 1010+ 1011+ picture_t *pic = alloc_opaque_pic(dec, buffer); 1012+#if TRACE_ALL 1013+ msg_Dbg(dec, "flags=%#x, video flags=%#x", buffer->flags, buffer->type->video.flags); 1014+#endif 1015+ if (pic == NULL) 1016+ msg_Err(dec, "Failed to allocate new picture"); 1017+ else 1018+ decoder_QueueVideo(dec, pic); 1019+ // Buffer released or attached to pic - do not release again 1020 return; 1021+ } 1022 1023- if (sys->component && sys->component->control->is_enabled) 1024- mmal_port_disable(sys->component->control); 1025+ if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) 1026+ { 1027+ decoder_sys_t * const sys = dec->p_sys; 1028+ MMAL_EVENT_FORMAT_CHANGED_T * const fmt = mmal_event_format_changed_get(buffer); 1029+ MMAL_ES_FORMAT_T * const format = mmal_format_alloc(); 1030 1031- if (sys->input && sys->input->is_enabled) 1032- mmal_port_disable(sys->input); 1033+ if (format == NULL) 1034+ msg_Err(dec, "Failed to allocate new format"); 1035+ else 1036+ { 1037+ mmal_format_full_copy(format, fmt->format); 1038+ format->encoding = MMAL_ENCODING_OPAQUE; 1039 1040- if (sys->output && sys->output->is_enabled) 1041- mmal_port_disable(sys->output); 1042+ // If no PAR in the stream - see if we've got one from the demux 1043+ if (format->es->video.par.den <= 0 || format->es->video.par.num <= 0) { 1044+ unsigned int n = dec->fmt_in.video.i_sar_num; 1045+ unsigned int d = dec->fmt_in.video.i_sar_den; 1046+ 1047+ if (n == 0 || d == 0) { 1048+ // Guesswork required 1049+ const unsigned int w = format->es->video.width; 1050+ const unsigned int h = format->es->video.height; 1051+ if ((w == 704 || w == 720) && (h == 480 || h == 576)) { 1052+ // Very likely SD 4:3 1053+ n = w * 3; 1054+ d = h * 4; 1055+ } 1056+ else 1057+ { 1058+ // Otherwise guess SAR 1:1 1059+ n = 1; 1060+ d = 1; 1061+ } 1062+ } 1063 1064- if (sys->component && sys->component->is_enabled) 1065- mmal_component_disable(sys->component); 1066+ format->es->video.par = rationalize_sar(n, d); 1067+ } 1068 1069- if (sys->input_pool) 1070- mmal_pool_destroy(sys->input_pool); 1071+ if (sys->output_format != NULL) 1072+ mmal_format_free(sys->output_format); 1073 1074- if (sys->output_format) 1075- mmal_format_free(sys->output_format); 1076+ sys->output_format = format; 1077+ } 1078+ } 1079+ else if (buffer->cmd != 0) { 1080+ char buf0[5]; 1081+ msg_Warn(dec, "Unexpected output cb event: %s", str_fourcc(buf0, buffer->cmd)); 1082+ } 1083 1084- if (sys->output_pool) 1085- mmal_pool_destroy(sys->output_pool); 1086+ // If we get here then we were flushing (cmd == 0 && len == 0) or 1087+ // that was an EVENT - in either case we want to release the buffer 1088+ // back to its pool rather than recycle it. 1089+ mmal_buffer_header_reset(buffer); 1090+ buffer->user_data = NULL; 1091+ mmal_buffer_header_release(buffer); 1092+} 1093 1094- if (sys->component) 1095- mmal_component_release(sys->component); 1096 1097- vlc_sem_destroy(&sys->sem); 1098- free(sys); 1099 1100- bcm_host_deinit(); 1101+static void fill_output_port(decoder_t *dec) 1102+{ 1103+ decoder_sys_t *sys = dec->p_sys; 1104+ 1105+ if (decoder_UpdateVideoFormat(dec) != 0) 1106+ { 1107+ // If we have a new format don't bother stuffing the buffer 1108+ // We should get a reset RSN 1109+#if TRACE_ALL 1110+ msg_Dbg(dec, "%s: Updated", __func__); 1111+#endif 1112+ 1113+ return; 1114+ } 1115+ 1116+ hw_mmal_port_pool_ref_fill(sys->ppr); 1117+ return; 1118 } 1119 1120 static int change_output_format(decoder_t *dec) 1121 { 1122 MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type; 1123- decoder_sys_t *sys = dec->p_sys; 1124+ decoder_sys_t * const sys = dec->p_sys; 1125 MMAL_STATUS_T status; 1126- int pool_size; 1127 int ret = 0; 1128 1129+#if TRACE_ALL 1130+ msg_Dbg(dec, "%s: <<<", __func__); 1131+#endif 1132+ 1133 if (atomic_load(&sys->started)) { 1134 mmal_format_full_copy(sys->output->format, sys->output_format); 1135 status = mmal_port_format_commit(sys->output); 1136@@ -300,7 +476,9 @@ static int change_output_format(decoder_ 1137 } 1138 1139 port_reset: 1140+#if TRACE_ALL 1141 msg_Dbg(dec, "%s: Do full port reset", __func__); 1142+#endif 1143 status = mmal_port_disable(sys->output); 1144 if (status != MMAL_SUCCESS) { 1145 msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)", 1146@@ -310,6 +488,7 @@ port_reset: 1147 } 1148 1149 mmal_format_full_copy(sys->output->format, sys->output_format); 1150+ 1151 status = mmal_port_format_commit(sys->output); 1152 if (status != MMAL_SUCCESS) { 1153 msg_Err(dec, "Failed to commit output format (status=%"PRIx32" %s)", 1154@@ -318,18 +497,10 @@ port_reset: 1155 goto out; 1156 } 1157 1158- if (sys->opaque) { 1159- sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS; 1160- pool_size = NUM_DECODER_BUFFER_HEADERS; 1161- } else { 1162- sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended, 1163- MIN_NUM_BUFFERS_IN_TRANSIT); 1164- pool_size = sys->output->buffer_num; 1165- } 1166- 1167+ sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS; 1168 sys->output->buffer_size = sys->output->buffer_size_recommended; 1169 1170- status = mmal_port_enable(sys->output, output_port_cb); 1171+ status = mmal_port_enable(sys->output, decoder_output_cb); 1172 if (status != MMAL_SUCCESS) { 1173 msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)", 1174 status, mmal_status_to_string(status)); 1175@@ -338,25 +509,14 @@ port_reset: 1176 } 1177 1178 if (!atomic_load(&sys->started)) { 1179- if (!sys->opaque) { 1180- sys->output_pool = mmal_port_pool_create(sys->output, pool_size, 0); 1181- msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num); 1182- } 1183- 1184 atomic_store(&sys->started, true); 1185 1186 /* we need one picture from vout for each buffer header on the output 1187 * port */ 1188- dec->i_extra_picture_buffers = pool_size; 1189- 1190- /* remove what VLC core reserves as it is part of the pool_size 1191- * already */ 1192- if (dec->fmt_in.i_codec == VLC_CODEC_H264) 1193- dec->i_extra_picture_buffers -= 19; 1194- else 1195- dec->i_extra_picture_buffers -= 3; 1196- 1197+ dec->i_extra_picture_buffers = 10; 1198+#if TRACE_ALL 1199 msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers); 1200+#endif 1201 } 1202 1203 apply_fmt: 1204@@ -366,8 +526,8 @@ apply_fmt: 1205 dec->fmt_out.video.i_y_offset = sys->output->format->es->video.crop.y; 1206 dec->fmt_out.video.i_visible_width = sys->output->format->es->video.crop.width; 1207 dec->fmt_out.video.i_visible_height = sys->output->format->es->video.crop.height; 1208- dec->fmt_out.video.i_sar_num = sys->output->format->es->video.par.num; 1209- dec->fmt_out.video.i_sar_den = sys->output->format->es->video.par.den; 1210+ dec->fmt_out.video.i_sar_num = sys->output_format->es->video.par.num; // SAR can be killed by commit 1211+ dec->fmt_out.video.i_sar_den = sys->output_format->es->video.par.den; 1212 dec->fmt_out.video.i_frame_rate = sys->output->format->es->video.frame_rate.num; 1213 dec->fmt_out.video.i_frame_rate_base = sys->output->format->es->video.frame_rate.den; 1214 1215@@ -382,12 +542,19 @@ apply_fmt: 1216 sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive); 1217 sys->b_top_field_first = sys->b_progressive ? true : 1218 (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst); 1219+#if TRACE_ALL 1220 msg_Dbg(dec, "Detected %s%s video (%d)", 1221 sys->b_progressive ? "progressive" : "interlaced", 1222 sys->b_progressive ? "" : (sys->b_top_field_first ? " tff" : " bff"), 1223 interlace_type.eMode); 1224+#endif 1225 } 1226 1227+ // Tell the rest of the world we have changed format 1228+ vlc_mutex_lock(&sys->pic_lock); 1229+ ret = decoder_UpdateVideoFormat(dec); 1230+ vlc_mutex_unlock(&sys->pic_lock); 1231+ 1232 out: 1233 mmal_format_free(sys->output_format); 1234 sys->output_format = NULL; 1235@@ -395,144 +562,85 @@ out: 1236 return ret; 1237 } 1238 1239-static int send_output_buffer(decoder_t *dec) 1240+static MMAL_STATUS_T 1241+set_extradata_and_commit(decoder_t * const dec, decoder_sys_t * const sys) 1242 { 1243- decoder_sys_t *sys = dec->p_sys; 1244- MMAL_BUFFER_HEADER_T *buffer; 1245- picture_sys_t *p_sys; 1246- picture_t *picture = NULL; 1247 MMAL_STATUS_T status; 1248- unsigned buffer_size = 0; 1249- int ret = 0; 1250 1251- if (!sys->output->is_enabled) 1252- return VLC_EGENERIC; 1253- 1254- /* If local output pool is allocated, use it - this is only the case for 1255- * non-opaque modes */ 1256- if (sys->output_pool) { 1257- buffer = mmal_queue_get(sys->output_pool->queue); 1258- if (!buffer) { 1259- msg_Warn(dec, "Failed to get new buffer"); 1260- return VLC_EGENERIC; 1261- } 1262- } 1263- 1264- if (!decoder_UpdateVideoFormat(dec)) 1265- picture = decoder_NewPicture(dec); 1266- if (!picture) { 1267- msg_Warn(dec, "Failed to get new picture"); 1268- ret = -1; 1269- goto err; 1270- } 1271- 1272- p_sys = picture->p_sys; 1273- for (int i = 0; i < picture->i_planes; i++) 1274- buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch; 1275- 1276- if (sys->output_pool) { 1277- mmal_buffer_header_reset(buffer); 1278- buffer->alloc_size = sys->output->buffer_size; 1279- if (buffer_size < sys->output->buffer_size) { 1280- msg_Err(dec, "Retrieved picture with too small data block (%d < %d)", 1281- buffer_size, sys->output->buffer_size); 1282- ret = VLC_EGENERIC; 1283- goto err; 1284- } 1285- 1286- if (!sys->opaque) 1287- buffer->data = picture->p[0].p_pixels; 1288- } else { 1289- buffer = p_sys->buffer; 1290- if (!buffer) { 1291- msg_Warn(dec, "Picture has no buffer attached"); 1292- picture_Release(picture); 1293- return VLC_EGENERIC; 1294- } 1295- buffer->data = p_sys->buffer->data; 1296- } 1297- buffer->user_data = picture; 1298- buffer->cmd = 0; 1299- 1300- status = mmal_port_send_buffer(sys->output, buffer); 1301+ status = mmal_port_format_commit(sys->input); 1302 if (status != MMAL_SUCCESS) { 1303- msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)", 1304- status, mmal_status_to_string(status)); 1305- ret = -1; 1306- goto err; 1307- } 1308- atomic_fetch_add(&sys->output_in_transit, 1); 1309- 1310- return ret; 1311- 1312-err: 1313- if (picture) 1314- picture_Release(picture); 1315- if (sys->output_pool && buffer) { 1316- buffer->data = NULL; 1317- mmal_buffer_header_release(buffer); 1318+ msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)", 1319+ sys->input->name, status, mmal_status_to_string(status)); 1320 } 1321- return ret; 1322+ return status; 1323 } 1324 1325-static void fill_output_port(decoder_t *dec) 1326+static MMAL_STATUS_T decoder_send_extradata(decoder_t * const dec, decoder_sys_t *const sys) 1327 { 1328- decoder_sys_t *sys = dec->p_sys; 1329- 1330- unsigned max_buffers_in_transit = 0; 1331- int buffers_available = 0; 1332- int buffers_to_send = 0; 1333- int i; 1334+ if (dec->fmt_in.i_codec == VLC_CODEC_H264 && 1335+ dec->fmt_in.i_extra > 0) 1336+ { 1337+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->input_pool->queue); 1338+ MMAL_STATUS_T status; 1339+ 1340+ mmal_buffer_header_reset(buf); 1341+ buf->cmd = 0; 1342+ buf->user_data = NULL; 1343+ buf->alloc_size = sys->input->buffer_size; 1344+ buf->length = dec->fmt_in.i_extra; 1345+ buf->data = dec->fmt_in.p_extra; 1346+ buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG; 1347 1348- if (sys->output_pool) { 1349- max_buffers_in_transit = __MAX(sys->output_pool->headers_num, 1350- MIN_NUM_BUFFERS_IN_TRANSIT); 1351- buffers_available = mmal_queue_length(sys->output_pool->queue); 1352- } else { 1353- max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS; 1354- buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit); 1355+ status = mmal_port_send_buffer(sys->input, buf); 1356+ if (status != MMAL_SUCCESS) { 1357+ msg_Err(dec, "Failed to send extradata buffer to input port (status=%"PRIx32" %s)", 1358+ status, mmal_status_to_string(status)); 1359+ return status; 1360+ } 1361 } 1362- buffers_to_send = max_buffers_in_transit - atomic_load(&sys->output_in_transit); 1363 1364- if (buffers_to_send > buffers_available) 1365- buffers_to_send = buffers_available; 1366- 1367-#ifndef NDEBUG 1368- msg_Dbg(dec, "Send %d buffers to output port (available: %d, " 1369- "in_transit: %d, buffer_num: %d)", 1370- buffers_to_send, buffers_available, 1371- atomic_load(&sys->output_in_transit), 1372- sys->output->buffer_num); 1373-#endif 1374- for (i = 0; i < buffers_to_send; ++i) 1375- if (send_output_buffer(dec) < 0) 1376- break; 1377+ return MMAL_SUCCESS; 1378 } 1379 1380 static void flush_decoder(decoder_t *dec) 1381 { 1382- decoder_sys_t *sys = dec->p_sys; 1383- MMAL_BUFFER_HEADER_T *buffer; 1384- MMAL_STATUS_T status; 1385+ decoder_sys_t *const sys = dec->p_sys; 1386 1387- msg_Dbg(dec, "Flushing decoder ports..."); 1388- mmal_port_flush(sys->output); 1389- mmal_port_flush(sys->input); 1390- 1391- while (atomic_load(&sys->output_in_transit) || 1392- atomic_load(&sys->input_in_transit)) 1393- vlc_sem_wait(&sys->sem); 1394+#if TRACE_ALL 1395+ msg_Dbg(dec, "%s: <<<", __func__); 1396+#endif 1397+ 1398+ if (!sys->b_flushed) { 1399+ mmal_port_disable(sys->input); 1400+ mmal_port_disable(sys->output); 1401+ // We can leave the input disabled, but we want the output enabled 1402+ // in order to sink any buffers returning from other modules 1403+ mmal_port_enable(sys->output, decoder_output_cb); 1404+ sys->b_flushed = true; 1405+ } 1406+#if TRACE_ALL 1407+ msg_Dbg(dec, "%s: >>>", __func__); 1408+#endif 1409 } 1410 1411 static int decode(decoder_t *dec, block_t *block) 1412 { 1413 decoder_sys_t *sys = dec->p_sys; 1414 MMAL_BUFFER_HEADER_T *buffer; 1415- bool need_flush = false; 1416 uint32_t len; 1417- uint32_t flags = 0; 1418+ uint32_t flags = MMAL_BUFFER_HEADER_FLAG_FRAME_START; 1419 MMAL_STATUS_T status; 1420 1421+#if TRACE_ALL 1422+ msg_Dbg(dec, "<<< %s: %lld/%lld", __func__, block == NULL ? -1LL : block->i_dts, block == NULL ? -1LL : block->i_pts); 1423+#endif 1424+ 1425+ if (sys->err_stream != MMAL_SUCCESS) { 1426+ msg_Err(dec, "MMAL error reported by ctrl"); 1427+ flush_decoder(dec); 1428+ return VLCDEC_ECRITICAL; /// I think they are all fatal 1429+ } 1430+ 1431 /* 1432 * Configure output port if necessary 1433 */ 1434@@ -541,18 +649,50 @@ static int decode(decoder_t *dec, block_ 1435 msg_Err(dec, "Failed to change output port format"); 1436 } 1437 1438- if (!block) 1439- goto out; 1440+ if (block == NULL) 1441+ return VLCDEC_SUCCESS; 1442 1443 /* 1444 * Check whether full flush is required 1445 */ 1446- if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) { 1447+ if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) { 1448+#if TRACE_ALL 1449+ msg_Dbg(dec, "%s: >>> Discontinuity", __func__); 1450+#endif 1451 flush_decoder(dec); 1452+ } 1453+ 1454+ if (block->i_buffer == 0) 1455+ { 1456 block_Release(block); 1457 return VLCDEC_SUCCESS; 1458 } 1459 1460+ // Reenable stuff if the last thing we did was flush 1461+ if (!sys->output->is_enabled && 1462+ (status = mmal_port_enable(sys->output, decoder_output_cb)) != MMAL_SUCCESS) 1463+ { 1464+ msg_Err(dec, "Output port enable failed"); 1465+ goto fail; 1466+ } 1467+ 1468+ if (!sys->input->is_enabled) 1469+ { 1470+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS) 1471+ goto fail; 1472+ 1473+ if ((status = mmal_port_enable(sys->input, input_port_cb)) != MMAL_SUCCESS) 1474+ { 1475+ msg_Err(dec, "Input port enable failed"); 1476+ goto fail; 1477+ } 1478+ 1479+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS) 1480+ goto fail; 1481+ } 1482+ 1483+ // *** We cannot get a picture to put the result in 'till we have 1484+ // reported the size & the output stages have been set up 1485 if (atomic_load(&sys->started)) 1486 fill_output_port(dec); 1487 1488@@ -563,18 +703,21 @@ static int decode(decoder_t *dec, block_ 1489 if (block->i_flags & BLOCK_FLAG_CORRUPTED) 1490 flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED; 1491 1492- while (block && block->i_buffer > 0) { 1493- buffer = mmal_queue_timedwait(sys->input_pool->queue, 100); 1494+ while (block != NULL) 1495+ { 1496+ buffer = mmal_queue_wait(sys->input_pool->queue); 1497 if (!buffer) { 1498 msg_Err(dec, "Failed to retrieve buffer header for input data"); 1499- need_flush = true; 1500- break; 1501+ goto fail; 1502 } 1503+ 1504 mmal_buffer_header_reset(buffer); 1505 buffer->cmd = 0; 1506- buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts; 1507+ buffer->pts = block->i_pts != VLC_TICK_INVALID ? block->i_pts : 1508+ block->i_dts != VLC_TICK_INVALID ? block->i_dts : MMAL_TIME_UNKNOWN; 1509 buffer->dts = block->i_dts; 1510 buffer->alloc_size = sys->input->buffer_size; 1511+ buffer->user_data = NULL; 1512 1513 len = block->i_buffer; 1514 if (len > buffer->alloc_size) 1515@@ -585,94 +728,1808 @@ static int decode(decoder_t *dec, block_ 1516 block->i_buffer -= len; 1517 buffer->length = len; 1518 if (block->i_buffer == 0) { 1519+ flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; 1520+ if (block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE) { 1521+ msg_Dbg(dec, "EOS sent"); 1522+ flags |= MMAL_BUFFER_HEADER_FLAG_EOS; 1523+ } 1524 buffer->user_data = block; 1525 block = NULL; 1526 } 1527 buffer->flags = flags; 1528 1529+#if TRACE_ALL 1530+ msg_Dbg(dec, "%s: -- Send buffer: cmd=%d, data=%p, size=%d, len=%d, offset=%d, flags=%#x, pts=%lld, dts=%lld", __func__,\ 1531+ buffer->cmd, buffer->data, buffer->alloc_size, buffer->length, buffer->offset, 1532+ buffer->flags, (long long)buffer->pts, (long long)buffer->dts); 1533+#endif 1534 status = mmal_port_send_buffer(sys->input, buffer); 1535 if (status != MMAL_SUCCESS) { 1536 msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)", 1537 status, mmal_status_to_string(status)); 1538- break; 1539+ goto fail; 1540 } 1541- atomic_fetch_add(&sys->input_in_transit, 1); 1542+ 1543+ // Reset flushed flag once we have sent a buf 1544+ sys->b_flushed = false; 1545+ flags &= ~MMAL_BUFFER_HEADER_FLAG_FRAME_START; 1546 } 1547+ return VLCDEC_SUCCESS; 1548 1549-out: 1550- if (need_flush) 1551- flush_decoder(dec); 1552+fail: 1553+ flush_decoder(dec); 1554+ return VLCDEC_ECRITICAL; 1555 1556- return VLCDEC_SUCCESS; 1557 } 1558 1559-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 1560+ 1561+static void CloseDecoder(decoder_t *dec) 1562 { 1563- decoder_t *dec = (decoder_t *)port->userdata; 1564+ decoder_sys_t *sys = dec->p_sys; 1565+ 1566+#if TRACE_ALL 1567+ msg_Dbg(dec, "%s: <<<", __func__); 1568+#endif 1569+ 1570+ if (!sys) 1571+ return; 1572+ 1573+ if (sys->component != NULL) { 1574+ if (sys->input->is_enabled) 1575+ mmal_port_disable(sys->input); 1576+ 1577+ if (sys->output->is_enabled) 1578+ mmal_port_disable(sys->output); 1579+ 1580+ if (sys->component->control->is_enabled) 1581+ mmal_port_disable(sys->component->control); 1582+ 1583+ if (sys->component->is_enabled) 1584+ mmal_component_disable(sys->component); 1585+ 1586+ mmal_component_release(sys->component); 1587+ } 1588+ 1589+ if (sys->input_pool != NULL) 1590+ mmal_pool_destroy(sys->input_pool); 1591+ 1592+ if (sys->output_format != NULL) 1593+ mmal_format_free(sys->output_format); 1594+ 1595+ hw_mmal_port_pool_ref_release(sys->ppr, false); 1596+ 1597+ cma_vcsm_exit(sys->vcsm_init_type); 1598+ 1599+ vlc_mutex_destroy(&sys->pic_lock); 1600+ free(sys); 1601+} 1602+ 1603+static int OpenDecoder(decoder_t *dec) 1604+{ 1605+ int ret = VLC_EGENERIC; 1606+ decoder_sys_t *sys; 1607 MMAL_STATUS_T status; 1608+ const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec); 1609+ 1610+#if TRACE_ALL || 1 1611+ { 1612+ char buf1[5], buf2[5], buf2a[5]; 1613+ char buf3[5], buf4[5]; 1614+ MMAL_RATIONAL_T r = rationalize_sar(dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den); 1615+ 1616+ msg_Dbg(dec, "%s: <<< (%s/%s)[%s] %dx%d %d/%d=%d/%d o:%#x -> (%s/%s) %dx%d %d/%d o:%#x", __func__, 1617+ str_fourcc(buf1, dec->fmt_in.i_codec), 1618+ str_fourcc(buf2, dec->fmt_in.video.i_chroma), 1619+ str_fourcc(buf2a, in_fcc), 1620+ dec->fmt_in.video.i_width, dec->fmt_in.video.i_height, 1621+ dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den, 1622+ r.num, r.den, 1623+ (int)dec->fmt_in.video.orientation, 1624+ str_fourcc(buf3, dec->fmt_out.i_codec), 1625+ str_fourcc(buf4, dec->fmt_out.video.i_chroma), 1626+ dec->fmt_out.video.i_width, dec->fmt_out.video.i_height, 1627+ dec->fmt_out.video.i_sar_num, dec->fmt_out.video.i_sar_den, 1628+ (int)dec->fmt_out.video.orientation); 1629+ } 1630+#endif 1631+ 1632+ if (!is_enc_supported(&supported_decode_in_enc, in_fcc)) 1633+ return VLC_EGENERIC; 1634+ 1635+ sys = calloc(1, sizeof(decoder_sys_t)); 1636+ if (!sys) { 1637+ ret = VLC_ENOMEM; 1638+ goto fail; 1639+ } 1640+ dec->p_sys = sys; 1641+ vlc_mutex_init(&sys->pic_lock); 1642+ 1643+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { 1644+ msg_Err(dec, "VCSM init failed"); 1645+ goto fail; 1646+ } 1647+ msg_Info(dec, "VCSM init succeeded: %s", cma_vcsm_init_str(sys->vcsm_init_type)); 1648+ 1649+ sys->err_stream = MMAL_SUCCESS; 1650+ 1651+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component); 1652+ if (status != MMAL_SUCCESS) { 1653+ msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)", 1654+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status)); 1655+ goto fail; 1656+ } 1657+ 1658+ sys->input = sys->component->input[0]; 1659+ sys->output = sys->component->output[0]; 1660+ 1661+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec; 1662+ sys->input->format->encoding = in_fcc; 1663+ 1664+ if (!set_and_test_enc_supported(&supported_decode_in_enc, sys->input, in_fcc)) { 1665+#if TRACE_ALL 1666+ char cbuf[5]; 1667+ msg_Dbg(dec, "Format not supported: %s", str_fourcc(cbuf, in_fcc)); 1668+#endif 1669+ goto fail; 1670+ } 1671+ 1672+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec; 1673+ status = mmal_port_enable(sys->component->control, control_port_cb); 1674+ if (status != MMAL_SUCCESS) { 1675+ msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)", 1676+ sys->component->control->name, status, mmal_status_to_string(status)); 1677+ goto fail; 1678+ } 1679+ 1680+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS) 1681+ goto fail; 1682+ 1683+ sys->input->buffer_size = sys->input->buffer_size_recommended; 1684+ sys->input->buffer_num = sys->input->buffer_num_recommended; 1685+ 1686+ status = mmal_port_enable(sys->input, input_port_cb); 1687+ if (status != MMAL_SUCCESS) { 1688+ msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)", 1689+ sys->input->name, status, mmal_status_to_string(status)); 1690+ goto fail; 1691+ } 1692+ 1693+ // Set vanishingly unlikely shape (or at least crop) 1694+ // to ensure that we get a resolution changed event 1695+ // Small wxh are rejected (128x128 is rejected) so pick a 1696+ // plausible size. 1697+ // Crop doesn't seem to be checked for being constrained by wxh 1698+ // so we could place it outside the pic to be sure that it is 1699+ // never matched but stick with something legal in case it is ever 1700+ // actually checked 1701+ sys->output->format->es->video.height = 256; 1702+ sys->output->format->es->video.width = 256; 1703+ sys->output->format->es->video.crop.height = 4; 1704+ sys->output->format->es->video.crop.width = 2; 1705+ sys->output->format->es->video.crop.x = 66; 1706+ sys->output->format->es->video.crop.y = 88; 1707+ 1708+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(dec), &sys->ppr, 1709+ sys->output, NUM_EXTRA_BUFFERS, decoder_output_cb)) != MMAL_SUCCESS) 1710+ goto fail; 1711+ 1712+ status = mmal_component_enable(sys->component); 1713+ if (status != MMAL_SUCCESS) { 1714+ msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)", 1715+ sys->component->name, status, mmal_status_to_string(status)); 1716+ goto fail; 1717+ } 1718+ 1719+ if ((sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) 1720+ { 1721+ msg_Err(dec, "Failed to create input pool"); 1722+ goto fail; 1723+ } 1724+ 1725+ sys->b_flushed = true; 1726+ 1727+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS) 1728+ goto fail; 1729+ 1730+ // Given no better ideas at this point copy input format to output 1731+ // This also copies container stuff (such as orientation) that we do not 1732+ // decode from the ES but may be important to display 1733+ video_format_Copy(&dec->fmt_out.video, &dec->fmt_in.video); 1734+ dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE; 1735+ dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE; 1736+ 1737+ 1738+ dec->pf_decode = decode; 1739+ dec->pf_flush = flush_decoder; 1740+ 1741+#if TRACE_ALL 1742+ msg_Dbg(dec, ">>> %s: ok", __func__); 1743+#endif 1744+ return 0; 1745+ 1746+fail: 1747+ CloseDecoder(dec); 1748+#if TRACE_ALL 1749+msg_Dbg(dec, ">>> %s: FAIL: ret=%d", __func__, ret); 1750+#endif 1751+ return ret; 1752+} 1753+ 1754+// ---------------------------- 1755+ 1756+#define CONV_MAX_LATENCY 1 // In frames 1757+ 1758+typedef struct pic_fifo_s { 1759+ picture_t * head; 1760+ picture_t * tail; 1761+} pic_fifo_t; 1762+ 1763+static inline picture_t * pic_fifo_get(pic_fifo_t * const pf) 1764+{ 1765+ picture_t * const pic = pf->head;; 1766+ if (pic != NULL) { 1767+ pf->head = pic->p_next; 1768+ pic->p_next = NULL; 1769+ } 1770+ return pic; 1771+} 1772+ 1773+static inline picture_t * pic_fifo_get_all(pic_fifo_t * const pf) 1774+{ 1775+ picture_t * const pic = pf->head;; 1776+ pf->head = NULL; 1777+ return pic; 1778+} 1779+ 1780+static inline void pic_fifo_release_all(pic_fifo_t * const pf) 1781+{ 1782+ picture_t * pic; 1783+ while ((pic = pic_fifo_get(pf)) != NULL) { 1784+ picture_Release(pic); 1785+ } 1786+} 1787+ 1788+static inline void pic_fifo_init(pic_fifo_t * const pf) 1789+{ 1790+ pf->head = NULL; 1791+ pf->tail = NULL; // Not strictly needed 1792+} 1793+ 1794+static inline void pic_fifo_put(pic_fifo_t * const pf, picture_t * pic) 1795+{ 1796+ pic->p_next = NULL; 1797+ if (pf->head == NULL) 1798+ pf->head = pic; 1799+ else 1800+ pf->tail->p_next = pic; 1801+ pf->tail = pic; 1802+} 1803+ 1804+#define SUBS_MAX 3 1805+ 1806+typedef enum filter_resizer_e { 1807+ FILTER_RESIZER_RESIZER, 1808+ FILTER_RESIZER_ISP, 1809+ FILTER_RESIZER_HVS 1810+} filter_resizer_t; 1811+ 1812+typedef struct conv_frame_stash_s 1813+{ 1814+ mtime_t pts; 1815+ MMAL_BUFFER_HEADER_T * sub_bufs[SUBS_MAX]; 1816+} conv_frame_stash_t; 1817+ 1818+typedef struct filter_sys_t { 1819+ filter_resizer_t resizer_type; 1820+ MMAL_COMPONENT_T *component; 1821+ MMAL_PORT_T *input; 1822+ MMAL_PORT_T *output; 1823+ MMAL_POOL_T *out_pool; // Free output buffers 1824+ MMAL_POOL_T *in_pool; // Input pool to get BH for replication 1825+ 1826+ cma_buf_pool_t * cma_in_pool; 1827+ cma_buf_pool_t * cma_out_pool; 1828+ 1829+ subpic_reg_stash_t subs[SUBS_MAX]; 1830+ 1831+ pic_fifo_t ret_pics; 1832+ 1833+ unsigned int pic_n; 1834+ vlc_sem_t sem; 1835+ vlc_mutex_t lock; 1836+ 1837+ MMAL_STATUS_T err_stream; 1838+ 1839+ bool needs_copy_in; 1840+ bool is_cma; 1841+ bool is_sliced; 1842+ bool out_fmt_set; 1843+ const char * component_name; 1844+ MMAL_PORT_BH_CB_T in_port_cb_fn; 1845+ MMAL_PORT_BH_CB_T out_port_cb_fn; 1846+ 1847+ uint64_t frame_seq; 1848+ conv_frame_stash_t stash[16]; 1849+ 1850+ // Slice specific tracking stuff 1851+ struct { 1852+ pic_fifo_t pics; 1853+ unsigned int line; // Lines filled 1854+ } slice; 1855+ 1856+ vcsm_init_type_t vcsm_init_type; 1857+} filter_sys_t; 1858+ 1859+ 1860+static MMAL_STATUS_T pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic) 1861+{ 1862+ unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3; 1863+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video; 1864+ 1865+ es_fmt->type = MMAL_ES_TYPE_VIDEO; 1866+ es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format); 1867+ es_fmt->encoding_variant = 0; 1868+ 1869+ // Fill in crop etc. 1870+ hw_mmal_vlc_fmt_to_mmal_fmt(es_fmt, &pic->format); 1871+ // Override width / height with strides if appropriate 1872+ if (bpp != 0) { 1873+ v_fmt->width = pic->p[0].i_pitch / bpp; 1874+ v_fmt->height = pic->p[0].i_lines; 1875+ } 1876+ return MMAL_SUCCESS; 1877+} 1878+ 1879+ 1880+static MMAL_STATUS_T conv_enable_in(filter_t * const p_filter, filter_sys_t * const sys) 1881+{ 1882+ MMAL_STATUS_T err = MMAL_SUCCESS; 1883+ 1884+ if (!sys->input->is_enabled && 1885+ (err = mmal_port_enable(sys->input, sys->in_port_cb_fn)) != MMAL_SUCCESS) 1886+ { 1887+ msg_Err(p_filter, "Failed to enable input port %s (status=%"PRIx32" %s)", 1888+ sys->input->name, err, mmal_status_to_string(err)); 1889+ } 1890+ return err; 1891+} 1892+ 1893+static MMAL_STATUS_T conv_enable_out(filter_t * const p_filter, filter_sys_t * const sys) 1894+{ 1895+ MMAL_STATUS_T err = MMAL_SUCCESS; 1896+ 1897+ if (sys->is_cma) 1898+ { 1899+ if (sys->cma_out_pool == NULL && 1900+ (sys->cma_out_pool = cma_buf_pool_new(CONVERTER_BUFFERS, CONVERTER_BUFFERS, true, "mmal_resizer")) == NULL) 1901+ { 1902+ msg_Err(p_filter, "Failed to alloc cma buf pool"); 1903+ return MMAL_ENOMEM; 1904+ } 1905+ } 1906+ else 1907+ { 1908+ cma_buf_pool_deletez(&sys->cma_out_pool); 1909+ } 1910+ 1911+ if (!sys->output->is_enabled && 1912+ (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS) 1913+ { 1914+ msg_Err(p_filter, "Failed to enable output port %s (status=%"PRIx32" %s)", 1915+ sys->output->name, err, mmal_status_to_string(err)); 1916+ } 1917+ return err; 1918+} 1919+ 1920+static void conv_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 1921+{ 1922+ filter_t * const p_filter = (filter_t *)port->userdata; 1923+ 1924+#if TRACE_ALL 1925+ msg_Dbg(p_filter, "%s: <<< cmd=%d, data=%p, pic=%p", __func__, buffer->cmd, buffer->data, buffer->user_data); 1926+#endif 1927 1928 if (buffer->cmd == MMAL_EVENT_ERROR) { 1929- status = *(uint32_t *)buffer->data; 1930- msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status, 1931+ MMAL_STATUS_T status = *(uint32_t *)buffer->data; 1932+ 1933+ p_filter->p_sys->err_stream = status; 1934+ 1935+ msg_Err(p_filter, "MMAL error %"PRIx32" \"%s\"", status, 1936 mmal_status_to_string(status)); 1937 } 1938 1939 mmal_buffer_header_release(buffer); 1940 } 1941 1942-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 1943+static void conv_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) 1944 { 1945- block_t *block = (block_t *)buffer->user_data; 1946- decoder_t *dec = (decoder_t *)port->userdata; 1947- decoder_sys_t *sys = dec->p_sys; 1948- buffer->user_data = NULL; 1949+#if TRACE_ALL 1950+ picture_context_t * ctx = buf->user_data; 1951+// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys; 1952+ 1953+ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, ctx=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld", 1954+ __func__, buf->cmd, ctx, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts); 1955+#else 1956+ VLC_UNUSED(port); 1957+#endif 1958+ 1959+ mmal_buffer_header_release(buf); 1960+ 1961+#if TRACE_ALL 1962+ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__); 1963+#endif 1964+} 1965+ 1966+static void conv_out_q_pic(filter_sys_t * const sys, picture_t * const pic) 1967+{ 1968+ pic->p_next = NULL; 1969+ 1970+ vlc_mutex_lock(&sys->lock); 1971+ pic_fifo_put(&sys->ret_pics, pic); 1972+ vlc_mutex_unlock(&sys->lock); 1973 1974- mmal_buffer_header_release(buffer); 1975- if (block) 1976- block_Release(block); 1977- atomic_fetch_sub(&sys->input_in_transit, 1); 1978 vlc_sem_post(&sys->sem); 1979 } 1980 1981-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 1982+static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) 1983 { 1984- decoder_t *dec = (decoder_t *)port->userdata; 1985- decoder_sys_t *sys = dec->p_sys; 1986- picture_t *picture; 1987- MMAL_EVENT_FORMAT_CHANGED_T *fmt; 1988- MMAL_ES_FORMAT_T *format; 1989- 1990- if (buffer->cmd == 0) { 1991- picture = (picture_t *)buffer->user_data; 1992- if (buffer->length > 0) { 1993- picture->date = buffer->pts; 1994- picture->b_progressive = sys->b_progressive; 1995- picture->b_top_field_first = sys->b_top_field_first; 1996- decoder_QueueVideo(dec, picture); 1997- } else { 1998- picture_Release(picture); 1999- if (sys->output_pool) { 2000- buffer->user_data = NULL; 2001- buffer->alloc_size = 0; 2002- buffer->data = NULL; 2003- mmal_buffer_header_release(buffer); 2004- } 2005- } 2006- atomic_fetch_sub(&sys->output_in_transit, 1); 2007- vlc_sem_post(&sys->sem); 2008- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { 2009- fmt = mmal_event_format_changed_get(buffer); 2010+ filter_t * const p_filter = (filter_t *)port->userdata; 2011+ filter_sys_t * const sys = p_filter->p_sys; 2012 2013- format = mmal_format_alloc(); 2014- mmal_format_full_copy(format, fmt->format); 2015+#if TRACE_ALL 2016+ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld/%lld", __func__, 2017+ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, 2018+ (long long)buf->pts, (long long)sys->stash[(unsigned int)(buf->pts & 0xf)].pts); 2019+#endif 2020+ if (buf->cmd == 0) { 2021+ picture_t * const pic = (picture_t *)buf->user_data; 2022 2023- if (sys->opaque) 2024- format->encoding = MMAL_ENCODING_OPAQUE; 2025+ if (pic == NULL) { 2026+ msg_Err(p_filter, "%s: Buffer has no attached picture", __func__); 2027+ } 2028+ else if (buf->data == NULL || buf->length == 0) 2029+ { 2030+#if TRACE_ALL 2031+ msg_Dbg(p_filter, "%s: Buffer has no data", __func__); 2032+#endif 2033+ } 2034+ else 2035+ { 2036+ buf_to_pic_copy_props(pic, buf); 2037+ 2038+ // Set pic data pointers from buf aux info now it has it 2039+ if (sys->is_cma) { 2040+ if (cma_pic_set_data(pic, sys->output->format, buf) != VLC_SUCCESS) 2041+ msg_Err(p_filter, "Failed to set data"); 2042+ } 2043+ 2044+// draw_corners(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, pic->p[0].i_visible_pitch / 4, pic->p[0].i_visible_lines); 2045+#if DEBUG_SQUARES 2046+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, 32, 32, 0xffff0000); 2047+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 32, 0, 32, 32, 0xff00ff00); 2048+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 64, 0, 32, 32, 0xff0000ff); 2049+#endif 2050+ 2051+ buf->user_data = NULL; // Responsability for this pic no longer with buffer 2052+ conv_out_q_pic(sys, pic); 2053+ } 2054+ } 2055+ 2056+ mmal_buffer_header_release(buf); 2057+} 2058+ 2059+ 2060+static void slice_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) 2061+{ 2062+ filter_t * const p_filter = (filter_t *)port->userdata; 2063+ filter_sys_t * const sys = p_filter->p_sys; 2064+ 2065+#if TRACE_ALL 2066+ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld", __func__, 2067+ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, (long long)buf->pts); 2068+#endif 2069+ 2070+ if (buf->cmd != 0) 2071+ { 2072+ mmal_buffer_header_release(buf); 2073+ return; 2074+ } 2075+ 2076+ if (buf->data == NULL || buf->length == 0) 2077+ { 2078+#if TRACE_ALL 2079+ msg_Dbg(p_filter, "%s: Buffer has no data", __func__); 2080+#endif 2081+ } 2082+ else 2083+ { 2084+ // Got slice 2085+ picture_t *pic = sys->slice.pics.head; 2086+ const unsigned int scale_lines = sys->output->format->es->video.height; // Expected lines of callback 2087+ 2088+ if (pic == NULL) { 2089+ msg_Err(p_filter, "No output picture"); 2090+ goto fail; 2091+ } 2092+ 2093+ // Copy lines 2094+ // * single plane only - fix for I420 2095+ { 2096+ const unsigned int scale_n = __MIN(scale_lines - sys->slice.line, MMAL_SLICE_HEIGHT); 2097+ const unsigned int pic_lines = pic->p[0].i_lines; 2098+ const unsigned int copy_n = sys->slice.line + scale_n <= pic_lines ? scale_n : 2099+ sys->slice.line >= pic_lines ? 0 : 2100+ pic_lines - sys->slice.line; 2101+ 2102+ const unsigned int src_stride = buf->type->video.pitch[0]; 2103+ const unsigned int dst_stride = pic->p[0].i_pitch; 2104+ uint8_t *dst = pic->p[0].p_pixels + sys->slice.line * dst_stride; 2105+ const uint8_t *src = buf->data + buf->type->video.offset[0]; 2106+ 2107+ if (src_stride == dst_stride) { 2108+ if (copy_n != 0) 2109+ memcpy(dst, src, src_stride * copy_n); 2110+ } 2111+ else { 2112+ unsigned int i; 2113+ for (i = 0; i != copy_n; ++i) { 2114+ memcpy(dst, src, __MIN(dst_stride, src_stride)); 2115+ dst += dst_stride; 2116+ src += src_stride; 2117+ } 2118+ } 2119+ sys->slice.line += scale_n; 2120+ } 2121+ 2122+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) { 2123+ 2124+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) { 2125+ // Stuff doesn't add up... 2126+ msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags); 2127+ goto fail; 2128+ } 2129+ else { 2130+ sys->slice.line = 0; 2131+ 2132+ vlc_mutex_lock(&sys->lock); 2133+ pic_fifo_get(&sys->slice.pics); // Remove head from Q 2134+ vlc_mutex_unlock(&sys->lock); 2135+ 2136+ buf_to_pic_copy_props(pic, buf); 2137+ conv_out_q_pic(sys, pic); 2138+ } 2139+ } 2140+ } 2141+ 2142+ // Put back 2143+ buf->user_data = NULL; // Zap here to make sure we can't reuse later 2144+ mmal_buffer_header_reset(buf); 2145+ 2146+ if (mmal_port_send_buffer(sys->output, buf) != MMAL_SUCCESS) { 2147+ mmal_buffer_header_release(buf); 2148+ } 2149+ return; 2150+ 2151+fail: 2152+ sys->err_stream = MMAL_EIO; 2153+ vlc_sem_post(&sys->sem); // If we were waiting then break us out - the flush should fix sem values 2154+} 2155+ 2156+ 2157+static void conv_flush(filter_t * p_filter) 2158+{ 2159+ filter_sys_t * const sys = p_filter->p_sys; 2160+ unsigned int i; 2161+ 2162+#if TRACE_ALL 2163+ msg_Dbg(p_filter, "<<< %s", __func__); 2164+#endif 2165+ 2166+ if (sys->resizer_type == FILTER_RESIZER_HVS) 2167+ { 2168+ for (i = 0; i != SUBS_MAX; ++i) { 2169+ hw_mmal_subpic_flush(VLC_OBJECT(p_filter), sys->subs + i); 2170+ } 2171+ } 2172+ 2173+ if (sys->input != NULL && sys->input->is_enabled) 2174+ mmal_port_disable(sys->input); 2175+ 2176+ if (sys->output != NULL && sys->output->is_enabled) 2177+ mmal_port_disable(sys->output); 2178+ 2179+// cma_buf_pool_deletez(&sys->cma_out_pool); 2180+ 2181+ // Free up anything we may have already lying around 2182+ // Don't need lock as the above disables should have prevented anything 2183+ // happening in the background 2184+ 2185+ for (i = 0; i != 16; ++i) { 2186+ conv_frame_stash_t *const stash = sys->stash + i; 2187+ unsigned int sub_no; 2188+ 2189+ stash->pts = MMAL_TIME_UNKNOWN; 2190+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { 2191+ if (stash->sub_bufs[sub_no] != NULL) { 2192+ mmal_buffer_header_release(stash->sub_bufs[sub_no]); 2193+ stash->sub_bufs[sub_no] = NULL; 2194+ } 2195+ } 2196+ } 2197+ 2198+ pic_fifo_release_all(&sys->slice.pics); 2199+ pic_fifo_release_all(&sys->ret_pics); 2200+ 2201+ // Reset sem values - easiest & most reliable way is to just kill & re-init 2202+ vlc_sem_destroy(&sys->sem); 2203+ vlc_sem_init(&sys->sem, 0); 2204+ sys->pic_n = 0; 2205+ 2206+ // Reset error status 2207+ sys->err_stream = MMAL_SUCCESS; 2208+ 2209+#if TRACE_ALL 2210+ msg_Dbg(p_filter, ">>> %s", __func__); 2211+#endif 2212+} 2213+ 2214+static void conv_stash_fixup(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const p_pic) 2215+{ 2216+ conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf); 2217+ unsigned int sub_no; 2218+ VLC_UNUSED(p_filter); 2219+ 2220+ p_pic->date = stash->pts; 2221+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { 2222+ if (stash->sub_bufs[sub_no] != NULL) { 2223+ // **** Do stashed blend 2224+ // **** Aaargh, bother... need to rescale subs too 2225+ 2226+ mmal_buffer_header_release(stash->sub_bufs[sub_no]); 2227+ stash->sub_bufs[sub_no] = NULL; 2228+ } 2229+ } 2230+} 2231+ 2232+// Output buffers may contain a pic ref on error or flush 2233+// Free it 2234+static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata) 2235+{ 2236+ VLC_UNUSED(userdata); 2237+ 2238+ picture_t * const pic = header->user_data; 2239+ header->user_data = NULL; 2240+ 2241+ if (pic != NULL) 2242+ picture_Release(pic); 2243+ 2244+ return MMAL_FALSE; 2245+} 2246+ 2247+static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const pic) 2248+{ 2249+ MMAL_STATUS_T status; 2250+ 2251+ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter; 2252+ sys->output->format->type = MMAL_ES_TYPE_VIDEO; 2253+ sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video); 2254+ sys->output->format->encoding_variant = 0; 2255+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->output->format, &p_filter->fmt_out.video); 2256+ 2257+ if (pic != NULL) 2258+ { 2259+ // Override default format width/height if we have a pic we need to match 2260+ if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS) 2261+ { 2262+ char cbuf[5]; 2263+ msg_Err(p_filter, "Bad format desc: %s, pic=%p, bits=%d", str_fourcc(cbuf, pic->format.i_chroma), pic, pic->format.i_bits_per_pixel); 2264+ return status; 2265+ } 2266+ 2267+ MMAL_VIDEO_FORMAT_T *fmt = &sys->output->format->es->video; 2268+ msg_Dbg(p_filter, "%s: %dx%d [(0,0) %dx%d]", __func__, fmt->width, fmt->height, fmt->crop.width, fmt->crop.height); 2269+ } 2270+ 2271+ if (sys->is_sliced) { 2272+ // Override height for slice 2273+ sys->output->format->es->video.height = MMAL_SLICE_HEIGHT; 2274+ } 2275+ 2276+ mmal_log_dump_format(sys->output->format); 2277+ 2278+ status = mmal_port_format_commit(sys->output); 2279+ if (status != MMAL_SUCCESS) { 2280+ msg_Err(p_filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)", 2281+ sys->output->name, status, mmal_status_to_string(status)); 2282+ return status; 2283+ } 2284+ 2285+ sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended); 2286+ sys->output->buffer_size = sys->output->buffer_size_recommended; 2287+ 2288+ if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS) 2289+ return status; 2290+ 2291+ return MMAL_SUCCESS; 2292+} 2293+ 2294+ 2295+static picture_t *conv_get_out_pics(filter_sys_t * const sys) 2296+{ 2297+ picture_t * ret_pics; 2298+ 2299+ vlc_sem_wait(&sys->sem); 2300+ 2301+ // Return a single pending buffer 2302+ vlc_mutex_lock(&sys->lock); 2303+ ret_pics = pic_fifo_get(&sys->ret_pics); 2304+ vlc_mutex_unlock(&sys->lock); 2305+ 2306+ return ret_pics; 2307+} 2308+ 2309+static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic) 2310+{ 2311+ filter_sys_t * const sys = p_filter->p_sys; 2312+ picture_t * ret_pics = NULL; 2313+ MMAL_STATUS_T err; 2314+ const uint64_t frame_seq = ++sys->frame_seq; 2315+ conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf); 2316+ MMAL_BUFFER_HEADER_T * out_buf = NULL; 2317+ 2318+#if TRACE_ALL 2319+ { 2320+ char dbuf0[5], dbuf1[5]; 2321+ msg_Dbg(p_filter, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] sar:%d/%d", __func__, 2322+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, 2323+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, 2324+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, 2325+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den, 2326+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, 2327+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, 2328+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, 2329+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den); 2330+ } 2331+#endif 2332+ 2333+ if (sys->err_stream != MMAL_SUCCESS) { 2334+ goto stream_fail; 2335+ } 2336+ 2337+ // Check pic fmt corresponds to what we have set up 2338+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic)) 2339+ { 2340+ msg_Dbg(p_filter, "Reset input port format"); 2341+ 2342+ // HVS can take new formats without disable, others need it 2343+ if (sys->resizer_type != FILTER_RESIZER_HVS) { 2344+ // Extract any pending pic 2345+ if (sys->pic_n >= 2) { 2346+ ret_pics = conv_get_out_pics(sys); 2347+ // If pic_n == 1 then we return without trying to get stuff 2348+ sys->pic_n = 1; 2349+ } 2350+ if (sys->input->is_enabled) { 2351+ if ((err = mmal_port_disable(sys->input)) != MMAL_SUCCESS) 2352+ msg_Warn(p_filter, "Format update disable failed: %s", mmal_status_to_string(err)); 2353+ } 2354+ } 2355+ 2356+// mmal_log_dump_port(sys->input); 2357+ if ((err = mmal_port_format_commit(sys->input)) != MMAL_SUCCESS) 2358+ msg_Warn(p_filter, "Format update commit failed: %s", mmal_status_to_string(err)); 2359+ 2360+ // (Re)enable if required will be done later 2361+ } 2362+ 2363+ if (p_pic->context == NULL) { 2364+ // Can't have stashed subpics if not one of our pics 2365+ if (!sys->needs_copy_in) 2366+ msg_Dbg(p_filter, "%s: No context", __func__); 2367+ } 2368+ else if (sys->resizer_type == FILTER_RESIZER_HVS) 2369+ { 2370+ unsigned int sub_no = 0; 2371+ 2372+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { 2373+ int rv; 2374+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter), 2375+ hw_mmal_pic_sub_buf_get(p_pic, sub_no), 2376+ sys->subs + sub_no, 2377+ &p_pic->format, 2378+ &sys->output->format->es->video.crop, 2379+ MMAL_DISPLAY_ROT0, 2380+ frame_seq)) == 0) 2381+ break; 2382+ else if (rv < 0) 2383+ goto fail; 2384+ } 2385+ } 2386+ else 2387+ { 2388+ unsigned int sub_no = 0; 2389+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { 2390+ if ((stash->sub_bufs[sub_no] = hw_mmal_pic_sub_buf_get(p_pic, sub_no)) != NULL) { 2391+ mmal_buffer_header_acquire(stash->sub_bufs[sub_no]); 2392+ } 2393+ } 2394+ } 2395+ 2396+ if (!sys->out_fmt_set) { 2397+ sys->out_fmt_set = true; 2398+ 2399+ if (sys->is_sliced) { 2400+ // If zc then we will do stride conversion when we copy to arm side 2401+ // so no need to worry about actual pic dimensions here 2402+ if ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS) 2403+ goto fail; 2404+ 2405+ sys->out_pool = mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size); 2406+ } 2407+ else { 2408+ picture_t *pic = filter_NewPicture(p_filter); 2409+ err = conv_set_output(p_filter, sys, pic); 2410+ picture_Release(pic); 2411+ if (err != MMAL_SUCCESS) 2412+ goto fail; 2413+ 2414+ sys->out_pool = mmal_pool_create(sys->output->buffer_num, 0); 2415+ } 2416+ 2417+ if (sys->out_pool == NULL) { 2418+ msg_Err(p_filter, "Failed to create output pool"); 2419+ goto fail; 2420+ } 2421+ } 2422+ 2423+ // Reenable stuff if the last thing we did was flush 2424+ if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS || 2425+ (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS) 2426+ goto fail; 2427+ 2428+ // We attach pic to buf before stuffing the output port 2429+ // We could attach the pic on output for cma, but it is a lot easier to keep 2430+ // the code common. 2431+ { 2432+ picture_t * const out_pic = filter_NewPicture(p_filter); 2433+ 2434+ if (out_pic == NULL) 2435+ { 2436+ msg_Err(p_filter, "Failed to alloc required filter output pic"); 2437+ goto fail; 2438+ } 2439+ 2440+ out_pic->format.i_sar_den = p_filter->fmt_out.video.i_sar_den; 2441+ out_pic->format.i_sar_num = p_filter->fmt_out.video.i_sar_num; 2442+ 2443+ if (sys->is_sliced) { 2444+ vlc_mutex_lock(&sys->lock); 2445+ pic_fifo_put(&sys->slice.pics, out_pic); 2446+ vlc_mutex_unlock(&sys->lock); 2447+ 2448+ // Poke any returned pic buffers into output 2449+ // In general this should only happen immediately after enable 2450+ while ((out_buf = mmal_queue_get(sys->out_pool->queue)) != NULL) 2451+ mmal_port_send_buffer(sys->output, out_buf); 2452+ } 2453+ else 2454+ { 2455+ // 1 in - 1 out 2456+ if ((out_buf = mmal_queue_wait(sys->out_pool->queue)) == NULL) 2457+ { 2458+ msg_Err(p_filter, "Failed to get output buffer"); 2459+ picture_Release(out_pic); 2460+ goto fail; 2461+ } 2462+ mmal_buffer_header_reset(out_buf); 2463+ 2464+ // Attach out_pic to the buffer & ensure it is freed when the buffer is released 2465+ // On a good send callback the pic will be extracted to avoid this 2466+ out_buf->user_data = out_pic; 2467+ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, NULL); 2468+ 2469+#if 0 2470+ { 2471+ char dbuf0[5]; 2472+ msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", 2473+ str_fourcc(dbuf0, out_pic->format.i_chroma), 2474+ out_pic->format.i_width, out_pic->format.i_height, 2475+ out_pic->format.i_x_offset, out_pic->format.i_y_offset, 2476+ out_pic->format.i_visible_width, out_pic->format.i_visible_height, 2477+ out_pic->format.i_sar_num, out_pic->format.i_sar_den); 2478+ } 2479+#endif 2480+ 2481+ if (sys->is_cma) { 2482+ int rv; 2483+ 2484+ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size); 2485+ if (cb == NULL) { 2486+ char dbuf0[5]; 2487+ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d", 2488+ str_fourcc(dbuf0, out_pic->format.i_chroma), 2489+ sys->output->buffer_size); 2490+ goto fail; 2491+ } 2492+ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable 2493+ out_buf->data = (uint8_t *)vc_h; 2494+ out_buf->alloc_size = sys->output->buffer_size; 2495+ 2496+ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS) 2497+ { 2498+ char dbuf0[5]; 2499+ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d", 2500+ str_fourcc(dbuf0, out_pic->format.i_chroma), 2501+ rv); 2502+ cma_buf_unref(cb); 2503+ goto fail; 2504+ } 2505+ } 2506+ else { 2507+ out_buf->data = out_pic->p[0].p_pixels; 2508+ out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines; 2509+ //**** stride ???? 2510+ } 2511+ 2512+#if TRACE_ALL 2513+ msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld", 2514+ p_pic, out_buf->data, out_buf->user_data, out_buf->flags, 2515+ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts); 2516+#endif 2517+ 2518+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) 2519+ { 2520+ msg_Err(p_filter, "Send buffer to output failed"); 2521+ goto fail; 2522+ } 2523+ out_buf = NULL; 2524+ } 2525+ } 2526+ 2527+ 2528+ // Stuff into input 2529+ // We assume the BH is already set up with values reflecting pic date etc. 2530+ stash->pts = p_pic->date; 2531+ { 2532+ MMAL_BUFFER_HEADER_T *const pic_buf = sys->needs_copy_in ? 2533+ hw_mmal_pic_buf_copied(p_pic, sys->in_pool, sys->input, sys->cma_in_pool) : 2534+ hw_mmal_pic_buf_replicated(p_pic, sys->in_pool); 2535+ 2536+ // Whether or not we extracted the pic_buf we are done with the picture 2537+ picture_Release(p_pic); 2538+ p_pic = NULL; 2539+ 2540+ if (pic_buf == NULL) { 2541+ msg_Err(p_filter, "Pic has no attached buffer"); 2542+ goto fail; 2543+ } 2544+ 2545+ pic_buf->pts = frame_seq; 2546+ 2547+#if TRACE_ALL 2548+ msg_Dbg(p_filter, "In buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d/%d, pts=%lld", 2549+ p_pic, pic_buf->data, pic_buf->user_data, pic_buf->flags, 2550+ pic_buf->length, pic_buf->alloc_size, sys->input->buffer_size, (long long)pic_buf->pts); 2551+#endif 2552+ 2553+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS) 2554+ { 2555+ msg_Err(p_filter, "Send buffer to input failed"); 2556+ mmal_buffer_header_release(pic_buf); 2557+ goto fail; 2558+ } 2559+ } 2560+ 2561+ // We have a 1 pic latency for everything except the 1st pic which we 2562+ // wait for. 2563+ // This means we get a single static pic out 2564+ if (sys->pic_n++ == 1) { 2565+#if TRACE_ALL 2566+ msg_Dbg(p_filter, ">>> %s: Pic1=%p", __func__, ret_pics); 2567+#endif 2568+ return ret_pics; 2569+ } 2570+ 2571+ ret_pics = conv_get_out_pics(sys); 2572+ 2573+ if (sys->err_stream != MMAL_SUCCESS) 2574+ goto stream_fail; 2575+ 2576+ conv_stash_fixup(p_filter, sys, ret_pics); 2577+ 2578+#if TRACE_ALL 2579+ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics); 2580+#endif 2581+ 2582+ return ret_pics; 2583+ 2584+stream_fail: 2585+ msg_Err(p_filter, "MMAL error reported by callback"); 2586+fail: 2587+#if TRACE_ALL 2588+ msg_Err(p_filter, ">>> %s: FAIL", __func__); 2589+#endif 2590+ if (ret_pics != NULL) 2591+ picture_Release(ret_pics); 2592+ if (out_buf != NULL) 2593+ mmal_buffer_header_release(out_buf); 2594+ if (p_pic != NULL) 2595+ picture_Release(p_pic); 2596+ conv_flush(p_filter); 2597+ return NULL; 2598+} 2599+ 2600+static void CloseConverter(vlc_object_t * obj) 2601+{ 2602+ filter_t * const p_filter = (filter_t *)obj; 2603+ filter_sys_t * const sys = p_filter->p_sys; 2604+ unsigned int i; 2605+ 2606+#if TRACE_ALL 2607+ msg_Dbg(obj, "<<< %s", __func__); 2608+#endif 2609+ 2610+ if (sys == NULL) 2611+ return; 2612+ 2613+ // Disables input & output ports 2614+ conv_flush(p_filter); 2615+ 2616+ cma_buf_pool_deletez(&sys->cma_in_pool); 2617+ cma_buf_pool_deletez(&sys->cma_out_pool); 2618+ 2619+ if (sys->component && sys->component->control->is_enabled) 2620+ mmal_port_disable(sys->component->control); 2621+ 2622+ if (sys->component && sys->component->is_enabled) 2623+ mmal_component_disable(sys->component); 2624+ 2625+ if (sys->resizer_type == FILTER_RESIZER_HVS) 2626+ { 2627+ for (i = 0; i != SUBS_MAX; ++i) { 2628+ hw_mmal_subpic_close(VLC_OBJECT(p_filter), sys->subs + i); 2629+ } 2630+ } 2631+ 2632+ if (sys->out_pool) 2633+ { 2634+ if (sys->is_sliced) 2635+ mmal_port_pool_destroy(sys->output, sys->out_pool); 2636+ else 2637+ mmal_pool_destroy(sys->out_pool); 2638+ } 2639+ 2640+ if (sys->in_pool != NULL) 2641+ mmal_pool_destroy(sys->in_pool); 2642+ 2643+ if (sys->component) 2644+ mmal_component_release(sys->component); 2645+ 2646+ cma_vcsm_exit(sys->vcsm_init_type); 2647+ 2648+ vlc_sem_destroy(&sys->sem); 2649+ vlc_mutex_destroy(&sys->lock); 2650+ 2651+ p_filter->p_sys = NULL; 2652+ free(sys); 2653+} 2654+ 2655+ 2656+static inline MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt) 2657+{ 2658+ if (hw_mmal_chroma_is_mmal(fmt->i_chroma)) 2659+ return vlc_to_mmal_video_fourcc(fmt); 2660+ 2661+ if (fmt->i_chroma == VLC_CODEC_I420 || 2662+ fmt->i_chroma == VLC_CODEC_I420_10L) 2663+ return MMAL_ENCODING_I420; 2664+ 2665+ return 0; 2666+} 2667+ 2668+static inline MMAL_FOURCC_T filter_enc_out(const video_format_t * const fmt) 2669+{ 2670+ const MMAL_FOURCC_T mmes = vlc_to_mmal_video_fourcc(fmt); 2671+ // Can only copy out single plane stuff currently - this could be fixed! 2672+ return hw_mmal_chroma_is_mmal(fmt->i_chroma) || mmes != MMAL_ENCODING_I420 ? mmes : 0; 2673+} 2674+ 2675+ 2676+static int OpenConverter(vlc_object_t * obj) 2677+{ 2678+ filter_t * const p_filter = (filter_t *)obj; 2679+ int ret = VLC_EGENERIC; 2680+ filter_sys_t *sys; 2681+ MMAL_STATUS_T status; 2682+ MMAL_FOURCC_T enc_out = filter_enc_out(&p_filter->fmt_out.video); 2683+ const MMAL_FOURCC_T enc_in = filter_enc_in(&p_filter->fmt_in.video); 2684+ bool use_resizer; 2685+ bool use_isp; 2686+ int gpu_mem; 2687+ 2688+ // At least in principle we should deal with any mmal format as input 2689+ if (enc_in == 0 || enc_out == 0) 2690+ return VLC_EGENERIC; 2691+ 2692+ // Can't transform 2693+ if (p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation) 2694+ return VLC_EGENERIC; 2695+ 2696+ use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME); 2697+ use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME); 2698+ 2699+retry: 2700+ // ** Make more generic by checking supported encs 2701+ // 2702+ // Must use ISP - HVS can't do this, nor can resizer 2703+ if (enc_in == MMAL_ENCODING_YUVUV64_10) { 2704+ // If resizer selected then just give up 2705+ if (use_resizer) 2706+ return VLC_EGENERIC; 2707+ // otherwise downgrade HVS to ISP 2708+ use_isp = true; 2709+ } 2710+ // HVS can't do I420 2711+ if (enc_out == MMAL_ENCODING_I420) { 2712+ use_isp = true; 2713+ } 2714+ // Only HVS can deal with SAND30 2715+ if (enc_in == MMAL_ENCODING_YUV10_COL) { 2716+ if (use_isp || use_resizer) 2717+ return VLC_EGENERIC; 2718+ } 2719 2720- sys->output_format = format; 2721 2722- mmal_buffer_header_release(buffer); 2723+ if (use_resizer) { 2724+ // use resizer overrides use_isp 2725+ use_isp = false; 2726+ } 2727+ 2728+ // Check we have a sliced version of the fourcc if we want the resizer 2729+ if (use_resizer && 2730+ (enc_out = pic_to_slice_mmal_fourcc(enc_out)) == 0) { 2731+ return VLC_EGENERIC; 2732+ } 2733+ 2734+ gpu_mem = hw_mmal_get_gpu_mem(); 2735+ 2736+ { 2737+ char dbuf0[5], dbuf1[5], dbuf2[5], dbuf3[5]; 2738+ msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s/%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d (gpu=%d)", __func__, 2739+ use_resizer ? "resize" : use_isp ? "isp" : "hvs", 2740+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), str_fourcc(dbuf2, enc_in), 2741+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, 2742+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, 2743+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, 2744+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den, 2745+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), str_fourcc(dbuf3, enc_out), 2746+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, 2747+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, 2748+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, 2749+ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask, 2750+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den, 2751+ gpu_mem); 2752+ } 2753+ 2754+ sys = calloc(1, sizeof(filter_sys_t)); 2755+ if (!sys) { 2756+ ret = VLC_ENOMEM; 2757+ goto fail; 2758+ } 2759+ p_filter->p_sys = sys; 2760+ 2761+ // Init stuff the we destroy unconditionaly in Close first 2762+ vlc_mutex_init(&sys->lock); 2763+ vlc_sem_init(&sys->sem, 0); 2764+ sys->err_stream = MMAL_SUCCESS; 2765+ pic_fifo_init(&sys->ret_pics); 2766+ pic_fifo_init(&sys->slice.pics); 2767+ 2768+ sys->needs_copy_in = !hw_mmal_chroma_is_mmal(p_filter->fmt_in.video.i_chroma); 2769+ sys->in_port_cb_fn = conv_input_port_cb; 2770+ 2771+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { 2772+ msg_Err(p_filter, "VCSM init failed"); 2773+ goto fail; 2774+ } 2775+ 2776+ if (use_resizer) { 2777+ sys->resizer_type = FILTER_RESIZER_RESIZER; 2778+ sys->is_sliced = true; 2779+ sys->component_name = MMAL_COMPONENT_DEFAULT_RESIZER; 2780+ sys->out_port_cb_fn = slice_output_port_cb; 2781+ } 2782+ else if (use_isp) { 2783+ sys->resizer_type = FILTER_RESIZER_ISP; 2784+ sys->is_sliced = false; // Copy directly into filter picture 2785+ sys->component_name = MMAL_COMPONENT_ISP_RESIZER; 2786+ sys->out_port_cb_fn = conv_output_port_cb; 2787 } else { 2788- mmal_buffer_header_release(buffer); 2789+ sys->resizer_type = FILTER_RESIZER_HVS; 2790+ sys->is_sliced = false; // Copy directly into filter picture 2791+ sys->component_name = MMAL_COMPONENT_HVS; 2792+ sys->out_port_cb_fn = conv_output_port_cb; 2793+ } 2794+ sys->is_cma = is_cma_buf_pic_chroma(p_filter->fmt_out.video.i_chroma); 2795+ 2796+ status = mmal_component_create(sys->component_name, &sys->component); 2797+ if (status != MMAL_SUCCESS) { 2798+ if (!use_isp && !use_resizer) { 2799+ msg_Warn(p_filter, "Failed to rcreate HVS resizer - retrying with ISP"); 2800+ CloseConverter(obj); 2801+ use_isp = true; 2802+ goto retry; 2803+ } 2804+ msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)", 2805+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status)); 2806+ goto fail; 2807 } 2808+ sys->output = sys->component->output[0]; 2809+ sys->input = sys->component->input[0]; 2810+ 2811+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter; 2812+ status = mmal_port_enable(sys->component->control, conv_control_port_cb); 2813+ if (status != MMAL_SUCCESS) { 2814+ msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)", 2815+ sys->component->control->name, status, mmal_status_to_string(status)); 2816+ goto fail; 2817+ } 2818+ 2819+ if (sys->needs_copy_in && 2820+ (sys->cma_in_pool = cma_buf_pool_new(2, 2, true, "conv-copy-in")) == NULL) 2821+ { 2822+ msg_Err(p_filter, "Failed to allocate input CMA pool"); 2823+ goto fail; 2824+ } 2825+ 2826+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter; 2827+ sys->input->format->type = MMAL_ES_TYPE_VIDEO; 2828+ sys->input->format->encoding = enc_in; 2829+ sys->input->format->encoding_variant = MMAL_ENCODING_I420; 2830+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &p_filter->fmt_in.video); 2831+ port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, 1); 2832+ 2833+ mmal_log_dump_format(sys->input->format); 2834+ 2835+ status = mmal_port_format_commit(sys->input); 2836+ if (status != MMAL_SUCCESS) { 2837+ msg_Err(p_filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)", 2838+ sys->input->name, status, mmal_status_to_string(status)); 2839+ goto fail; 2840+ } 2841+ sys->input->buffer_size = sys->input->buffer_size_recommended; 2842+ sys->input->buffer_num = NUM_DECODER_BUFFER_HEADERS; 2843+ 2844+ if ((status = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS) 2845+ goto fail; 2846+ 2847+ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced || sys->is_cma); 2848+ 2849+ status = mmal_component_enable(sys->component); 2850+ if (status != MMAL_SUCCESS) { 2851+ msg_Err(p_filter, "Failed to enable component %s (status=%"PRIx32" %s)", 2852+ sys->component->name, status, mmal_status_to_string(status)); 2853+ goto fail; 2854+ } 2855+ 2856+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) 2857+ { 2858+ msg_Err(p_filter, "Failed to create input pool"); 2859+ goto fail; 2860+ } 2861+ 2862+ if (sys->resizer_type == FILTER_RESIZER_HVS) 2863+ { 2864+ unsigned int i; 2865+ for (i = 0; i != SUBS_MAX; ++i) { 2866+ if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], -1, i + 1) != MMAL_SUCCESS) 2867+ { 2868+ msg_Err(p_filter, "Failed to open subpic %d", i); 2869+ goto fail; 2870+ } 2871+ } 2872+ } 2873+ 2874+ p_filter->pf_video_filter = conv_filter; 2875+ p_filter->pf_flush = conv_flush; 2876+ // video_drain NIF in filter structure 2877+ 2878+#if TRACE_ALL 2879+ msg_Dbg(p_filter, ">>> %s: ok", __func__); 2880+#endif 2881+ 2882+ return VLC_SUCCESS; 2883+ 2884+fail: 2885+ CloseConverter(obj); 2886+ 2887+ if (!use_resizer && status == MMAL_ENOMEM) { 2888+ use_resizer = true; 2889+ msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer"); 2890+ goto retry; 2891+ } 2892+ 2893+#if TRACE_ALL 2894+ msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret); 2895+#endif 2896+ return ret; 2897+} 2898+ 2899+#if OPT_TO_FROM_ZC 2900+//---------------------------------------------------------------------------- 2901+// 2902+// Simple copy in to ZC 2903+ 2904+typedef struct to_zc_sys_s { 2905+ vcsm_init_type_t vcsm_init_type; 2906+ cma_buf_pool_t * cma_out_pool; 2907+} to_zc_sys_t; 2908+ 2909+ 2910+static size_t buf_alloc_size(const vlc_fourcc_t i_chroma, const unsigned int width, const unsigned int height) 2911+{ 2912+ const unsigned int pels = width * height; 2913+ 2914+ switch (i_chroma) 2915+ { 2916+ case VLC_CODEC_MMAL_ZC_RGB32: 2917+ return pels * 4; 2918+ case VLC_CODEC_MMAL_ZC_I420: 2919+ return pels * 3 / 2; 2920+ default: 2921+ break; 2922+ } 2923+ return 0; 2924+} 2925+ 2926+ 2927+static picture_t * 2928+to_zc_filter(filter_t *p_filter, picture_t *in_pic) 2929+{ 2930+ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys; 2931+#if TRACE_ALL 2932+ msg_Dbg(p_filter, "<<< %s", __func__); 2933+#endif 2934+ 2935+ assert(p_filter->fmt_out.video.i_chroma == VLC_CODEC_MMAL_ZC_I420); 2936+ 2937+ picture_t * const out_pic = filter_NewPicture(p_filter); 2938+ if (out_pic == NULL) 2939+ goto fail0; 2940+ 2941+ MMAL_ES_SPECIFIC_FORMAT_T mm_vfmt = {.video={0}}; 2942+ MMAL_ES_FORMAT_T mm_esfmt = { 2943+ .encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video), 2944+ .es = &mm_vfmt}; 2945+ 2946+ hw_mmal_vlc_fmt_to_mmal_fmt(&mm_esfmt, &p_filter->fmt_out.video); 2947+ 2948+ const size_t buf_alloc = buf_alloc_size(p_filter->fmt_out.video.i_chroma, 2949+ mm_vfmt.video.width, mm_vfmt.video.height); 2950+ if (buf_alloc == 0) 2951+ goto fail1; 2952+ cma_buf_t *const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, buf_alloc); 2953+ if (cb == NULL) 2954+ goto fail1; 2955+ 2956+ if (cma_buf_pic_attach(cb, out_pic) != VLC_SUCCESS) 2957+ goto fail2; 2958+ cma_pic_set_data(out_pic, &mm_esfmt, NULL); 2959+ 2960+ hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), NULL, &mm_esfmt, in_pic); 2961+ 2962+ // Copy pic properties 2963+ out_pic->date = in_pic->date; 2964+ out_pic->b_force = in_pic->b_force; 2965+ out_pic->b_progressive = in_pic->b_progressive; 2966+ out_pic->b_top_field_first = in_pic->b_top_field_first; 2967+ out_pic->i_nb_fields = in_pic->i_nb_fields; 2968+ 2969+ picture_Release(in_pic); 2970+ 2971+ return out_pic; 2972+ 2973+fail2: 2974+ cma_buf_unref(cb); 2975+fail1: 2976+ picture_Release(out_pic); 2977+fail0: 2978+ picture_Release(in_pic); 2979+ return NULL; 2980+} 2981+ 2982+static void to_zc_flush(filter_t * p_filter) 2983+{ 2984+ VLC_UNUSED(p_filter); 2985 } 2986+ 2987+static void CloseConverterToZc(vlc_object_t * obj) 2988+{ 2989+ filter_t * const p_filter = (filter_t *)obj; 2990+ to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys; 2991+ 2992+ if (sys == NULL) 2993+ return; 2994+ 2995+ p_filter->p_sys = NULL; 2996+ 2997+ cma_buf_pool_deletez(&sys->cma_out_pool); 2998+ cma_vcsm_exit(sys->vcsm_init_type); 2999+ 3000+ free(sys); 3001+} 3002+ 3003+static bool to_zc_validate_fmt(const video_format_t * const f_in, const video_format_t * const f_out) 3004+{ 3005+ if (!((f_in->i_chroma == VLC_CODEC_I420 || f_in->i_chroma == VLC_CODEC_I420_10L) && 3006+ f_out->i_chroma == VLC_CODEC_MMAL_ZC_I420)) 3007+ { 3008+ return false; 3009+ } 3010+ if (f_in->i_height != f_out->i_height || 3011+ f_in->i_width != f_out->i_width) 3012+ { 3013+ return false; 3014+ } 3015+ 3016+ return true; 3017+} 3018+ 3019+static int OpenConverterToZc(vlc_object_t * obj) 3020+{ 3021+ int ret = VLC_EGENERIC; 3022+ filter_t * const p_filter = (filter_t *)obj; 3023+ 3024+ if (!to_zc_validate_fmt(&p_filter->fmt_in.video, &p_filter->fmt_out.video)) 3025+ goto fail; 3026+ 3027+ { 3028+ char dbuf0[5], dbuf1[5]; 3029+ msg_Dbg(p_filter, "%s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d", __func__, 3030+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), 3031+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, 3032+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, 3033+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, 3034+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den, 3035+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), 3036+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, 3037+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, 3038+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, 3039+ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask, 3040+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den); 3041+ } 3042+ 3043+ to_zc_sys_t * const sys = calloc(1, sizeof(*sys)); 3044+ if (!sys) { 3045+ ret = VLC_ENOMEM; 3046+ goto fail; 3047+ } 3048+ p_filter->p_sys = (filter_sys_t *)sys; 3049+ 3050+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { 3051+ msg_Err(p_filter, "VCSM init failed"); 3052+ goto fail; 3053+ } 3054+ 3055+ if ((sys->cma_out_pool = cma_buf_pool_new(5, 5, true, "conv-to-zc")) == NULL) 3056+ { 3057+ msg_Err(p_filter, "Failed to allocate input CMA pool"); 3058+ goto fail; 3059+ } 3060+ 3061+ p_filter->pf_video_filter = to_zc_filter; 3062+ p_filter->pf_flush = to_zc_flush; 3063+ return VLC_SUCCESS; 3064+ 3065+fail: 3066+ CloseConverterToZc(obj); 3067+ return ret; 3068+} 3069+ 3070+//---------------------------------------------------------------------------- 3071+// 3072+// Simple "copy" from ZC 3073+ 3074+static void CloseConverterFromZc(vlc_object_t * obj) 3075+{ 3076+ VLC_UNUSED(obj); 3077+} 3078+ 3079+static int OpenConverterFromZc(vlc_object_t * obj) 3080+{ 3081+ return VLC_EGENERIC; 3082+} 3083+#endif 3084+//---------------------------------------------------------------------------- 3085+ 3086+typedef struct blend_sys_s { 3087+ vzc_pool_ctl_t * vzc; 3088+ const picture_t * last_dst; // Not a ref, just a hint that we have a new pic 3089+ vcsm_init_type_t vcsm_init_type; 3090+} blend_sys_t; 3091+ 3092+static void FilterBlendMmal(filter_t *p_filter, 3093+ picture_t *dst, const picture_t * src, 3094+ int x_offset, int y_offset, int alpha) 3095+{ 3096+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys; 3097+#if TRACE_ALL 3098+ msg_Dbg(p_filter, "%s (%d,%d:%d) pic=%p, pts=%lld, force=%d", __func__, x_offset, y_offset, alpha, src, src->date, src->b_force); 3099+#endif 3100+ // If nothing to do then do nothing 3101+ if (alpha == 0 || 3102+ src->format.i_visible_height == 0 || 3103+ src->format.i_visible_width == 0) 3104+ { 3105+ return; 3106+ } 3107+ 3108+ if (dst->context == NULL) 3109+ msg_Err(p_filter, "MMAL pic missing context"); 3110+ else 3111+ { 3112+ // cast away src const so we can ref it 3113+ MMAL_BUFFER_HEADER_T *buf = hw_mmal_vzc_buf_from_pic(sys->vzc, (picture_t *)src, 3114+ vis_mmal_rect(&dst->format), 3115+ x_offset, y_offset, 3116+ alpha, 3117+ dst != sys->last_dst || !hw_mmal_pic_has_sub_bufs(dst)); 3118+ if (buf == NULL) { 3119+ msg_Err(p_filter, "Failed to allocate vzc buffer for subpic"); 3120+ return; 3121+ } 3122+ 3123+ hw_mmal_pic_sub_buf_add(dst, buf); 3124+ 3125+ sys->last_dst = dst; 3126+ } 3127+} 3128+ 3129+static void FlushBlendMmal(filter_t * p_filter) 3130+{ 3131+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys; 3132+ sys->last_dst = NULL; 3133+ hw_mmal_vzc_pool_flush(sys->vzc); 3134+} 3135+ 3136+static void CloseBlendMmal(vlc_object_t *object) 3137+{ 3138+ filter_t * const p_filter = (filter_t *)object; 3139+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys; 3140+ 3141+ if (sys != NULL) { 3142+ p_filter->p_sys = NULL; 3143+ 3144+ hw_mmal_vzc_pool_release(sys->vzc); 3145+ cma_vcsm_exit(sys->vcsm_init_type); 3146+ free(sys); 3147+ } 3148+} 3149+ 3150+static int OpenBlendMmal(vlc_object_t *object) 3151+{ 3152+ filter_t * const p_filter = (filter_t *)object; 3153+ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma; 3154+ 3155+ if (!hw_mmal_chroma_is_mmal(vfcc_dst) || 3156+ !hw_mmal_vzc_subpic_fmt_valid(&p_filter->fmt_in.video)) 3157+ { 3158+ return VLC_EGENERIC; 3159+ } 3160+ 3161+ { 3162+ char dbuf0[5], dbuf1[5]; 3163+ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %dx%d]->%s,%dx%d [(%d,%d) %dx%d]", __func__, 3164+ "blend", 3165+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, 3166+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, 3167+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, 3168+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, 3169+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, 3170+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height); 3171+ } 3172+ 3173+ { 3174+ blend_sys_t * const sys = calloc(1, sizeof (*sys)); 3175+ if (sys == NULL) 3176+ return VLC_ENOMEM; 3177+ 3178+ p_filter->p_sys = (filter_sys_t *)sys; 3179+ 3180+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { 3181+ msg_Err(p_filter, "VCSM init failed"); 3182+ goto fail; 3183+ } 3184+ 3185+ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL) 3186+ goto fail; 3187+ } 3188+ 3189+ p_filter->pf_video_blend = FilterBlendMmal; 3190+ p_filter->pf_flush = FlushBlendMmal; 3191+ 3192+ return VLC_SUCCESS; 3193+ 3194+fail: 3195+ CloseBlendMmal(VLC_OBJECT(p_filter)); 3196+ return VLC_ENOMEM; 3197+} 3198+ 3199+// --------------------------------------------------------------------------- 3200+ 3201+static void FilterBlendNeon(filter_t *p_filter, 3202+ picture_t *dst_pic, const picture_t * src_pic, 3203+ int x_offset, int y_offset, int alpha) 3204+{ 3205+ const uint8_t * s_data; 3206+ uint8_t * d_data; 3207+ int width = src_pic->format.i_visible_width; 3208+ int height = src_pic->format.i_visible_height; 3209+ blend_neon_fn *const blend_fn = (blend_neon_fn * )p_filter->p_sys; 3210+ 3211+#if TRACE_ALL 3212+ msg_Dbg(p_filter, "%s (%d,%d:%d) pic=%p, pts=%lld, force=%d", __func__, x_offset, y_offset, alpha, src_pic, src_pic->date, src_pic->b_force); 3213+#endif 3214+ 3215+ if (alpha == 0 || 3216+ src_pic->format.i_visible_height == 0 || 3217+ src_pic->format.i_visible_width == 0) 3218+ { 3219+ return; 3220+ } 3221+ 3222+ x_offset += dst_pic->format.i_x_offset; 3223+ y_offset += dst_pic->format.i_y_offset; 3224+ 3225+ // Deal with R/B overrun 3226+ if (x_offset + width >= (int)(dst_pic->format.i_x_offset + dst_pic->format.i_visible_width)) 3227+ width = dst_pic->format.i_x_offset + dst_pic->format.i_visible_width - x_offset; 3228+ if (y_offset + height >= (int)(dst_pic->format.i_y_offset + dst_pic->format.i_visible_height)) 3229+ height = dst_pic->format.i_y_offset + dst_pic->format.i_visible_height - y_offset; 3230+ 3231+ if (width <= 0 || height <= 0) { 3232+ return; 3233+ } 3234+ 3235+ // *** L/U overrun 3236+ 3237+ s_data = src_pic->p[0].p_pixels + 3238+ src_pic->p[0].i_pixel_pitch * src_pic->format.i_x_offset + 3239+ src_pic->p[0].i_pitch * src_pic->format.i_y_offset; 3240+ d_data = dst_pic->p[0].p_pixels + 3241+ dst_pic->p[0].i_pixel_pitch * x_offset + 3242+ dst_pic->p[0].i_pitch * y_offset; 3243+ 3244+ 3245+ do { 3246+ blend_fn(d_data, s_data, alpha, width); 3247+ s_data += src_pic->p[0].i_pitch; 3248+ d_data += dst_pic->p[0].i_pitch; 3249+ } while (--height > 0); 3250+} 3251+ 3252+static void CloseBlendNeon(vlc_object_t *object) 3253+{ 3254+ VLC_UNUSED(object); 3255+} 3256+ 3257+static int OpenBlendNeon(vlc_object_t *object) 3258+{ 3259+ filter_t * const p_filter = (filter_t *)object; 3260+ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma; 3261+ MMAL_FOURCC_T mfcc_src = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video); 3262+ MMAL_FOURCC_T mfcc_dst = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video); 3263+ blend_neon_fn * blend_fn = (blend_neon_fn *)0; 3264+ 3265+ // Non-alpha RGB only for dest 3266+ if (vfcc_dst != VLC_CODEC_RGB32) 3267+ return VLC_EGENERIC; 3268+ 3269+ // Check we have appropriate blend fn (mmal doesn't have a non-alpha RGB32) 3270+ switch (mfcc_src) { 3271+ case MMAL_ENCODING_RGBA: 3272+ if (mfcc_dst == MMAL_ENCODING_RGBA) 3273+ blend_fn = blend_rgbx_rgba_neon; 3274+ else if (mfcc_dst == MMAL_ENCODING_BGRA) 3275+ blend_fn = blend_bgrx_rgba_neon; 3276+ break; 3277+ 3278+ case MMAL_ENCODING_BGRA: 3279+ if (mfcc_dst == MMAL_ENCODING_BGRA) 3280+ blend_fn = blend_rgbx_rgba_neon; 3281+ else if (mfcc_dst == MMAL_ENCODING_RGBA) 3282+ blend_fn = blend_bgrx_rgba_neon; 3283+ break; 3284+ 3285+ default: 3286+ break; 3287+ } 3288+ 3289+ if (blend_fn == (blend_neon_fn *)0) 3290+ { 3291+ return VLC_EGENERIC; 3292+ } 3293+ 3294+ p_filter->p_sys = (void *)blend_fn; 3295+ p_filter->pf_video_blend = FilterBlendNeon; 3296+ 3297+ { 3298+ char dbuf0[5], dbuf1[5]; 3299+ char dbuf0a[5], dbuf1a[5]; 3300+ msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %dx%d]->%s/%s,%dx%d [(%d,%d) %dx%d]", __func__, 3301+ "blend", 3302+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), 3303+ str_fourcc(dbuf0a, mfcc_src), 3304+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, 3305+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, 3306+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, 3307+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), 3308+ str_fourcc(dbuf1a, mfcc_dst), 3309+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, 3310+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, 3311+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height); 3312+ } 3313+ 3314+ return VLC_SUCCESS; 3315+} 3316+ 3317+vlc_module_begin() 3318+ set_category( CAT_INPUT ) 3319+ set_subcategory( SUBCAT_INPUT_VCODEC ) 3320+ set_shortname(N_("MMAL decoder")) 3321+ set_description(N_("MMAL-based decoder plugin for Raspberry Pi")) 3322+ set_capability("video decoder", 90) 3323+ add_shortcut("mmal_decoder") 3324+ add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false) 3325+ set_callbacks(OpenDecoder, CloseDecoder) 3326+ 3327+ add_submodule() 3328+ set_category( CAT_VIDEO ) 3329+ set_subcategory( SUBCAT_VIDEO_VFILTER ) 3330+ set_shortname(N_("MMAL resizer")) 3331+ set_description(N_("MMAL resizing conversion filter")) 3332+ add_shortcut("mmal_converter") 3333+ set_capability( "video converter", 900 ) 3334+ add_bool(MMAL_RESIZE_NAME, /* default */ false, MMAL_RESIZE_TEXT, MMAL_RESIZE_LONGTEXT, /* advanced option */ false) 3335+ add_bool(MMAL_ISP_NAME, /* default */ false, MMAL_ISP_TEXT, MMAL_ISP_LONGTEXT, /* advanced option */ false) 3336+ set_callbacks(OpenConverter, CloseConverter) 3337+ 3338+#if OPT_TO_FROM_ZC 3339+ add_submodule() 3340+ set_category( CAT_VIDEO ) 3341+ set_subcategory( SUBCAT_VIDEO_VFILTER ) 3342+ set_shortname(N_("MMAL to ZC")) 3343+ set_description(N_("MMAL conversion to ZC filter")) 3344+ add_shortcut("mmal_to_zc") 3345+ set_capability( "video converter", 901 ) 3346+ set_callbacks(OpenConverterToZc, CloseConverterToZc) 3347+ 3348+ add_submodule() 3349+ set_category( CAT_VIDEO ) 3350+ set_subcategory( SUBCAT_VIDEO_VFILTER ) 3351+ set_shortname(N_("MMAL from ZC")) 3352+ set_description(N_("MMAL conversion from ZC filter")) 3353+ add_shortcut("mmal_from_zc") 3354+ set_capability( "video converter", 902 ) 3355+ set_callbacks(OpenConverterFromZc, CloseConverterFromZc) 3356+#endif 3357+ 3358+ add_submodule() 3359+ set_category( CAT_VIDEO ) 3360+ set_subcategory( SUBCAT_VIDEO_VFILTER ) 3361+ set_description(N_("Video pictures blending for MMAL")) 3362+ add_shortcut("mmal_blend") 3363+ set_capability("video blending", 120) 3364+ set_callbacks(OpenBlendMmal, CloseBlendMmal) 3365+ 3366+ add_submodule() 3367+ set_category( CAT_VIDEO ) 3368+ set_subcategory( SUBCAT_VIDEO_VFILTER ) 3369+ set_description(N_("Video pictures blending for neon")) 3370+ add_shortcut("neon_blend") 3371+ set_capability("video blending", 110) 3372+ set_callbacks(OpenBlendNeon, CloseBlendNeon) 3373+ 3374+vlc_module_end() 3375+ 3376+ 3377--- /dev/null 3378+++ b/modules/hw/mmal/converter_mmal.c 3379@@ -0,0 +1,479 @@ 3380+#ifdef HAVE_CONFIG_H 3381+# include "config.h" 3382+#endif 3383+ 3384+#include <unistd.h> 3385+#include <fcntl.h> 3386+#include <sys/ioctl.h> 3387+#include <sys/mman.h> 3388+ 3389+#include <interface/vcsm/user-vcsm.h> 3390+ 3391+#include <vlc_common.h> 3392+#include <vlc_picture.h> 3393+ 3394+#include <libdrm/drm_fourcc.h> 3395+#include <EGL/egl.h> 3396+#include <EGL/eglext.h> 3397+#include <GLES2/gl2.h> 3398+#include <GLES2/gl2ext.h> 3399+ 3400+#include "mmal_cma.h" 3401+ 3402+#include "../../video_output/opengl/converter.h" 3403+ 3404+#include "mmal_picture.h" 3405+ 3406+#include <assert.h> 3407+ 3408+#define TRACE_ALL 0 3409+ 3410+typedef struct mmal_gl_converter_s 3411+{ 3412+ EGLint drm_fourcc; 3413+ vcsm_init_type_t vcsm_init_type; 3414+ cma_buf_t * last_cb; 3415+ 3416+ PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; 3417+} mmal_gl_converter_t; 3418+ 3419+ 3420+static EGLint vlc_to_gl_fourcc(const video_format_t * const fmt) 3421+{ 3422+ // Converting to mmal selects the right RGB32 varient 3423+ switch(vlc_to_mmal_video_fourcc(fmt)) 3424+ { 3425+ case MMAL_ENCODING_I420: 3426+ return MMAL_FOURCC('Y','U','1','2'); 3427+ case MMAL_ENCODING_YV12: 3428+ return MMAL_FOURCC('Y','V','1','2'); 3429+ case MMAL_ENCODING_I422: 3430+ return MMAL_FOURCC('Y','U','1','6'); 3431+// case MMAL_ENCODING_YUVUV128: // Doesn't actually work yet 3432+ case MMAL_ENCODING_NV12: 3433+ return MMAL_FOURCC('N','V','1','2'); 3434+ case MMAL_ENCODING_NV21: 3435+ return MMAL_FOURCC('N','V','2','1'); 3436+ case MMAL_ENCODING_RGB16: 3437+ return MMAL_FOURCC('R','G','1','6'); 3438+ case MMAL_ENCODING_RGB24: 3439+ return MMAL_FOURCC('B','G','2','4'); 3440+ case MMAL_ENCODING_BGR24: 3441+ return MMAL_FOURCC('R','G','2','4'); 3442+ case MMAL_ENCODING_BGR32: 3443+ case MMAL_ENCODING_BGRA: 3444+ return MMAL_FOURCC('X','R','2','4'); 3445+ case MMAL_ENCODING_RGB32: 3446+ case MMAL_ENCODING_RGBA: 3447+ return MMAL_FOURCC('X','B','2','4'); 3448+ default: 3449+ break; 3450+ } 3451+ return 0; 3452+} 3453+ 3454+typedef struct tex_context_s { 3455+ picture_context_t cmn; 3456+ GLuint texture; 3457+ 3458+ PFNGLDELETETEXTURESPROC DeleteTextures; // Copy fn pointer so we don't need tc on delete 3459+} tex_context_t; 3460+ 3461+static void tex_context_delete(tex_context_t * const tex) 3462+{ 3463+ tex->DeleteTextures(1, &tex->texture); 3464+ free(tex); 3465+} 3466+ 3467+static void tex_context_destroy(picture_context_t * pic_ctx) 3468+{ 3469+ tex_context_delete((tex_context_t *)pic_ctx); 3470+} 3471+ 3472+static picture_context_t * tex_context_copy(picture_context_t * pic_ctx) 3473+{ 3474+ return pic_ctx; 3475+} 3476+ 3477+static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic, cma_buf_t * const cb) 3478+{ 3479+ mmal_gl_converter_t * const sys = tc->priv; 3480+ tex_context_t * tex = (tex_context_t *)cma_buf_context2(cb); 3481+ if (tex != NULL) 3482+ return tex; 3483+ 3484+ if ((tex = malloc(sizeof(*tex))) == NULL) 3485+ return NULL; 3486+ 3487+ *tex = (tex_context_t){ 3488+ .cmn = { 3489+ .destroy = tex_context_destroy, 3490+ .copy = tex_context_copy 3491+ }, 3492+ .texture = 0, 3493+ .DeleteTextures = tc->vt->DeleteTextures 3494+ }; 3495+ 3496+ { 3497+ EGLint attribs[30]; 3498+ EGLint * a = attribs; 3499+ const int fd = cma_buf_fd(cb); 3500+ uint8_t * base_addr = cma_buf_addr(cb); 3501+ 3502+ if (pic->i_planes >= 4 || pic->i_planes <= 0) 3503+ { 3504+ msg_Err(tc, "%s: Bad planes: %d", __func__, pic->i_planes); 3505+ goto fail; 3506+ } 3507+ 3508+ *a++ = EGL_WIDTH; 3509+ *a++ = pic->format.i_visible_width; 3510+ *a++ = EGL_HEIGHT; 3511+ *a++ = pic->format.i_visible_height; 3512+ *a++ = EGL_LINUX_DRM_FOURCC_EXT; 3513+ *a++ = sys->drm_fourcc; 3514+ 3515+ if (pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8) 3516+ { 3517+ // Sand is its own very special bunny :-( 3518+ static const EGLint attnames[] = { 3519+ EGL_DMA_BUF_PLANE0_FD_EXT, 3520+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, 3521+ EGL_DMA_BUF_PLANE0_PITCH_EXT, 3522+ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, 3523+ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, 3524+ EGL_DMA_BUF_PLANE1_FD_EXT, 3525+ EGL_DMA_BUF_PLANE1_OFFSET_EXT, 3526+ EGL_DMA_BUF_PLANE1_PITCH_EXT, 3527+ EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, 3528+ EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 3529+ }; 3530+ 3531+ const EGLint * n = attnames; 3532+ 3533+ for (int i = 0; i < pic->i_planes; ++i) 3534+ { 3535+ const uint64_t mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(pic->p[i].i_pitch >> 7); 3536+ 3537+ *a++ = *n++; 3538+ *a++ = fd; 3539+ *a++ = *n++; 3540+ *a++ = pic->p[i].p_pixels - base_addr; 3541+ *a++ = *n++; 3542+ *a++ = pic->format.i_width; 3543+ *a++ = *n++; 3544+ *a++ = (EGLint)(mod >> 32); 3545+ *a++ = *n++; 3546+ *a++ = (EGLint)(mod & 0xffffffff); 3547+ } 3548+ } 3549+ else 3550+ { 3551+ static const EGLint attnames[] = { 3552+ EGL_DMA_BUF_PLANE0_FD_EXT, 3553+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, 3554+ EGL_DMA_BUF_PLANE0_PITCH_EXT, 3555+ EGL_DMA_BUF_PLANE1_FD_EXT, 3556+ EGL_DMA_BUF_PLANE1_OFFSET_EXT, 3557+ EGL_DMA_BUF_PLANE1_PITCH_EXT, 3558+ EGL_DMA_BUF_PLANE2_FD_EXT, 3559+ EGL_DMA_BUF_PLANE2_OFFSET_EXT, 3560+ EGL_DMA_BUF_PLANE2_PITCH_EXT, 3561+ EGL_DMA_BUF_PLANE3_FD_EXT, 3562+ EGL_DMA_BUF_PLANE3_OFFSET_EXT, 3563+ EGL_DMA_BUF_PLANE3_PITCH_EXT 3564+ }; 3565+ 3566+ const EGLint * n = attnames; 3567+ 3568+ for (int i = 0; i < pic->i_planes; ++i) 3569+ { 3570+ *a++ = *n++; 3571+ *a++ = fd; 3572+ *a++ = *n++; 3573+ *a++ = pic->p[i].p_pixels - base_addr; 3574+ *a++ = *n++; 3575+ *a++ = pic->p[i].i_pitch; 3576+ } 3577+ } 3578+ 3579+ *a = EGL_NONE; 3580+ 3581+ const EGLImage image = tc->gl->egl.createImageKHR(tc->gl, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); 3582+ if (!image) { 3583+ msg_Err(tc, "Failed to import fd %d: Err=%#x", fd, tc->vt->GetError()); 3584+ goto fail; 3585+ } 3586+ 3587+ // ** ?? tc->tex_target 3588+ tc->vt->GenTextures(1, &tex->texture); 3589+ tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture); 3590+ tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 3591+ tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 3592+ sys->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); 3593+ 3594+ tc->gl->egl.destroyImageKHR(tc->gl, image); 3595+ } 3596+ 3597+ if (cma_buf_add_context2(cb, &tex->cmn) != VLC_SUCCESS) 3598+ { 3599+ msg_Err(tc, "%s: add_context2 failed", __func__); 3600+ goto fail; 3601+ } 3602+ return tex; 3603+ 3604+fail: 3605+ tex_context_delete(tex); 3606+ return NULL; 3607+} 3608+ 3609+ 3610+static int 3611+tc_mmal_update(const opengl_tex_converter_t *tc, GLuint *textures, 3612+ const GLsizei *tex_width, const GLsizei *tex_height, 3613+ picture_t *pic, const size_t *plane_offset) 3614+{ 3615+ mmal_gl_converter_t * const sys = tc->priv; 3616+#if TRACE_ALL 3617+ { 3618+ char cbuf[5]; 3619+ msg_Dbg(tc, "%s: %s %d*%dx%d : %d*%dx%d", __func__, 3620+ str_fourcc(cbuf, pic->format.i_chroma), 3621+ tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines); 3622+ } 3623+#endif 3624+ VLC_UNUSED(tex_width); 3625+ VLC_UNUSED(tex_height); 3626+ VLC_UNUSED(plane_offset); 3627+ 3628+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma)) 3629+ { 3630+ char cbuf[5]; 3631+ msg_Err(tc, "Pic with unexpected chroma: %s", str_fourcc(cbuf, pic->format.i_chroma)); 3632+ return VLC_EGENERIC; 3633+ } 3634+ 3635+ cma_buf_t * const cb = cma_buf_pic_get(pic); 3636+ if (cb == NULL) 3637+ { 3638+ msg_Err(tc, "Pic missing cma buf"); 3639+ return VLC_EGENERIC; 3640+ } 3641+ 3642+ tex_context_t * const tex = get_tex_context(tc, pic, cb); 3643+ if (tex == NULL) 3644+ return VLC_EGENERIC; 3645+ 3646+// tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture); 3647+ 3648+ cma_buf_unref(sys->last_cb); 3649+ sys->last_cb = cma_buf_ref(cb); 3650+ 3651+ textures[0] = tex->texture; 3652+ return VLC_SUCCESS; 3653+} 3654+ 3655+static int 3656+tc_mmal_fetch_locations(opengl_tex_converter_t *tc, GLuint program) 3657+{ 3658+ tc->uloc.Texture[0] = tc->vt->GetUniformLocation(program, "Texture0"); 3659+ return tc->uloc.Texture[0] != -1 ? VLC_SUCCESS : VLC_EGENERIC; 3660+} 3661+ 3662+static void 3663+tc_mmal_prepare_shader(const opengl_tex_converter_t *tc, 3664+ const GLsizei *tex_width, const GLsizei *tex_height, 3665+ float alpha) 3666+{ 3667+ (void) tex_width; (void) tex_height; (void) alpha; 3668+ VLC_UNUSED(tc); 3669+// tc->vt->Uniform1i(tc->uloc.Texture[0], 0); 3670+} 3671+ 3672+static GLuint 3673+tc_fragment_shader_init(opengl_tex_converter_t * const tc, const GLenum tex_target, 3674+ const vlc_fourcc_t chroma, const video_color_space_t yuv_space) 3675+{ 3676+ VLC_UNUSED(yuv_space); 3677+ 3678+ tc->tex_count = 1; 3679+ tc->tex_target = tex_target; 3680+ tc->texs[0] = (struct opengl_tex_cfg) { 3681+ { 1, 1 }, { 1, 1 }, GL_RGB, chroma, GL_UNSIGNED_SHORT //** ?? 3682+ }; 3683+ 3684+ tc->pf_fetch_locations = tc_mmal_fetch_locations; 3685+ tc->pf_prepare_shader = tc_mmal_prepare_shader; 3686+ 3687+ 3688+ const char fs[] = 3689+ "#extension GL_OES_EGL_image_external : enable\n" 3690+ "precision mediump float;\n" 3691+ "uniform samplerExternalOES Texture0;\n" 3692+ "varying vec2 TexCoord0;\n" 3693+ "void main() {\n" 3694+ " gl_FragColor = texture2D(Texture0, TexCoord0);\n" 3695+ "}\n"; 3696+ 3697+ 3698+ const char *code = fs; 3699+ 3700+ GLuint fragment_shader = tc->vt->CreateShader(GL_FRAGMENT_SHADER); 3701+ tc->vt->ShaderSource(fragment_shader, 1, &code, NULL); 3702+ tc->vt->CompileShader(fragment_shader); 3703+ return fragment_shader; 3704+} 3705+ 3706+ 3707+static void 3708+CloseGLConverter(vlc_object_t *obj) 3709+{ 3710+ opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj; 3711+ mmal_gl_converter_t * const sys = tc->priv; 3712+ 3713+ if (sys == NULL) 3714+ return; 3715+ 3716+ cma_buf_unref(sys->last_cb); 3717+ cma_vcsm_exit(sys->vcsm_init_type); 3718+ free(sys); 3719+} 3720+ 3721+ 3722+// Pick a chroma that we can convert to 3723+// Prefer I420 as smallest 3724+static vlc_fourcc_t chroma_in_out(const vlc_fourcc_t chroma_in) 3725+{ 3726+ switch (chroma_in) 3727+ { 3728+ case VLC_CODEC_MMAL_OPAQUE: 3729+ case VLC_CODEC_MMAL_ZC_I420: 3730+ case VLC_CODEC_MMAL_ZC_SAND8: 3731+ case VLC_CODEC_MMAL_ZC_SAND10: // ISP only 3732+ return VLC_CODEC_MMAL_ZC_I420; 3733+ case VLC_CODEC_MMAL_ZC_SAND30: // HVS only 3734+ case VLC_CODEC_MMAL_ZC_RGB32: 3735+ return VLC_CODEC_MMAL_ZC_RGB32; // HVS can't generate YUV of any sort 3736+ default: 3737+ break; 3738+ } 3739+ return 0; 3740+} 3741+ 3742+ 3743+static int 3744+OpenGLConverter(vlc_object_t *obj) 3745+{ 3746+ opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj; 3747+ int rv = VLC_EGENERIC; 3748+ const EGLint eglfmt = vlc_to_gl_fourcc(&tc->fmt); 3749+ const vlc_fourcc_t chroma_out = chroma_in_out(tc->fmt.i_chroma); 3750+ 3751+ // Do we know what to do with this? 3752+ if (chroma_out == 0) 3753+ return rv; 3754+ 3755+ { 3756+ char dbuf0[5], dbuf1[5], dbuf2[5]; 3757+ msg_Dbg(tc, "<<< %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__, 3758+ str_fourcc(dbuf0, tc->fmt.i_chroma), 3759+ str_fourcc(dbuf1, eglfmt), 3760+ tc->fmt.i_width, tc->fmt.i_height, 3761+ tc->fmt.i_x_offset, tc->fmt.i_y_offset, 3762+ tc->fmt.i_visible_width, tc->fmt.i_visible_height, 3763+ tc->fmt.i_sar_num, tc->fmt.i_sar_den, 3764+ str_fourcc(dbuf2, chroma_out)); 3765+ } 3766+ 3767+ if (tc->gl->ext != VLC_GL_EXT_EGL || 3768+ !tc->gl->egl.createImageKHR || !tc->gl->egl.destroyImageKHR) 3769+ { 3770+ // Missing an important callback 3771+ msg_Dbg(tc, "Missing EGL xxxImageKHR calls"); 3772+ return rv; 3773+ } 3774+ 3775+ if ((tc->priv = calloc(1, sizeof(mmal_gl_converter_t))) == NULL) 3776+ { 3777+ msg_Err(tc, "priv alloc failure"); 3778+ rv = VLC_ENOMEM; 3779+ goto fail; 3780+ } 3781+ mmal_gl_converter_t * const sys = tc->priv; 3782+ 3783+ sys->drm_fourcc = eglfmt; 3784+ 3785+ if ((sys->vcsm_init_type = cma_vcsm_init()) != VCSM_INIT_CMA) { 3786+ msg_Dbg(tc, "VCSM init failed"); 3787+ goto fail; 3788+ } 3789+ 3790+ if ((sys->glEGLImageTargetTexture2DOES = vlc_gl_GetProcAddress(tc->gl, "glEGLImageTargetTexture2DOES")) == NULL) 3791+ { 3792+ msg_Err(tc, "Failed to bind GL fns"); 3793+ goto fail; 3794+ } 3795+ 3796+ if ((tc->fshader = tc_fragment_shader_init(tc, GL_TEXTURE_EXTERNAL_OES, 3797+ eglfmt == 0 ? VLC_CODEC_RGB32 : tc->fmt.i_chroma, 3798+ eglfmt == 0 ? COLOR_SPACE_SRGB : tc->fmt.space)) == 0) 3799+ { 3800+ msg_Err(tc, "Failed to make shader"); 3801+ goto fail; 3802+ } 3803+ 3804+ if (eglfmt == 0) 3805+ { 3806+ tc->fmt.i_chroma = chroma_out; 3807+ tc->fmt.i_bits_per_pixel = 8; 3808+ if (tc->fmt.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) 3809+ { 3810+ tc->fmt.i_rmask = 0xff0000; 3811+ tc->fmt.i_gmask = 0xff00; 3812+ tc->fmt.i_bmask = 0xff; 3813+ tc->fmt.space = COLOR_SPACE_SRGB; 3814+ } 3815+ else 3816+ { 3817+ tc->fmt.i_rmask = 0; 3818+ tc->fmt.i_gmask = 0; 3819+ tc->fmt.i_bmask = 0; 3820+ tc->fmt.space = COLOR_SPACE_UNDEF; 3821+ } 3822+ sys->drm_fourcc = vlc_to_gl_fourcc(&tc->fmt); 3823+ } 3824+ 3825+ tc->handle_texs_gen = true; // We manage the texs 3826+ tc->pf_update = tc_mmal_update; 3827+ 3828+#if TRACE_ALL 3829+ { 3830+ char dbuf0[5], dbuf1[5], dbuf2[5]; 3831+ msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__, 3832+ str_fourcc(dbuf0, tc->fmt.i_chroma), 3833+ str_fourcc(dbuf1, sys->drm_fourcc), 3834+ tc->fmt.i_width, tc->fmt.i_height, 3835+ tc->fmt.i_x_offset, tc->fmt.i_y_offset, 3836+ tc->fmt.i_visible_width, tc->fmt.i_visible_height, 3837+ tc->fmt.i_sar_num, tc->fmt.i_sar_den, 3838+ str_fourcc(dbuf2, chroma_out)); 3839+ } 3840+#endif 3841+ 3842+ return VLC_SUCCESS; 3843+ 3844+fail: 3845+ CloseGLConverter(obj); 3846+ return rv; 3847+} 3848+ 3849+vlc_module_begin () 3850+ set_description("MMAL OpenGL surface converter") 3851+ set_shortname (N_("MMALGLConverter")) 3852+ set_capability("glconv", 900) 3853+ set_callbacks(OpenGLConverter, CloseGLConverter) 3854+ set_category(CAT_VIDEO) 3855+ set_subcategory(SUBCAT_VIDEO_VOUT) 3856+ add_shortcut("mmal_gl_converter") 3857+vlc_module_end () 3858+ 3859--- a/modules/hw/mmal/deinterlace.c 3860+++ b/modules/hw/mmal/deinterlace.c 3861@@ -26,11 +26,12 @@ 3862 #include "config.h" 3863 #endif 3864 3865-#include <vlc_picture_pool.h> 3866+#include <stdatomic.h> 3867+ 3868 #include <vlc_common.h> 3869+#include <vlc_picture_pool.h> 3870 #include <vlc_plugin.h> 3871 #include <vlc_filter.h> 3872-#include <vlc_atomic.h> 3873 3874 #include "mmal_picture.h" 3875 3876@@ -39,468 +40,814 @@ 3877 #include <interface/mmal/util/mmal_util.h> 3878 #include <interface/mmal/util/mmal_default_components.h> 3879 3880-#define MIN_NUM_BUFFERS_IN_TRANSIT 2 3881+#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu" 3882+#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.") 3883+#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.") 3884 3885-#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu" 3886-#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.") 3887-#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.") 3888+#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv" 3889+#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace") 3890+#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace") 3891 3892-static int Open(filter_t *filter); 3893-static void Close(filter_t *filter); 3894+#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast" 3895+#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace") 3896+#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace") 3897 3898-vlc_module_begin() 3899- set_shortname(N_("MMAL deinterlace")) 3900- set_description(N_("MMAL-based deinterlace filter plugin")) 3901- set_capability("video filter", 0) 3902- set_category(CAT_VIDEO) 3903- set_subcategory(SUBCAT_VIDEO_VFILTER) 3904- set_callbacks(Open, Close) 3905- add_shortcut("deinterlace") 3906- add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT, 3907- MMAL_DEINTERLACE_QPU_LONGTEXT, true); 3908-vlc_module_end() 3909+#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none" 3910+#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace") 3911+#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\ 3912+ "This is the default for > SD if < 96M gpu-mem") 3913+ 3914+#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate" 3915+#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate") 3916+#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input") 3917+ 3918+#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate" 3919+#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate") 3920+#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input") 3921 3922-struct filter_sys_t { 3923+ 3924+typedef struct filter_sys_t 3925+{ 3926 MMAL_COMPONENT_T *component; 3927 MMAL_PORT_T *input; 3928 MMAL_PORT_T *output; 3929+ MMAL_POOL_T *in_pool; 3930+ 3931+ MMAL_QUEUE_T * out_q; 3932+ 3933+ // Bind this lot somehow into ppr???? 3934+ bool is_cma; 3935+ cma_buf_pool_t * cma_out_pool; 3936+ MMAL_POOL_T * out_pool; 3937+ 3938+ hw_mmal_port_pool_ref_t *out_ppr; 3939+ 3940+ bool half_rate; 3941+ bool use_qpu; 3942+ bool use_fast; 3943+ bool use_passthrough; 3944+ unsigned int seq_in; // Seq of next frame to submit (1-15) [Init=1] 3945+ unsigned int seq_out; // Seq of last frame received (1-15) [Init=15] 3946 3947- MMAL_QUEUE_T *filtered_pictures; 3948- vlc_sem_t sem; 3949+ vcsm_init_type_t vcsm_init_type; 3950 3951- atomic_bool started; 3952+} filter_sys_t; 3953 3954- /* statistics */ 3955- int output_in_transit; 3956- int input_in_transit; 3957-}; 3958- 3959-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); 3960-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); 3961-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); 3962-static picture_t *deinterlace(filter_t *filter, picture_t *picture); 3963-static void flush(filter_t *filter); 3964 3965 #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx" 3966 3967-static int Open(filter_t *filter) 3968+#define TRACE_ALL 0 3969+ 3970+ 3971+ 3972+// Buffer attached to pic on success, is still valid on failure 3973+static picture_t * di_alloc_opaque(filter_t * const p_filter, MMAL_BUFFER_HEADER_T * const buf) 3974 { 3975- int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ? 3976- (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base / 3977- filter->fmt_in.video.i_frame_rate : 0; 3978- bool use_qpu = var_InheritBool(filter, MMAL_DEINTERLACE_QPU); 3979+ filter_sys_t *const filter_sys = p_filter->p_sys; 3980+ picture_t * const pic = filter_NewPicture(p_filter); 3981 3982- MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = { 3983- { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) }, 3984- MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV, 3985- 4, 3986- { 3, frame_duration, 0, use_qpu } 3987- }; 3988+ if (pic == NULL) 3989+ goto fail1; 3990 3991- int ret = VLC_SUCCESS; 3992- MMAL_STATUS_T status; 3993- filter_sys_t *sys; 3994+ if (buf->length == 0) { 3995+ msg_Err(p_filter, "%s: Empty buffer", __func__); 3996+ goto fail2; 3997+ } 3998 3999- msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!", 4000- frame_duration, use_qpu ? "used" : "unused"); 4001+ if ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL) 4002+ goto fail2; 4003 4004- if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE) 4005- return VLC_EGENERIC; 4006+ buf_to_pic_copy_props(pic, buf); 4007 4008- if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE) 4009- return VLC_EGENERIC; 4010+#if TRACE_ALL 4011+ msg_Dbg(p_filter, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date); 4012+#endif 4013 4014- sys = calloc(1, sizeof(filter_sys_t)); 4015- if (!sys) 4016- return VLC_ENOMEM; 4017- filter->p_sys = sys; 4018+ return pic; 4019 4020- bcm_host_init(); 4021+fail2: 4022+ picture_Release(pic); 4023+fail1: 4024+// mmal_buffer_header_release(buf); 4025+ return NULL; 4026+} 4027 4028- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component); 4029- if (status != MMAL_SUCCESS) { 4030- msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)", 4031- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); 4032- ret = VLC_EGENERIC; 4033- goto out; 4034- } 4035+static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 4036+{ 4037+#if TRACE_ALL 4038+ pic_ctx_mmal_t * ctx = buffer->user_data; 4039+// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys; 4040+ 4041+ msg_Dbg((filter_t *)port->userdata, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buffer->cmd, ctx, buffer, 4042+ buffer->flags, (long long)buffer->pts); 4043+#else 4044+ VLC_UNUSED(port); 4045+#endif 4046 4047- status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr); 4048- if (status != MMAL_SUCCESS) { 4049- msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)", 4050- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); 4051- ret = VLC_EGENERIC; 4052- goto out; 4053- } 4054+ mmal_buffer_header_release(buffer); 4055 4056- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter; 4057- status = mmal_port_enable(sys->component->control, control_port_cb); 4058- if (status != MMAL_SUCCESS) { 4059- msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)", 4060- sys->component->control->name, status, mmal_status_to_string(status)); 4061- ret = VLC_EGENERIC; 4062- goto out; 4063+#if TRACE_ALL 4064+ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__); 4065+#endif 4066+} 4067+ 4068+static void di_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) 4069+{ 4070+ if (buf->cmd == 0 && buf->length != 0) 4071+ { 4072+ // The filter structure etc. should always exist if we have contents 4073+ // but might not on later flushes as we shut down 4074+ filter_t * const p_filter = (filter_t *)port->userdata; 4075+ filter_sys_t * const sys = p_filter->p_sys; 4076+ 4077+#if TRACE_ALL 4078+ msg_Dbg(p_filter, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts); 4079+#endif 4080+ mmal_queue_put(sys->out_q, buf); 4081+#if TRACE_ALL 4082+ msg_Dbg(p_filter, ">>> %s: out Q len=%d", __func__, mmal_queue_length(sys->out_q)); 4083+#endif 4084+ return; 4085 } 4086 4087- sys->input = sys->component->input[0]; 4088- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter; 4089- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) 4090- sys->input->format->encoding = MMAL_ENCODING_OPAQUE; 4091- sys->input->format->es->video.width = filter->fmt_in.video.i_width; 4092- sys->input->format->es->video.height = filter->fmt_in.video.i_height; 4093- sys->input->format->es->video.crop.x = 0; 4094- sys->input->format->es->video.crop.y = 0; 4095- sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width; 4096- sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height; 4097- sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num; 4098- sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den; 4099+ mmal_buffer_header_reset(buf); // User data stays intact so release will kill pic 4100+ mmal_buffer_header_release(buf); 4101+} 4102 4103- es_format_Copy(&filter->fmt_out, &filter->fmt_in); 4104- filter->fmt_out.video.i_frame_rate *= 2; 4105 4106- status = mmal_port_format_commit(sys->input); 4107- if (status != MMAL_SUCCESS) { 4108- msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)", 4109- sys->input->name, status, mmal_status_to_string(status)); 4110- ret = VLC_EGENERIC; 4111- goto out; 4112- } 4113- sys->input->buffer_size = sys->input->buffer_size_recommended; 4114- sys->input->buffer_num = sys->input->buffer_num_recommended; 4115 4116- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) { 4117- MMAL_PARAMETER_BOOLEAN_T zero_copy = { 4118- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, 4119- 1 4120- }; 4121+static MMAL_STATUS_T fill_output_from_q(filter_t * const p_filter, filter_sys_t * const sys, MMAL_QUEUE_T * const q) 4122+{ 4123+ MMAL_BUFFER_HEADER_T * out_buf; 4124 4125- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr); 4126- if (status != MMAL_SUCCESS) { 4127- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", 4128- sys->input->name, status, mmal_status_to_string(status)); 4129- goto out; 4130+ while ((out_buf = mmal_queue_get(q)) != NULL) 4131+ { 4132+ MMAL_STATUS_T err; 4133+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) 4134+ { 4135+ msg_Err(p_filter, "Send buffer to output failed"); 4136+ mmal_queue_put_back(q, out_buf); 4137+ return err; 4138 } 4139 } 4140+ return MMAL_SUCCESS; 4141+} 4142 4143- status = mmal_port_enable(sys->input, input_port_cb); 4144- if (status != MMAL_SUCCESS) { 4145- msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)", 4146- sys->input->name, status, mmal_status_to_string(status)); 4147- ret = VLC_EGENERIC; 4148- goto out; 4149- } 4150+// Output buffers may contain a pic ref on error or flush 4151+// Free it 4152+static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata) 4153+{ 4154+ VLC_UNUSED(userdata); 4155 4156- sys->output = sys->component->output[0]; 4157- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter; 4158- mmal_format_full_copy(sys->output->format, sys->input->format); 4159+ cma_buf_t * const cb = header->user_data; 4160+ header->user_data = NULL; 4161+ cma_buf_unref(cb); // Copes fine with NULL 4162 4163- status = mmal_port_format_commit(sys->output); 4164- if (status != MMAL_SUCCESS) { 4165- msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)", 4166- sys->input->name, status, mmal_status_to_string(status)); 4167- ret = VLC_EGENERIC; 4168- goto out; 4169+ return MMAL_FALSE; 4170+} 4171+ 4172+static inline unsigned int seq_inc(unsigned int x) 4173+{ 4174+ return x + 1 >= 16 ? 1 : x + 1; 4175+} 4176+ 4177+static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq) 4178+{ 4179+ return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq); 4180+} 4181+ 4182+static picture_t *deinterlace(filter_t * p_filter, picture_t * p_pic) 4183+{ 4184+ filter_sys_t * const sys = p_filter->p_sys; 4185+ picture_t *ret_pics = NULL; 4186+ MMAL_STATUS_T err; 4187+ MMAL_BUFFER_HEADER_T * out_buf = NULL; 4188+ 4189+#if TRACE_ALL 4190+ msg_Dbg(p_filter, "<<< %s", __func__); 4191+#endif 4192+ 4193+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic)) 4194+ { 4195+ // ****** Breaks on opaque (at least) 4196+ 4197+ if (sys->input->is_enabled) 4198+ mmal_port_disable(sys->input); 4199+#if 0 4200+ if (sys->output->is_enabled) 4201+ mmal_port_disable(sys->output); 4202+ 4203+ mmal_format_full_copy(sys->output->format, sys->input->format); 4204+ mmal_port_format_commit(sys->output); 4205+ sys->output->buffer_num = 30; 4206+ sys->output->buffer_size = sys->input->buffer_size_recommended; 4207+ mmal_port_enable(sys->output, di_output_port_cb); 4208+#endif 4209+ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS) 4210+ msg_Err(p_filter, "Failed to update pic format"); 4211+ sys->input->buffer_num = 30; 4212+ sys->input->buffer_size = sys->input->buffer_size_recommended; 4213+ mmal_log_dump_format(sys->input->format); 4214+ } 4215+ 4216+ // Reenable stuff if the last thing we did was flush 4217+ // Output should always be enabled 4218+ if (!sys->input->is_enabled && 4219+ (err = mmal_port_enable(sys->input, di_input_port_cb)) != MMAL_SUCCESS) 4220+ { 4221+ msg_Err(p_filter, "Input port reenable failed"); 4222+ goto fail; 4223+ } 4224+ 4225+ if (!sys->is_cma) 4226+ { 4227+ // Fill output from anything that has turned up in pool Q 4228+ if (hw_mmal_port_pool_ref_fill(sys->out_ppr) != MMAL_SUCCESS) 4229+ { 4230+ msg_Err(p_filter, "Out port fill fail"); 4231+ goto fail; 4232+ } 4233 } 4234+ else 4235+ { 4236+ // We are expecting one in - one out so simply wedge a new bufer 4237+ // into the output port. Flow control will happen on cma alloc. 4238+ 4239+ if ((out_buf = mmal_queue_get(sys->out_pool->queue)) == NULL) 4240+ { 4241+ // Should never happen 4242+ msg_Err(p_filter, "Failed to get output buffer"); 4243+ goto fail; 4244+ } 4245+ mmal_buffer_header_reset(out_buf); 4246 4247- sys->output->buffer_num = 3; 4248+ // Attach cma_buf to the buffer & ensure it is freed when the buffer is released 4249+ // On a good send callback the pic will be extracted to avoid this 4250+ mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, p_filter); 4251+ 4252+ cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size); 4253+ if ((out_buf->user_data = cb) == NULL) // Check & attach cb to buf 4254+ { 4255+ char dbuf0[5]; 4256+ msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d", 4257+ str_fourcc(dbuf0, p_pic->format.i_chroma), 4258+ sys->output->buffer_size); 4259+ goto fail; 4260+ } 4261+ const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable 4262+ out_buf->data = (uint8_t *)vc_h; 4263+ out_buf->alloc_size = sys->output->buffer_size; 4264+ 4265+#if TRACE_ALL 4266+ msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld", 4267+ p_pic, out_buf->data, out_buf->user_data, out_buf->flags, 4268+ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts); 4269+#endif 4270 4271- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) { 4272- MMAL_PARAMETER_UINT32_T extra_buffers = { 4273- { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) }, 4274- 5 4275- }; 4276- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr); 4277- if (status != MMAL_SUCCESS) { 4278- msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)", 4279- status, mmal_status_to_string(status)); 4280- goto out; 4281+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) 4282+ { 4283+ msg_Err(p_filter, "Send buffer to output failed"); 4284+ goto fail; 4285 } 4286+ out_buf = NULL; 4287+ } 4288 4289- MMAL_PARAMETER_BOOLEAN_T zero_copy = { 4290- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, 4291- 1 4292- }; 4293+ // Stuff into input 4294+ // We assume the BH is already set up with values reflecting pic date etc. 4295+ { 4296+ MMAL_BUFFER_HEADER_T * const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->in_pool); 4297+ 4298+ if (pic_buf == NULL) 4299+ { 4300+ msg_Err(p_filter, "Pic has not attached buffer"); 4301+ goto fail; 4302+ } 4303 4304- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr); 4305- if (status != MMAL_SUCCESS) { 4306- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", 4307- sys->output->name, status, mmal_status_to_string(status)); 4308- goto out; 4309+ picture_Release(p_pic); 4310+ 4311+ // Add a sequence to the flags so we can track what we have actually 4312+ // deinterlaced 4313+ pic_buf->flags = (pic_buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0)); 4314+ sys->seq_in = seq_inc(sys->seq_in); 4315+ 4316+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS) 4317+ { 4318+ msg_Err(p_filter, "Send buffer to input failed"); 4319+ mmal_buffer_header_release(pic_buf); 4320+ goto fail; 4321 } 4322 } 4323 4324- status = mmal_port_enable(sys->output, output_port_cb); 4325- if (status != MMAL_SUCCESS) { 4326- msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)", 4327- sys->output->name, status, mmal_status_to_string(status)); 4328- ret = VLC_EGENERIC; 4329- goto out; 4330+ // Return anything that is in the out Q 4331+ { 4332+ picture_t ** pp_pic = &ret_pics; 4333+ 4334+ // Advanced di has a 3 frame latency, so if the seq delta is greater 4335+ // than that then we are expecting at least two frames of output. Wait 4336+ // for one of those. 4337+ // seq_in is seq of the next frame we are going to submit (1-15, no 0) 4338+ // seq_out is last frame we removed from Q 4339+ // So after 4 frames sent (1st time we want to wait), 0 rx seq_in=5, seq_out=15, delta=5 4340+ 4341+ while ((out_buf = (seq_delta(sys->seq_in, sys->seq_out) >= 5 ? mmal_queue_timedwait(sys->out_q, 1000) : mmal_queue_get(sys->out_q))) != NULL) 4342+ { 4343+ const unsigned int seq_out = (out_buf->flags / MMAL_BUFFER_HEADER_FLAG_USER0) & 0xf; 4344+ int rv; 4345+ 4346+ picture_t * out_pic; 4347+ 4348+ if (sys->is_cma) 4349+ { 4350+ // Alloc pic 4351+ if ((out_pic = filter_NewPicture(p_filter)) == NULL) 4352+ { 4353+ // Can't alloc pic - just stop extraction 4354+ mmal_queue_put_back(sys->out_q, out_buf); 4355+ out_buf = NULL; 4356+ msg_Warn(p_filter, "Failed to alloc new filter output pic"); 4357+ break; 4358+ } 4359+ 4360+ // Extract cma_buf from buf & attach to pic 4361+ cma_buf_t * const cb = (cma_buf_t *)out_buf->user_data; 4362+ if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS) 4363+ { 4364+ char dbuf0[5]; 4365+ msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d", 4366+ str_fourcc(dbuf0, out_pic->format.i_chroma), 4367+ rv); 4368+ // cb still attached to buffer and will be freed with it 4369+ goto fail; 4370+ } 4371+ out_buf->user_data = NULL; 4372+ 4373+ buf_to_pic_copy_props(out_pic, out_buf); 4374+ 4375+ // Set pic data pointers from buf aux info now it has it 4376+ if ((rv = cma_pic_set_data(out_pic, sys->output->format, out_buf)) != VLC_SUCCESS) 4377+ { 4378+ char dbuf0[5]; 4379+ msg_Err(p_filter, "Failed to set data: fmt=%s, rv=%d", 4380+ str_fourcc(dbuf0, sys->output->format->encoding), 4381+ rv); 4382+ } 4383+ 4384+ out_buf->user_data = NULL; // Responsability for this pic no longer with buffer 4385+ mmal_buffer_header_release(out_buf); 4386+ } 4387+ else 4388+ { 4389+ out_pic = di_alloc_opaque(p_filter, out_buf); 4390+ 4391+ if (out_pic == NULL) { 4392+ msg_Warn(p_filter, "Failed to alloc new filter output pic"); 4393+ mmal_queue_put_back(sys->out_q, out_buf); // Wedge buf back into Q in the hope we can alloc a pic later 4394+ out_buf = NULL; 4395+ break; 4396+ } 4397+ } 4398+ out_buf = NULL; // Now attached to pic or recycled 4399+ 4400+#if TRACE_ALL 4401+ msg_Dbg(p_filter, "-- %s: Q pic=%p: seq_in=%d, seq_out=%d, delta=%d", __func__, out_pic, sys->seq_in, seq_out, seq_delta(sys->seq_in, seq_out)); 4402+#endif 4403+ 4404+ *pp_pic = out_pic; 4405+ pp_pic = &out_pic->p_next; 4406+ 4407+ // Ignore 0 seqs 4408+ // Don't think these should actually happen 4409+ if (seq_out != 0) 4410+ sys->seq_out = seq_out; 4411+ } 4412+ 4413+ // Crash on lockup 4414+ assert(ret_pics != NULL || seq_delta(sys->seq_in, sys->seq_out) < 5); 4415 } 4416 4417- status = mmal_component_enable(sys->component); 4418- if (status != MMAL_SUCCESS) { 4419- msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)", 4420- sys->component->name, status, mmal_status_to_string(status)); 4421- ret = VLC_EGENERIC; 4422- goto out; 4423+#if TRACE_ALL 4424+ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics); 4425+#endif 4426+ 4427+ return ret_pics; 4428+ 4429+fail: 4430+ if (out_buf != NULL) 4431+ mmal_buffer_header_release(out_buf); 4432+ picture_Release(p_pic); 4433+ return NULL; 4434+} 4435+ 4436+static void di_flush(filter_t *p_filter) 4437+{ 4438+ filter_sys_t * const sys = p_filter->p_sys; 4439+ 4440+#if TRACE_ALL 4441+ msg_Dbg(p_filter, "<<< %s", __func__); 4442+#endif 4443+ 4444+ if (sys->input != NULL && sys->input->is_enabled) 4445+ mmal_port_disable(sys->input); 4446+ 4447+ if (sys->output != NULL && sys->output->is_enabled) 4448+ { 4449+ if (sys->is_cma) 4450+ { 4451+ MMAL_BUFFER_HEADER_T * buf; 4452+ mmal_port_disable(sys->output); 4453+ while ((buf = mmal_queue_get(sys->out_q)) != NULL) 4454+ mmal_buffer_header_release(buf); 4455+ } 4456+ else 4457+ { 4458+ // Wedge anything we've got into the output port as that will free the underlying buffers 4459+ fill_output_from_q(p_filter, sys, sys->out_q); 4460+ 4461+ mmal_port_disable(sys->output); 4462+ 4463+ // If that dumped anything real into the out_q then have another go 4464+ if (mmal_queue_length(sys->out_q) != 0) 4465+ { 4466+ mmal_port_enable(sys->output, di_output_port_cb); 4467+ fill_output_from_q(p_filter, sys, sys->out_q); 4468+ mmal_port_disable(sys->output); 4469+ // Out q should now be empty & should remain so until the input is reenabled 4470+ } 4471+ } 4472+ mmal_port_enable(sys->output, di_output_port_cb); 4473+ 4474+ // Leaving the input disabled is fine - but we want to leave the output enabled 4475+ // so we can retrieve buffers that are still bound to pictures 4476 } 4477 4478- sys->filtered_pictures = mmal_queue_create(); 4479+ sys->seq_in = 1; 4480+ sys->seq_out = 15; 4481 4482- filter->pf_video_filter = deinterlace; 4483- filter->pf_flush = flush; 4484+#if TRACE_ALL 4485+ msg_Dbg(p_filter, ">>> %s", __func__); 4486+#endif 4487+} 4488 4489- vlc_sem_init(&sys->sem, 0); 4490 4491-out: 4492- if (ret != VLC_SUCCESS) 4493- Close(filter); 4494+static void pass_flush(filter_t *p_filter) 4495+{ 4496+ // Nothing to do 4497+ VLC_UNUSED(p_filter); 4498+} 4499 4500- return ret; 4501+static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic) 4502+{ 4503+ VLC_UNUSED(p_filter); 4504+ 4505+ p_pic->b_progressive = true; 4506+ return p_pic; 4507 } 4508 4509-static void Close(filter_t *filter) 4510+ 4511+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 4512 { 4513- filter_sys_t *sys = filter->p_sys; 4514- MMAL_BUFFER_HEADER_T *buffer; 4515+ filter_t *filter = (filter_t *)port->userdata; 4516+ MMAL_STATUS_T status; 4517 4518- if (!sys) 4519+ if (buffer->cmd == MMAL_EVENT_ERROR) { 4520+ status = *(uint32_t *)buffer->data; 4521+ msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status, 4522+ mmal_status_to_string(status)); 4523+ } 4524+ 4525+ mmal_buffer_header_reset(buffer); 4526+ mmal_buffer_header_release(buffer); 4527+} 4528+ 4529+static void CloseMmalDeinterlace(filter_t *filter) 4530+{ 4531+ filter_sys_t * const sys = filter->p_sys; 4532+ 4533+#if TRACE_ALL 4534+ msg_Dbg(filter, "<<< %s", __func__); 4535+#endif 4536+ 4537+ if (sys == NULL) 4538 return; 4539 4540- if (sys->component && sys->component->control->is_enabled) 4541- mmal_port_disable(sys->component->control); 4542+ if (sys->use_passthrough) 4543+ { 4544+ free(sys); 4545+ return; 4546+ } 4547 4548- if (sys->input && sys->input->is_enabled) 4549- mmal_port_disable(sys->input); 4550+ di_flush(filter); 4551 4552- if (sys->output && sys->output->is_enabled) 4553- mmal_port_disable(sys->output); 4554+ if (sys->component && sys->component->control->is_enabled) 4555+ mmal_port_disable(sys->component->control); 4556 4557 if (sys->component && sys->component->is_enabled) 4558 mmal_component_disable(sys->component); 4559 4560- while ((buffer = mmal_queue_get(sys->filtered_pictures))) { 4561- picture_t *pic = (picture_t *)buffer->user_data; 4562- picture_Release(pic); 4563+ if (sys->in_pool != NULL) 4564+ mmal_pool_destroy(sys->in_pool); 4565+ 4566+ hw_mmal_port_pool_ref_release(sys->out_ppr, false); 4567+ // Once we exit filter & sys are invalid so mark as such 4568+ if (sys->output != NULL) 4569+ sys->output->userdata = NULL; 4570+ 4571+ if (sys->is_cma) 4572+ { 4573+ if (sys->output && sys->output->is_enabled) 4574+ mmal_port_disable(sys->output); 4575+ 4576+ cma_buf_pool_deletez(&sys->cma_out_pool); 4577+ 4578+ if (sys->out_pool != NULL) 4579+ mmal_pool_destroy(sys->out_pool); 4580 } 4581 4582- if (sys->filtered_pictures) 4583- mmal_queue_destroy(sys->filtered_pictures); 4584+ if (sys->out_q != NULL) 4585+ mmal_queue_destroy(sys->out_q); 4586 4587 if (sys->component) 4588 mmal_component_release(sys->component); 4589 4590- vlc_sem_destroy(&sys->sem); 4591+ cma_vcsm_exit(sys->vcsm_init_type); 4592+ 4593 free(sys); 4594+} 4595+ 4596 4597- bcm_host_deinit(); 4598+static bool is_fmt_valid_in(const vlc_fourcc_t fmt) 4599+{ 4600+ return fmt == VLC_CODEC_MMAL_OPAQUE || 4601+ fmt == VLC_CODEC_MMAL_ZC_I420 || 4602+ fmt == VLC_CODEC_MMAL_ZC_SAND8; 4603 } 4604 4605-static int send_output_buffer(filter_t *filter) 4606+static int OpenMmalDeinterlace(filter_t *filter) 4607 { 4608- filter_sys_t *sys = filter->p_sys; 4609- MMAL_BUFFER_HEADER_T *buffer; 4610+ int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ? 4611+ CLOCK_FREQ * filter->fmt_in.video.i_frame_rate_base / 4612+ filter->fmt_in.video.i_frame_rate : 0; 4613+ 4614+ int ret = VLC_EGENERIC; 4615 MMAL_STATUS_T status; 4616- picture_t *picture; 4617- int ret = 0; 4618+ filter_sys_t *sys; 4619+ 4620+ msg_Dbg(filter, "<<< %s", __func__); 4621+ 4622+ if (!is_fmt_valid_in(filter->fmt_in.video.i_chroma) || 4623+ filter->fmt_out.video.i_chroma != filter->fmt_in.video.i_chroma) 4624+ return VLC_EGENERIC; 4625 4626- if (!sys->output->is_enabled) { 4627- ret = VLC_EGENERIC; 4628- goto out; 4629+ sys = calloc(1, sizeof(filter_sys_t)); 4630+ if (!sys) 4631+ return VLC_ENOMEM; 4632+ filter->p_sys = sys; 4633+ 4634+ sys->seq_in = 1; 4635+ sys->seq_out = 15; 4636+ sys->is_cma = is_cma_buf_pic_chroma(filter->fmt_out.video.i_chroma); 4637+ 4638+ if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { 4639+ msg_Err(filter, "VCSM init failed"); 4640+ goto fail; 4641+ } 4642+ 4643+ if (rpi_is_model_pi4()) 4644+ { 4645+ sys->half_rate = true; 4646+ sys->use_qpu = false; 4647+ sys->use_fast = true; 4648+ } 4649+ else 4650+ { 4651+ sys->half_rate = false; 4652+ sys->use_qpu = true; 4653+ sys->use_fast = false; 4654+ } 4655+ sys->use_passthrough = false; 4656+ 4657+ if (filter->fmt_in.video.i_width * filter->fmt_in.video.i_height > 768 * 576) 4658+ { 4659+ // We get stressed if we have to try too hard - so make life easier 4660+ sys->half_rate = true; 4661+ // Also check we actually have enough memory to do this 4662+ // Memory always comes from GPU if Opaque 4663+ // Assume we have plenty of memory if it comes from CMA 4664+ if ((!sys->is_cma || sys->vcsm_init_type == VCSM_INIT_LEGACY) && 4665+ hw_mmal_get_gpu_mem() < (96 << 20)) 4666+ { 4667+ sys->use_passthrough = true; 4668+ msg_Warn(filter, "Deinterlace bypassed due to lack of GPU memory"); 4669+ } 4670 } 4671 4672- picture = filter_NewPicture(filter); 4673- if (!picture) { 4674- msg_Warn(filter, "Failed to get new picture"); 4675- ret = -1; 4676- goto out; 4677+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU)) 4678+ sys->use_qpu = false; 4679+ if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV)) 4680+ { 4681+ sys->use_fast = false; 4682+ sys->use_passthrough = false; 4683+ } 4684+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FAST)) 4685+ { 4686+ sys->use_fast = true; 4687+ sys->use_passthrough = false; 4688+ } 4689+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NONE)) 4690+ sys->use_passthrough = true; 4691+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FULL_RATE)) 4692+ sys->half_rate = false; 4693+ if (var_InheritBool(filter, MMAL_DEINTERLACE_HALF_RATE)) 4694+ sys->half_rate = true; 4695+ 4696+ if (sys->use_passthrough) 4697+ { 4698+ filter->pf_video_filter = pass_deinterlace; 4699+ filter->pf_flush = pass_flush; 4700+ // Don't need VCSM - get rid of it now 4701+ cma_vcsm_exit(sys->vcsm_init_type); 4702+ sys->vcsm_init_type = VCSM_INIT_NONE; 4703+ return 0; 4704+ } 4705+ 4706+ { 4707+ char dbuf0[5], dbuf1[5]; 4708+ msg_Dbg(filter, "%s: %s,%dx%d [(%d,%d) %d/%d] -> %s,%dx%d [(%d,%d) %dx%d]: %s %s %s", __func__, 4709+ str_fourcc(dbuf0, filter->fmt_in.video.i_chroma), 4710+ filter->fmt_in.video.i_width, filter->fmt_in.video.i_height, 4711+ filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset, 4712+ filter->fmt_in.video.i_visible_width, filter->fmt_in.video.i_visible_height, 4713+ str_fourcc(dbuf1, filter->fmt_out.video.i_chroma), 4714+ filter->fmt_out.video.i_width, filter->fmt_out.video.i_height, 4715+ filter->fmt_out.video.i_x_offset, filter->fmt_out.video.i_y_offset, 4716+ filter->fmt_out.video.i_visible_width, filter->fmt_out.video.i_visible_height, 4717+ sys->use_qpu ? "QPU" : "VPU", 4718+ sys->use_fast ? "FAST" : "ADV", 4719+ sys->use_passthrough ? "PASS" : sys->half_rate ? "HALF" : "FULL"); 4720 } 4721- picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate; 4722- picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base; 4723 4724- buffer = picture->p_sys->buffer; 4725- buffer->user_data = picture; 4726- buffer->cmd = 0; 4727+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component); 4728+ if (status != MMAL_SUCCESS) { 4729+ msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)", 4730+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); 4731+ goto fail; 4732+ } 4733 4734- mmal_picture_lock(picture); 4735+ { 4736+ const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = { 4737+ { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) }, 4738+ sys->use_fast ? 4739+ MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST : 4740+ MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV, 4741+ 4, 4742+ { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu } 4743+ }; 4744 4745- status = mmal_port_send_buffer(sys->output, buffer); 4746+ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr); 4747+ if (status != MMAL_SUCCESS) { 4748+ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)", 4749+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); 4750+ goto fail; 4751+ } 4752+ } 4753+ 4754+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter; 4755+ status = mmal_port_enable(sys->component->control, control_port_cb); 4756 if (status != MMAL_SUCCESS) { 4757- msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)", 4758- status, mmal_status_to_string(status)); 4759- mmal_buffer_header_release(buffer); 4760- picture_Release(picture); 4761- ret = -1; 4762- } else { 4763- atomic_fetch_add(&sys->output_in_transit, 1); 4764- vlc_sem_post(&sys->sem); 4765+ msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)", 4766+ sys->component->control->name, status, mmal_status_to_string(status)); 4767+ goto fail; 4768 } 4769 4770-out: 4771- return ret; 4772-} 4773+ sys->input = sys->component->input[0]; 4774+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter; 4775+ sys->input->format->encoding = vlc_to_mmal_video_fourcc(&filter->fmt_in.video); 4776+ hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &filter->fmt_in.video); 4777 4778-static void fill_output_port(filter_t *filter) 4779-{ 4780- filter_sys_t *sys = filter->p_sys; 4781- /* allow at least 2 buffers in transit */ 4782- unsigned max_buffers_in_transit = __MAX(2, MIN_NUM_BUFFERS_IN_TRANSIT); 4783- int buffers_available = sys->output->buffer_num - 4784- atomic_load(&sys->output_in_transit) - 4785- mmal_queue_length(sys->filtered_pictures); 4786- int buffers_to_send = max_buffers_in_transit - sys->output_in_transit; 4787- int i; 4788+ es_format_Copy(&filter->fmt_out, &filter->fmt_in); 4789+ if (!sys->half_rate) 4790+ filter->fmt_out.video.i_frame_rate *= 2; 4791 4792- if (buffers_to_send > buffers_available) 4793- buffers_to_send = buffers_available; 4794+ status = mmal_port_format_commit(sys->input); 4795+ if (status != MMAL_SUCCESS) { 4796+ msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)", 4797+ sys->input->name, status, mmal_status_to_string(status)); 4798+ goto fail; 4799+ } 4800+ sys->input->buffer_size = sys->input->buffer_size_recommended; 4801+ sys->input->buffer_num = 30; 4802+// sys->input->buffer_num = sys->input->buffer_num_recommended; 4803 4804-#ifndef NDEBUG 4805- msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)", 4806- buffers_to_send, buffers_available, sys->output_in_transit, 4807- sys->output->buffer_num); 4808-#endif 4809- for (i = 0; i < buffers_to_send; ++i) { 4810- if (send_output_buffer(filter) < 0) 4811- break; 4812+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) 4813+ { 4814+ msg_Err(filter, "Failed to create input pool"); 4815+ goto fail; 4816 } 4817-} 4818 4819-static picture_t *deinterlace(filter_t *filter, picture_t *picture) 4820-{ 4821- filter_sys_t *sys = filter->p_sys; 4822- MMAL_BUFFER_HEADER_T *buffer; 4823- picture_t *out_picture = NULL; 4824- picture_t *ret = NULL; 4825- MMAL_STATUS_T status; 4826- unsigned i = 0; 4827+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true); 4828+ if (status != MMAL_SUCCESS) { 4829+ msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", 4830+ sys->input->name, status, mmal_status_to_string(status)); 4831+ goto fail; 4832+ } 4833 4834- fill_output_port(filter); 4835+ status = mmal_port_enable(sys->input, di_input_port_cb); 4836+ if (status != MMAL_SUCCESS) { 4837+ msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)", 4838+ sys->input->name, status, mmal_status_to_string(status)); 4839+ goto fail; 4840+ } 4841 4842- buffer = picture->p_sys->buffer; 4843- buffer->user_data = picture; 4844- buffer->pts = picture->date; 4845- buffer->cmd = 0; 4846 4847- if (!picture->p_sys->displayed) { 4848- status = mmal_port_send_buffer(sys->input, buffer); 4849- if (status != MMAL_SUCCESS) { 4850- msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)", 4851- status, mmal_status_to_string(status)); 4852- picture_Release(picture); 4853- } else { 4854- picture->p_sys->displayed = true; 4855- atomic_fetch_add(&sys->input_in_transit, 1); 4856- vlc_sem_post(&sys->sem); 4857- } 4858- } else { 4859- picture_Release(picture); 4860- } 4861- 4862- /* 4863- * Send output buffers 4864- */ 4865- while(atomic_load(&sys->started) && i < 2) { 4866- if (buffer = mmal_queue_timedwait(sys->filtered_pictures, 2000)) { 4867- i++; 4868- if (!out_picture) { 4869- out_picture = (picture_t *)buffer->user_data; 4870- ret = out_picture; 4871- } else { 4872- out_picture->p_next = (picture_t *)buffer->user_data; 4873- out_picture = out_picture->p_next; 4874- } 4875- out_picture->date = buffer->pts; 4876- } else { 4877- msg_Dbg(filter, "Failed waiting for filtered picture"); 4878- break; 4879- } 4880+ if ((sys->out_q = mmal_queue_create()) == NULL) 4881+ { 4882+ msg_Err(filter, "Failed to create out Q"); 4883+ goto fail; 4884 } 4885- if (out_picture) 4886- out_picture->p_next = NULL; 4887 4888- return ret; 4889-} 4890- 4891-static void flush(filter_t *filter) 4892-{ 4893- filter_sys_t *sys = filter->p_sys; 4894- MMAL_BUFFER_HEADER_T *buffer; 4895+ sys->output = sys->component->output[0]; 4896+ mmal_format_full_copy(sys->output->format, sys->input->format); 4897 4898- msg_Dbg(filter, "flush deinterlace filter"); 4899+ if (!sys->is_cma) 4900+ { 4901+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS) 4902+ goto fail; 4903+ } 4904+ else 4905+ { 4906+ // CMA stuff 4907+ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter; 4908+ 4909+ if ((sys->cma_out_pool = cma_buf_pool_new(8, 8, true, "deinterlace")) == NULL) 4910+ { 4911+ msg_Err(filter, "Failed to alloc cma buf pool"); 4912+ goto fail; 4913+ } 4914 4915- msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)", 4916- sys->input_in_transit, sys->output_in_transit); 4917- mmal_port_flush(sys->output); 4918- mmal_port_flush(sys->input); 4919- 4920- msg_Dbg(filter, "flush: wait for all buffers to be returned"); 4921- while (atomic_load(&sys->input_in_transit) || 4922- atomic_load(&sys->output_in_transit)) 4923- vlc_sem_wait(&sys->sem); 4924- 4925- while ((buffer = mmal_queue_get(sys->filtered_pictures))) { 4926- picture_t *pic = (picture_t *)buffer->user_data; 4927- msg_Dbg(filter, "flush: release already filtered pic %p", 4928- (void *)pic); 4929- picture_Release(pic); 4930- } 4931- atomic_store(&sys->started, false); 4932- msg_Dbg(filter, "flush: done"); 4933-} 4934+ // Rate control done by CMA in flight logic, so have "inexhaustable" pool here 4935+ if ((sys->out_pool = mmal_pool_create(30, 0)) == NULL) 4936+ { 4937+ msg_Err(filter, "Failed to alloc out pool"); 4938+ goto fail; 4939+ } 4940 4941-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 4942-{ 4943- filter_t *filter = (filter_t *)port->userdata; 4944- MMAL_STATUS_T status; 4945+ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, true); 4946 4947- if (buffer->cmd == MMAL_EVENT_ERROR) { 4948- status = *(uint32_t *)buffer->data; 4949- msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status, 4950- mmal_status_to_string(status)); 4951- } 4952+ if ((status = mmal_port_format_commit(sys->output)) != MMAL_SUCCESS) 4953+ { 4954+ msg_Err(filter, "Output port format commit failed"); 4955+ goto fail; 4956+ } 4957 4958- mmal_buffer_header_release(buffer); 4959-} 4960+ sys->output->buffer_num = 30; 4961+ sys->output->buffer_size = sys->output->buffer_size_recommended; 4962 4963-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 4964-{ 4965- picture_t *picture = (picture_t *)buffer->user_data; 4966- filter_t *filter = (filter_t *)port->userdata; 4967- filter_sys_t *sys = filter->p_sys; 4968+ // CB just drops all bufs into out_q 4969+ if ((status = mmal_port_enable(sys->output, di_output_port_cb)) != MMAL_SUCCESS) 4970+ { 4971+ msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)", 4972+ sys->output->name, status, mmal_status_to_string(status)); 4973+ goto fail; 4974+ } 4975+ } 4976 4977- if (picture) { 4978- picture_Release(picture); 4979- } else { 4980- msg_Warn(filter, "Got buffer without picture on input port - OOOPS"); 4981- mmal_buffer_header_release(buffer); 4982+ status = mmal_component_enable(sys->component); 4983+ if (status != MMAL_SUCCESS) { 4984+ msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)", 4985+ sys->component->name, status, mmal_status_to_string(status)); 4986+ goto fail; 4987 } 4988 4989- atomic_fetch_sub(&sys->input_in_transit, 1); 4990- vlc_sem_post(&sys->sem); 4991+ filter->pf_video_filter = deinterlace; 4992+ filter->pf_flush = di_flush; 4993+ return 0; 4994+ 4995+fail: 4996+ CloseMmalDeinterlace(filter); 4997+ return ret; 4998 } 4999 5000-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 5001-{ 5002- filter_t *filter = (filter_t *)port->userdata; 5003- filter_sys_t *sys = filter->p_sys; 5004- picture_t *picture; 5005+vlc_module_begin() 5006+ set_shortname(N_("MMAL deinterlace")) 5007+ set_description(N_("MMAL-based deinterlace filter plugin")) 5008+ set_capability("video filter", 900) 5009+ set_category(CAT_VIDEO) 5010+ set_subcategory(SUBCAT_VIDEO_VFILTER) 5011+ set_callbacks(OpenMmalDeinterlace, CloseMmalDeinterlace) 5012+ add_shortcut("deinterlace") 5013+ add_bool(MMAL_DEINTERLACE_NO_QPU, false, MMAL_DEINTERLACE_NO_QPU_TEXT, 5014+ MMAL_DEINTERLACE_NO_QPU_LONGTEXT, true); 5015+ add_bool(MMAL_DEINTERLACE_ADV, false, MMAL_DEINTERLACE_ADV_TEXT, 5016+ MMAL_DEINTERLACE_ADV_LONGTEXT, true); 5017+ add_bool(MMAL_DEINTERLACE_FAST, false, MMAL_DEINTERLACE_FAST_TEXT, 5018+ MMAL_DEINTERLACE_FAST_LONGTEXT, true); 5019+ add_bool(MMAL_DEINTERLACE_NONE, false, MMAL_DEINTERLACE_NONE_TEXT, 5020+ MMAL_DEINTERLACE_NONE_LONGTEXT, true); 5021+ add_bool(MMAL_DEINTERLACE_HALF_RATE, false, MMAL_DEINTERLACE_HALF_RATE_TEXT, 5022+ MMAL_DEINTERLACE_HALF_RATE_LONGTEXT, true); 5023+ add_bool(MMAL_DEINTERLACE_FULL_RATE, false, MMAL_DEINTERLACE_FULL_RATE_TEXT, 5024+ MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true); 5025+ 5026+vlc_module_end() 5027+ 5028 5029- if (buffer->cmd == 0) { 5030- if (buffer->length > 0) { 5031- atomic_store(&sys->started, true); 5032- mmal_queue_put(sys->filtered_pictures, buffer); 5033- picture = (picture_t *)buffer->user_data; 5034- } else { 5035- picture = (picture_t *)buffer->user_data; 5036- picture_Release(picture); 5037- } 5038- 5039- atomic_fetch_sub(&sys->output_in_transit, 1); 5040- vlc_sem_post(&sys->sem); 5041- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { 5042- msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled"); 5043- mmal_buffer_header_release(buffer); 5044- } else { 5045- mmal_buffer_header_release(buffer); 5046- } 5047-} 5048--- /dev/null 5049+++ b/modules/hw/mmal/mmal_avcodec.c 5050@@ -0,0 +1,2175 @@ 5051+/***************************************************************************** 5052+ * video.c: video decoder using the libavcodec library 5053+ ***************************************************************************** 5054+ * Copyright (C) 1999-2001 VLC authors and VideoLAN 5055+ * $Id$ 5056+ * 5057+ * Authors: Laurent Aimar <fenrir@via.ecp.fr> 5058+ * Gildas Bazin <gbazin@videolan.org> 5059+ * 5060+ * This program is free software; you can redistribute it and/or modify it 5061+ * under the terms of the GNU Lesser General Public License as published by 5062+ * the Free Software Foundation; either version 2.1 of the License, or 5063+ * (at your option) any later version. 5064+ * 5065+ * This program is distributed in the hope that it will be useful, 5066+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 5067+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 5068+ * GNU Lesser General Public License for more details. 5069+ * 5070+ * You should have received a copy of the GNU Lesser General Public License 5071+ * along with this program; if not, write to the Free Software Foundation, 5072+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 5073+ *****************************************************************************/ 5074+ 5075+/***************************************************************************** 5076+ * Preamble 5077+ *****************************************************************************/ 5078+#include "config.h" 5079+ 5080+#include <vlc_common.h> 5081+#include <vlc_codec.h> 5082+#include <vlc_avcodec.h> 5083+#include <vlc_cpu.h> 5084+#include <vlc_atomic.h> 5085+#include <assert.h> 5086+ 5087+#include <libavcodec/avcodec.h> 5088+#include <libavutil/mem.h> 5089+#include <libavutil/pixdesc.h> 5090+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) ) 5091+#include <libavutil/mastering_display_metadata.h> 5092+#endif 5093+ 5094+//#include "avcodec.h" 5095+//#include "va.h" 5096+ 5097+#include <vlc_plugin.h> 5098+#include <libavutil/rpi_sand_fns.h> 5099+#include <libavcodec/rpi_zc.h> 5100+#include "../../codec/cc.h" 5101+#include "../../codec/avcodec/avcommon.h" // ??? Beware over inclusion 5102+#include "mmal_cma.h" 5103+#include "mmal_picture.h" 5104+ 5105+#define TRACE_ALL 0 5106+ 5107+#define BUFFERS_IN_FLIGHT 5 // Default max value for in flight buffers 5108+#define BUFFERS_IN_FLIGHT_UHD 3 // Fewer if very big 5109+ 5110+#define MMAL_AVCODEC_BUFFERS "mmal-avcodec-buffers" 5111+#define MMAL_AVCODEC_BUFFERS_TEXT N_("In flight buffer count before blocking.") 5112+#define MMAL_AVCODEC_BUFFERS_LONGTEXT N_("In flight buffer count before blocking. " \ 5113+"Beware that incautious changing of this can lead to lockup. " \ 5114+"Zero will disable the module.") 5115+ 5116+ 5117+// Fwd declarations required due to wanting to avoid reworking the original 5118+// code too much 5119+static void MmalAvcodecCloseDecoder( vlc_object_t *obj ); 5120+ 5121+ 5122+/***************************************************************************** 5123+ * decoder_sys_t : decoder descriptor 5124+ *****************************************************************************/ 5125+struct decoder_sys_t 5126+{ 5127+ AVCodecContext *p_context; 5128+ const AVCodec *p_codec; 5129+ 5130+ /* Video decoder specific part */ 5131+ date_t pts; 5132+ 5133+ /* Closed captions for decoders */ 5134+ cc_data_t cc; 5135+ 5136+ /* for frame skipping algo */ 5137+ bool b_hurry_up; 5138+ bool b_show_corrupted; 5139+ bool b_from_preroll; 5140+ enum AVDiscard i_skip_frame; 5141+ 5142+ /* how many decoded frames are late */ 5143+ int i_late_frames; 5144+ mtime_t i_late_frames_start; 5145+ mtime_t i_last_late_delay; 5146+ 5147+ /* for direct rendering */ 5148+ bool b_direct_rendering; 5149+ atomic_bool b_dr_failure; 5150+ 5151+ /* Hack to force display of still pictures */ 5152+ bool b_first_frame; 5153+ 5154+ 5155+ /* */ 5156+ bool palette_sent; 5157+ 5158+ /* VA API */ 5159+// vlc_va_t *p_va; 5160+ enum PixelFormat pix_fmt; 5161+ int profile; 5162+ int level; 5163+ 5164+ vlc_sem_t sem_mt; 5165+ 5166+ // Rpi vars 5167+ cma_buf_pool_t * cma_pool; 5168+ bool pool_alloc_1; 5169+ vcsm_init_type_t vcsm_init_type; 5170+ int cma_in_flight_max; 5171+ // Debug 5172+ decoder_t * p_dec; 5173+}; 5174+ 5175+ 5176+static vlc_fourcc_t 5177+ZcFindVlcChroma(const int i_ffmpeg_chroma) 5178+{ 5179+ switch (i_ffmpeg_chroma) 5180+ { 5181+ // This is all we claim to deal with 5182+ // In theory RGB should be doable within our current framework 5183+ case AV_PIX_FMT_YUV420P: 5184+ return VLC_CODEC_MMAL_ZC_I420; 5185+ case AV_PIX_FMT_SAND128: 5186+ case AV_PIX_FMT_RPI4_8: 5187+ return VLC_CODEC_MMAL_ZC_SAND8; 5188+ case AV_PIX_FMT_SAND64_10: 5189+ return VLC_CODEC_MMAL_ZC_SAND10; 5190+ case AV_PIX_FMT_RPI4_10: 5191+ return VLC_CODEC_MMAL_ZC_SAND30; 5192+ default: 5193+ break; 5194+ } 5195+ return 0; 5196+} 5197+ 5198+// Pix Fmt conv for MMal 5199+// video_fromat from ffmpeg pic_fmt 5200+static int 5201+ZcGetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma ) 5202+{ 5203+ fmt->i_rmask = 0; 5204+ fmt->i_gmask = 0; 5205+ fmt->i_bmask = 0; 5206+ fmt->i_chroma = ZcFindVlcChroma(i_ffmpeg_chroma); 5207+ 5208+ return fmt->i_chroma == 0 ? -1 : 0; 5209+} 5210+ 5211+ 5212+// Format chooser is way simpler than vlc 5213+static enum PixelFormat 5214+ZcGetFormat(AVCodecContext *p_context, const enum PixelFormat *pi_fmt) 5215+{ 5216+ enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt); 5217+ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++) 5218+ { 5219+ if (ZcFindVlcChroma(pi_fmt[i]) != 0) 5220+ return pi_fmt[i]; 5221+ } 5222+ return swfmt; 5223+} 5224+ 5225+ 5226+static void cma_avbuf_pool_free(void * v) 5227+{ 5228+ cma_buf_unref(v); 5229+} 5230+ 5231+static unsigned int zc_buf_vcsm_handle(void * v) 5232+{ 5233+ return cma_buf_vcsm_handle(v); 5234+} 5235+ 5236+static unsigned int zc_buf_vc_handle(void * v) 5237+{ 5238+ return cma_buf_vc_handle(v); 5239+} 5240+ 5241+static void * zc_buf_map_arm(void * v) 5242+{ 5243+ return cma_buf_addr(v); 5244+} 5245+ 5246+static unsigned int zc_buf_map_vc(void * v) 5247+{ 5248+ return cma_buf_vc_addr(v); 5249+} 5250+ 5251+ 5252+ 5253+static const av_rpi_zc_buf_fn_tab_t zc_buf_fn_tab = { 5254+ .free = cma_avbuf_pool_free, 5255+ 5256+ .vcsm_handle = zc_buf_vcsm_handle, 5257+ .vc_handle = zc_buf_vc_handle, 5258+ .map_arm = zc_buf_map_arm, 5259+ .map_vc = zc_buf_map_vc 5260+}; 5261+ 5262+ 5263+static AVBufferRef * 5264+zc_alloc_buf(void * v, size_t size, const AVRpiZcFrameGeometry * geo) 5265+{ 5266+ decoder_t * const dec = v; 5267+ decoder_sys_t * const sys = dec->p_sys; 5268+ 5269+ VLC_UNUSED(geo); 5270+ 5271+ assert(sys != NULL); 5272+ 5273+ const unsigned int dec_pool_req = av_rpi_zc_get_decoder_pool_size(sys->p_context->opaque); 5274+ if (dec_pool_req != 0) 5275+ { 5276+ cma_buf_pool_resize(sys->cma_pool, dec_pool_req + sys->cma_in_flight_max, sys->cma_in_flight_max); 5277+ 5278+ if (!sys->pool_alloc_1) 5279+ { 5280+ sys->pool_alloc_1 = true; 5281+ msg_Dbg(dec, "Pool size: (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size); 5282+ if (cma_buf_pool_fill(sys->cma_pool, size) != 0) 5283+ msg_Warn(dec, "Failed to preallocate decoder pool (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size); 5284+ } 5285+ } 5286+ 5287+ void * const cmabuf = cma_buf_pool_alloc_buf(sys->cma_pool, size); 5288+ 5289+ if (cmabuf == NULL) 5290+ { 5291+ msg_Err(dec, "CMA buf pool alloc buf failed"); 5292+ return NULL; 5293+ } 5294+ 5295+ AVBufferRef *const avbuf = av_rpi_zc_buf(cma_buf_size(cmabuf), 0, cmabuf, &zc_buf_fn_tab); 5296+ 5297+ if (avbuf == NULL) 5298+ { 5299+ msg_Err(dec, "av_rpi_zc_buf failed"); 5300+ cma_buf_unref(cmabuf); 5301+ return NULL; 5302+ } 5303+ 5304+ return avbuf; 5305+} 5306+ 5307+static void 5308+zc_free_pool(void * v) 5309+{ 5310+ decoder_t * const dec = v; 5311+ cma_buf_pool_delete(dec->p_sys->cma_pool); 5312+} 5313+ 5314+ 5315+static const uint8_t shift_01[] = {0,1,1,1}; 5316+static const uint8_t pb_1[] = {1,1,1,1}; 5317+static const uint8_t pb_12[] = {1,2,2,2}; 5318+static const uint8_t pb_24[] = {2,4,4,4}; 5319+static const uint8_t pb_4[] = {4,4,4,4}; 5320+ 5321+static int set_pic_from_frame(picture_t * const pic, const AVFrame * const frame) 5322+{ 5323+ const uint8_t * hs = shift_01; 5324+ const uint8_t * ws = shift_01; 5325+ const uint8_t * pb = pb_1; 5326+ 5327+ switch (pic->format.i_chroma) 5328+ { 5329+ case VLC_CODEC_MMAL_ZC_RGB32: 5330+ pic->i_planes = 1; 5331+ pb = pb_4; 5332+ break; 5333+ case VLC_CODEC_MMAL_ZC_I420: 5334+ pic->i_planes = 3; 5335+ break; 5336+ case VLC_CODEC_MMAL_ZC_SAND8: 5337+ pic->i_planes = 2; 5338+ pb = pb_12; 5339+ break; 5340+ case VLC_CODEC_MMAL_ZC_SAND10: 5341+ case VLC_CODEC_MMAL_ZC_SAND30: // Lies: SAND30 is "special" 5342+ pic->i_planes = 2; 5343+ pb = pb_24; 5344+ break; 5345+ default: 5346+ return VLC_EGENERIC; 5347+ } 5348+ 5349+ const cma_buf_t * const cb = cma_buf_pic_get(pic); 5350+ uint8_t * const data = cma_buf_addr(cb); 5351+ if (data == NULL) { 5352+ return VLC_ENOMEM; 5353+ } 5354+ 5355+ uint8_t * frame_end = frame->data[0] + cma_buf_size(cb); 5356+ for (int i = 0; i != pic->i_planes; ++i) { 5357+ // Calculate lines from gap between planes 5358+ // This will give us an accurate "height" for later use by MMAL 5359+ const int lines = ((i + 1 == pic->i_planes ? frame_end : frame->data[i + 1]) - 5360+ frame->data[i]) / frame->linesize[i]; 5361+ pic->p[i] = (plane_t){ 5362+ .p_pixels = data + (frame->data[i] - frame->data[0]), 5363+ .i_lines = lines, 5364+ .i_pitch = frame->linesize[i], 5365+ .i_pixel_pitch = pb[i], 5366+ .i_visible_lines = av_frame_cropped_height(frame) >> hs[i], 5367+ .i_visible_pitch = av_frame_cropped_width(frame) >> ws[i] 5368+ }; 5369+ } 5370+ return 0; 5371+} 5372+ 5373+ 5374+//============================================================================ 5375+// 5376+// Nicked from avcodec/fourcc.c 5377+// 5378+// * Really we should probably use that directly 5379+ 5380+/* 5381+ * Video Codecs 5382+ */ 5383+ 5384+struct vlc_avcodec_fourcc 5385+{ 5386+ vlc_fourcc_t i_fourcc; 5387+ unsigned i_codec; 5388+}; 5389+ 5390+ 5391+static const struct vlc_avcodec_fourcc video_codecs[] = 5392+{ 5393+ { VLC_CODEC_MP1V, AV_CODEC_ID_MPEG1VIDEO }, 5394+ { VLC_CODEC_MP2V, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */ 5395+ { VLC_CODEC_MPGV, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */ 5396+ /* AV_CODEC_ID_MPEG2VIDEO_XVMC */ 5397+ { VLC_CODEC_H261, AV_CODEC_ID_H261 }, 5398+ { VLC_CODEC_H263, AV_CODEC_ID_H263 }, 5399+ { VLC_CODEC_RV10, AV_CODEC_ID_RV10 }, 5400+ { VLC_CODEC_RV13, AV_CODEC_ID_RV10 }, 5401+ { VLC_CODEC_RV20, AV_CODEC_ID_RV20 }, 5402+ { VLC_CODEC_MJPG, AV_CODEC_ID_MJPEG }, 5403+ { VLC_CODEC_MJPGB, AV_CODEC_ID_MJPEGB }, 5404+ { VLC_CODEC_LJPG, AV_CODEC_ID_LJPEG }, 5405+ { VLC_CODEC_SP5X, AV_CODEC_ID_SP5X }, 5406+ { VLC_CODEC_JPEGLS, AV_CODEC_ID_JPEGLS }, 5407+ { VLC_CODEC_MP4V, AV_CODEC_ID_MPEG4 }, 5408+ /* AV_CODEC_ID_RAWVIDEO */ 5409+ { VLC_CODEC_DIV1, AV_CODEC_ID_MSMPEG4V1 }, 5410+ { VLC_CODEC_DIV2, AV_CODEC_ID_MSMPEG4V2 }, 5411+ { VLC_CODEC_DIV3, AV_CODEC_ID_MSMPEG4V3 }, 5412+ { VLC_CODEC_WMV1, AV_CODEC_ID_WMV1 }, 5413+ { VLC_CODEC_WMV2, AV_CODEC_ID_WMV2 }, 5414+ { VLC_CODEC_H263P, AV_CODEC_ID_H263P }, 5415+ { VLC_CODEC_H263I, AV_CODEC_ID_H263I }, 5416+ { VLC_CODEC_FLV1, AV_CODEC_ID_FLV1 }, 5417+ { VLC_CODEC_SVQ1, AV_CODEC_ID_SVQ1 }, 5418+ { VLC_CODEC_SVQ3, AV_CODEC_ID_SVQ3 }, 5419+ { VLC_CODEC_DV, AV_CODEC_ID_DVVIDEO }, 5420+ { VLC_CODEC_HUFFYUV, AV_CODEC_ID_HUFFYUV }, 5421+ { VLC_CODEC_CYUV, AV_CODEC_ID_CYUV }, 5422+ { VLC_CODEC_H264, AV_CODEC_ID_H264 }, 5423+ { VLC_CODEC_INDEO3, AV_CODEC_ID_INDEO3 }, 5424+ { VLC_CODEC_VP3, AV_CODEC_ID_VP3 }, 5425+ { VLC_CODEC_THEORA, AV_CODEC_ID_THEORA }, 5426+#if ( !defined( WORDS_BIGENDIAN ) ) 5427+ /* Asus Video (Another thing that doesn't work on PPC) */ 5428+ { VLC_CODEC_ASV1, AV_CODEC_ID_ASV1 }, 5429+ { VLC_CODEC_ASV2, AV_CODEC_ID_ASV2 }, 5430+#endif 5431+ { VLC_CODEC_FFV1, AV_CODEC_ID_FFV1 }, 5432+ { VLC_CODEC_4XM, AV_CODEC_ID_4XM }, 5433+ { VLC_CODEC_VCR1, AV_CODEC_ID_VCR1 }, 5434+ { VLC_CODEC_CLJR, AV_CODEC_ID_CLJR }, 5435+ { VLC_CODEC_MDEC, AV_CODEC_ID_MDEC }, 5436+ { VLC_CODEC_ROQ, AV_CODEC_ID_ROQ }, 5437+ { VLC_CODEC_INTERPLAY, AV_CODEC_ID_INTERPLAY_VIDEO }, 5438+ { VLC_CODEC_XAN_WC3, AV_CODEC_ID_XAN_WC3 }, 5439+ { VLC_CODEC_XAN_WC4, AV_CODEC_ID_XAN_WC4 }, 5440+ { VLC_CODEC_RPZA, AV_CODEC_ID_RPZA }, 5441+ { VLC_CODEC_CINEPAK, AV_CODEC_ID_CINEPAK }, 5442+ { VLC_CODEC_WS_VQA, AV_CODEC_ID_WS_VQA }, 5443+ { VLC_CODEC_MSRLE, AV_CODEC_ID_MSRLE }, 5444+ { VLC_CODEC_MSVIDEO1, AV_CODEC_ID_MSVIDEO1 }, 5445+ { VLC_CODEC_IDCIN, AV_CODEC_ID_IDCIN }, 5446+ { VLC_CODEC_8BPS, AV_CODEC_ID_8BPS }, 5447+ { VLC_CODEC_SMC, AV_CODEC_ID_SMC }, 5448+ { VLC_CODEC_FLIC, AV_CODEC_ID_FLIC }, 5449+ { VLC_CODEC_TRUEMOTION1, AV_CODEC_ID_TRUEMOTION1 }, 5450+ { VLC_CODEC_VMDVIDEO, AV_CODEC_ID_VMDVIDEO }, 5451+ { VLC_CODEC_LCL_MSZH, AV_CODEC_ID_MSZH }, 5452+ { VLC_CODEC_LCL_ZLIB, AV_CODEC_ID_ZLIB }, 5453+ { VLC_CODEC_QTRLE, AV_CODEC_ID_QTRLE }, 5454+ { VLC_CODEC_TSCC, AV_CODEC_ID_TSCC }, 5455+ { VLC_CODEC_ULTI, AV_CODEC_ID_ULTI }, 5456+ { VLC_CODEC_QDRAW, AV_CODEC_ID_QDRAW }, 5457+ { VLC_CODEC_VIXL, AV_CODEC_ID_VIXL }, 5458+ { VLC_CODEC_QPEG, AV_CODEC_ID_QPEG }, 5459+ { VLC_CODEC_PNG, AV_CODEC_ID_PNG }, 5460+ { VLC_CODEC_PPM, AV_CODEC_ID_PPM }, 5461+ /* AV_CODEC_ID_PBM */ 5462+ { VLC_CODEC_PGM, AV_CODEC_ID_PGM }, 5463+ { VLC_CODEC_PGMYUV, AV_CODEC_ID_PGMYUV }, 5464+ { VLC_CODEC_PAM, AV_CODEC_ID_PAM }, 5465+ { VLC_CODEC_FFVHUFF, AV_CODEC_ID_FFVHUFF }, 5466+ { VLC_CODEC_RV30, AV_CODEC_ID_RV30 }, 5467+ { VLC_CODEC_RV40, AV_CODEC_ID_RV40 }, 5468+ { VLC_CODEC_VC1, AV_CODEC_ID_VC1 }, 5469+ { VLC_CODEC_WMVA, AV_CODEC_ID_VC1 }, 5470+ { VLC_CODEC_WMV3, AV_CODEC_ID_WMV3 }, 5471+ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3 }, 5472+ { VLC_CODEC_LOCO, AV_CODEC_ID_LOCO }, 5473+ { VLC_CODEC_WNV1, AV_CODEC_ID_WNV1 }, 5474+ { VLC_CODEC_AASC, AV_CODEC_ID_AASC }, 5475+ { VLC_CODEC_INDEO2, AV_CODEC_ID_INDEO2 }, 5476+ { VLC_CODEC_FRAPS, AV_CODEC_ID_FRAPS }, 5477+ { VLC_CODEC_TRUEMOTION2, AV_CODEC_ID_TRUEMOTION2 }, 5478+ { VLC_CODEC_BMP, AV_CODEC_ID_BMP }, 5479+ { VLC_CODEC_CSCD, AV_CODEC_ID_CSCD }, 5480+ { VLC_CODEC_MMVIDEO, AV_CODEC_ID_MMVIDEO }, 5481+ { VLC_CODEC_ZMBV, AV_CODEC_ID_ZMBV }, 5482+ { VLC_CODEC_AVS, AV_CODEC_ID_AVS }, 5483+ { VLC_CODEC_SMACKVIDEO, AV_CODEC_ID_SMACKVIDEO }, 5484+ { VLC_CODEC_NUV, AV_CODEC_ID_NUV }, 5485+ { VLC_CODEC_KMVC, AV_CODEC_ID_KMVC }, 5486+ { VLC_CODEC_FLASHSV, AV_CODEC_ID_FLASHSV }, 5487+ { VLC_CODEC_CAVS, AV_CODEC_ID_CAVS }, 5488+ { VLC_CODEC_JPEG2000, AV_CODEC_ID_JPEG2000 }, 5489+ { VLC_CODEC_VMNC, AV_CODEC_ID_VMNC }, 5490+ { VLC_CODEC_VP5, AV_CODEC_ID_VP5 }, 5491+ { VLC_CODEC_VP6, AV_CODEC_ID_VP6 }, 5492+ { VLC_CODEC_VP6F, AV_CODEC_ID_VP6F }, 5493+ { VLC_CODEC_TARGA, AV_CODEC_ID_TARGA }, 5494+ { VLC_CODEC_DSICINVIDEO, AV_CODEC_ID_DSICINVIDEO }, 5495+ { VLC_CODEC_TIERTEXSEQVIDEO, AV_CODEC_ID_TIERTEXSEQVIDEO }, 5496+ { VLC_CODEC_TIFF, AV_CODEC_ID_TIFF }, 5497+ { VLC_CODEC_GIF, AV_CODEC_ID_GIF }, 5498+ { VLC_CODEC_DXA, AV_CODEC_ID_DXA }, 5499+ { VLC_CODEC_DNXHD, AV_CODEC_ID_DNXHD }, 5500+ { VLC_CODEC_THP, AV_CODEC_ID_THP }, 5501+ { VLC_CODEC_SGI, AV_CODEC_ID_SGI }, 5502+ { VLC_CODEC_C93, AV_CODEC_ID_C93 }, 5503+ { VLC_CODEC_BETHSOFTVID, AV_CODEC_ID_BETHSOFTVID }, 5504+ /* AV_CODEC_ID_PTX */ 5505+ { VLC_CODEC_TXD, AV_CODEC_ID_TXD }, 5506+ { VLC_CODEC_VP6A, AV_CODEC_ID_VP6A }, 5507+ { VLC_CODEC_AMV, AV_CODEC_ID_AMV }, 5508+ { VLC_CODEC_VB, AV_CODEC_ID_VB }, 5509+ { VLC_CODEC_PCX, AV_CODEC_ID_PCX }, 5510+ /* AV_CODEC_ID_SUNRAST */ 5511+ { VLC_CODEC_INDEO4, AV_CODEC_ID_INDEO4 }, 5512+ { VLC_CODEC_INDEO5, AV_CODEC_ID_INDEO5 }, 5513+ { VLC_CODEC_MIMIC, AV_CODEC_ID_MIMIC }, 5514+ { VLC_CODEC_RL2, AV_CODEC_ID_RL2 }, 5515+ { VLC_CODEC_ESCAPE124, AV_CODEC_ID_ESCAPE124 }, 5516+ { VLC_CODEC_DIRAC, AV_CODEC_ID_DIRAC }, 5517+ { VLC_CODEC_BFI, AV_CODEC_ID_BFI }, 5518+ { VLC_CODEC_CMV, AV_CODEC_ID_CMV }, 5519+ { VLC_CODEC_MOTIONPIXELS, AV_CODEC_ID_MOTIONPIXELS }, 5520+ { VLC_CODEC_TGV, AV_CODEC_ID_TGV }, 5521+ { VLC_CODEC_TGQ, AV_CODEC_ID_TGQ }, 5522+ { VLC_CODEC_TQI, AV_CODEC_ID_TQI }, 5523+ { VLC_CODEC_AURA, AV_CODEC_ID_AURA }, 5524+ /* AV_CODEC_ID_AURA2 */ 5525+ /* AV_CODEC_ID_V210X */ 5526+ { VLC_CODEC_TMV, AV_CODEC_ID_TMV }, 5527+ { VLC_CODEC_V210, AV_CODEC_ID_V210 }, 5528+#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 54, 50, 100 ) && LIBAVCODEC_VERSION_MICRO >= 100 5529+ { VLC_CODEC_VUYA, AV_CODEC_ID_AYUV }, 5530+#endif 5531+ /* AV_CODEC_ID_DPX */ 5532+ { VLC_CODEC_MAD, AV_CODEC_ID_MAD }, 5533+ { VLC_CODEC_FRWU, AV_CODEC_ID_FRWU }, 5534+ { VLC_CODEC_FLASHSV2, AV_CODEC_ID_FLASHSV2 }, 5535+ /* AV_CODEC_ID_CDGRAPHICS */ 5536+ /* AV_CODEC_ID_R210 */ 5537+ { VLC_CODEC_ANM, AV_CODEC_ID_ANM }, 5538+ { VLC_CODEC_BINKVIDEO, AV_CODEC_ID_BINKVIDEO }, 5539+ /* AV_CODEC_ID_IFF_ILBM */ 5540+ /* AV_CODEC_ID_IFF_BYTERUN1 */ 5541+ { VLC_CODEC_KGV1, AV_CODEC_ID_KGV1 }, 5542+ { VLC_CODEC_YOP, AV_CODEC_ID_YOP }, 5543+ { VLC_CODEC_VP8, AV_CODEC_ID_VP8 }, 5544+ /* AV_CODEC_ID_PICTOR */ 5545+ /* AV_CODEC_ID_ANSI */ 5546+ /* AV_CODEC_ID_A64_MULTI */ 5547+ /* AV_CODEC_ID_A64_MULTI5 */ 5548+ /* AV_CODEC_ID_R10K */ 5549+ { VLC_CODEC_MXPEG, AV_CODEC_ID_MXPEG }, 5550+ { VLC_CODEC_LAGARITH, AV_CODEC_ID_LAGARITH }, 5551+ { VLC_CODEC_PRORES, AV_CODEC_ID_PRORES }, 5552+ { VLC_CODEC_JV, AV_CODEC_ID_JV }, 5553+ { VLC_CODEC_DFA, AV_CODEC_ID_DFA }, 5554+ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3IMAGE }, 5555+ { VLC_CODEC_WMVP2, AV_CODEC_ID_VC1IMAGE }, 5556+ { VLC_CODEC_UTVIDEO, AV_CODEC_ID_UTVIDEO }, 5557+ { VLC_CODEC_BMVVIDEO, AV_CODEC_ID_BMV_VIDEO }, 5558+ { VLC_CODEC_VBLE, AV_CODEC_ID_VBLE }, 5559+ { VLC_CODEC_DXTORY, AV_CODEC_ID_DXTORY }, 5560+ /* AV_CODEC_ID_V410 */ 5561+ /* AV_CODEC_ID_XWD */ 5562+ { VLC_CODEC_CDXL, AV_CODEC_ID_CDXL }, 5563+ /* AV_CODEC_ID_XBM */ 5564+ /* AV_CODEC_ID_ZEROCODEC */ 5565+ { VLC_CODEC_MSS1, AV_CODEC_ID_MSS1 }, 5566+ { VLC_CODEC_MSA1, AV_CODEC_ID_MSA1 }, 5567+ { VLC_CODEC_TSC2, AV_CODEC_ID_TSCC2 }, 5568+ { VLC_CODEC_MTS2, AV_CODEC_ID_MTS2 }, 5569+ { VLC_CODEC_CLLC, AV_CODEC_ID_CLLC }, 5570+ { VLC_CODEC_MSS2, AV_CODEC_ID_MSS2 }, 5571+ { VLC_CODEC_VP9, AV_CODEC_ID_VP9 }, 5572+#if LIBAVCODEC_VERSION_CHECK( 57, 26, 0, 83, 101 ) 5573+ { VLC_CODEC_AV1, AV_CODEC_ID_AV1 }, 5574+#endif 5575+ { VLC_CODEC_ICOD, AV_CODEC_ID_AIC }, 5576+ /* AV_CODEC_ID_ESCAPE130 */ 5577+ { VLC_CODEC_G2M4, AV_CODEC_ID_G2M }, 5578+ { VLC_CODEC_G2M2, AV_CODEC_ID_G2M }, 5579+ { VLC_CODEC_G2M3, AV_CODEC_ID_G2M }, 5580+ /* AV_CODEC_ID_WEBP */ 5581+ { VLC_CODEC_HNM4_VIDEO, AV_CODEC_ID_HNM4_VIDEO }, 5582+ { VLC_CODEC_HEVC, AV_CODEC_ID_HEVC }, 5583+ 5584+ { VLC_CODEC_FIC , AV_CODEC_ID_FIC }, 5585+ /* AV_CODEC_ID_ALIAS_PIX */ 5586+ /* AV_CODEC_ID_BRENDER_PIX */ 5587+ /* AV_CODEC_ID_PAF_VIDEO */ 5588+ /* AV_CODEC_ID_EXR */ 5589+ 5590+ { VLC_CODEC_VP7 , AV_CODEC_ID_VP7 }, 5591+ /* AV_CODEC_ID_SANM */ 5592+ /* AV_CODEC_ID_SGIRLE */ 5593+ /* AV_CODEC_ID_MVC1 */ 5594+ /* AV_CODEC_ID_MVC2 */ 5595+ { VLC_CODEC_HQX, AV_CODEC_ID_HQX }, 5596+ 5597+ { VLC_CODEC_TDSC, AV_CODEC_ID_TDSC }, 5598+ 5599+ { VLC_CODEC_HQ_HQA, AV_CODEC_ID_HQ_HQA }, 5600+ 5601+ { VLC_CODEC_HAP, AV_CODEC_ID_HAP }, 5602+ /* AV_CODEC_ID_DDS */ 5603+ 5604+ { VLC_CODEC_DXV, AV_CODEC_ID_DXV }, 5605+ 5606+ /* ffmpeg only: AV_CODEC_ID_BRENDER_PIX */ 5607+ /* ffmpeg only: AV_CODEC_ID_Y41P */ 5608+ /* ffmpeg only: AV_CODEC_ID_EXR */ 5609+ /* ffmpeg only: AV_CODEC_ID_AVRP */ 5610+ /* ffmpeg only: AV_CODEC_ID_012V */ 5611+ /* ffmpeg only: AV_CODEC_ID_AVUI */ 5612+ /* ffmpeg only: AV_CODEC_ID_TARGA_Y216 */ 5613+ /* ffmpeg only: AV_CODEC_ID_V308 */ 5614+ /* ffmpeg only: AV_CODEC_ID_V408 */ 5615+ /* ffmpeg only: AV_CODEC_ID_YUV4 */ 5616+ /* ffmpeg only: AV_CODEC_ID_SANM */ 5617+ /* ffmpeg only: AV_CODEC_ID_PAF_VIDEO */ 5618+ /* ffmpeg only: AV_CODEC_ID_AVRN */ 5619+ /* ffmpeg only: AV_CODEC_ID_CPIA */ 5620+ /* ffmpeg only: AV_CODEC_ID_XFACE */ 5621+ /* ffmpeg only: AV_CODEC_ID_SGIRLE */ 5622+ /* ffmpeg only: AV_CODEC_ID_MVC1 */ 5623+ /* ffmpeg only: AV_CODEC_ID_MVC2 */ 5624+ /* ffmpeg only: AV_CODEC_ID_SNOW */ 5625+ /* ffmpeg only: AV_CODEC_ID_SMVJPEG */ 5626+ 5627+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 24, 102 ) 5628+ { VLC_CODEC_CINEFORM, AV_CODEC_ID_CFHD }, 5629+#endif 5630+ 5631+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 70, 100 ) 5632+ { VLC_CODEC_PIXLET, AV_CODEC_ID_PIXLET }, 5633+#endif 5634+ 5635+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 101 ) 5636+ { VLC_CODEC_SPEEDHQ, AV_CODEC_ID_SPEEDHQ }, 5637+#endif 5638+ 5639+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 79, 100 ) 5640+ { VLC_CODEC_FMVC, AV_CODEC_ID_FMVC }, 5641+#endif 5642+}; 5643+ 5644+// *** Really we should probably use GetFfmpegCodec with a pre-kludge for the bits we care about 5645+static bool 5646+ZcGetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc, 5647+ unsigned *pi_ffmpeg_codec, const char **ppsz_name ) 5648+{ 5649+ const struct vlc_avcodec_fourcc *base; 5650+ size_t count; 5651+ 5652+ base = video_codecs; 5653+ count = ARRAY_SIZE(video_codecs); 5654+ i_fourcc = vlc_fourcc_GetCodec( cat, i_fourcc ); 5655+ 5656+ for( size_t i = 0; i < count; i++ ) 5657+ { 5658+ if( base[i].i_fourcc == i_fourcc ) 5659+ { 5660+ if( pi_ffmpeg_codec != NULL ) 5661+ *pi_ffmpeg_codec = base[i].i_codec; 5662+ if( ppsz_name ) 5663+ *ppsz_name = vlc_fourcc_GetDescription( cat, i_fourcc ); 5664+ return true; 5665+ } 5666+ } 5667+ return false; 5668+} 5669+ 5670+ 5671+ 5672+//============================================================================ 5673+// Derived from codec/avcodec/avcodec.c 5674+ 5675+static AVCodecContext * 5676+ZcFfmpeg_AllocContext( decoder_t *p_dec, 5677+ const AVCodec **restrict codecp ) 5678+{ 5679+ unsigned i_codec_id; 5680+ const char *psz_namecodec; 5681+ const AVCodec *p_codec = NULL; 5682+ 5683+ /* *** determine codec type *** */ 5684+ if( !ZcGetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec, 5685+ &i_codec_id, &psz_namecodec ) ) 5686+ return NULL; 5687+ 5688+ msg_Dbg( p_dec, "using %s %s", AVPROVIDER(LIBAVCODEC), LIBAVCODEC_IDENT ); 5689+ 5690+ /* Initialization must be done before avcodec_find_decoder() */ 5691+ vlc_init_avcodec(VLC_OBJECT(p_dec)); 5692+ 5693+ /* *** ask ffmpeg for a decoder *** */ 5694+ char *psz_decoder = var_InheritString( p_dec, "avcodec-codec" ); 5695+ if( psz_decoder != NULL ) 5696+ { 5697+ p_codec = avcodec_find_decoder_by_name( psz_decoder ); 5698+ if( !p_codec ) 5699+ msg_Err( p_dec, "Decoder `%s' not found", psz_decoder ); 5700+ else if( p_codec->id != i_codec_id ) 5701+ { 5702+ msg_Err( p_dec, "Decoder `%s' can't handle %4.4s", 5703+ psz_decoder, (char*)&p_dec->fmt_in.i_codec ); 5704+ p_codec = NULL; 5705+ } 5706+ free( psz_decoder ); 5707+ } 5708+ if( !p_codec ) 5709+// p_codec = avcodec_find_decoder( i_codec_id ); 5710+ { 5711+ if( p_dec->fmt_in.i_codec != VLC_CODEC_HEVC ) 5712+ p_codec = avcodec_find_decoder(i_codec_id); 5713+ else 5714+ { 5715+ psz_namecodec = rpi_is_model_pi4() ? "hevc" : "hevc_rpi"; 5716+ msg_Info(p_dec, "Looking for HEVC decoder '%s'", psz_namecodec); 5717+ p_codec = avcodec_find_decoder_by_name(psz_namecodec); 5718+ } 5719+ } 5720+ 5721+ if( !p_codec ) 5722+ { 5723+ msg_Dbg( p_dec, "codec not found (%s)", psz_namecodec ); 5724+ return NULL; 5725+ } 5726+ 5727+ *codecp = p_codec; 5728+ 5729+ /* *** get a p_context *** */ 5730+ AVCodecContext *avctx = avcodec_alloc_context3(p_codec); 5731+ if( unlikely(avctx == NULL) ) 5732+ return NULL; 5733+ 5734+ avctx->debug = var_InheritInteger( p_dec, "avcodec-debug" ); 5735+ avctx->opaque = p_dec; 5736+ return avctx; 5737+} 5738+ 5739+/***************************************************************************** 5740+ * ffmpeg_OpenCodec: 5741+ *****************************************************************************/ 5742+ 5743+static int 5744+ZcFfmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx, 5745+ const AVCodec *codec ) 5746+{ 5747+ char *psz_opts = var_InheritString( p_dec, "avcodec-options" ); 5748+ AVDictionary *options = NULL; 5749+ int ret; 5750+ 5751+ if (psz_opts) { 5752+ vlc_av_get_options(psz_opts, &options); 5753+ free(psz_opts); 5754+ } 5755+ 5756+ if (av_rpi_zc_init2(ctx, p_dec, zc_alloc_buf, zc_free_pool) != 0) 5757+ { 5758+ msg_Err(p_dec, "Failed to init AV ZC"); 5759+ return VLC_EGENERIC; 5760+ } 5761+ 5762+ vlc_avcodec_lock(); 5763+ ret = avcodec_open2( ctx, codec, options ? &options : NULL ); 5764+ vlc_avcodec_unlock(); 5765+ 5766+ AVDictionaryEntry *t = NULL; 5767+ while ((t = av_dict_get(options, "", t, AV_DICT_IGNORE_SUFFIX))) { 5768+ msg_Err( p_dec, "Unknown option \"%s\"", t->key ); 5769+ } 5770+ av_dict_free(&options); 5771+ 5772+ if( ret < 0 ) 5773+ { 5774+ msg_Err( p_dec, "cannot start codec (%s)", codec->name ); 5775+ return VLC_EGENERIC; 5776+ } 5777+ 5778+ msg_Dbg( p_dec, "codec (%s) started", codec->name ); 5779+ return VLC_SUCCESS; 5780+} 5781+ 5782+//============================================================================ 5783+// Derived from 3.0.7.1 codec/avcodec/video.c 5784+ 5785+static inline void wait_mt(decoder_sys_t *sys) 5786+{ 5787+#if 1 5788+ // As we only ever update the output in our main thread this lock is 5789+ // redundant 5790+ VLC_UNUSED(sys); 5791+#else 5792+ vlc_sem_wait(&sys->sem_mt); 5793+#endif 5794+} 5795+ 5796+static inline void post_mt(decoder_sys_t *sys) 5797+{ 5798+#if 1 5799+ // As we only ever update the output in our main thread this lock is 5800+ // redundant 5801+ VLC_UNUSED(sys); 5802+#else 5803+ vlc_sem_post(&sys->sem_mt); 5804+#endif 5805+} 5806+ 5807+/***************************************************************************** 5808+ * Local prototypes 5809+ *****************************************************************************/ 5810+static void ffmpeg_InitCodec ( decoder_t * ); 5811+static int DecodeVideo( decoder_t *, block_t * ); 5812+static void Flush( decoder_t * ); 5813+ 5814+static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc ) 5815+{ 5816+ uint8_t *p = (uint8_t*)&fcc; 5817+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); 5818+} 5819+ 5820+/***************************************************************************** 5821+ * Local Functions 5822+ *****************************************************************************/ 5823+ 5824+/** 5825+ * Sets the decoder output format. 5826+ */ 5827+static int lavc_GetVideoFormat(decoder_t *dec, video_format_t *restrict fmt, 5828+ AVCodecContext *ctx, enum AVPixelFormat pix_fmt, 5829+ enum AVPixelFormat sw_pix_fmt) 5830+{ 5831+ int width = ctx->coded_width; 5832+ int height = ctx->coded_height; 5833+ 5834+ video_format_Init(fmt, 0); 5835+ 5836+#if 1 5837+ VLC_UNUSED(sw_pix_fmt); 5838+ if ((fmt->i_chroma = ZcFindVlcChroma(pix_fmt)) == 0) 5839+ return -1; 5840+#else 5841+ if (pix_fmt == sw_pix_fmt) 5842+ { /* software decoding */ 5843+ int aligns[AV_NUM_DATA_POINTERS]; 5844+ 5845+ if (GetVlcChroma(fmt, pix_fmt)) 5846+ return -1; 5847+ 5848+ /* The libavcodec palette can only be fetched when the first output 5849+ * frame is decoded. Assume that the current chroma is RGB32 while we 5850+ * are waiting for a valid palette. Indeed, fmt_out.video.p_palette 5851+ * doesn't trigger a new vout request, but a new chroma yes. */ 5852+ if (pix_fmt == AV_PIX_FMT_PAL8 && !dec->fmt_out.video.p_palette) 5853+ fmt->i_chroma = VLC_CODEC_RGB32; 5854+ 5855+ avcodec_align_dimensions2(ctx, &width, &height, aligns); 5856+ } 5857+ else /* hardware decoding */ 5858+ fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt); 5859+#endif 5860+ 5861+ if( width == 0 || height == 0 || width > 8192 || height > 8192 || 5862+ width < ctx->width || height < ctx->height ) 5863+ { 5864+ msg_Err(dec, "Invalid frame size %dx%d vsz %dx%d", 5865+ width, height, ctx->width, ctx->height ); 5866+ return -1; /* invalid display size */ 5867+ } 5868+ 5869+ fmt->i_width = width; 5870+ fmt->i_height = height; 5871+ fmt->i_visible_width = ctx->width; 5872+ fmt->i_visible_height = ctx->height; 5873+ 5874+ /* If an aspect-ratio was specified in the input format then force it */ 5875+ if (dec->fmt_in.video.i_sar_num > 0 && dec->fmt_in.video.i_sar_den > 0) 5876+ { 5877+ fmt->i_sar_num = dec->fmt_in.video.i_sar_num; 5878+ fmt->i_sar_den = dec->fmt_in.video.i_sar_den; 5879+ } 5880+ else 5881+ { 5882+ fmt->i_sar_num = ctx->sample_aspect_ratio.num; 5883+ fmt->i_sar_den = ctx->sample_aspect_ratio.den; 5884+ 5885+ if (fmt->i_sar_num == 0 || fmt->i_sar_den == 0) 5886+ fmt->i_sar_num = fmt->i_sar_den = 1; 5887+ } 5888+ 5889+ if (dec->fmt_in.video.i_frame_rate > 0 5890+ && dec->fmt_in.video.i_frame_rate_base > 0) 5891+ { 5892+ fmt->i_frame_rate = dec->fmt_in.video.i_frame_rate; 5893+ fmt->i_frame_rate_base = dec->fmt_in.video.i_frame_rate_base; 5894+ } 5895+ else if (ctx->framerate.num > 0 && ctx->framerate.den > 0) 5896+ { 5897+ fmt->i_frame_rate = ctx->framerate.num; 5898+ fmt->i_frame_rate_base = ctx->framerate.den; 5899+# if LIBAVCODEC_VERSION_MICRO < 100 5900+ // for some reason libav don't thinkg framerate presents actually same thing as in ffmpeg 5901+ fmt->i_frame_rate_base *= __MAX(ctx->ticks_per_frame, 1); 5902+# endif 5903+ } 5904+ else if (ctx->time_base.num > 0 && ctx->time_base.den > 0) 5905+ { 5906+ fmt->i_frame_rate = ctx->time_base.den; 5907+ fmt->i_frame_rate_base = ctx->time_base.num 5908+ * __MAX(ctx->ticks_per_frame, 1); 5909+ } 5910+ 5911+ /* FIXME we should only set the known values and let the core decide 5912+ * later of fallbacks, but we can't do that with a boolean */ 5913+ switch ( ctx->color_range ) 5914+ { 5915+ case AVCOL_RANGE_JPEG: 5916+ fmt->b_color_range_full = true; 5917+ break; 5918+ case AVCOL_RANGE_UNSPECIFIED: 5919+ fmt->b_color_range_full = !vlc_fourcc_IsYUV( fmt->i_chroma ); 5920+ break; 5921+ case AVCOL_RANGE_MPEG: 5922+ default: 5923+ fmt->b_color_range_full = false; 5924+ break; 5925+ } 5926+ 5927+ switch( ctx->colorspace ) 5928+ { 5929+ case AVCOL_SPC_BT709: 5930+ fmt->space = COLOR_SPACE_BT709; 5931+ break; 5932+ case AVCOL_SPC_SMPTE170M: 5933+ case AVCOL_SPC_BT470BG: 5934+ fmt->space = COLOR_SPACE_BT601; 5935+ break; 5936+ case AVCOL_SPC_BT2020_NCL: 5937+ case AVCOL_SPC_BT2020_CL: 5938+ fmt->space = COLOR_SPACE_BT2020; 5939+ break; 5940+ default: 5941+ break; 5942+ } 5943+ 5944+ switch( ctx->color_trc ) 5945+ { 5946+ case AVCOL_TRC_LINEAR: 5947+ fmt->transfer = TRANSFER_FUNC_LINEAR; 5948+ break; 5949+ case AVCOL_TRC_GAMMA22: 5950+ fmt->transfer = TRANSFER_FUNC_SRGB; 5951+ break; 5952+ case AVCOL_TRC_BT709: 5953+ fmt->transfer = TRANSFER_FUNC_BT709; 5954+ break; 5955+ case AVCOL_TRC_SMPTE170M: 5956+ case AVCOL_TRC_BT2020_10: 5957+ case AVCOL_TRC_BT2020_12: 5958+ fmt->transfer = TRANSFER_FUNC_BT2020; 5959+ break; 5960+#if LIBAVUTIL_VERSION_CHECK( 55, 14, 0, 31, 100) 5961+ case AVCOL_TRC_ARIB_STD_B67: 5962+ fmt->transfer = TRANSFER_FUNC_ARIB_B67; 5963+ break; 5964+#endif 5965+#if LIBAVUTIL_VERSION_CHECK( 55, 17, 0, 37, 100) 5966+ case AVCOL_TRC_SMPTE2084: 5967+ fmt->transfer = TRANSFER_FUNC_SMPTE_ST2084; 5968+ break; 5969+ case AVCOL_TRC_SMPTE240M: 5970+ fmt->transfer = TRANSFER_FUNC_SMPTE_240; 5971+ break; 5972+ case AVCOL_TRC_GAMMA28: 5973+ fmt->transfer = TRANSFER_FUNC_BT470_BG; 5974+ break; 5975+#endif 5976+ default: 5977+ break; 5978+ } 5979+ 5980+ switch( ctx->color_primaries ) 5981+ { 5982+ case AVCOL_PRI_BT709: 5983+ fmt->primaries = COLOR_PRIMARIES_BT709; 5984+ break; 5985+ case AVCOL_PRI_BT470BG: 5986+ fmt->primaries = COLOR_PRIMARIES_BT601_625; 5987+ break; 5988+ case AVCOL_PRI_SMPTE170M: 5989+ case AVCOL_PRI_SMPTE240M: 5990+ fmt->primaries = COLOR_PRIMARIES_BT601_525; 5991+ break; 5992+ case AVCOL_PRI_BT2020: 5993+ fmt->primaries = COLOR_PRIMARIES_BT2020; 5994+ break; 5995+ default: 5996+ break; 5997+ } 5998+ 5999+ switch( ctx->chroma_sample_location ) 6000+ { 6001+ case AVCHROMA_LOC_LEFT: 6002+ fmt->chroma_location = CHROMA_LOCATION_LEFT; 6003+ break; 6004+ case AVCHROMA_LOC_CENTER: 6005+ fmt->chroma_location = CHROMA_LOCATION_CENTER; 6006+ break; 6007+ case AVCHROMA_LOC_TOPLEFT: 6008+ fmt->chroma_location = CHROMA_LOCATION_TOP_LEFT; 6009+ break; 6010+ default: 6011+ break; 6012+ } 6013+ 6014+ return 0; 6015+} 6016+ 6017+static int lavc_UpdateVideoFormat(decoder_t *dec, AVCodecContext *ctx, 6018+ enum AVPixelFormat fmt, 6019+ enum AVPixelFormat swfmt) 6020+{ 6021+ video_format_t fmt_out; 6022+ int val; 6023+#if TRACE_ALL 6024+ msg_Dbg(dec, "<<< %s", __func__); 6025+#endif 6026+ val = lavc_GetVideoFormat(dec, &fmt_out, ctx, fmt, swfmt); 6027+ if (val) 6028+ { 6029+ msg_Dbg(dec, "Failed to get format"); 6030+ return val; 6031+ } 6032+ 6033+ /* always have date in fields/ticks units */ 6034+ if(dec->p_sys->pts.i_divider_num) 6035+ date_Change(&dec->p_sys->pts, fmt_out.i_frame_rate * 6036+ __MAX(ctx->ticks_per_frame, 1), 6037+ fmt_out.i_frame_rate_base); 6038+ else 6039+ date_Init(&dec->p_sys->pts, fmt_out.i_frame_rate * 6040+ __MAX(ctx->ticks_per_frame, 1), 6041+ fmt_out.i_frame_rate_base); 6042+ 6043+ fmt_out.p_palette = dec-> fmt_out.video.p_palette; 6044+ dec->fmt_out.video.p_palette = NULL; 6045+ 6046+ es_format_Change(&dec->fmt_out, VIDEO_ES, fmt_out.i_chroma); 6047+ dec->fmt_out.video = fmt_out; 6048+ dec->fmt_out.video.orientation = dec->fmt_in.video.orientation; 6049+ dec->fmt_out.video.projection_mode = dec->fmt_in.video.projection_mode; 6050+ dec->fmt_out.video.multiview_mode = dec->fmt_in.video.multiview_mode; 6051+ dec->fmt_out.video.pose = dec->fmt_in.video.pose; 6052+ if ( dec->fmt_in.video.mastering.max_luminance ) 6053+ dec->fmt_out.video.mastering = dec->fmt_in.video.mastering; 6054+ dec->fmt_out.video.lighting = dec->fmt_in.video.lighting; 6055+ 6056+ val = decoder_UpdateVideoFormat(dec); 6057+#if TRACE_ALL 6058+ msg_Dbg(dec, ">>> %s: rv=%d", __func__, val); 6059+#endif 6060+ return val; 6061+} 6062+ 6063+static int OpenVideoCodec( decoder_t *p_dec ) 6064+{ 6065+ decoder_sys_t *p_sys = p_dec->p_sys; 6066+ AVCodecContext *ctx = p_sys->p_context; 6067+ const AVCodec *codec = p_sys->p_codec; 6068+ int ret; 6069+ 6070+ if( ctx->extradata_size <= 0 ) 6071+ { 6072+ if( codec->id == AV_CODEC_ID_VC1 || 6073+ codec->id == AV_CODEC_ID_THEORA ) 6074+ { 6075+ msg_Warn( p_dec, "waiting for extra data for codec %s", 6076+ codec->name ); 6077+ return 1; 6078+ } 6079+ } 6080+ 6081+ ctx->width = p_dec->fmt_in.video.i_visible_width; 6082+ ctx->height = p_dec->fmt_in.video.i_visible_height; 6083+ 6084+ ctx->coded_width = p_dec->fmt_in.video.i_width; 6085+ ctx->coded_height = p_dec->fmt_in.video.i_height; 6086+ 6087+ ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel; 6088+ p_sys->pix_fmt = AV_PIX_FMT_NONE; 6089+ p_sys->profile = -1; 6090+ p_sys->level = -1; 6091+ cc_Init( &p_sys->cc ); 6092+ 6093+ set_video_color_settings( &p_dec->fmt_in.video, ctx ); 6094+ if( p_dec->fmt_in.video.i_frame_rate_base && 6095+ p_dec->fmt_in.video.i_frame_rate && 6096+ (double) p_dec->fmt_in.video.i_frame_rate / 6097+ p_dec->fmt_in.video.i_frame_rate_base < 6 ) 6098+ { 6099+ ctx->flags |= AV_CODEC_FLAG_LOW_DELAY; 6100+ } 6101+ 6102+ post_mt( p_sys ); 6103+ ret = ZcFfmpeg_OpenCodec( p_dec, ctx, codec ); 6104+ wait_mt( p_sys ); 6105+ if( ret < 0 ) 6106+ return ret; 6107+ 6108+ switch( ctx->active_thread_type ) 6109+ { 6110+ case FF_THREAD_FRAME: 6111+ msg_Dbg( p_dec, "using frame thread mode with %d threads", 6112+ ctx->thread_count ); 6113+ break; 6114+ case FF_THREAD_SLICE: 6115+ msg_Dbg( p_dec, "using slice thread mode with %d threads", 6116+ ctx->thread_count ); 6117+ break; 6118+ case 0: 6119+ if( ctx->thread_count > 1 ) 6120+ msg_Warn( p_dec, "failed to enable threaded decoding" ); 6121+ break; 6122+ default: 6123+ msg_Warn( p_dec, "using unknown thread mode with %d threads", 6124+ ctx->thread_count ); 6125+ break; 6126+ } 6127+ return 0; 6128+} 6129+ 6130+/***************************************************************************** 6131+ * InitVideo: initialize the video decoder 6132+ ***************************************************************************** 6133+ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet 6134+ * opened (done after the first decoded frame). 6135+ *****************************************************************************/ 6136+static int MmalAvcodecOpenDecoder( vlc_object_t *obj ) 6137+{ 6138+ decoder_t *p_dec = (decoder_t *)obj; 6139+ const AVCodec *p_codec; 6140+ 6141+ int extra_buffers = var_InheritInteger(p_dec, MMAL_AVCODEC_BUFFERS); 6142+ 6143+ if (extra_buffers < 0) 6144+ { 6145+ extra_buffers = p_dec->fmt_in.video.i_height * p_dec->fmt_in.video.i_width >= 1920 * 1088 ? 6146+ BUFFERS_IN_FLIGHT_UHD : BUFFERS_IN_FLIGHT; 6147+ } 6148+ 6149+ if (extra_buffers <= 0) 6150+ { 6151+ msg_Dbg(p_dec, "%s: extra_buffers=%d - cannot use module", __func__, extra_buffers); 6152+ return VLC_EGENERIC; 6153+ } 6154+ 6155+ const vcsm_init_type_t vcsm_type = cma_vcsm_init(); 6156+ const int vcsm_size = 6157+ vcsm_type == VCSM_INIT_LEGACY ? hw_mmal_get_gpu_mem() : 512 << 20; 6158+ 6159+#if 1 6160+ { 6161+ char buf1[5], buf2[5], buf2a[5]; 6162+ char buf3[5], buf4[5]; 6163+ uint32_t in_fcc = 0; 6164+ msg_Dbg(p_dec, "%s: <<< (%s/%s)[%s] %dx%d -> (%s/%s) %dx%d [%s/%d] xb:%d", __func__, 6165+ str_fourcc(buf1, p_dec->fmt_in.i_codec), 6166+ str_fourcc(buf2, p_dec->fmt_in.video.i_chroma), 6167+ str_fourcc(buf2a, in_fcc), 6168+ p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height, 6169+ str_fourcc(buf3, p_dec->fmt_out.i_codec), 6170+ str_fourcc(buf4, p_dec->fmt_out.video.i_chroma), 6171+ p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height, 6172+ cma_vcsm_init_str(vcsm_type), vcsm_size, extra_buffers); 6173+ } 6174+#endif 6175+ 6176+ if( vcsm_type == VCSM_INIT_NONE ) 6177+ return VLC_EGENERIC; 6178+#if 1 6179+ if( (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC && 6180+ (vcsm_type == VCSM_INIT_CMA || vcsm_size < (96 << 20))) || 6181+ (p_dec->fmt_in.i_codec == VLC_CODEC_HEVC && 6182+ vcsm_size < (128 << 20))) 6183+ { 6184+ cma_vcsm_exit(vcsm_type); 6185+ return VLC_EGENERIC; 6186+ } 6187+#endif 6188+ 6189+ AVCodecContext *p_context = ZcFfmpeg_AllocContext( p_dec, &p_codec ); 6190+ if( p_context == NULL ) 6191+ { 6192+ cma_vcsm_exit(vcsm_type); 6193+ return VLC_EGENERIC; 6194+ } 6195+ 6196+ int i_val; 6197+ 6198+ /* Allocate the memory needed to store the decoder's structure */ 6199+ decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) ); 6200+ if( unlikely(p_sys == NULL) ) 6201+ { 6202+ avcodec_free_context( &p_context ); 6203+ cma_vcsm_exit(vcsm_type); 6204+ return VLC_ENOMEM; 6205+ } 6206+ 6207+ p_dec->p_sys = p_sys; 6208+ p_sys->p_context = p_context; 6209+ p_sys->p_codec = p_codec; 6210+ p_sys->p_dec = p_dec; 6211+// p_sys->p_va = NULL; 6212+ p_sys->cma_in_flight_max = extra_buffers; 6213+ p_sys->vcsm_init_type = vcsm_type; 6214+ vlc_sem_init( &p_sys->sem_mt, 0 ); 6215+ 6216+ /* ***** Fill p_context with init values ***** */ 6217+ p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ? 6218+ p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec ); 6219+ 6220+ /* ***** Get configuration of ffmpeg plugin ***** */ 6221+ p_context->workaround_bugs = 6222+ var_InheritInteger( p_dec, "avcodec-workaround-bugs" ); 6223+ p_context->err_recognition = 6224+ var_InheritInteger( p_dec, "avcodec-error-resilience" ); 6225+ 6226+ if( var_CreateGetBool( p_dec, "grayscale" ) ) 6227+ p_context->flags |= AV_CODEC_FLAG_GRAY; 6228+ 6229+ /* ***** Output always the frames ***** */ 6230+ p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT; 6231+ 6232+ i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" ); 6233+ if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL; 6234+ else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY; 6235+ else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR; 6236+ else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF; 6237+ else p_context->skip_loop_filter = AVDISCARD_DEFAULT; 6238+ 6239+ if( var_CreateGetBool( p_dec, "avcodec-fast" ) ) 6240+ p_context->flags2 |= AV_CODEC_FLAG2_FAST; 6241+ 6242+ /* ***** libavcodec frame skipping ***** */ 6243+ p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" ); 6244+ p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" ); 6245+ 6246+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" ); 6247+ if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL; 6248+ else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY; 6249+ else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR; 6250+ else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF; 6251+ else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE; 6252+ else p_sys->i_skip_frame = AVDISCARD_DEFAULT; 6253+ p_context->skip_frame = p_sys->i_skip_frame; 6254+ 6255+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" ); 6256+ if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL; 6257+ else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY; 6258+ else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR; 6259+ else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF; 6260+ else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE; 6261+ else p_context->skip_idct = AVDISCARD_DEFAULT; 6262+ 6263+ /* ***** libavcodec direct rendering ***** */ 6264+ p_sys->b_direct_rendering = false; 6265+ atomic_init(&p_sys->b_dr_failure, false); 6266+ if( var_CreateGetBool( p_dec, "avcodec-dr" ) && 6267+ (p_codec->capabilities & AV_CODEC_CAP_DR1) && 6268+ /* No idea why ... but this fixes flickering on some TSCC streams */ 6269+ p_sys->p_codec->id != AV_CODEC_ID_TSCC && 6270+ p_sys->p_codec->id != AV_CODEC_ID_CSCD && 6271+ p_sys->p_codec->id != AV_CODEC_ID_CINEPAK ) 6272+ { 6273+ /* Some codecs set pix_fmt only after the 1st frame has been decoded, 6274+ * so we need to do another check in ffmpeg_GetFrameBuf() */ 6275+ p_sys->b_direct_rendering = true; 6276+ } 6277+ 6278+ p_context->get_format = ZcGetFormat; 6279+#if 0 6280+ p_context->get_format = ffmpeg_GetFormat; 6281+ /* Always use our get_buffer wrapper so we can calculate the 6282+ * PTS correctly */ 6283+ p_context->get_buffer2 = lavc_GetFrame; 6284+ p_context->opaque = p_dec; 6285+#endif 6286+ 6287+ int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" ); 6288+ if( i_thread_count <= 0 ) 6289+#if 1 6290+ { 6291+ // Pick 5 threads for everything on Pi except for HEVC where the h/w 6292+ // really limits the useful size to 3 6293+ i_thread_count = p_codec->id == AV_CODEC_ID_HEVC ? 3 : 5; 6294+ } 6295+#else 6296+ { 6297+ i_thread_count = vlc_GetCPUCount(); 6298+ if( i_thread_count > 1 ) 6299+ i_thread_count++; 6300+ 6301+ //FIXME: take in count the decoding time 6302+#if VLC_WINSTORE_APP 6303+ i_thread_count = __MIN( i_thread_count, 6 ); 6304+#else 6305+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 ); 6306+#endif 6307+ } 6308+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 ); 6309+#endif 6310+ msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count ); 6311+ p_context->thread_count = i_thread_count; 6312+ p_context->thread_safe_callbacks = true; 6313+ 6314+ switch( p_codec->id ) 6315+ { 6316+ case AV_CODEC_ID_MPEG4: 6317+ case AV_CODEC_ID_H263: 6318+ p_context->thread_type = 0; 6319+ break; 6320+ case AV_CODEC_ID_MPEG1VIDEO: 6321+ case AV_CODEC_ID_MPEG2VIDEO: 6322+ p_context->thread_type &= ~FF_THREAD_SLICE; 6323+ /* fall through */ 6324+# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0)) 6325+ case AV_CODEC_ID_H264: 6326+ case AV_CODEC_ID_VC1: 6327+ case AV_CODEC_ID_WMV3: 6328+ p_context->thread_type &= ~FF_THREAD_FRAME; 6329+# endif 6330+ default: 6331+ break; 6332+ } 6333+ 6334+ if( p_context->thread_type & FF_THREAD_FRAME ) 6335+ p_dec->i_extra_picture_buffers = 2 * p_context->thread_count; 6336+ 6337+ /* ***** misc init ***** */ 6338+ date_Init(&p_sys->pts, 1, 30001); 6339+ date_Set(&p_sys->pts, VLC_TS_INVALID); 6340+ p_sys->b_first_frame = true; 6341+ p_sys->i_late_frames = 0; 6342+ p_sys->b_from_preroll = false; 6343+ 6344+ /* Set output properties */ 6345+ if( ZcGetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS ) 6346+ { 6347+ /* we are doomed. but not really, because most codecs set their pix_fmt later on */ 6348+// p_dec->fmt_out.i_codec = VLC_CODEC_I420; 6349+ p_dec->fmt_out.i_codec = VLC_CODEC_MMAL_ZC_I420; 6350+ } 6351+ p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma; 6352+ 6353+ p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation; 6354+ 6355+ if( p_dec->fmt_in.video.p_palette ) { 6356+ p_sys->palette_sent = false; 6357+ p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) ); 6358+ if( p_dec->fmt_out.video.p_palette ) 6359+ *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette; 6360+ } else 6361+ p_sys->palette_sent = true; 6362+ 6363+ if ((p_sys->cma_pool = cma_buf_pool_new(p_sys->cma_in_flight_max, p_sys->cma_in_flight_max, false, "mmal_avcodec")) == NULL) 6364+ { 6365+ msg_Err(p_dec, "CMA pool alloc failure"); 6366+ goto fail; 6367+ } 6368+ 6369+ /* ***** init this codec with special data ***** */ 6370+ ffmpeg_InitCodec( p_dec ); 6371+ 6372+ /* ***** Open the codec ***** */ 6373+ if( OpenVideoCodec( p_dec ) < 0 ) 6374+ { 6375+ vlc_sem_destroy( &p_sys->sem_mt ); 6376+ free( p_sys ); 6377+ avcodec_free_context( &p_context ); 6378+ return VLC_EGENERIC; 6379+ } 6380+ 6381+ p_dec->pf_decode = DecodeVideo; 6382+ p_dec->pf_flush = Flush; 6383+ 6384+ /* XXX: Writing input format makes little sense. */ 6385+ if( p_context->profile != FF_PROFILE_UNKNOWN ) 6386+ p_dec->fmt_in.i_profile = p_context->profile; 6387+ if( p_context->level != FF_LEVEL_UNKNOWN ) 6388+ p_dec->fmt_in.i_level = p_context->level; 6389+ 6390+#if 1 6391+ // Most of the time we have nothing useful by way of a format here 6392+ // wait till we've decoded something 6393+#else 6394+ // Update output format 6395+ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, 6396+ p_context->pix_fmt) != 0) 6397+ { 6398+ msg_Err(p_dec, "Unable to update format: pix_fmt=%d", p_context->pix_fmt); 6399+// goto fail; 6400+ } 6401+#endif 6402+ 6403+#if TRACE_ALL 6404+ msg_Dbg(p_dec, "<<< %s: OK", __func__); 6405+#endif 6406+ return VLC_SUCCESS; 6407+ 6408+fail: 6409+ MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec)); 6410+ 6411+#if TRACE_ALL 6412+ msg_Dbg(p_dec, "<<< %s: FAIL", __func__); 6413+#endif 6414+ 6415+ return VLC_EGENERIC; 6416+} 6417+ 6418+/***************************************************************************** 6419+ * Flush: 6420+ *****************************************************************************/ 6421+static void Flush( decoder_t *p_dec ) 6422+{ 6423+ decoder_sys_t *p_sys = p_dec->p_sys; 6424+ AVCodecContext *p_context = p_sys->p_context; 6425+ 6426+#if TRACE_ALL 6427+ msg_Dbg(p_dec, "<<< %s", __func__); 6428+#endif 6429+ 6430+ date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */ 6431+ p_sys->i_late_frames = 0; 6432+ cc_Flush( &p_sys->cc ); 6433+ 6434+ /* Abort pictures in order to unblock all avcodec workers threads waiting 6435+ * for a picture. This will avoid a deadlock between avcodec_flush_buffers 6436+ * and workers threads */ 6437+// It would probably be good to use AbortPicture but that often deadlocks on close 6438+// and given that we wait for pics in the main thread it should be unneeded (whereas 6439+// cma is alloced in the depths of ffmpeg on its own threads) 6440+// decoder_AbortPictures( p_dec, true ); 6441+ cma_buf_pool_cancel(p_sys->cma_pool); 6442+ 6443+ post_mt( p_sys ); 6444+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ 6445+ if( avcodec_is_open( p_context ) ) 6446+ avcodec_flush_buffers( p_context ); 6447+ wait_mt( p_sys ); 6448+ 6449+ /* Reset cancel state to false */ 6450+ cma_buf_pool_uncancel(p_sys->cma_pool); 6451+// decoder_AbortPictures( p_dec, false ); 6452+ 6453+#if TRACE_ALL 6454+ msg_Dbg(p_dec, ">>> %s", __func__); 6455+#endif 6456+ 6457+} 6458+ 6459+static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) 6460+{ 6461+ if( !block) 6462+ return true; 6463+ 6464+ if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) 6465+ { 6466+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ 6467+ cc_Flush( &p_sys->cc ); 6468+ 6469+ p_sys->i_late_frames = 0; 6470+ if( block->i_flags & BLOCK_FLAG_CORRUPTED ) 6471+ { 6472+ block_Release( block ); 6473+ return false; 6474+ } 6475+ } 6476+ return true; 6477+} 6478+ 6479+static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time) 6480+{ 6481+ if( !block ) 6482+ return false; 6483+ if( block->i_flags & BLOCK_FLAG_PREROLL ) 6484+ { 6485+ /* Do not care about late frames when prerolling 6486+ * TODO avoid decoding of non reference frame 6487+ * (ie all B except for H264 where it depends only on nal_ref_idc) */ 6488+ p_sys->i_late_frames = 0; 6489+ p_sys->b_from_preroll = true; 6490+ p_sys->i_last_late_delay = INT64_MAX; 6491+ } 6492+ 6493+ if( p_sys->i_late_frames <= 0 ) 6494+ return false; 6495+ 6496+ if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ)) 6497+ { 6498+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ 6499+ block_Release( block ); 6500+ p_sys->i_late_frames--; 6501+ return true; 6502+ } 6503+ return false; 6504+} 6505+ 6506+static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture ) 6507+{ 6508+ if( p_sys->i_late_frames <= 4) 6509+ return false; 6510+ 6511+ *b_need_output_picture = false; 6512+ if( p_sys->i_late_frames < 12 ) 6513+ { 6514+ p_context->skip_frame = 6515+ (p_sys->i_skip_frame <= AVDISCARD_NONREF) ? 6516+ AVDISCARD_NONREF : p_sys->i_skip_frame; 6517+ } 6518+ else 6519+ { 6520+ /* picture too late, won't decode 6521+ * but break picture until a new I, and for mpeg4 ...*/ 6522+ p_sys->i_late_frames--; /* needed else it will never be decrease */ 6523+ return true; 6524+ } 6525+ return false; 6526+} 6527+ 6528+static mtime_t interpolate_next_pts( decoder_t *p_dec, AVFrame *frame ) 6529+{ 6530+ decoder_sys_t *p_sys = p_dec->p_sys; 6531+ AVCodecContext *p_context = p_sys->p_context; 6532+ 6533+ if( date_Get( &p_sys->pts ) == VLC_TS_INVALID || 6534+ p_sys->pts.i_divider_num == 0 ) 6535+ return VLC_TS_INVALID; 6536+ 6537+ int i_tick = p_context->ticks_per_frame; 6538+ if( i_tick <= 0 ) 6539+ i_tick = 1; 6540+ 6541+ /* interpolate the next PTS */ 6542+ return date_Increment( &p_sys->pts, i_tick + frame->repeat_pict ); 6543+} 6544+ 6545+static void update_late_frame_count( decoder_t *p_dec, block_t *p_block, 6546+ mtime_t current_time, mtime_t i_pts, 6547+ mtime_t i_next_pts ) 6548+{ 6549+ decoder_sys_t *p_sys = p_dec->p_sys; 6550+ /* Update frame late count (except when doing preroll) */ 6551+ mtime_t i_display_date = VLC_TS_INVALID; 6552+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) 6553+ i_display_date = decoder_GetDisplayDate( p_dec, i_pts ); 6554+ 6555+ mtime_t i_threshold = i_next_pts != VLC_TS_INVALID ? (i_next_pts - i_pts) / 2 : 20000; 6556+ 6557+ if( i_display_date > VLC_TS_INVALID && i_display_date + i_threshold <= current_time ) 6558+ { 6559+ /* Out of preroll, consider only late frames on rising delay */ 6560+ if( p_sys->b_from_preroll ) 6561+ { 6562+ if( p_sys->i_last_late_delay > current_time - i_display_date ) 6563+ { 6564+ p_sys->i_last_late_delay = current_time - i_display_date; 6565+ return; 6566+ } 6567+ p_sys->b_from_preroll = false; 6568+ } 6569+ 6570+ p_sys->i_late_frames++; 6571+ if( p_sys->i_late_frames == 1 ) 6572+ p_sys->i_late_frames_start = current_time; 6573+ 6574+ } 6575+ else 6576+ { 6577+ p_sys->i_late_frames = 0; 6578+ } 6579+} 6580+ 6581+ 6582+static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic ) 6583+{ 6584+ decoder_sys_t *p_sys = p_dec->p_sys; 6585+ bool format_changed = false; 6586+ 6587+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) ) 6588+#define FROM_AVRAT(default_factor, avrat) \ 6589+(uint64_t)(default_factor) * (avrat).num / (avrat).den 6590+ const AVFrameSideData *metadata = 6591+ av_frame_get_side_data( frame, 6592+ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA ); 6593+ if ( metadata ) 6594+ { 6595+ const AVMasteringDisplayMetadata *hdr_meta = 6596+ (const AVMasteringDisplayMetadata *) metadata->data; 6597+ if ( hdr_meta->has_luminance ) 6598+ { 6599+#define ST2086_LUMA_FACTOR 10000 6600+ p_pic->format.mastering.max_luminance = 6601+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance); 6602+ p_pic->format.mastering.min_luminance = 6603+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance); 6604+ } 6605+ if ( hdr_meta->has_primaries ) 6606+ { 6607+#define ST2086_RED 2 6608+#define ST2086_GREEN 0 6609+#define ST2086_BLUE 1 6610+#define LAV_RED 0 6611+#define LAV_GREEN 1 6612+#define LAV_BLUE 2 6613+#define ST2086_PRIM_FACTOR 50000 6614+ p_pic->format.mastering.primaries[ST2086_RED*2 + 0] = 6615+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]); 6616+ p_pic->format.mastering.primaries[ST2086_RED*2 + 1] = 6617+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]); 6618+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] = 6619+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]); 6620+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] = 6621+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]); 6622+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] = 6623+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]); 6624+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] = 6625+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]); 6626+ p_pic->format.mastering.white_point[0] = 6627+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]); 6628+ p_pic->format.mastering.white_point[1] = 6629+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]); 6630+ } 6631+ 6632+ if ( memcmp( &p_dec->fmt_out.video.mastering, 6633+ &p_pic->format.mastering, 6634+ sizeof(p_pic->format.mastering) ) ) 6635+ { 6636+ p_dec->fmt_out.video.mastering = p_pic->format.mastering; 6637+ format_changed = true; 6638+ } 6639+#undef FROM_AVRAT 6640+ } 6641+#endif 6642+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) ) 6643+ const AVFrameSideData *metadata_lt = 6644+ av_frame_get_side_data( frame, 6645+ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL ); 6646+ if ( metadata_lt ) 6647+ { 6648+ const AVContentLightMetadata *light_meta = 6649+ (const AVContentLightMetadata *) metadata_lt->data; 6650+ p_pic->format.lighting.MaxCLL = light_meta->MaxCLL; 6651+ p_pic->format.lighting.MaxFALL = light_meta->MaxFALL; 6652+ if ( memcmp( &p_dec->fmt_out.video.lighting, 6653+ &p_pic->format.lighting, 6654+ sizeof(p_pic->format.lighting) ) ) 6655+ { 6656+ p_dec->fmt_out.video.lighting = p_pic->format.lighting; 6657+ format_changed = true; 6658+ } 6659+ } 6660+#endif 6661+ 6662+ if (format_changed && decoder_UpdateVideoFormat( p_dec )) 6663+ return -1; 6664+ 6665+ const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC ); 6666+ if( p_avcc ) 6667+ { 6668+ cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size ); 6669+ if( p_sys->cc.b_reorder || p_sys->cc.i_data ) 6670+ { 6671+ block_t *p_cc = block_Alloc( p_sys->cc.i_data ); 6672+ if( p_cc ) 6673+ { 6674+ memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data ); 6675+ if( p_sys->cc.b_reorder ) 6676+ p_cc->i_dts = p_cc->i_pts = p_pic->date; 6677+ else 6678+ p_cc->i_pts = p_cc->i_dts; 6679+ decoder_cc_desc_t desc; 6680+ desc.i_608_channels = p_sys->cc.i_608channels; 6681+ desc.i_708_channels = p_sys->cc.i_708channels; 6682+ desc.i_reorder_depth = 4; 6683+ decoder_QueueCc( p_dec, p_cc, &desc ); 6684+ } 6685+ cc_Flush( &p_sys->cc ); 6686+ } 6687+ } 6688+ return 0; 6689+} 6690+ 6691+/***************************************************************************** 6692+ * DecodeBlock: Called to decode one or more frames 6693+ *****************************************************************************/ 6694+ 6695+static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error ) 6696+{ 6697+ decoder_sys_t *p_sys = p_dec->p_sys; 6698+ AVCodecContext *p_context = p_sys->p_context; 6699+ /* Boolean if we assume that we should get valid pic as result */ 6700+ bool b_need_output_picture = true; 6701+ 6702+ /* Boolean for END_OF_SEQUENCE */ 6703+ bool eos_spotted = false; 6704+ 6705+#if TRACE_ALL 6706+ msg_Dbg(p_dec, "<<< %s: (buf_size=%d)", __func__, pp_block == NULL || *pp_block == NULL ? 0 : (*pp_block)->i_buffer); 6707+#endif 6708+ 6709+ block_t *p_block; 6710+ mtime_t current_time; 6711+ picture_t *p_pic = NULL; 6712+ AVFrame *frame = NULL; 6713+ 6714+ // By default we are OK 6715+ *error = false; 6716+ 6717+ if( !p_context->extradata_size && p_dec->fmt_in.i_extra ) 6718+ { 6719+ ffmpeg_InitCodec( p_dec ); 6720+ if( !avcodec_is_open( p_context ) ) 6721+ OpenVideoCodec( p_dec ); 6722+ } 6723+ 6724+ p_block = pp_block ? *pp_block : NULL; 6725+ if(!p_block && !(p_sys->p_codec->capabilities & AV_CODEC_CAP_DELAY) ) 6726+ return NULL; 6727+ 6728+ if( !avcodec_is_open( p_context ) ) 6729+ { 6730+ if( p_block ) 6731+ block_Release( p_block ); 6732+ return NULL; 6733+ } 6734+ 6735+ if( !check_block_validity( p_sys, p_block ) ) 6736+ return NULL; 6737+ 6738+ current_time = mdate(); 6739+ if( p_dec->b_frame_drop_allowed && check_block_being_late( p_sys, p_block, current_time) ) 6740+ { 6741+ msg_Err( p_dec, "more than 5 seconds of late video -> " 6742+ "dropping frame (computer too slow ?)" ); 6743+ return NULL; 6744+ } 6745+ 6746+ 6747+ /* A good idea could be to decode all I pictures and see for the other */ 6748+ 6749+ /* Defaults that if we aren't in prerolling, we want output picture 6750+ same for if we are flushing (p_block==NULL) */ 6751+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) 6752+ b_need_output_picture = true; 6753+ else 6754+ b_need_output_picture = false; 6755+ 6756+ /* Change skip_frame config only if hurry_up is enabled */ 6757+ if( p_sys->b_hurry_up ) 6758+ { 6759+ p_context->skip_frame = p_sys->i_skip_frame; 6760+ 6761+ /* Check also if we should/can drop the block and move to next block 6762+ as trying to catchup the speed*/ 6763+ if( p_dec->b_frame_drop_allowed && 6764+ check_frame_should_be_dropped( p_sys, p_context, &b_need_output_picture ) ) 6765+ { 6766+ if( p_block ) 6767+ block_Release( p_block ); 6768+ msg_Warn( p_dec, "More than 11 late frames, dropping frame" ); 6769+ return NULL; 6770+ } 6771+ } 6772+ if( !b_need_output_picture ) 6773+ { 6774+ p_context->skip_frame = __MAX( p_context->skip_frame, 6775+ AVDISCARD_NONREF ); 6776+ } 6777+ 6778+ /* 6779+ * Do the actual decoding now */ 6780+ 6781+ /* Don't forget that libavcodec requires a little more bytes 6782+ * that the real frame size */ 6783+ if( p_block && p_block->i_buffer > 0 ) 6784+ { 6785+ eos_spotted = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0; 6786+ 6787+ p_block = block_Realloc( p_block, 0, 6788+ p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE ); 6789+ if( !p_block ) 6790+ return NULL; 6791+ p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE; 6792+ *pp_block = p_block; 6793+ memset( p_block->p_buffer + p_block->i_buffer, 0, 6794+ FF_INPUT_BUFFER_PADDING_SIZE ); 6795+ } 6796+ 6797+ while( !p_block || p_block->i_buffer > 0 || eos_spotted ) 6798+ { 6799+ int i_used; 6800+ AVPacket pkt; 6801+ 6802+ post_mt( p_sys ); 6803+ 6804+ av_init_packet( &pkt ); 6805+ if( p_block && p_block->i_buffer > 0 ) 6806+ { 6807+ pkt.data = p_block->p_buffer; 6808+ pkt.size = p_block->i_buffer; 6809+ pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE; 6810+ pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE; 6811+ } 6812+ else 6813+ { 6814+ /* Return delayed frames if codec has CODEC_CAP_DELAY */ 6815+ pkt.data = NULL; 6816+ pkt.size = 0; 6817+ } 6818+ 6819+ if( !p_sys->palette_sent ) 6820+ { 6821+ uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); 6822+ if (pal) { 6823+ memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE); 6824+ p_sys->palette_sent = true; 6825+ } 6826+ } 6827+ 6828+ /* Make sure we don't reuse the same timestamps twice */ 6829+ if( p_block ) 6830+ { 6831+ p_block->i_pts = 6832+ p_block->i_dts = VLC_TS_INVALID; 6833+ } 6834+ 6835+ int ret = avcodec_send_packet(p_context, &pkt); 6836+ if( ret != 0 && ret != AVERROR(EAGAIN) ) 6837+ { 6838+ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL)) 6839+ { 6840+ msg_Err(p_dec, "avcodec_send_packet critical error"); 6841+ *error = true; 6842+ } 6843+ av_packet_unref( &pkt ); 6844+ break; 6845+ } 6846+ i_used = ret != AVERROR(EAGAIN) ? pkt.size : 0; 6847+ av_packet_unref( &pkt ); 6848+ 6849+ frame = av_frame_alloc(); 6850+ if (unlikely(frame == NULL)) 6851+ { 6852+ *error = true; 6853+ break; 6854+ } 6855+ 6856+ ret = avcodec_receive_frame(p_context, frame); 6857+ if( ret != 0 && ret != AVERROR(EAGAIN) ) 6858+ { 6859+ msg_Dbg(p_dec, "No receive"); 6860+ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL)) 6861+ { 6862+ msg_Err(p_dec, "avcodec_receive_frame critical error"); 6863+ *error = true; 6864+ } 6865+ av_frame_free(&frame); 6866+ /* After draining, we need to reset decoder with a flush */ 6867+ if( ret == AVERROR_EOF ) 6868+ avcodec_flush_buffers( p_sys->p_context ); 6869+ break; 6870+ } 6871+ bool not_received_frame = ret; 6872+ 6873+ wait_mt( p_sys ); 6874+ 6875+ if( eos_spotted ) 6876+ p_sys->b_first_frame = true; 6877+ 6878+ if( p_block ) 6879+ { 6880+ if( p_block->i_buffer <= 0 ) 6881+ eos_spotted = false; 6882+ 6883+ /* Consumed bytes */ 6884+ p_block->p_buffer += i_used; 6885+ p_block->i_buffer -= i_used; 6886+ } 6887+ 6888+ /* Nothing to display */ 6889+ if( not_received_frame ) 6890+ { 6891+// msg_Dbg(p_dec, "No rx: used=%d", i_used); 6892+ av_frame_free(&frame); 6893+ if( i_used == 0 ) break; 6894+ continue; 6895+ } 6896+ 6897+ /* Compute the PTS */ 6898+#ifdef FF_API_PKT_PTS 6899+ mtime_t i_pts = frame->pts; 6900+#else 6901+ mtime_t i_pts = frame->pkt_pts; 6902+#endif 6903+ if (i_pts == AV_NOPTS_VALUE ) 6904+ i_pts = frame->pkt_dts; 6905+ 6906+ if( i_pts == AV_NOPTS_VALUE ) 6907+ i_pts = date_Get( &p_sys->pts ); 6908+ 6909+ /* Interpolate the next PTS */ 6910+ if( i_pts > VLC_TS_INVALID ) 6911+ date_Set( &p_sys->pts, i_pts ); 6912+ 6913+ const mtime_t i_next_pts = interpolate_next_pts(p_dec, frame); 6914+ 6915+ update_late_frame_count( p_dec, p_block, current_time, i_pts, i_next_pts); 6916+ 6917+ if( !b_need_output_picture || 6918+// ( !p_sys->p_va && !frame->linesize[0] ) || 6919+ ( !frame->linesize[0] ) || 6920+ ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) && 6921+ !p_sys->b_show_corrupted ) ) 6922+ { 6923+ av_frame_free(&frame); 6924+// msg_Dbg(p_dec, "Bad frame"); 6925+ continue; 6926+ } 6927+ 6928+ if( p_context->pix_fmt == AV_PIX_FMT_PAL8 6929+ && !p_dec->fmt_out.video.p_palette ) 6930+ { 6931+ /* See AV_PIX_FMT_PAL8 comment in avc_GetVideoFormat(): update the 6932+ * fmt_out palette and change the fmt_out chroma to request a new 6933+ * vout */ 6934+ assert( p_dec->fmt_out.video.i_chroma != VLC_CODEC_RGBP ); 6935+ 6936+ video_palette_t *p_palette; 6937+ p_palette = p_dec->fmt_out.video.p_palette 6938+ = malloc( sizeof(video_palette_t) ); 6939+ if( !p_palette ) 6940+ { 6941+ *error = true; 6942+ av_frame_free(&frame); 6943+ break; 6944+ } 6945+ static_assert( sizeof(p_palette->palette) == AVPALETTE_SIZE, 6946+ "Palette size mismatch between vlc and libavutil" ); 6947+ assert( frame->data[1] != NULL ); 6948+ memcpy( p_palette->palette, frame->data[1], AVPALETTE_SIZE ); 6949+ p_palette->i_entries = AVPALETTE_COUNT; 6950+ p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBP; 6951+ if( decoder_UpdateVideoFormat( p_dec ) ) 6952+ { 6953+ av_frame_free(&frame); 6954+ continue; 6955+ } 6956+ } 6957+ 6958+#if 1 6959+ { 6960+ cma_buf_t * const cb = av_rpi_zc_buf_v(frame->buf[0]); 6961+ 6962+ if (cb == NULL) 6963+ { 6964+ msg_Err(p_dec, "Frame has no attached CMA buffer"); 6965+ goto fail; 6966+ } 6967+ 6968+ if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, 6969+ p_context->pix_fmt) != 0) 6970+ { 6971+ msg_Err(p_dec, "Failed to update format"); 6972+ goto fail; 6973+ } 6974+ 6975+ if ((p_pic = decoder_NewPicture(p_dec)) == NULL) 6976+ { 6977+ msg_Err(p_dec, "Failed to allocate pic"); 6978+ goto fail; 6979+ } 6980+ 6981+ if (cma_buf_pic_attach(cma_buf_ref(cb), p_pic) != 0) 6982+ { 6983+ cma_buf_unref(cb); // Undo the in_flight 6984+ char dbuf0[5]; 6985+ msg_Err(p_dec, "Failed to attach bufs to pic: fmt=%s", str_fourcc(dbuf0, p_pic->format.i_chroma)); 6986+ goto fail; 6987+ } 6988+ 6989+ // ****** Set planes etc. 6990+ set_pic_from_frame(p_pic, frame); 6991+ } 6992+#else 6993+ picture_t *p_pic = frame->opaque; 6994+ if( p_pic == NULL ) 6995+ { /* When direct rendering is not used, get_format() and get_buffer() 6996+ * might not be called. The output video format must be set here 6997+ * then picture buffer can be allocated. */ 6998+ if (p_sys->p_va == NULL 6999+ && lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, 7000+ p_context->pix_fmt) == 0) 7001+ p_pic = decoder_NewPicture(p_dec); 7002+ 7003+ if( !p_pic ) 7004+ { 7005+ av_frame_free(&frame); 7006+ break; 7007+ } 7008+ 7009+ /* Fill picture_t from AVFrame */ 7010+ if( lavc_CopyPicture( p_dec, p_pic, frame ) != VLC_SUCCESS ) 7011+ { 7012+ av_frame_free(&frame); 7013+ picture_Release( p_pic ); 7014+ break; 7015+ } 7016+ } 7017+ else 7018+ { 7019+ /* Some codecs can return the same frame multiple times. By the 7020+ * time that the same frame is returned a second time, it will be 7021+ * too late to clone the underlying picture. So clone proactively. 7022+ * A single picture CANNOT be queued multiple times. 7023+ */ 7024+ p_pic = picture_Clone( p_pic ); 7025+ if( unlikely(p_pic == NULL) ) 7026+ { 7027+ av_frame_free(&frame); 7028+ break; 7029+ } 7030+ } 7031+#endif 7032+ 7033+ if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den ) 7034+ { 7035+ /* Fetch again the aspect ratio in case it changed */ 7036+ p_dec->fmt_out.video.i_sar_num 7037+ = p_context->sample_aspect_ratio.num; 7038+ p_dec->fmt_out.video.i_sar_den 7039+ = p_context->sample_aspect_ratio.den; 7040+ 7041+ if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den ) 7042+ { 7043+ p_dec->fmt_out.video.i_sar_num = 1; 7044+ p_dec->fmt_out.video.i_sar_den = 1; 7045+ } 7046+ } 7047+ 7048+ p_pic->date = i_pts; 7049+ /* Hack to force display of still pictures */ 7050+ p_pic->b_force = p_sys->b_first_frame; 7051+ p_pic->i_nb_fields = 2 + frame->repeat_pict; 7052+ p_pic->b_progressive = !frame->interlaced_frame; 7053+ p_pic->b_top_field_first = frame->top_field_first; 7054+ 7055+ if (DecodeSidedata(p_dec, frame, p_pic)) 7056+ i_pts = VLC_TS_INVALID; 7057+ 7058+ av_frame_free(&frame); 7059+ 7060+ /* Send decoded frame to vout */ 7061+ if (i_pts > VLC_TS_INVALID) 7062+ { 7063+ p_sys->b_first_frame = false; 7064+#if TRACE_ALL 7065+ msg_Dbg(p_dec, ">>> %s: Got pic", __func__); 7066+#endif 7067+ return p_pic; 7068+ } 7069+ else 7070+ picture_Release( p_pic ); 7071+ } 7072+ 7073+ if( p_block ) 7074+ block_Release( p_block ); 7075+ 7076+#if TRACE_ALL 7077+ msg_Dbg(p_dec, ">>> %s: NULL", __func__); 7078+#endif 7079+ return NULL; 7080+ 7081+fail: 7082+#if TRACE_ALL 7083+ msg_Dbg(p_dec, ">>> %s: FAIL", __func__); 7084+#endif 7085+ av_frame_free(&frame); 7086+ if (p_pic != NULL) 7087+ picture_Release(p_pic); 7088+ if (p_block != NULL) 7089+ block_Release(p_block); 7090+ *error = true; 7091+ return NULL; 7092+} 7093+ 7094+static int DecodeVideo( decoder_t *p_dec, block_t *p_block ) 7095+{ 7096+ block_t **pp_block = p_block ? &p_block : NULL; 7097+ picture_t *p_pic; 7098+ bool error = false; 7099+ while( ( p_pic = DecodeBlock( p_dec, pp_block, &error ) ) != NULL ) 7100+ decoder_QueueVideo( p_dec, p_pic ); 7101+ return VLCDEC_SUCCESS; 7102+// Easiest to just ignore all errors - returning a real error seems to 7103+// kill output forever 7104+// return error ? VLCDEC_ECRITICAL : VLCDEC_SUCCESS; 7105+} 7106+ 7107+/***************************************************************************** 7108+ * EndVideo: decoder destruction 7109+ ***************************************************************************** 7110+ * This function is called when the thread ends after a successful 7111+ * initialization. 7112+ *****************************************************************************/ 7113+static void MmalAvcodecCloseDecoder( vlc_object_t *obj ) 7114+{ 7115+ decoder_t *p_dec = (decoder_t *)obj; 7116+ decoder_sys_t *p_sys = p_dec->p_sys; 7117+ AVCodecContext *ctx = p_sys->p_context; 7118+// void *hwaccel_context; 7119+ 7120+ msg_Dbg(obj, "<<< %s", __func__); 7121+ 7122+ post_mt( p_sys ); 7123+ 7124+ cma_buf_pool_cancel(p_sys->cma_pool); // Abort any pending frame allocs 7125+ 7126+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ 7127+ if( avcodec_is_open( ctx ) ) 7128+ avcodec_flush_buffers( ctx ); 7129+ 7130+ av_rpi_zc_uninit2(ctx); 7131+ 7132+ wait_mt( p_sys ); 7133+ 7134+ cc_Flush( &p_sys->cc ); 7135+ 7136+// hwaccel_context = ctx->hwaccel_context; 7137+ avcodec_free_context( &ctx ); 7138+ 7139+// if( p_sys->p_va ) 7140+// vlc_va_Delete( p_sys->p_va, &hwaccel_context ); 7141+ 7142+ cma_vcsm_exit(p_sys->vcsm_init_type); 7143+ 7144+ vlc_sem_destroy( &p_sys->sem_mt ); 7145+ free( p_sys ); 7146+} 7147+ 7148+/***************************************************************************** 7149+ * ffmpeg_InitCodec: setup codec extra initialization data for ffmpeg 7150+ *****************************************************************************/ 7151+static void ffmpeg_InitCodec( decoder_t *p_dec ) 7152+{ 7153+ decoder_sys_t *p_sys = p_dec->p_sys; 7154+ size_t i_size = p_dec->fmt_in.i_extra; 7155+ 7156+ if( !i_size ) return; 7157+ 7158+ if( p_sys->p_codec->id == AV_CODEC_ID_SVQ3 ) 7159+ { 7160+ uint8_t *p; 7161+ 7162+ p_sys->p_context->extradata_size = i_size + 12; 7163+ p = p_sys->p_context->extradata = 7164+ av_malloc( p_sys->p_context->extradata_size + 7165+ FF_INPUT_BUFFER_PADDING_SIZE ); 7166+ if( !p ) 7167+ return; 7168+ 7169+ memcpy( &p[0], "SVQ3", 4 ); 7170+ memset( &p[4], 0, 8 ); 7171+ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size ); 7172+ 7173+ /* Now remove all atoms before the SMI one */ 7174+ if( p_sys->p_context->extradata_size > 0x5a && 7175+ strncmp( (char*)&p[0x56], "SMI ", 4 ) ) 7176+ { 7177+ uint8_t *psz = &p[0x52]; 7178+ 7179+ while( psz < &p[p_sys->p_context->extradata_size - 8] ) 7180+ { 7181+ uint_fast32_t atom_size = GetDWBE( psz ); 7182+ if( atom_size <= 1 ) 7183+ { 7184+ /* FIXME handle 1 as long size */ 7185+ break; 7186+ } 7187+ if( !strncmp( (char*)&psz[4], "SMI ", 4 ) ) 7188+ { 7189+ memmove( &p[0x52], psz, 7190+ &p[p_sys->p_context->extradata_size] - psz ); 7191+ break; 7192+ } 7193+ 7194+ psz += atom_size; 7195+ } 7196+ } 7197+ } 7198+ else 7199+ { 7200+ p_sys->p_context->extradata_size = i_size; 7201+ p_sys->p_context->extradata = 7202+ av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE ); 7203+ if( p_sys->p_context->extradata ) 7204+ { 7205+ memcpy( p_sys->p_context->extradata, 7206+ p_dec->fmt_in.p_extra, i_size ); 7207+ memset( p_sys->p_context->extradata + i_size, 7208+ 0, FF_INPUT_BUFFER_PADDING_SIZE ); 7209+ } 7210+ } 7211+} 7212+ 7213+ 7214+vlc_module_begin() 7215+ set_category( CAT_INPUT ) 7216+ set_subcategory( SUBCAT_INPUT_VCODEC ) 7217+ set_shortname(N_("MMAL avcodec")) 7218+ set_description(N_("MMAL buffered avcodec ")) 7219+ set_capability("video decoder", 80) 7220+ add_shortcut("mmal_avcodec") 7221+ add_integer(MMAL_AVCODEC_BUFFERS, -1, MMAL_AVCODEC_BUFFERS_TEXT, 7222+ MMAL_AVCODEC_BUFFERS_LONGTEXT, true) 7223+ set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder) 7224+vlc_module_end() 7225+ 7226--- /dev/null 7227+++ b/modules/hw/mmal/mmal_cma.c 7228@@ -0,0 +1,668 @@ 7229+#ifdef HAVE_CONFIG_H 7230+# include "config.h" 7231+#endif 7232+ 7233+#include <stdatomic.h> 7234+#include <unistd.h> 7235+#include <fcntl.h> 7236+#include <sys/ioctl.h> 7237+#include <sys/mman.h> 7238+ 7239+#include <interface/vcsm/user-vcsm.h> 7240+ 7241+#include <vlc_common.h> 7242+#include <vlc_picture.h> 7243+ 7244+#include "mmal_cma.h" 7245+#include "mmal_picture.h" 7246+ 7247+#include <assert.h> 7248+ 7249+#define TRACE_ALL 0 7250+ 7251+//----------------------------------------------------------------------------- 7252+// 7253+// Generic pool functions 7254+// Knows nothing about pool entries 7255+ 7256+typedef void * cma_pool_alloc_fn(void * v, size_t size); 7257+typedef void cma_pool_free_fn(void * v, void * el, size_t size); 7258+ 7259+#if TRACE_ALL 7260+static atomic_int pool_seq; 7261+#endif 7262+ 7263+// Pool structure 7264+// Ref count is held by pool owner and pool els that have been got 7265+// Els in the pool do not count towards its ref count 7266+struct cma_pool_fixed_s 7267+{ 7268+ atomic_int ref_count; 7269+ 7270+ vlc_mutex_t lock; 7271+ unsigned int n_in; 7272+ unsigned int n_out; 7273+ unsigned int pool_size; 7274+ int flight_size; 7275+ size_t el_size; 7276+ void ** pool; 7277+ 7278+ bool cancel; 7279+ int in_flight; 7280+ vlc_cond_t flight_cond; 7281+ 7282+ void * alloc_v; 7283+ cma_pool_alloc_fn * el_alloc_fn; 7284+ cma_pool_free_fn * el_free_fn; 7285+ cma_pool_on_delete_fn * on_delete_fn; 7286+ 7287+ const char * name; 7288+#if TRACE_ALL 7289+ int seq; 7290+#endif 7291+}; 7292+ 7293+static inline unsigned int inc_mod(const unsigned int n, const unsigned int m) 7294+{ 7295+ return n + 1 >= m ? 0 : n + 1; 7296+} 7297+ 7298+static void free_pool(const cma_pool_fixed_t * const p, void ** const pool, 7299+ const unsigned int pool_size, const size_t el_size) 7300+{ 7301+ if (pool == NULL) 7302+ return; 7303+ 7304+ for (unsigned int n = 0; n != pool_size; ++n) 7305+ if (pool[n] != NULL) 7306+ p->el_free_fn(p->alloc_v, pool[n], el_size); 7307+ free(pool); 7308+} 7309+ 7310+// Just kill this - no checks 7311+static void cma_pool_fixed_delete(cma_pool_fixed_t * const p) 7312+{ 7313+ cma_pool_on_delete_fn *const on_delete_fn = p->on_delete_fn; 7314+ void *const v = p->alloc_v; 7315+ 7316+ free_pool(p, p->pool, p->pool_size, p->el_size); 7317+ 7318+ if (p->name != NULL) 7319+ free((void *)p->name); // Discard const 7320+ 7321+ vlc_cond_destroy(&p->flight_cond); 7322+ vlc_mutex_destroy(&p->lock); 7323+ free(p); 7324+ 7325+ // Inform our container that we are dead (if it cares) 7326+ if (on_delete_fn) 7327+ on_delete_fn(v); 7328+} 7329+ 7330+static void cma_pool_fixed_unref(cma_pool_fixed_t * const p) 7331+{ 7332+ if (atomic_fetch_sub(&p->ref_count, 1) <= 1) 7333+ cma_pool_fixed_delete(p); 7334+} 7335+ 7336+static void cma_pool_fixed_ref(cma_pool_fixed_t * const p) 7337+{ 7338+ atomic_fetch_add(&p->ref_count, 1); 7339+} 7340+ 7341+static void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p) 7342+{ 7343+ vlc_mutex_lock(&p->lock); 7344+ ++p->in_flight; 7345+ vlc_mutex_unlock(&p->lock); 7346+} 7347+ 7348+static void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p) 7349+{ 7350+ vlc_mutex_lock(&p->lock); 7351+ if (--p->in_flight == 0) 7352+ vlc_cond_signal(&p->flight_cond); 7353+ vlc_mutex_unlock(&p->lock); 7354+} 7355+ 7356+static void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool inc_flight, const bool no_pool) 7357+{ 7358+ void * v = NULL; 7359+ 7360+ vlc_mutex_lock(&p->lock); 7361+ 7362+ for (;;) 7363+ { 7364+ if (req_el_size != p->el_size) 7365+ { 7366+ void ** const deadpool = p->pool; 7367+ const size_t dead_size = p->el_size; 7368+ const unsigned int dead_n = p->pool_size; 7369+ 7370+ p->pool = NULL; 7371+ p->n_in = 0; 7372+ p->n_out = 0; 7373+ p->el_size = req_el_size; 7374+ 7375+ if (deadpool != NULL) 7376+ { 7377+ vlc_mutex_unlock(&p->lock); 7378+ // Do the free old op outside the mutex in case the free is slow 7379+ free_pool(p, deadpool, dead_n, dead_size); 7380+ vlc_mutex_lock(&p->lock); 7381+ continue; 7382+ } 7383+ } 7384+ 7385+ // Late abort if flush or cancel so we can still kill the pool 7386+ if (req_el_size == 0 || p->cancel) 7387+ { 7388+ vlc_mutex_unlock(&p->lock); 7389+ return NULL; 7390+ } 7391+ 7392+ if (p->pool != NULL && !no_pool) 7393+ { 7394+ v = p->pool[p->n_in]; 7395+ if (v != NULL) 7396+ { 7397+ p->pool[p->n_in] = NULL; 7398+ p->n_in = inc_mod(p->n_in, p->pool_size); 7399+ break; 7400+ } 7401+ } 7402+ 7403+ if (p->in_flight <= 0) 7404+ break; 7405+ 7406+ vlc_cond_wait(&p->flight_cond, &p->lock); 7407+ } 7408+ 7409+ if (inc_flight) 7410+ ++p->in_flight; 7411+ 7412+ vlc_mutex_unlock(&p->lock); 7413+ 7414+ if (v == NULL && req_el_size != 0) 7415+ v = p->el_alloc_fn(p->alloc_v, req_el_size); 7416+ 7417+ // Tag ref 7418+ if (v != NULL) 7419+ cma_pool_fixed_ref(p); 7420+ // Remove flight if we set it and error 7421+ else if (inc_flight) 7422+ cma_pool_fixed_dec_in_flight(p); 7423+ 7424+ return v; 7425+} 7426+ 7427+static void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight) 7428+{ 7429+ vlc_mutex_lock(&p->lock); 7430+ 7431+ if (el_size == p->el_size && (p->pool == NULL || p->pool[p->n_out] == NULL)) 7432+ { 7433+ if (p->pool == NULL) 7434+ p->pool = calloc(p->pool_size, sizeof(void*)); 7435+ 7436+ p->pool[p->n_out] = v; 7437+ p->n_out = inc_mod(p->n_out, p->pool_size); 7438+ v = NULL; 7439+ } 7440+ 7441+ if (was_in_flight) 7442+ --p->in_flight; 7443+ 7444+ vlc_mutex_unlock(&p->lock); 7445+ 7446+ vlc_cond_signal(&p->flight_cond); 7447+ 7448+ if (v != NULL) 7449+ p->el_free_fn(p->alloc_v, v, el_size); 7450+ 7451+ cma_pool_fixed_unref(p); 7452+} 7453+ 7454+static int cma_pool_fixed_resize(cma_pool_fixed_t * const p, 7455+ const unsigned int new_pool_size, const int new_flight_size) 7456+{ 7457+ void ** dead_pool = NULL; 7458+ size_t dead_size = 0; 7459+ unsigned int dead_n = 0; 7460+ 7461+ // This makes this non-reentrant but saves us a lot of time in the normal 7462+ // "nothing happens" case 7463+ if (p->pool_size == new_pool_size && p->flight_size == new_flight_size) 7464+ return 0; 7465+ 7466+ vlc_mutex_lock(&p->lock); 7467+ 7468+ if (p->pool != NULL && new_pool_size != p->pool_size) 7469+ { 7470+ void ** const new_pool = calloc(new_pool_size, sizeof(void*)); 7471+ unsigned int d, s; 7472+ dead_pool = p->pool; 7473+ dead_size = p->el_size; 7474+ dead_n = p->pool_size; 7475+ 7476+ if (new_pool == NULL) 7477+ { 7478+ vlc_mutex_unlock(&p->lock); 7479+ return -1; 7480+ } 7481+ 7482+ for (d = 0, s = p->n_in; d != new_pool_size && (new_pool[d] = dead_pool[s]) != NULL; ++d, s = inc_mod(s, dead_n)) 7483+ dead_pool[s] = NULL; 7484+ 7485+ p->n_out = 0; 7486+ p->n_in = (d != new_pool_size) ? d : 0; 7487+ p->pool = new_pool; 7488+ } 7489+ 7490+ p->pool_size = new_pool_size; 7491+ if (new_flight_size > p->flight_size) 7492+ vlc_cond_broadcast(&p->flight_cond); // Lock still active so nothing happens till we release it 7493+ p->in_flight += p->flight_size - new_flight_size; 7494+ p->flight_size = new_flight_size; 7495+ 7496+ vlc_mutex_unlock(&p->lock); 7497+ 7498+ free_pool(p, dead_pool, dead_n, dead_size); 7499+ return 0; 7500+} 7501+ 7502+static int cma_pool_fixed_fill(cma_pool_fixed_t * const p, const size_t el_size) 7503+{ 7504+ for (;;) 7505+ { 7506+ vlc_mutex_lock(&p->lock); 7507+ bool done = el_size == p->el_size && p->pool != NULL && p->pool[p->n_out] != NULL; 7508+ vlc_mutex_unlock(&p->lock); 7509+ if (done) 7510+ break; 7511+ void * buf = cma_pool_fixed_get(p, el_size, false, true); 7512+ if (buf == NULL) 7513+ return -ENOMEM; 7514+ cma_pool_fixed_put(p, buf, el_size, false); 7515+ } 7516+ return 0; 7517+} 7518+ 7519+static void cma_pool_fixed_cancel(cma_pool_fixed_t * const p) 7520+{ 7521+ vlc_mutex_lock(&p->lock); 7522+ p->cancel = true; 7523+ vlc_cond_broadcast(&p->flight_cond); 7524+ vlc_mutex_unlock(&p->lock); 7525+} 7526+ 7527+static void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p) 7528+{ 7529+ vlc_mutex_lock(&p->lock); 7530+ p->cancel = false; 7531+ vlc_mutex_unlock(&p->lock); 7532+} 7533+ 7534+ 7535+// Purge pool & unref 7536+static void cma_pool_fixed_kill(cma_pool_fixed_t * const p) 7537+{ 7538+ if (p == NULL) 7539+ return; 7540+ 7541+ // This flush is not strictly needed but it reclaims what memory we can reclaim asap 7542+ cma_pool_fixed_get(p, 0, false, false); 7543+ cma_pool_fixed_unref(p); 7544+} 7545+ 7546+// Create a new pool 7547+static cma_pool_fixed_t* 7548+cma_pool_fixed_new(const unsigned int pool_size, 7549+ const int flight_size, 7550+ void * const alloc_v, 7551+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn, 7552+ cma_pool_on_delete_fn * const on_delete_fn, 7553+ const char * const name) 7554+{ 7555+ cma_pool_fixed_t* const p = calloc(1, sizeof(cma_pool_fixed_t)); 7556+ if (p == NULL) 7557+ return NULL; 7558+ 7559+ atomic_store(&p->ref_count, 1); 7560+ vlc_mutex_init(&p->lock); 7561+ vlc_cond_init(&p->flight_cond); 7562+ 7563+ p->pool_size = pool_size; 7564+ p->flight_size = flight_size; 7565+ p->in_flight = -flight_size; 7566+ 7567+ p->alloc_v = alloc_v; 7568+ p->el_alloc_fn = alloc_fn; 7569+ p->el_free_fn = free_fn; 7570+ p->on_delete_fn = on_delete_fn; 7571+ p->name = name == NULL ? NULL : strdup(name); 7572+#if TRACE_ALL 7573+ p->seq = atomic_fetch_add(&pool_seq, 1); 7574+#endif 7575+ 7576+ return p; 7577+} 7578+ 7579+// --------------------------------------------------------------------------- 7580+// 7581+// CMA buffer functions - uses cma_pool_fixed for pooling 7582+ 7583+struct cma_buf_pool_s { 7584+ cma_pool_fixed_t * pool; 7585+ vcsm_init_type_t init_type; 7586+ 7587+ bool all_in_flight; 7588+#if TRACE_ALL 7589+ size_t alloc_n; 7590+ size_t alloc_size; 7591+#endif 7592+}; 7593+ 7594+typedef struct cma_buf_s { 7595+ atomic_int ref_count; 7596+ cma_buf_pool_t * cbp; 7597+ bool in_flight; 7598+ size_t size; 7599+ unsigned int vcsm_h; // VCSM handle from initial alloc 7600+ unsigned int vc_h; // VC handle for ZC mmal buffers 7601+ unsigned int vc_addr; // VC addr - unused by us but wanted by FFmpeg 7602+ int fd; // dmabuf handle for GL 7603+ void * mmap; // ARM mapped address 7604+ picture_context_t *ctx2; 7605+} cma_buf_t; 7606+ 7607+static void cma_pool_delete(cma_buf_t * const cb) 7608+{ 7609+ assert(atomic_load(&cb->ref_count) == 0); 7610+#if TRACE_ALL 7611+ cb->cbp->alloc_size -= cb->size; 7612+ --cb->cbp->alloc_n; 7613+ fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cb->cbp->pool->seq, cb->cbp->pool->name, cb->cbp->alloc_n, cb->cbp->alloc_size); 7614+#endif 7615+ 7616+ if (cb->ctx2 != NULL) 7617+ cb->ctx2->destroy(cb->ctx2); 7618+ 7619+ if (cb->mmap != MAP_FAILED) 7620+ { 7621+ if (cb->cbp->init_type == VCSM_INIT_CMA) 7622+ munmap(cb->mmap, cb->size); 7623+ else 7624+ vcsm_unlock_hdl(cb->vcsm_h); 7625+ } 7626+ if (cb->fd != -1) 7627+ close(cb->fd); 7628+ if (cb->vcsm_h != 0) 7629+ vcsm_free(cb->vcsm_h); 7630+ free(cb); 7631+} 7632+ 7633+static void cma_pool_free_cb(void * v, void * el, size_t size) 7634+{ 7635+ VLC_UNUSED(v); 7636+ VLC_UNUSED(size); 7637+ 7638+ cma_pool_delete(el); 7639+} 7640+ 7641+static void * cma_pool_alloc_cb(void * v, size_t size) 7642+{ 7643+ cma_buf_pool_t * const cbp = v; 7644+ 7645+ cma_buf_t * const cb = malloc(sizeof(cma_buf_t)); 7646+ if (cb == NULL) 7647+ return NULL; 7648+ 7649+ *cb = (cma_buf_t){ 7650+ .ref_count = ATOMIC_VAR_INIT(0), 7651+ .cbp = cbp, 7652+ .in_flight = 0, 7653+ .size = size, 7654+ .vcsm_h = 0, 7655+ .vc_h = 0, 7656+ .fd = -1, 7657+ .mmap = MAP_FAILED, 7658+ .ctx2 = NULL 7659+ }; 7660+#if TRACE_ALL 7661+ cb->cbp->alloc_size += cb->size; 7662+ ++cb->cbp->alloc_n; 7663+ fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cbp->pool->seq, cbp->pool->name, cbp->alloc_n, cbp->alloc_size); 7664+#endif 7665+ 7666+ // 0x80 is magic value to force full ARM-side mapping - otherwise 7667+ // cache requests can cause kernel crashes 7668+ if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST | 0x80, "VLC frame")) == 0) 7669+ { 7670+#if TRACE_ALL 7671+ fprintf(stderr, "vcsm_malloc_cache fail\n"); 7672+#endif 7673+ goto fail; 7674+ } 7675+ 7676+ if ((cb->vc_h = vcsm_vc_hdl_from_hdl(cb->vcsm_h)) == 0) 7677+ { 7678+#if TRACE_ALL 7679+ fprintf(stderr, "vcsm_vc_hdl_from_hdl fail\n"); 7680+#endif 7681+ goto fail; 7682+ } 7683+ 7684+ if (cbp->init_type == VCSM_INIT_CMA) 7685+ { 7686+ if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1) 7687+ { 7688+#if TRACE_ALL 7689+ fprintf(stderr, "vcsm_export_dmabuf fail\n"); 7690+#endif 7691+ goto fail; 7692+ } 7693+ 7694+ if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED) 7695+ goto fail; 7696+ } 7697+ else 7698+ { 7699+ void * arm_addr; 7700+ if ((arm_addr = vcsm_lock(cb->vcsm_h)) == NULL) 7701+ { 7702+#if TRACE_ALL 7703+ fprintf(stderr, "vcsm_lock fail\n"); 7704+#endif 7705+ goto fail; 7706+ } 7707+ cb->mmap = arm_addr; 7708+ } 7709+ 7710+ cb->vc_addr = vcsm_vc_addr_from_hdl(cb->vcsm_h); 7711+ 7712+ return cb; 7713+ 7714+fail: 7715+ cma_pool_delete(cb); 7716+ return NULL; 7717+} 7718+ 7719+// Pool has died - safe now to exit vcsm 7720+static void cma_buf_pool_on_delete_cb(void * v) 7721+{ 7722+ cma_buf_pool_t * const cbp = v; 7723+ 7724+ cma_vcsm_exit(cbp->init_type); 7725+ free(cbp); 7726+} 7727+ 7728+void cma_buf_pool_cancel(cma_buf_pool_t * const cbp) 7729+{ 7730+ if (cbp == NULL || cbp->pool == NULL) 7731+ return; 7732+ 7733+ cma_pool_fixed_cancel(cbp->pool); 7734+} 7735+ 7736+void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp) 7737+{ 7738+ if (cbp == NULL || cbp->pool == NULL) 7739+ return; 7740+ 7741+ cma_pool_fixed_uncancel(cbp->pool); 7742+} 7743+ 7744+// User finished with pool 7745+void cma_buf_pool_delete(cma_buf_pool_t * const cbp) 7746+{ 7747+ if (cbp == NULL) 7748+ return; 7749+ 7750+ if (cbp->pool != NULL) 7751+ { 7752+ // We will call cma_buf_pool_on_delete_cb when the pool finally dies 7753+ // (might be now) which will free up our env. 7754+ cma_pool_fixed_kill(cbp->pool); 7755+ } 7756+ else 7757+ { 7758+ // Had no pool for some reason (error) but must still finish cleanup 7759+ cma_buf_pool_on_delete_cb(cbp); 7760+ } 7761+} 7762+ 7763+int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size) 7764+{ 7765+ return cma_pool_fixed_fill(cbp->pool, el_size); 7766+} 7767+ 7768+int cma_buf_pool_resize(cma_buf_pool_t * const cbp, 7769+ const unsigned int new_pool_size, const int new_flight_size) 7770+{ 7771+ return cma_pool_fixed_resize(cbp->pool, new_pool_size, new_flight_size); 7772+} 7773+ 7774+cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size, const bool all_in_flight, const char * const name) 7775+{ 7776+ vcsm_init_type_t const init_type = cma_vcsm_init(); 7777+ if (init_type == VCSM_INIT_NONE) 7778+ return NULL; 7779+ 7780+ cma_buf_pool_t * const cbp = calloc(1, sizeof(cma_buf_pool_t)); 7781+ if (cbp == NULL) 7782+ return NULL; 7783+ 7784+ cbp->init_type = init_type; 7785+ cbp->all_in_flight = all_in_flight; 7786+ 7787+ if ((cbp->pool = cma_pool_fixed_new(pool_size, flight_size, cbp, cma_pool_alloc_cb, cma_pool_free_cb, cma_buf_pool_on_delete_cb, name)) == NULL) 7788+ goto fail; 7789+ return cbp; 7790+ 7791+fail: 7792+ cma_buf_pool_delete(cbp); 7793+ return NULL; 7794+} 7795+ 7796+ 7797+void cma_buf_in_flight(cma_buf_t * const cb) 7798+{ 7799+ if (!cb->cbp->all_in_flight) 7800+ { 7801+ assert(!cb->in_flight); 7802+ cb->in_flight = true; 7803+ cma_pool_fixed_inc_in_flight(cb->cbp->pool); 7804+ } 7805+} 7806+ 7807+void cma_buf_end_flight(cma_buf_t * const cb) 7808+{ 7809+ if (cb != NULL && !cb->cbp->all_in_flight && cb->in_flight) 7810+ { 7811+ cb->in_flight = false; 7812+ cma_pool_fixed_dec_in_flight(cb->cbp->pool); 7813+ } 7814+} 7815+ 7816+ 7817+// Return vcsm handle 7818+unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb) 7819+{ 7820+ return cb->vcsm_h; 7821+} 7822+ 7823+size_t cma_buf_size(const cma_buf_t * const cb) 7824+{ 7825+ return cb->size; 7826+} 7827+ 7828+int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2) 7829+{ 7830+ if (cb->ctx2 != NULL) 7831+ return VLC_EGENERIC; 7832+ 7833+ cb->ctx2 = ctx2; 7834+ return VLC_SUCCESS; 7835+} 7836+ 7837+unsigned int cma_buf_vc_handle(const cma_buf_t *const cb) 7838+{ 7839+ return cb->vc_h; 7840+} 7841+ 7842+int cma_buf_fd(const cma_buf_t *const cb) 7843+{ 7844+ return cb->fd; 7845+} 7846+ 7847+void * cma_buf_addr(const cma_buf_t *const cb) 7848+{ 7849+ return cb->mmap; 7850+} 7851+ 7852+unsigned int cma_buf_vc_addr(const cma_buf_t *const cb) 7853+{ 7854+ return cb->vc_addr; 7855+} 7856+ 7857+ 7858+picture_context_t * cma_buf_context2(const cma_buf_t *const cb) 7859+{ 7860+ return cb->ctx2; 7861+} 7862+ 7863+ 7864+void cma_buf_unref(cma_buf_t * const cb) 7865+{ 7866+ if (cb == NULL) 7867+ return; 7868+ if (atomic_fetch_sub(&cb->ref_count, 1) <= 1) 7869+ { 7870+ const bool was_in_flight = cb->in_flight; 7871+ cb->in_flight = false; 7872+ cma_pool_fixed_put(cb->cbp->pool, cb, cb->size, was_in_flight); 7873+ } 7874+} 7875+ 7876+cma_buf_t * cma_buf_ref(cma_buf_t * const cb) 7877+{ 7878+ if (cb == NULL) 7879+ return NULL; 7880+ atomic_fetch_add(&cb->ref_count, 1); 7881+ return cb; 7882+} 7883+ 7884+cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const cbp, const size_t size) 7885+{ 7886+ cma_buf_t *const cb = cma_pool_fixed_get(cbp->pool, size, cbp->all_in_flight, false); 7887+ 7888+ if (cb == NULL) 7889+ return NULL; 7890+ 7891+ cb->in_flight = cbp->all_in_flight; 7892+ // When 1st allocated or retrieved from the pool the block will have a 7893+ // ref count of 0 so ref here 7894+ return cma_buf_ref(cb); 7895+} 7896+ 7897--- /dev/null 7898+++ b/modules/hw/mmal/mmal_cma.h 7899@@ -0,0 +1,71 @@ 7900+#ifndef VLC_MMAL_MMAL_CMA_H_ 7901+#define VLC_MMAL_MMAL_CMA_H_ 7902+ 7903+ 7904+struct cma_pool_fixed_s; 7905+typedef struct cma_pool_fixed_s cma_pool_fixed_t; 7906+ 7907+typedef void * cma_pool_alloc_fn(void * v, size_t size); 7908+typedef void cma_pool_free_fn(void * v, void * el, size_t size); 7909+typedef void cma_pool_on_delete_fn(void * v); 7910+ 7911+#if 0 7912+void cma_pool_fixed_unref(cma_pool_fixed_t * const p); 7913+void cma_pool_fixed_ref(cma_pool_fixed_t * const p); 7914+void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool in_flight); 7915+void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight); 7916+void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p); 7917+void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p); 7918+void cma_pool_fixed_cancel(cma_pool_fixed_t * const p); 7919+void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p); 7920+void cma_pool_fixed_kill(cma_pool_fixed_t * const p); 7921+int cma_pool_fixed_resize(cma_pool_fixed_t * const p, 7922+ const unsigned int new_pool_size, const int new_flight_size); 7923+cma_pool_fixed_t * cma_pool_fixed_new(const unsigned int pool_size, 7924+ const int flight_size, 7925+ void * const alloc_v, 7926+ cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn, 7927+ cma_pool_on_delete_fn * const on_delete_fn, 7928+ const char * const name); 7929+#endif 7930+ 7931+struct cma_buf_s; 7932+typedef struct cma_buf_s cma_buf_t; 7933+ 7934+void cma_buf_in_flight(cma_buf_t * const cb); 7935+void cma_buf_end_flight(cma_buf_t * const cb); 7936+unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb); 7937+size_t cma_buf_size(const cma_buf_t * const cb); 7938+int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2); 7939+unsigned int cma_buf_vc_handle(const cma_buf_t *const cb); 7940+int cma_buf_fd(const cma_buf_t *const cb); 7941+void * cma_buf_addr(const cma_buf_t *const cb); 7942+unsigned int cma_buf_vc_addr(const cma_buf_t *const cb); 7943+picture_context_t * cma_buf_context2(const cma_buf_t *const cb); 7944+ 7945+void cma_buf_unref(cma_buf_t * const cb); 7946+cma_buf_t * cma_buf_ref(cma_buf_t * const cb); 7947+ 7948+struct cma_buf_pool_s; 7949+typedef struct cma_buf_pool_s cma_buf_pool_t; 7950+ 7951+cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const p, const size_t size); 7952+void cma_buf_pool_cancel(cma_buf_pool_t * const cbp); 7953+void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp); 7954+void cma_buf_pool_delete(cma_buf_pool_t * const p); 7955+int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size); 7956+int cma_buf_pool_resize(cma_buf_pool_t * const cbp, 7957+ const unsigned int new_pool_size, const int new_flight_size); 7958+cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size, 7959+ const bool all_in_flight, const char * const name); 7960+ 7961+static inline void cma_buf_pool_deletez(cma_buf_pool_t ** const pp) 7962+{ 7963+ cma_buf_pool_t * const p = *pp; 7964+ if (p != NULL) { 7965+ *pp = NULL; 7966+ cma_buf_pool_delete(p); 7967+ } 7968+} 7969+ 7970+#endif // VLC_MMAL_MMAL_CMA_H_ 7971--- /dev/null 7972+++ b/modules/hw/mmal/mmal_gl.h 7973@@ -0,0 +1,45 @@ 7974+// Trim this include list! 7975+ 7976+#include <libdrm/drm.h> 7977+#include <libdrm/drm_mode.h> 7978+#include <libdrm/drm_fourcc.h> 7979+//#include <xf86drm.h> 7980+//#include <xf86drmMode.h> 7981+#include <X11/Xlib.h> 7982+#include <X11/Xutil.h> 7983+#include <X11/Xlib-xcb.h> 7984+#include <epoxy/gl.h> 7985+#include <epoxy/egl.h> 7986+#include <xcb/xcb.h> 7987+#include <xcb/dri3.h> 7988+ 7989+struct mmal_gl_converter_s; 7990+ 7991+typedef struct cma_buf_s { 7992+ struct mmal_gl_converter_s * sys; 7993+ 7994+ size_t size; 7995+ __u32 h_dumb; 7996+ int fd; 7997+ unsigned int h_vcsm; 7998+ void * mapped_addr; 7999+ GLuint texture; 8000+} cma_buf_t; 8001+ 8002+typedef struct cma_pic_sys_s { 8003+ cma_buf_t * cmabuf; 8004+} cma_pic_sys_t; 8005+ 8006+static inline unsigned int 8007+hw_mmal_h_vcsm(const picture_t * const pic) 8008+{ 8009+ const cma_pic_sys_t *const pic_sys = (cma_pic_sys_t *)pic->p_sys; 8010+ 8011+ if (pic->format.i_chroma != VLC_CODEC_MMAL_GL_RGB32 || 8012+ pic_sys == NULL || pic_sys->cmabuf == NULL) { 8013+ return 0; 8014+ } 8015+ 8016+ return pic_sys->cmabuf->h_vcsm; 8017+} 8018+ 8019--- /dev/null 8020+++ b/modules/hw/mmal/mmal_piccpy_neon.S 8021@@ -0,0 +1,105 @@ 8022+// Copy pix 8023+ 8024+ .syntax unified 8025+ .arm 8026+// .thumb 8027+ .text 8028+ .align 16 8029+ .arch armv7-a 8030+ .fpu neon-vfpv4 8031+ 8032+ 8033+.macro function name 8034+ .global \name 8035+#ifdef __ELF__ 8036+ .type \name, %function 8037+#endif 8038+\name: 8039+.endm 8040+ 8041+ 8042+.macro piccpy_to_8, bit_depth 8043+ subs r2, #128 8044+ vpush {q4-q7} 8045+ blt 2f 8046+1: 8047+ vldm r1!, {q0-q7} 8048+ subs r2, #128 8049+ vqrshrn.u16 d0, q0, #\bit_depth - 8 8050+ vqrshrn.u16 d1, q1, #\bit_depth - 8 8051+ vqrshrn.u16 d2, q2, #\bit_depth - 8 8052+ vqrshrn.u16 d3, q3, #\bit_depth - 8 8053+ vldm r1!, {q8-q15} 8054+ vqrshrn.u16 d4, q4, #\bit_depth - 8 8055+ vqrshrn.u16 d5, q5, #\bit_depth - 8 8056+ vqrshrn.u16 d6, q6, #\bit_depth - 8 8057+ vqrshrn.u16 d7, q7, #\bit_depth - 8 8058+ vqrshrn.u16 d8, q8, #\bit_depth - 8 8059+ vqrshrn.u16 d9, q9, #\bit_depth - 8 8060+ vqrshrn.u16 d10, q10, #\bit_depth - 8 8061+ vqrshrn.u16 d11, q11, #\bit_depth - 8 8062+ vqrshrn.u16 d12, q12, #\bit_depth - 8 8063+ vqrshrn.u16 d13, q13, #\bit_depth - 8 8064+ vqrshrn.u16 d14, q14, #\bit_depth - 8 8065+ vqrshrn.u16 d15, q15, #\bit_depth - 8 8066+ vstm r0!, {q0-q7} 8067+ bge 1b 8068+2: 8069+ adds r2, #64 8070+ blt 1f 8071+ 8072+ vldm r1!, {q0-q7} 8073+ vqrshrn.u16 d0, q0, #\bit_depth - 8 8074+ vqrshrn.u16 d1, q1, #\bit_depth - 8 8075+ vqrshrn.u16 d2, q2, #\bit_depth - 8 8076+ vqrshrn.u16 d3, q3, #\bit_depth - 8 8077+ vqrshrn.u16 d4, q4, #\bit_depth - 8 8078+ vqrshrn.u16 d5, q5, #\bit_depth - 8 8079+ vqrshrn.u16 d6, q6, #\bit_depth - 8 8080+ vqrshrn.u16 d7, q7, #\bit_depth - 8 8081+ vstm r0!, {q0-q3} 8082+1: 8083+ adds r2, #32 8084+ blt 1f 8085+ 8086+ vldm r1!, {q0-q3} 8087+ vqrshrn.u16 d0, q0, #\bit_depth - 8 8088+ vqrshrn.u16 d1, q1, #\bit_depth - 8 8089+ vqrshrn.u16 d2, q2, #\bit_depth - 8 8090+ vqrshrn.u16 d3, q3, #\bit_depth - 8 8091+ vstm r0!, {q0-q1} 8092+1: 8093+ adds r2, #16 8094+ blt 1f 8095+ 8096+ vldm r1!, {q0-q1} 8097+ vqrshrn.u16 d0, q0, #\bit_depth - 8 8098+ vqrshrn.u16 d1, q1, #\bit_depth - 8 8099+ vstm r0!, {q0} 8100+1: 8101+ adds r2, #8 8102+ blt 1f 8103+ 8104+ vldm r1!, {q0} 8105+ vqrshrn.u16 d0, q0, #\bit_depth - 8 8106+ vstr d0, [r0] 8107+ add r0, #8 8108+1: 8109+ adds r2, #4 8110+ blt 1f 8111+ 8112+ vldr d0, [r1] 8113+ vqrshrn.u16 d0, q0, #\bit_depth - 8 8114+ vstr s0, [r0] 8115+1: 8116+ vpop {q4-q7} 8117+ bx lr 8118+.endm 8119+ 8120+ 8121+@ [r0] Dest 8122+@ [r1] Src 8123+@ r2 Pels 8124+function mmal_piccpy_10_to_8_neon 8125+ piccpy_to_8 10 8126+ 8127--- a/modules/hw/mmal/mmal_picture.c 8128+++ b/modules/hw/mmal/mmal_picture.c 8129@@ -21,25 +21,1542 @@ 8130 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 8131 *****************************************************************************/ 8132 8133+// We would really like to use vlc_thread.h but the detach thread stuff can't be 8134+// used here :-( 8135+#include <pthread.h> 8136+ 8137+#include <stdatomic.h> 8138+#include <unistd.h> 8139+#include <fcntl.h> 8140+ 8141 #include <vlc_common.h> 8142+#include <vlc_cpu.h> 8143 #include <vlc_picture.h> 8144+ 8145+#pragma GCC diagnostic push 8146+#pragma GCC diagnostic ignored "-Wbad-function-cast" 8147+#include <bcm_host.h> 8148+#pragma GCC diagnostic pop 8149 #include <interface/mmal/mmal.h> 8150+#include <interface/mmal/util/mmal_util.h> 8151+#include <interface/mmal/util/mmal_default_components.h> 8152+#include <interface/vmcs_host/vcgencmd.h> 8153+#include <interface/vcsm/user-vcsm.h> 8154 8155+#include "mmal_cma.h" 8156 #include "mmal_picture.h" 8157+#include "transform_ops.h" 8158+ 8159+#define TRACE_TRANSFORMS 0 8160+ 8161+#define UINT64_SIZE(s) (((s) + sizeof(uint64_t) - 1)/sizeof(uint64_t)) 8162+ 8163+static inline char safe_char(const unsigned int c0) 8164+{ 8165+ const unsigned int c = c0 & 0xff; 8166+ return c > ' ' && c < 0x7f ? c : '.'; 8167+} 8168+ 8169+const char * str_fourcc(char * const buf, const unsigned int fcc) 8170+{ 8171+ if (fcc == 0) 8172+ return "----"; 8173+ buf[0] = safe_char(fcc >> 0); 8174+ buf[1] = safe_char(fcc >> 8); 8175+ buf[2] = safe_char(fcc >> 16); 8176+ buf[3] = safe_char(fcc >> 24); 8177+ buf[4] = 0; 8178+ return buf; 8179+} 8180+ 8181+// WB + Inv 8182+static inline void flush_range(void * const start, const size_t len) 8183+{ 8184+ uint64_t buf[UINT64_SIZE(sizeof(struct vcsm_user_clean_invalid2_s) + sizeof(struct vcsm_user_clean_invalid2_block_s))]; 8185+ struct vcsm_user_clean_invalid2_s * const b = (struct vcsm_user_clean_invalid2_s *)buf; 8186+ 8187+ *b = (struct vcsm_user_clean_invalid2_s){ 8188+ .op_count = 1 8189+ }; 8190+ 8191+ b->s[0] = (struct vcsm_user_clean_invalid2_block_s){ 8192+ .invalidate_mode = 3, // wb + invalidate 8193+ .block_count = 1, 8194+ .start_address = start, // Rely on clean inv to fix up align & size boundries 8195+ .block_size = len, 8196+ .inter_block_stride = 0 8197+ }; 8198+ 8199+ vcsm_clean_invalid2(b); 8200+} 8201+ 8202+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs) 8203+{ 8204+ switch (vlc_cs) 8205+ { 8206+ case COLOR_SPACE_BT601: 8207+ return MMAL_COLOR_SPACE_ITUR_BT601; 8208+ case COLOR_SPACE_BT709: 8209+ return MMAL_COLOR_SPACE_ITUR_BT709; 8210+ default: 8211+ break; 8212+ } 8213+ return MMAL_COLOR_SPACE_UNKNOWN; 8214+} 8215+ 8216+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc) 8217+{ 8218+ switch (vf_vlc->i_chroma) { 8219+ case VLC_CODEC_MMAL_ZC_RGB32: 8220+ case VLC_CODEC_RGB32: 8221+ { 8222+ // VLC RGB32 aka RV32 means we have to look at the mask values 8223+ const uint32_t r = vf_vlc->i_rmask; 8224+ const uint32_t g = vf_vlc->i_gmask; 8225+ const uint32_t b = vf_vlc->i_bmask; 8226+ if (r == 0xff0000 && g == 0xff00 && b == 0xff) 8227+ return MMAL_ENCODING_BGRA; 8228+ if (r == 0xff && g == 0xff00 && b == 0xff0000) 8229+ return MMAL_ENCODING_RGBA; 8230+ if (r == 0xff000000 && g == 0xff0000 && b == 0xff00) 8231+ return MMAL_ENCODING_ABGR; 8232+ if (r == 0xff00 && g == 0xff0000 && b == 0xff000000) 8233+ return MMAL_ENCODING_ARGB; 8234+ break; 8235+ } 8236+ case VLC_CODEC_RGB16: 8237+ { 8238+ // VLC RGB16 aka RV16 means we have to look at the mask values 8239+ const uint32_t r = vf_vlc->i_rmask; 8240+ const uint32_t g = vf_vlc->i_gmask; 8241+ const uint32_t b = vf_vlc->i_bmask; 8242+ if (r == 0xf800 && g == 0x7e0 && b == 0x1f) 8243+ return MMAL_ENCODING_RGB16; 8244+ break; 8245+ } 8246+ case VLC_CODEC_I420: 8247+ case VLC_CODEC_MMAL_ZC_I420: 8248+ return MMAL_ENCODING_I420; 8249+ case VLC_CODEC_RGBA: 8250+ return MMAL_ENCODING_RGBA; 8251+ case VLC_CODEC_BGRA: 8252+ return MMAL_ENCODING_BGRA; 8253+ case VLC_CODEC_ARGB: 8254+ return MMAL_ENCODING_ARGB; 8255+ // VLC_CODEC_ABGR does not exist in VLC 8256+ case VLC_CODEC_MMAL_OPAQUE: 8257+ return MMAL_ENCODING_OPAQUE; 8258+ case VLC_CODEC_MMAL_ZC_SAND8: 8259+ return MMAL_ENCODING_YUVUV128; 8260+ case VLC_CODEC_MMAL_ZC_SAND10: 8261+ return MMAL_ENCODING_YUVUV64_10; 8262+ case VLC_CODEC_MMAL_ZC_SAND30: 8263+ return MMAL_ENCODING_YUV10_COL; 8264+ default: 8265+ break; 8266+ } 8267+ return 0; 8268+} 8269+ 8270+static void vlc_fmt_to_video_format(MMAL_VIDEO_FORMAT_T *const vf_mmal, const video_frame_format_t * const vf_vlc) 8271+{ 8272+ const unsigned int wmask = (vf_vlc->i_chroma == VLC_CODEC_MMAL_ZC_I420 || 8273+ vf_vlc->i_chroma == VLC_CODEC_I420) ? 31 : 15; 8274+ 8275+ vf_mmal->width = (vf_vlc->i_width + wmask) & ~wmask; 8276+ vf_mmal->height = (vf_vlc->i_height + 15) & ~15; 8277+ vf_mmal->crop.x = vf_vlc->i_x_offset; 8278+ vf_mmal->crop.y = vf_vlc->i_y_offset; 8279+ vf_mmal->crop.width = vf_vlc->i_visible_width; 8280+ vf_mmal->crop.height = vf_vlc->i_visible_height; 8281+ if (vf_vlc->i_sar_num == 0 || vf_vlc->i_sar_den == 0) { 8282+ vf_mmal->par.num = 1; 8283+ vf_mmal->par.den = 1; 8284+ } else { 8285+ vf_mmal->par.num = vf_vlc->i_sar_num; 8286+ vf_mmal->par.den = vf_vlc->i_sar_den; 8287+ } 8288+ vf_mmal->frame_rate.num = vf_vlc->i_frame_rate; 8289+ vf_mmal->frame_rate.den = vf_vlc->i_frame_rate_base; 8290+ vf_mmal->color_space = vlc_to_mmal_color_space(vf_vlc->space); 8291+} 8292+ 8293+ 8294+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc) 8295+{ 8296+ vlc_fmt_to_video_format(&es_fmt->es->video, vf_vlc); 8297+} 8298+ 8299+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic) 8300+{ 8301+ MMAL_VIDEO_FORMAT_T vf_new_ss; 8302+ MMAL_VIDEO_FORMAT_T *const vf_old = &es_fmt->es->video; 8303+ MMAL_VIDEO_FORMAT_T *const vf_new = &vf_new_ss; 8304+ 8305+ vlc_fmt_to_video_format(vf_new, &pic->format); 8306+ 8307+ // If we have a format that might have come from ffmpeg then rework for 8308+ // a better guess as to layout. All sand stuff is "special" with regards to 8309+ // width/height vs real layout so leave as is if that 8310+ if ((pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 || 8311+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) && 8312+ pic->p[0].i_pixel_pitch != 0) 8313+ { 8314+ // Now overwrite width/height with a better guess as to actual layout info 8315+ vf_new->height = pic->p[0].i_lines; 8316+ vf_new->width = pic->p[0].i_pitch / pic->p[0].i_pixel_pitch; 8317+ } 8318+ 8319+ if ( 8320+ vf_new->width != vf_old->width || 8321+ vf_new->height != vf_old->height || 8322+ vf_new->crop.x != vf_old->crop.x || 8323+ vf_new->crop.y != vf_old->crop.y || 8324+ vf_new->crop.width != vf_old->crop.width || 8325+ vf_new->crop.height != vf_old->crop.height || 8326+ vf_new->par.num != vf_old->par.num || 8327+ vf_new->par.den != vf_old->par.den || 8328+ // Frame rate ignored 8329+ vf_new->color_space != vf_old->color_space) 8330+ { 8331+#if 0 8332+ char dbuf0[5], dbuf1[5]; 8333+ printf("%dx%d (%d,%d %dx%d) par:%d/%d %s -> %dx%d (%d,%d %dx%d) par:%d/%d %s\n", 8334+ vf_old->width , 8335+ vf_old->height , 8336+ vf_old->crop.x , 8337+ vf_old->crop.y , 8338+ vf_old->crop.width , 8339+ vf_old->crop.height , 8340+ vf_old->par.num , 8341+ vf_old->par.den , 8342+ str_fourcc(dbuf0, vf_old->color_space) , 8343+ vf_new->width , 8344+ vf_new->height , 8345+ vf_new->crop.x , 8346+ vf_new->crop.y , 8347+ vf_new->crop.width , 8348+ vf_new->crop.height , 8349+ vf_new->par.num , 8350+ vf_new->par.den , 8351+ str_fourcc(dbuf1, vf_new->color_space) ); 8352+#endif 8353+ *vf_old = *vf_new; 8354+ return true; 8355+ } 8356+ return false; 8357+} 8358+ 8359+ 8360+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port, 8361+ const unsigned int headers, const uint32_t payload_size) 8362+{ 8363+ hw_mmal_port_pool_ref_t * ppr = calloc(1, sizeof(hw_mmal_port_pool_ref_t)); 8364+ if (ppr == NULL) 8365+ return NULL; 8366+ 8367+ if ((ppr->pool = mmal_port_pool_create(port, headers, payload_size)) == NULL) 8368+ goto fail; 8369+ 8370+ ppr->port = port; 8371+ atomic_store(&ppr->refs, 1); 8372+ return ppr; 8373+ 8374+fail: 8375+ free(ppr); 8376+ return NULL; 8377+} 8378+ 8379+static void do_detached(void *(*fn)(void *), void * v) 8380+{ 8381+ pthread_t dothread; 8382+ pthread_create(&dothread, NULL, fn, v); 8383+ pthread_detach(dothread); 8384+} 8385+ 8386+// Destroy a ppr - aranged s.t. it has the correct prototype for a pthread 8387+static void * kill_ppr(void * v) 8388+{ 8389+ hw_mmal_port_pool_ref_t * const ppr = v; 8390+ if (ppr->port->is_enabled) 8391+ mmal_port_disable(ppr->port); // Avoid annoyed messages from MMAL when we kill the pool 8392+ mmal_port_pool_destroy(ppr->port, ppr->pool); 8393+ free(ppr); 8394+ return NULL; 8395+} 8396+ 8397+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb) 8398+{ 8399+ if (ppr == NULL) 8400+ return; 8401+ if (atomic_fetch_sub(&ppr->refs, 1) != 1) 8402+ return; 8403+ if (in_cb) 8404+ do_detached(kill_ppr, ppr); 8405+ else 8406+ kill_ppr(ppr); 8407+} 8408+ 8409+// Put buffer in port if possible - if not then release to pool 8410+// Returns true if sent, false if recycled 8411+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf) 8412+{ 8413+ mmal_buffer_header_reset(buf); 8414+ buf->user_data = NULL; 8415+ 8416+ if (mmal_port_send_buffer(ppr->port, buf) == MMAL_SUCCESS) 8417+ return true; 8418+ mmal_buffer_header_release(buf); 8419+ return false; 8420+} 8421+ 8422+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr) 8423+{ 8424+ MMAL_BUFFER_HEADER_T * buf; 8425+ MMAL_STATUS_T err = MMAL_SUCCESS; 8426+ 8427+ while ((buf = mmal_queue_get(ppr->pool->queue)) != NULL) { 8428+ if ((err = mmal_port_send_buffer(ppr->port, buf)) != MMAL_SUCCESS) 8429+ { 8430+ mmal_queue_put_back(ppr->pool->queue, buf); 8431+ break; 8432+ } 8433+ } 8434+ return err; 8435+} 8436+ 8437+ 8438+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj, 8439+ hw_mmal_port_pool_ref_t ** pppr, 8440+ MMAL_PORT_T * const port, 8441+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback) 8442+{ 8443+ MMAL_STATUS_T status; 8444+ 8445+ port->userdata = (struct MMAL_PORT_USERDATA_T *)obj; 8446+ 8447+ status = port_parameter_set_uint32(port, MMAL_PARAMETER_EXTRA_BUFFERS, extra_buffers); 8448+ if (status != MMAL_SUCCESS) { 8449+ msg_Err(obj, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)", 8450+ status, mmal_status_to_string(status)); 8451+ return status; 8452+ } 8453+ 8454+ status = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, 1); 8455+ if (status != MMAL_SUCCESS) { 8456+ msg_Err(obj, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", 8457+ port->name, status, mmal_status_to_string(status)); 8458+ return status; 8459+ } 8460+ 8461+ port->format->encoding = MMAL_ENCODING_OPAQUE; 8462+ port->format->encoding_variant = 0; 8463+ if ((status = mmal_port_format_commit(port)) != MMAL_SUCCESS) 8464+ { 8465+ msg_Err(obj, "Failed to commit format on port %s (status=%"PRIx32" %s)", 8466+ port->name, status, mmal_status_to_string(status)); 8467+ return status; 8468+ } 8469+ 8470+ port->buffer_num = 30; 8471+ port->buffer_size = port->buffer_size_recommended; 8472+ 8473+ if ((*pppr = hw_mmal_port_pool_ref_create(port, port->buffer_num, port->buffer_size)) == NULL) { 8474+ msg_Err(obj, "Failed to create output pool"); 8475+ return status; 8476+ } 8477+ 8478+ status = mmal_port_enable(port, callback); 8479+ if (status != MMAL_SUCCESS) { 8480+ hw_mmal_port_pool_ref_release(*pppr, false); 8481+ *pppr = NULL; 8482+ msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)", 8483+ port->name, status, mmal_status_to_string(status)); 8484+ return status; 8485+ } 8486+ 8487+ return MMAL_SUCCESS; 8488+} 8489+ 8490+ 8491+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn) 8492+{ 8493+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic_ctx_cmn; 8494+ unsigned int i; 8495+ 8496+ for (i = 0; i != ctx->buf_count; ++i) { 8497+ if (ctx->bufs[i] != NULL) 8498+ mmal_buffer_header_release(ctx->bufs[i]); 8499+ } 8500+ 8501+ cma_buf_end_flight(ctx->cb); 8502+ cma_buf_unref(ctx->cb); 8503+ 8504+ free(ctx); 8505+} 8506+ 8507+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn) 8508+{ 8509+ const pic_ctx_mmal_t * const src_ctx = (pic_ctx_mmal_t *)pic_ctx_cmn; 8510+ pic_ctx_mmal_t * const dst_ctx = calloc(1, sizeof(*dst_ctx)); 8511+ unsigned int i; 8512+ 8513+ if (dst_ctx == NULL) 8514+ return NULL; 8515+ 8516+ // Copy 8517+ dst_ctx->cmn = src_ctx->cmn; 8518+ 8519+ dst_ctx->cb = cma_buf_ref(src_ctx->cb); 8520+ 8521+ dst_ctx->buf_count = src_ctx->buf_count; 8522+ for (i = 0; i != src_ctx->buf_count; ++i) { 8523+ dst_ctx->bufs[i] = src_ctx->bufs[i]; 8524+ if (dst_ctx->bufs[i] != NULL) 8525+ mmal_buffer_header_acquire(dst_ctx->bufs[i]); 8526+ } 8527+ 8528+ return &dst_ctx->cmn; 8529+} 8530+ 8531+static MMAL_BOOL_T 8532+buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata) 8533+{ 8534+ hw_mmal_port_pool_ref_t * const ppr = userdata; 8535+ 8536+ // Kill the callback - otherwise we will go in circles! 8537+ mmal_buffer_header_pre_release_cb_set(buf, (MMAL_BH_PRE_RELEASE_CB_T)0, NULL); 8538+ mmal_buffer_header_acquire(buf); // Ref it again 8539+ 8540+ // As we have re-acquired the buffer we need a full release 8541+ // (not continue) to zap the ref count back to zero 8542+ // This is "safe" 'cos we have already reset the cb 8543+ hw_mmal_port_pool_ref_recycle(ppr, buf); 8544+ hw_mmal_port_pool_ref_release(ppr, true); // Assume in callback 8545+ 8546+ return MMAL_TRUE; 8547+} 8548+ 8549+// Buffer belongs to context on successful return from this fn 8550+// is still valid on failure 8551+picture_context_t * 8552+hw_mmal_gen_context(MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr) 8553+{ 8554+ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t)); 8555+ 8556+ if (ctx == NULL) 8557+ return NULL; 8558+ 8559+ // If we have an associated ppr then ref & set appropriate callbacks 8560+ if (ppr != NULL) { 8561+ hw_mmal_port_pool_ref_acquire(ppr); 8562+ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr); 8563+ buf->user_data = NULL; 8564+ } 8565+ 8566+ ctx->cmn.copy = hw_mmal_pic_ctx_copy; 8567+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy; 8568+ 8569+ ctx->buf_count = 1; 8570+ ctx->bufs[0] = buf; 8571+ 8572+ return &ctx->cmn; 8573+} 8574+ 8575+// n is els 8576+// * Make NEON! 8577+typedef void piccpy_fn(void * dest, const void * src, size_t n); 8578+ 8579+extern piccpy_fn mmal_piccpy_10_to_8_neon; 8580+ 8581+static void piccpy_10_to_8_c(void * dest, const void * src, size_t n) 8582+{ 8583+ uint8_t * d = dest; 8584+ const uint16_t * s = src; 8585+ while (n-- != 0) 8586+ *d++ = *s++ >> 2; 8587+} 8588+ 8589+// Do a stride converting copy - if the strides are the same and line_len is 8590+// close then do a single block copy - we don't expect to have to preserve 8591+// pixels in the output frame 8592+static void mem_copy_2d(uint8_t * d_ptr, const size_t d_stride, 8593+ const uint8_t * s_ptr, const size_t s_stride, 8594+ size_t lines, const size_t line_len) 8595+{ 8596+ if (s_stride == d_stride && d_stride < line_len + 32) 8597+ { 8598+ memcpy(d_ptr, s_ptr, d_stride * lines); 8599+ } 8600+ else 8601+ { 8602+ while (lines-- != 0) { 8603+ memcpy(d_ptr, s_ptr, line_len); 8604+ d_ptr += d_stride; 8605+ s_ptr += s_stride; 8606+ } 8607+ } 8608+} 8609+ 8610+// line_len in D units 8611+static void mem_copy_2d_10_to_8(uint8_t * d_ptr, const size_t d_stride, 8612+ const uint8_t * s_ptr, const size_t s_stride, 8613+ size_t lines, const size_t line_len) 8614+{ 8615+ piccpy_fn * const docpy = vlc_CPU_ARM_NEON() ? mmal_piccpy_10_to_8_neon : piccpy_10_to_8_c; 8616+ if (s_stride == d_stride * 2 && d_stride < line_len + 32) 8617+ { 8618+ docpy(d_ptr, s_ptr, d_stride * lines); 8619+ } 8620+ else 8621+ { 8622+ while (lines-- != 0) { 8623+ docpy(d_ptr, s_ptr, line_len); 8624+ d_ptr += d_stride; 8625+ s_ptr += s_stride; 8626+ } 8627+ } 8628+} 8629+ 8630+ 8631+int hw_mmal_copy_pic_to_buf(void * const buf_data, 8632+ uint32_t * const pLength, 8633+ const MMAL_ES_FORMAT_T * const fmt, 8634+ const picture_t * const pic) 8635+{ 8636+ const MMAL_VIDEO_FORMAT_T *const video = &fmt->es->video; 8637+ uint8_t * const dest = buf_data; 8638+ size_t length = 0; 8639+ 8640+ //**** Worry about x/y_offsets 8641+ 8642+ assert(fmt->encoding == MMAL_ENCODING_I420); 8643+ 8644+ switch (pic->format.i_chroma) { 8645+ case VLC_CODEC_I420: 8646+ { 8647+ const size_t y_size = video->width * video->height; 8648+ mem_copy_2d(dest, video->width, 8649+ pic->p[0].p_pixels, pic->p[0].i_pitch, 8650+ video->crop.height, 8651+ video->crop.width); 8652+ 8653+ mem_copy_2d(dest + y_size, video->width / 2, 8654+ pic->p[1].p_pixels, pic->p[1].i_pitch, 8655+ video->crop.height / 2, 8656+ video->crop.width / 2); 8657+ 8658+ mem_copy_2d(dest + y_size + y_size / 4, video->width / 2, 8659+ pic->p[2].p_pixels, pic->p[2].i_pitch, 8660+ video->crop.height / 2, 8661+ video->crop.width / 2); 8662+ 8663+ // And make sure it is actually in memory 8664+ length = y_size + y_size / 2; 8665+ break; 8666+ } 8667+ 8668+ case VLC_CODEC_I420_10L: 8669+ { 8670+ const size_t y_size = video->width * video->height; 8671+ mem_copy_2d_10_to_8(dest, video->width, 8672+ pic->p[0].p_pixels, pic->p[0].i_pitch, 8673+ video->crop.height, 8674+ video->crop.width); 8675+ 8676+ mem_copy_2d_10_to_8(dest + y_size, video->width / 2, 8677+ pic->p[1].p_pixels, pic->p[1].i_pitch, 8678+ video->crop.height / 2, 8679+ video->crop.width / 2); 8680+ 8681+ mem_copy_2d_10_to_8(dest + y_size + y_size / 4, video->width / 2, 8682+ pic->p[2].p_pixels, pic->p[2].i_pitch, 8683+ video->crop.height / 2, 8684+ video->crop.width / 2); 8685+ 8686+ // And make sure it is actually in memory 8687+ length = y_size + y_size / 2; 8688+ break; 8689+ } 8690+ 8691+ default: 8692+ if (pLength != NULL) 8693+ *pLength = 0; 8694+ return VLC_EBADVAR; 8695+ } 8696+ 8697+ if (cma_vcsm_type() == VCSM_INIT_LEGACY) { // ** CMA is currently always uncached 8698+ flush_range(dest, length); 8699+ } 8700+ 8701+ if (pLength != NULL) 8702+ *pLength = (uint32_t)length; 8703+ 8704+ return VLC_SUCCESS; 8705+} 8706+ 8707+ 8708+static MMAL_BOOL_T rep_buf_free_cb(MMAL_BUFFER_HEADER_T *header, void *userdata) 8709+{ 8710+ cma_buf_t * const cb = userdata; 8711+ VLC_UNUSED(header); 8712+ 8713+ cma_buf_unref(cb); 8714+ return MMAL_FALSE; 8715+} 8716+ 8717+static int cma_buf_buf_attach(MMAL_BUFFER_HEADER_T * const buf, cma_buf_t * const cb) 8718+{ 8719+ // Just a CMA buffer - fill in new buffer 8720+ const uintptr_t vc_h = cma_buf_vc_handle(cb); 8721+ if (vc_h == 0) 8722+ return VLC_EGENERIC; 8723+ 8724+ mmal_buffer_header_reset(buf); 8725+ buf->data = (uint8_t *)vc_h; 8726+ buf->alloc_size = cma_buf_size(cb); 8727+ buf->length = buf->alloc_size; 8728+ // Ensure cb remains valid for the duration of this buffer 8729+ mmal_buffer_header_pre_release_cb_set(buf, rep_buf_free_cb, cma_buf_ref(cb)); 8730+ return VLC_SUCCESS; 8731+} 8732+ 8733+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic, 8734+ MMAL_POOL_T * const rep_pool, 8735+ MMAL_PORT_T * const port, 8736+ cma_buf_pool_t * const cbp) 8737+{ 8738+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(rep_pool->queue); 8739+ if (buf == NULL) 8740+ goto fail0; 8741+ 8742+ cma_buf_t * const cb = cma_buf_pool_alloc_buf(cbp, port->buffer_size); 8743+ if (cb == NULL) 8744+ goto fail1; 8745+ 8746+ if (cma_buf_buf_attach(buf, cb) != VLC_SUCCESS) 8747+ goto fail2; 8748+ 8749+ pic_to_buf_copy_props(buf, pic); 8750+ 8751+ if (hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), &buf->length, port->format, pic) != VLC_SUCCESS) 8752+ goto fail2; 8753+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; 8754+ 8755+ cma_buf_unref(cb); 8756+ return buf; 8757+ 8758+fail2: 8759+ cma_buf_unref(cb); 8760+fail1: 8761+ mmal_buffer_header_release(buf); 8762+fail0: 8763+ return NULL; 8764+} 8765+ 8766+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool) 8767+{ 8768+ pic_ctx_mmal_t *const ctx = (pic_ctx_mmal_t *)pic->context; 8769+ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue); 8770+ 8771+ if (rep_buf == NULL) 8772+ return NULL; 8773+ 8774+ if (ctx->bufs[0] != NULL) 8775+ { 8776+ // Existing buffer - replicate it 8777+ if (mmal_buffer_header_replicate(rep_buf, ctx->bufs[0]) != MMAL_SUCCESS) 8778+ goto fail; 8779+ } 8780+ else if (ctx->cb != NULL) 8781+ { 8782+ // Just a CMA buffer - fill in new buffer 8783+ if (cma_buf_buf_attach(rep_buf, ctx->cb) != 0) 8784+ goto fail; 8785+ } 8786+ else 8787+ goto fail; 8788+ 8789+ pic_to_buf_copy_props(rep_buf, pic); 8790+ return rep_buf; 8791+ 8792+fail: 8793+ mmal_buffer_header_release(rep_buf); 8794+ return NULL; 8795+} 8796+ 8797+ 8798+ 8799+ 8800+int hw_mmal_get_gpu_mem(void) { 8801+ static int stashed_val = -2; 8802+ VCHI_INSTANCE_T vchi_instance; 8803+ VCHI_CONNECTION_T *vchi_connection = NULL; 8804+ char rbuf[1024] = { 0 }; 8805+ 8806+ if (stashed_val >= -1) 8807+ return stashed_val; 8808+ 8809+ if (vchi_initialise(&vchi_instance) != 0) 8810+ goto fail0; 8811+ 8812+ //create a vchi connection 8813+ if (vchi_connect(NULL, 0, vchi_instance) != 0) 8814+ goto fail0; 8815+ 8816+ vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1); 8817+ 8818+ //send the gencmd for the argument 8819+ if (vc_gencmd_send("get_mem gpu") != 0) 8820+ goto fail; 8821+ 8822+ if (vc_gencmd_read_response(rbuf, sizeof(rbuf) - 1) != 0) 8823+ goto fail; 8824+ 8825+ if (strncmp(rbuf, "gpu=", 4) != 0) 8826+ goto fail; 8827+ 8828+ char *p; 8829+ unsigned long m = strtoul(rbuf + 4, &p, 10); 8830+ 8831+ if (p[0] != 'M' || p[1] != '\0') 8832+ stashed_val = -1; 8833+ else 8834+ stashed_val = (int)m << 20; 8835+ 8836+ vc_gencmd_stop(); 8837+ 8838+ //close the vchi connection 8839+ vchi_disconnect(vchi_instance); 8840+ 8841+ return stashed_val; 8842+ 8843+fail: 8844+ vc_gencmd_stop(); 8845+ vchi_disconnect(vchi_instance); 8846+fail0: 8847+ stashed_val = -1; 8848+ return -1; 8849+}; 8850+ 8851+// =========================================================================== 8852+ 8853+typedef struct pool_ent_s 8854+{ 8855+ struct pool_ent_s * next; 8856+ struct pool_ent_s * prev; 8857+ 8858+ atomic_int ref_count; 8859+ unsigned int seq; 8860+ 8861+ size_t size; 8862+ 8863+ int vcsm_hdl; 8864+ int vc_hdl; 8865+ void * buf; 8866+ 8867+ unsigned int width; 8868+ unsigned int height; 8869+ MMAL_FOURCC_T enc_type; 8870+ 8871+ picture_t * pic; 8872+} pool_ent_t; 8873+ 8874+ 8875+typedef struct ent_list_hdr_s 8876+{ 8877+ pool_ent_t * ents; 8878+ pool_ent_t * tail; 8879+ unsigned int n; 8880+} ent_list_hdr_t; 8881+ 8882+#define ENT_LIST_HDR_INIT (ent_list_hdr_t){ \ 8883+ .ents = NULL, \ 8884+ .tail = NULL, \ 8885+ .n = 0 \ 8886+} 8887+ 8888+struct vzc_pool_ctl_s 8889+{ 8890+ atomic_int ref_count; 8891+ 8892+ ent_list_hdr_t ent_pool; 8893+ ent_list_hdr_t ents_cur; 8894+ ent_list_hdr_t ents_prev; 8895+ 8896+ unsigned int max_n; 8897+ unsigned int seq; 8898+ 8899+ vlc_mutex_t lock; 8900+ 8901+ MMAL_POOL_T * buf_pool; 8902+ 8903+ vcsm_init_type_t vcsm_init_type; 8904+}; 8905+ 8906+typedef struct vzc_subbuf_ent_s 8907+{ 8908+ pool_ent_t * ent; 8909+ MMAL_RECT_T pic_rect; 8910+ MMAL_RECT_T orig_dest_rect; 8911+ MMAL_DISPLAYREGION_T dreg; 8912+} vzc_subbuf_ent_t; 8913+ 8914+ 8915+static pool_ent_t * ent_extract(ent_list_hdr_t * const elh, pool_ent_t * const ent) 8916+{ 8917+// printf("List %p [%d]: Ext %p\n", elh, elh->n, ent); 8918+ 8919+ if (ent == NULL) 8920+ return NULL; 8921+ 8922+ if (ent->next == NULL) 8923+ elh->tail = ent->prev; 8924+ else 8925+ ent->next->prev = ent->prev; 8926+ 8927+ if (ent->prev == NULL) 8928+ elh->ents = ent->next; 8929+ else 8930+ ent->prev->next = ent->next; 8931+ 8932+ ent->prev = ent->next = NULL; 8933+ 8934+ --elh->n; 8935+ 8936+ return ent; // For convienience 8937+} 8938+ 8939+static inline pool_ent_t * ent_extract_tail(ent_list_hdr_t * const elh) 8940+{ 8941+ return ent_extract(elh, elh->tail); 8942+} 8943+ 8944+static void ent_add_head(ent_list_hdr_t * const elh, pool_ent_t * const ent) 8945+{ 8946+// printf("List %p [%d]: Add %p\n", elh, elh->n, ent); 8947+ 8948+ if ((ent->next = elh->ents) == NULL) 8949+ elh->tail = ent; 8950+ else 8951+ ent->next->prev = ent; 8952+ 8953+ ent->prev = NULL; 8954+ elh->ents = ent; 8955+ ++elh->n; 8956+} 8957+ 8958+static void ent_free(pool_ent_t * const ent) 8959+{ 8960+// printf("Free ent: %p\n", ent); 8961+ if (ent != NULL) { 8962+ // If we still have a ref to a pic - kill it now 8963+ if (ent->pic != NULL) 8964+ picture_Release(ent->pic); 8965+ 8966+ // Free contents 8967+ vcsm_unlock_hdl(ent->vcsm_hdl); 8968+ 8969+ vcsm_free(ent->vcsm_hdl); 8970+ 8971+ free(ent); 8972+ } 8973+} 8974+ 8975+static void ent_free_list(ent_list_hdr_t * const elh) 8976+{ 8977+ pool_ent_t * ent = elh->ents; 8978+ 8979+// printf("Free list: %p [%d]\n", elh, elh->n); 8980+ 8981+ *elh = ENT_LIST_HDR_INIT; 8982+ 8983+ while (ent != NULL) { 8984+ pool_ent_t * const t = ent; 8985+ ent = t->next; 8986+ ent_free(t); 8987+ } 8988+} 8989+ 8990+static void ent_list_move(ent_list_hdr_t * const dst, ent_list_hdr_t * const src) 8991+{ 8992+// printf("Move %p->%p\n", src, dst); 8993+ 8994+ *dst = *src; 8995+ *src = ENT_LIST_HDR_INIT; 8996+} 8997+ 8998+// Scans "backwards" as that should give us the fastest match if we are 8999+// presented with pics in the same order each time 9000+static pool_ent_t * ent_list_extract_pic_ent(ent_list_hdr_t * const elh, picture_t * const pic) 9001+{ 9002+ pool_ent_t *ent = elh->tail; 9003+ 9004+// printf("Find list: %p [%d]; pic:%p\n", elh, elh->n, pic); 9005+ 9006+ while (ent != NULL) { 9007+// printf("Check ent: %p, pic:%p\n", ent, ent->pic); 9008+ 9009+ if (ent->pic == pic) 9010+ return ent_extract(elh, ent); 9011+ ent = ent->prev; 9012+ } 9013+ return NULL; 9014+} 9015+ 9016+#define POOL_ENT_ALLOC_BLOCK 0x10000 9017+ 9018+static pool_ent_t * pool_ent_alloc_new(size_t req_size) 9019+{ 9020+ pool_ent_t * ent = calloc(1, sizeof(*ent)); 9021+ const size_t alloc_size = (req_size + POOL_ENT_ALLOC_BLOCK - 1) & ~(POOL_ENT_ALLOC_BLOCK - 1); 9022+ 9023+ if (ent == NULL) 9024+ return NULL; 9025+ 9026+ ent->next = ent->prev = NULL; 9027+ 9028+ // Alloc from vcsm 9029+ if ((ent->vcsm_hdl = vcsm_malloc_cache(alloc_size, VCSM_CACHE_TYPE_HOST, (char *)"vlc-subpic")) == -1) 9030+ goto fail1; 9031+ if ((ent->vc_hdl = vcsm_vc_hdl_from_hdl(ent->vcsm_hdl)) == 0) 9032+ goto fail2; 9033+ if ((ent->buf = vcsm_lock(ent->vcsm_hdl)) == NULL) 9034+ goto fail2; 9035+ 9036+ ent->size = alloc_size; 9037+ return ent; 9038+ 9039+fail2: 9040+ vcsm_free(ent->vcsm_hdl); 9041+fail1: 9042+ free(ent); 9043+ return NULL; 9044+} 9045+ 9046+static inline pool_ent_t * pool_ent_ref(pool_ent_t * const ent) 9047+{ 9048+// int n = atomic_fetch_add(&ent->ref_count, 1) + 1; 9049+// printf("Ref: %p: %d\n", ent, n); 9050+ atomic_fetch_add(&ent->ref_count, 1); 9051+ return ent; 9052+} 9053+ 9054+static void pool_recycle(vzc_pool_ctl_t * const pc, pool_ent_t * const ent) 9055+{ 9056+ pool_ent_t * xs = NULL; 9057+ int n; 9058+ 9059+ if (ent == NULL) 9060+ return; 9061+ 9062+ n = atomic_fetch_sub(&ent->ref_count, 1) - 1; 9063+ 9064+// printf("%s: Pool: %p: Ent: %p: %d\n", __func__, &pc->ent_pool, ent, n); 9065+ 9066+ if (n != 0) 9067+ return; 9068+ 9069+ if (ent->pic != NULL) { 9070+ picture_Release(ent->pic); 9071+ ent->pic = NULL; 9072+ } 9073+ 9074+ vlc_mutex_lock(&pc->lock); 9075+ 9076+ // If we have a full pool then extract the LRU and free it 9077+ // Free done outside mutex 9078+ if (pc->ent_pool.n >= pc->max_n) 9079+ xs = ent_extract_tail(&pc->ent_pool); 9080+ 9081+ ent_add_head(&pc->ent_pool, ent); 9082+ 9083+ vlc_mutex_unlock(&pc->lock); 9084+ 9085+ ent_free(xs); 9086+} 9087+ 9088+// * This could be made more efficient, but this is easy 9089+static void pool_recycle_list(vzc_pool_ctl_t * const pc, ent_list_hdr_t * const elh) 9090+{ 9091+ pool_ent_t * ent; 9092+ while ((ent = ent_extract_tail(elh)) != NULL) { 9093+ pool_recycle(pc, ent); 9094+ } 9095+} 9096+ 9097+static pool_ent_t * pool_best_fit(vzc_pool_ctl_t * const pc, size_t req_size) 9098+{ 9099+ pool_ent_t * best = NULL; 9100+ 9101+ vlc_mutex_lock(&pc->lock); 9102+ 9103+ { 9104+ pool_ent_t * ent = pc->ent_pool.ents; 9105+ 9106+ // Simple scan 9107+ while (ent != NULL) { 9108+ if (ent->size >= req_size && ent->size <= req_size * 2 + POOL_ENT_ALLOC_BLOCK && 9109+ (best == NULL || best->size > ent->size)) 9110+ best = ent; 9111+ ent = ent->next; 9112+ } 9113+ 9114+ // extract best from chain if we've found it 9115+ ent_extract(&pc->ent_pool, best); 9116+ } 9117+ 9118+ vlc_mutex_unlock(&pc->lock); 9119+ 9120+ if (best == NULL) 9121+ best = pool_ent_alloc_new(req_size); 9122+ 9123+ if ((best->seq = ++pc->seq) == 0) 9124+ best->seq = ++pc->seq; // Never allow to be zero 9125+ 9126+ atomic_store(&best->ref_count, 1); 9127+ return best; 9128+} 9129+ 9130+ 9131+const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[] = { VLC_CODEC_RGBA, VLC_CODEC_BGRA, VLC_CODEC_ARGB, 0 }; 9132+ 9133+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH) 9134+{ 9135+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent; 9136+ *pW = ent->width; 9137+ *pH = ent->height; 9138+} 9139+ 9140+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt) 9141+{ 9142+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent; 9143+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video; 9144+ 9145+ es_fmt->type = MMAL_ES_TYPE_VIDEO; 9146+ es_fmt->encoding = ent->enc_type; 9147+ es_fmt->encoding_variant = 0; 9148+ 9149+ v_fmt->width = ent->width; 9150+ v_fmt->height = ent->height; 9151+ v_fmt->crop.x = 0; 9152+ v_fmt->crop.y = 0; 9153+ v_fmt->crop.width = ent->width; 9154+ v_fmt->crop.height = ent->height; 9155+ 9156+ return true; 9157+} 9158+ 9159+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf, 9160+ uint32_t * const pWidth, uint32_t * const pHeight) 9161+{ 9162+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent; 9163+ *pWidth = ent->width; 9164+ *pHeight = ent->height; 9165+} 9166+ 9167+ 9168+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf) 9169+{ 9170+ vzc_subbuf_ent_t * sb = buf->user_data; 9171+ return &sb->dreg; 9172+} 9173+ 9174+static inline int rescale_x(int x, int mul, int div) 9175+{ 9176+ return div == 0 ? x * mul : (x * mul + div/2) / div; 9177+} 9178+ 9179+static void rescale_rect(MMAL_RECT_T * const d, const MMAL_RECT_T * const s, const MMAL_RECT_T * mul_rect, const MMAL_RECT_T * div_rect) 9180+{ 9181+ d->x = rescale_x(s->x - div_rect->x, mul_rect->width, div_rect->width) + mul_rect->x; 9182+ d->y = rescale_x(s->y - div_rect->y, mul_rect->height, div_rect->height) + mul_rect->y; 9183+ d->width = rescale_x(s->width, mul_rect->width, div_rect->width); 9184+ d->height = rescale_x(s->height, mul_rect->height, div_rect->height); 9185+#if TRACE_TRANSFORMS 9186+ fprintf(stderr, "(%d,%d %dx%d) * (%d,%d %dx%d) / (%d,%d %dx%d) -> (%d,%d %dx%d)\n", 9187+ s->x, s->y, s->width, s->height, 9188+ mul_rect->x, mul_rect->y, mul_rect->width, mul_rect->height, 9189+ div_rect->x, div_rect->y, div_rect->width, div_rect->height, 9190+ d->x, d->y, d->width, d->height); 9191+#endif 9192+} 9193+ 9194+static MMAL_RECT_T 9195+rect_untransform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t) 9196+{ 9197+#if TRACE_TRANSFORMS 9198+ fprintf(stderr, "t=%d, s=%d,%d:%dx%d, c=%d,%d:%dx%d -> ", (int)t, 9199+ s.x,s.y,s.width,s.height, 9200+ c.x,c.y,c.width,c.height); 9201+#endif 9202+ if (is_transform_hflip(t)) 9203+ s = rect_hflip(s, c); 9204+ if (is_transform_vflip(t) != 0) 9205+ s = rect_vflip(s, c); 9206+ if (is_transform_transpose(t) != 0) 9207+ s = rect_transpose(s); 9208+#if TRACE_TRANSFORMS 9209+ fprintf(stderr, "s=%d,%d:%dx%d\n", 9210+ s.x,s.y,s.width,s.height); 9211+#endif 9212+ return s; 9213+} 9214+ 9215+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect, const MMAL_DISPLAYTRANSFORM_T scale_transform) 9216+{ 9217+ vzc_subbuf_ent_t * sb = buf->user_data; 9218+ if (scale_rect == NULL) { 9219+ sb->dreg.dest_rect = sb->orig_dest_rect; 9220+ sb->dreg.transform = MMAL_DISPLAY_ROT0; 9221+ } 9222+ else 9223+ { 9224+ // The scale rect has been transposed if we have a transposing 9225+ // transform - untranspose so we are the same way up as the source 9226+ const MMAL_RECT_T c = (scale_transform & 4) == 0 ? *scale_rect : rect_transpose(*scale_rect); 9227+ rescale_rect(&sb->dreg.dest_rect, &sb->orig_dest_rect, 9228+ &c, &sb->pic_rect); 9229+ sb->dreg.dest_rect = rect_untransform(sb->dreg.dest_rect, c, scale_transform); 9230+ sb->dreg.transform = scale_transform; 9231+ } 9232+} 9233+ 9234+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf) 9235+{ 9236+ vzc_subbuf_ent_t * sb = buf->user_data; 9237+ return sb->ent->seq; 9238+} 9239+ 9240+ 9241+// The intent with the ents_cur & ents_last stuff is to remember the buffers 9242+// we used on the last frame and reuse them on the current one if they are the 9243+// same. Unfortunately detection of "is_first" is only a heuristic (there are 9244+// no rules governing the order in which things are blended) so we must deal 9245+// (fairly) gracefully with it never (or always) being set. 9246+ 9247+// dst_fmt gives the number space in which the destination pixels are specified 9248+ 9249+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, 9250+ picture_t * const pic, 9251+ const MMAL_RECT_T dst_pic_rect, 9252+ const int x_offset, const int y_offset, 9253+ const unsigned int alpha, 9254+ const bool is_first) 9255+{ 9256+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(pc->buf_pool->queue); 9257+ vzc_subbuf_ent_t * sb; 9258+ 9259+ if (buf == NULL) 9260+ return NULL; 9261+ 9262+ if ((sb = calloc(1, sizeof(*sb))) == NULL) 9263+ goto fail1; 9264+ 9265+ // If first or we've had a lot of stuff move everything to the last list 9266+ // (we could deal more gracefully with the "too many" case but it shouldn't 9267+ // really happen) 9268+ if (is_first || pc->ents_cur.n >= CTX_BUFS_MAX) { 9269+ pool_recycle_list(pc, &pc->ents_prev); 9270+ ent_list_move(&pc->ents_prev, &pc->ents_cur); 9271+ } 9272+ 9273+ sb->dreg.hdr.id = MMAL_PARAMETER_DISPLAYREGION; 9274+ sb->dreg.hdr.size = sizeof(sb->dreg); 9275+ buf->user_data = sb; 9276+ 9277+ { 9278+ // ?? Round start offset as well as length 9279+ const video_format_t *const fmt = &pic->format; 9280+ 9281+ const unsigned int bpp = (fmt->i_bits_per_pixel + 7) >> 3; 9282+ const unsigned int xl = (fmt->i_x_offset & ~15); 9283+ const unsigned int xr = (fmt->i_x_offset + fmt->i_visible_width + 15) & ~15; 9284+ const size_t dst_stride = (xr - xl) * bpp; 9285+ const size_t dst_lines = ((fmt->i_visible_height + 15) & ~15); 9286+ const size_t dst_size = dst_stride * dst_lines; 9287+ 9288+ pool_ent_t * ent = ent_list_extract_pic_ent(&pc->ents_prev, pic); 9289+ bool needs_copy = false; 9290+ 9291+ // If we didn't find ent in last then look in cur in case is_first 9292+ // isn't working 9293+ if (ent == NULL) 9294+ ent = ent_list_extract_pic_ent(&pc->ents_cur, pic); 9295+ 9296+// printf("ent_found: %p\n", ent); 9297 9298-int mmal_picture_lock(picture_t *picture) 9299+ if (ent == NULL) 9300+ { 9301+ // Need a new ent 9302+ needs_copy = true; 9303+ 9304+ if ((ent = pool_best_fit(pc, dst_size)) == NULL) 9305+ goto fail2; 9306+ if ((ent->enc_type = vlc_to_mmal_video_fourcc(&pic->format)) == 0) 9307+ goto fail2; 9308+ 9309+ ent->pic = picture_Hold(pic); 9310+ } 9311+ 9312+ ent_add_head(&pc->ents_cur, ent); 9313+ 9314+ sb->ent = pool_ent_ref(ent); 9315+ hw_mmal_vzc_pool_ref(pc); 9316+ 9317+ // Copy data 9318+ buf->next = NULL; 9319+ buf->cmd = 0; 9320+ buf->data = (uint8_t *)(ent->vc_hdl); 9321+ buf->alloc_size = buf->length = dst_size; 9322+ buf->offset = 0; 9323+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; 9324+ buf->pts = buf->dts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN; 9325+ buf->type->video = (MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T){ 9326+ .planes = 1, 9327+ .pitch = { dst_stride } 9328+ }; 9329+ 9330+ // Remember offsets 9331+ sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT | 9332+ MMAL_DISPLAY_SET_DEST_RECT | 9333+ MMAL_DISPLAY_SET_FULLSCREEN | 9334+ MMAL_DISPLAY_SET_TRANSFORM | 9335+ MMAL_DISPLAY_SET_ALPHA; 9336+ 9337+ sb->dreg.fullscreen = 0; 9338+ 9339+ // Will be set later - zero now to avoid any confusion 9340+ sb->dreg.transform = MMAL_DISPLAY_ROT0; 9341+ sb->dreg.dest_rect = (MMAL_RECT_T){0, 0, 0, 0}; 9342+ 9343+ sb->dreg.alpha = (uint32_t)(alpha & 0xff) | MMAL_DISPLAY_ALPHA_FLAGS_MIX; 9344+ 9345+// printf("+++ bpp:%d, vis:%dx%d wxh:%dx%d, d:%dx%d\n", bpp, fmt->i_visible_width, fmt->i_visible_height, fmt->i_width, fmt->i_height, dst_stride, dst_lines); 9346+ 9347+ sb->dreg.src_rect = (MMAL_RECT_T){ 9348+ .x = (fmt->i_x_offset - xl), 9349+ .y = 0, 9350+ .width = fmt->i_visible_width, 9351+ .height = fmt->i_visible_height 9352+ }; 9353+ 9354+ sb->pic_rect = dst_pic_rect; 9355+ 9356+ sb->orig_dest_rect = (MMAL_RECT_T){ 9357+ .x = x_offset, 9358+ .y = y_offset, 9359+ .width = fmt->i_visible_width, 9360+ .height = fmt->i_visible_height 9361+ }; 9362+ 9363+ if (needs_copy) 9364+ { 9365+ ent->width = dst_stride / bpp; 9366+ ent->height = dst_lines; 9367+ 9368+ // 2D copy 9369+ { 9370+ uint8_t *d = ent->buf; 9371+ const uint8_t *s = pic->p[0].p_pixels + xl * bpp + fmt->i_y_offset * pic->p[0].i_pitch; 9372+ 9373+ mem_copy_2d(d, dst_stride, s, pic->p[0].i_pitch, fmt->i_visible_height, dst_stride); 9374+ 9375+ // And make sure it is actually in memory 9376+ if (pc->vcsm_init_type != VCSM_INIT_CMA) { // ** CMA is currently always uncached 9377+ flush_range(ent->buf, dst_stride * fmt->i_visible_height); 9378+ } 9379+ } 9380+ } 9381+ } 9382+ 9383+ return buf; 9384+ 9385+fail2: 9386+ free(sb); 9387+fail1: 9388+ mmal_buffer_header_release(buf); 9389+ return NULL; 9390+} 9391+ 9392+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc) 9393+{ 9394+ pool_recycle_list(pc, &pc->ents_prev); 9395+ pool_recycle_list(pc, &pc->ents_cur); 9396+} 9397+ 9398+static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc) 9399+{ 9400+ 9401+// printf("<<< %s\n", __func__); 9402+ 9403+ hw_mmal_vzc_pool_flush(pc); 9404+ 9405+ ent_free_list(&pc->ent_pool); 9406+ 9407+ if (pc->buf_pool != NULL) 9408+ mmal_pool_destroy(pc->buf_pool); 9409+ 9410+ vlc_mutex_destroy(&pc->lock); 9411+ 9412+ cma_vcsm_exit(pc->vcsm_init_type); 9413+ 9414+// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash 9415+ free (pc); 9416+ 9417+ // printf(">>> %s\n", __func__); 9418+} 9419+ 9420+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc) 9421+{ 9422+ int n; 9423+ 9424+ if (pc == NULL) 9425+ return; 9426+ 9427+ n = atomic_fetch_sub(&pc->ref_count, 1) - 1; 9428+ 9429+ if (n != 0) 9430+ return; 9431+ 9432+ hw_mmal_vzc_pool_delete(pc); 9433+} 9434+ 9435+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc) 9436+{ 9437+ atomic_fetch_add(&pc->ref_count, 1); 9438+} 9439+ 9440+static MMAL_BOOL_T vcz_pool_release_cb(MMAL_POOL_T * buf_pool, MMAL_BUFFER_HEADER_T *buf, void *userdata) 9441+{ 9442+ vzc_pool_ctl_t * const pc = userdata; 9443+ vzc_subbuf_ent_t * const sb = buf->user_data; 9444+ 9445+ VLC_UNUSED(buf_pool); 9446+ 9447+// printf("<<< %s\n", __func__); 9448+ 9449+ if (sb != NULL) { 9450+ buf->user_data = NULL; 9451+ pool_recycle(pc, sb->ent); 9452+ hw_mmal_vzc_pool_release(pc); 9453+ free(sb); 9454+ } 9455+ 9456+// printf(">>> %s\n", __func__); 9457+ 9458+ return MMAL_TRUE; 9459+} 9460+ 9461+vzc_pool_ctl_t * hw_mmal_vzc_pool_new() 9462+{ 9463+ vzc_pool_ctl_t * const pc = calloc(1, sizeof(*pc)); 9464+ 9465+ if (pc == NULL) 9466+ return NULL; 9467+ 9468+ if ((pc->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) 9469+ { 9470+ free(pc); 9471+ return NULL; 9472+ } 9473+ 9474+ pc->max_n = 8; 9475+ vlc_mutex_init(&pc->lock); // Must init before potential destruction 9476+ 9477+ if ((pc->buf_pool = mmal_pool_create(64, 0)) == NULL) 9478+ { 9479+ hw_mmal_vzc_pool_delete(pc); 9480+ return NULL; 9481+ } 9482+ 9483+ atomic_store(&pc->ref_count, 1); 9484+ 9485+ mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc); 9486+ 9487+ return pc; 9488+} 9489+ 9490+//---------------------------------------------------------------------------- 9491+ 9492+ 9493+static const uint8_t shift_00[] = {0,0,0,0}; 9494+static const uint8_t shift_01[] = {0,1,1,1}; 9495+ 9496+int cma_pic_set_data(picture_t * const pic, 9497+ const MMAL_ES_FORMAT_T * const mm_esfmt, 9498+ const MMAL_BUFFER_HEADER_T * const buf) 9499 { 9500- picture_sys_t *pic_sys = picture->p_sys; 9501- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; 9502+ const MMAL_VIDEO_FORMAT_T * const mm_fmt = &mm_esfmt->es->video; 9503+ const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = (buf == NULL) ? NULL : &buf->type->video; 9504+ cma_buf_t *const cb = cma_buf_pic_get(pic); 9505+ unsigned int planes = 1; 9506+ 9507+ uint8_t * const data = cma_buf_addr(cb); 9508+ if (data == NULL) { 9509+ return VLC_ENOMEM; 9510+ } 9511+ 9512+ const uint8_t * ws = shift_00; 9513+ const uint8_t * hs = shift_00; 9514+ int pb = 1; 9515+ 9516+ switch (mm_esfmt->encoding) 9517+ { 9518+ case MMAL_ENCODING_ARGB: 9519+ case MMAL_ENCODING_ABGR: 9520+ case MMAL_ENCODING_RGBA: 9521+ case MMAL_ENCODING_BGRA: 9522+ case MMAL_ENCODING_RGB32: 9523+ case MMAL_ENCODING_BGR32: 9524+ pb = 4; 9525+ break; 9526+ case MMAL_ENCODING_RGB16: 9527+ pb = 2; 9528+ break; 9529 9530- int offset = 0; 9531- picture->p[0].p_pixels = buffer->data; 9532- for (int i = 1; i < picture->i_planes; i++) { 9533- offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines; 9534- picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset; 9535+ case MMAL_ENCODING_I420: 9536+ ws = shift_01; 9537+ hs = shift_01; 9538+ planes = 3; 9539+ break; 9540+ 9541+ case MMAL_ENCODING_YUVUV128: 9542+ hs = shift_01; 9543+ planes = 2; 9544+ break; 9545+ 9546+ default: 9547+// msg_Err(p_filter, "%s: Unexpected format", __func__); 9548+ return VLC_EGENERIC; 9549 } 9550 9551- pic_sys->displayed = false; 9552+ // Fix up SAR if unset 9553+ if (pic->format.i_sar_den == 0 || pic->format.i_sar_num == 0) { 9554+ pic->format.i_sar_den = mm_fmt->par.den; 9555+ pic->format.i_sar_num = mm_fmt->par.num; 9556+ } 9557 9558+ pic->i_planes = planes; 9559+ unsigned int offset = 0; 9560+ for (unsigned int i = 0; i != planes; ++i) { 9561+ pic->p[i] = (plane_t){ 9562+ .p_pixels = data + (buf_vid != NULL ? buf_vid->offset[i] : offset), 9563+ .i_lines = mm_fmt->height >> hs[i], 9564+ .i_pitch = buf_vid != NULL ? buf_vid->pitch[i] : mm_fmt->width * pb, 9565+ .i_pixel_pitch = pb, 9566+ .i_visible_lines = mm_fmt->crop.height >> hs[i], 9567+ .i_visible_pitch = mm_fmt->crop.width >> ws[i] 9568+ }; 9569+ offset += pic->p[i].i_pitch * pic->p[i].i_lines; 9570+ } 9571 return VLC_SUCCESS; 9572 } 9573+ 9574+int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic) 9575+{ 9576+ if (!is_cma_buf_pic_chroma(pic->format.i_chroma)) 9577+ return VLC_EGENERIC; 9578+ if (pic->context != NULL) 9579+ return VLC_EBADVAR; 9580+ 9581+ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t)); 9582+ 9583+ if (ctx == NULL) 9584+ return VLC_ENOMEM; 9585+ 9586+ ctx->cmn.copy = hw_mmal_pic_ctx_copy; 9587+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy; 9588+ ctx->buf_count = 1; // cb takes the place of the 1st buf 9589+ ctx->cb = cb; 9590+ 9591+ cma_buf_in_flight(cb); 9592+ 9593+ pic->context = &ctx->cmn; 9594+ return VLC_SUCCESS; 9595+} 9596+ 9597+cma_buf_t * cma_buf_pic_get(picture_t * const pic) 9598+{ 9599+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; 9600+ return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? 0 : ctx->cb; 9601+} 9602+ 9603+ 9604+//---------------------------------------------------------------------------- 9605+ 9606+/* Returns the type of the Pi being used 9607+*/ 9608+bool rpi_is_model_pi4(void) { 9609+ return bcm_host_is_model_pi4(); 9610+} 9611+ 9612+// Preferred mode - none->cma on Pi4 otherwise legacy 9613+static volatile vcsm_init_type_t last_vcsm_type = VCSM_INIT_NONE; 9614+ 9615+vcsm_init_type_t cma_vcsm_type(void) 9616+{ 9617+ return last_vcsm_type; 9618+} 9619+ 9620+vcsm_init_type_t cma_vcsm_init(void) 9621+{ 9622+ vcsm_init_type_t rv = VCSM_INIT_NONE; 9623+ // We don't bother locking - taking a copy here should be good enough 9624+ vcsm_init_type_t try_type = last_vcsm_type; 9625+ 9626+ if (try_type == VCSM_INIT_NONE) { 9627+ if (bcm_host_is_fkms_active()) 9628+ try_type = VCSM_INIT_CMA; 9629+ else 9630+ try_type = VCSM_INIT_LEGACY; 9631+ } 9632+ 9633+ if (try_type == VCSM_INIT_CMA) { 9634+ if (vcsm_init_ex(1, -1) == 0) 9635+ rv = VCSM_INIT_CMA; 9636+ else if (vcsm_init_ex(0, -1) == 0) 9637+ rv = VCSM_INIT_LEGACY; 9638+ } 9639+ else 9640+ { 9641+ if (vcsm_init_ex(0, -1) == 0) 9642+ rv = VCSM_INIT_LEGACY; 9643+ else if (vcsm_init_ex(1, -1) == 0) 9644+ rv = VCSM_INIT_CMA; 9645+ } 9646+ 9647+ // Just in case this affects vcsm init do after that 9648+ if (rv != VCSM_INIT_NONE) 9649+ bcm_host_init(); 9650+ 9651+ last_vcsm_type = rv; 9652+ return rv; 9653+} 9654+ 9655+void cma_vcsm_exit(const vcsm_init_type_t init_mode) 9656+{ 9657+ if (init_mode != VCSM_INIT_NONE) 9658+ { 9659+ vcsm_exit(); 9660+ bcm_host_deinit(); // Does nothing but add in case it ever does 9661+ } 9662+} 9663+ 9664+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode) 9665+{ 9666+ switch (init_mode) 9667+ { 9668+ case VCSM_INIT_CMA: 9669+ return "CMA"; 9670+ case VCSM_INIT_LEGACY: 9671+ return "Legacy"; 9672+ case VCSM_INIT_NONE: 9673+ return "none"; 9674+ default: 9675+ break; 9676+ } 9677+ return "???"; 9678+} 9679+ 9680+ 9681--- a/modules/hw/mmal/mmal_picture.h 9682+++ b/modules/hw/mmal/mmal_picture.h 9683@@ -24,19 +24,298 @@ 9684 #ifndef VLC_MMAL_MMAL_PICTURE_H_ 9685 #define VLC_MMAL_MMAL_PICTURE_H_ 9686 9687+#include <stdatomic.h> 9688+ 9689 #include <vlc_common.h> 9690 #include <interface/mmal/mmal.h> 9691 9692+#include "mmal_cma.h" 9693+ 9694 /* Think twice before changing this. Incorrect values cause havoc. */ 9695 #define NUM_ACTUAL_OPAQUE_BUFFERS 30 9696 9697-struct picture_sys_t { 9698- vlc_object_t *owner; 9699+#ifndef VLC_TICK_INVALID 9700+#define VLC_TICK_INVALID VLC_TS_INVALID 9701+#define VLC_VER_3 1 9702+#else 9703+#define VLC_VER_3 0 9704+#endif 9705+ 9706+typedef struct mmal_port_pool_ref_s 9707+{ 9708+ atomic_uint refs; 9709+ MMAL_POOL_T * pool; 9710+ MMAL_PORT_T * port; 9711+} hw_mmal_port_pool_ref_t; 9712+ 9713+typedef struct pic_ctx_subpic_s { 9714+ picture_t * subpic; 9715+ int x, y; 9716+ int alpha; 9717+} pic_ctx_subpic_t; 9718+ 9719+ 9720+#define CTX_BUFS_MAX 4 9721+typedef struct pic_ctx_mmal_s { 9722+ picture_context_t cmn; // PARENT: Common els at start 9723+ 9724+ cma_buf_t * cb; 9725+ 9726+ unsigned int buf_count; 9727+ MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX]; 9728+ 9729+} pic_ctx_mmal_t; 9730+ 9731+const char * str_fourcc(char * const buf, const unsigned int fcc); 9732+ 9733+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc); 9734+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs); 9735+void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc); 9736+// Returns true if fmt_changed 9737+// frame_rate ignored for compare, but is set if something else is updated 9738+bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic); 9739+ 9740+// Copy pic contents into an existing buffer 9741+int hw_mmal_copy_pic_to_buf(void * const buf_data, uint32_t * const pLength, 9742+ const MMAL_ES_FORMAT_T * const fmt, const picture_t * const pic); 9743+ 9744+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port, 9745+ const unsigned int headers, const uint32_t payload_size); 9746+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb); 9747+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf); 9748+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr); 9749+static inline void hw_mmal_port_pool_ref_acquire(hw_mmal_port_pool_ref_t * const ppr) 9750+{ 9751+ atomic_fetch_add(&ppr->refs, 1); 9752+} 9753+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj, 9754+ hw_mmal_port_pool_ref_t ** pppr, 9755+ MMAL_PORT_T * const port, 9756+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback); 9757+ 9758+static inline int hw_mmal_pic_has_sub_bufs(picture_t * const pic) 9759+{ 9760+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; 9761+ return ctx->buf_count > 1; 9762+} 9763+ 9764+static inline void hw_mmal_pic_sub_buf_add(picture_t * const pic, MMAL_BUFFER_HEADER_T * const sub) 9765+{ 9766+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; 9767+ 9768+ if (ctx->buf_count >= CTX_BUFS_MAX) { 9769+ mmal_buffer_header_release(sub); 9770+ return; 9771+ } 9772+ 9773+ ctx->bufs[ctx->buf_count++] = sub; 9774+} 9775+ 9776+static inline MMAL_BUFFER_HEADER_T * hw_mmal_pic_sub_buf_get(picture_t * const pic, const unsigned int n) 9777+{ 9778+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; 9779+ 9780+ return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1]; 9781+} 9782+ 9783+static inline bool hw_mmal_chroma_is_mmal(const vlc_fourcc_t chroma) 9784+{ 9785+ return 9786+ chroma == VLC_CODEC_MMAL_OPAQUE || 9787+ chroma == VLC_CODEC_MMAL_ZC_SAND8 || 9788+ chroma == VLC_CODEC_MMAL_ZC_SAND10 || 9789+ chroma == VLC_CODEC_MMAL_ZC_SAND30 || 9790+ chroma == VLC_CODEC_MMAL_ZC_I420 || 9791+ chroma == VLC_CODEC_MMAL_ZC_RGB32; 9792+} 9793+ 9794+static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic) 9795+{ 9796+ return hw_mmal_chroma_is_mmal(pic->format.i_chroma); 9797+} 9798+ 9799+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn); 9800+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn); 9801+picture_context_t * hw_mmal_gen_context( 9802+ MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr); 9803+ 9804+int hw_mmal_get_gpu_mem(void); 9805+ 9806+ 9807+static inline MMAL_STATUS_T port_parameter_set_uint32(MMAL_PORT_T * port, uint32_t id, uint32_t val) 9808+{ 9809+ const MMAL_PARAMETER_UINT32_T param = { 9810+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_UINT32_T)}, 9811+ .value = val 9812+ }; 9813+ return mmal_port_parameter_set(port, ¶m.hdr); 9814+} 9815+ 9816+static inline MMAL_STATUS_T port_parameter_set_bool(MMAL_PORT_T * const port, const uint32_t id, const bool val) 9817+{ 9818+ const MMAL_PARAMETER_BOOLEAN_T param = { 9819+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_BOOLEAN_T)}, 9820+ .enable = val 9821+ }; 9822+ return mmal_port_parameter_set(port, ¶m.hdr); 9823+} 9824+ 9825+static inline MMAL_STATUS_T port_send_replicated(MMAL_PORT_T * const port, MMAL_POOL_T * const rep_pool, 9826+ MMAL_BUFFER_HEADER_T * const src_buf, 9827+ const uint64_t seq) 9828+{ 9829+ MMAL_STATUS_T err; 9830+ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue); 9831+ 9832+ if (rep_buf == NULL) 9833+ return MMAL_ENOSPC; 9834+ 9835+ if ((err = mmal_buffer_header_replicate(rep_buf, src_buf)) != MMAL_SUCCESS) 9836+ return err; 9837+ 9838+ rep_buf->pts = seq; 9839+ 9840+ if ((err = mmal_port_send_buffer(port, rep_buf)) != MMAL_SUCCESS) 9841+ { 9842+ mmal_buffer_header_release(rep_buf); 9843+ return err; 9844+ } 9845+ 9846+ return MMAL_SUCCESS; 9847+} 9848+ 9849+ 9850+static inline void pic_to_buf_copy_props(MMAL_BUFFER_HEADER_T * const buf, const picture_t * const pic) 9851+{ 9852+ if (!pic->b_progressive) 9853+ { 9854+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED; 9855+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED; 9856+ } 9857+ else 9858+ { 9859+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED; 9860+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED; 9861+ } 9862+ if (pic->b_top_field_first) 9863+ { 9864+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST; 9865+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST; 9866+ } 9867+ else 9868+ { 9869+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST; 9870+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST; 9871+ } 9872+ buf->pts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN; 9873+ buf->dts = buf->pts; 9874+} 9875+ 9876+static inline void buf_to_pic_copy_props(picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf) 9877+{ 9878+ // Contrary to docn the interlace & tff flags turn up in the header flags rather than the 9879+ // video specific flags (which appear to be currently unused). 9880+ pic->b_progressive = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED) == 0; 9881+ pic->b_top_field_first = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST) != 0; 9882+ 9883+ pic->date = buf->pts != MMAL_TIME_UNKNOWN ? buf->pts : 9884+ buf->dts != MMAL_TIME_UNKNOWN ? buf->dts : 9885+ VLC_TICK_INVALID; 9886+} 9887+ 9888+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic, 9889+ MMAL_POOL_T * const rep_pool, 9890+ MMAL_PORT_T * const port, 9891+ cma_buf_pool_t * const cbp); 9892+ 9893+MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool); 9894+ 9895+struct vzc_pool_ctl_s; 9896+typedef struct vzc_pool_ctl_s vzc_pool_ctl_t; 9897+ 9898+// At the moment we cope with any mono-planar RGBA thing 9899+// We could cope with many other things but they currently don't occur 9900+extern const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[]; 9901+static inline bool hw_mmal_vzc_subpic_fmt_valid(const video_frame_format_t * const vf_vlc) 9902+{ 9903+ const vlc_fourcc_t vfcc_src = vf_vlc->i_chroma; 9904+ for (const vlc_fourcc_t * p = hw_mmal_vzc_subpicture_chromas; *p != 0; ++p) 9905+ if (*p == vfcc_src) 9906+ return true; 9907+ 9908+ return false; 9909+} 9910+ 9911+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt); 9912+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf); 9913+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect, const MMAL_DISPLAYTRANSFORM_T scale_transform); 9914+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH); 9915+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf); 9916+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic, 9917+ const MMAL_RECT_T dst_pic_rect, 9918+ const int x_offset, const int y_offset, 9919+ const unsigned int alpha, const bool is_first); 9920+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf, 9921+ uint32_t * const pWidth, uint32_t * const pHeight); 9922+ 9923+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc); 9924+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc); 9925+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc); 9926+vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void); 9927+ 9928+ 9929+static inline MMAL_RECT_T vis_mmal_rect(const video_format_t * const fmt) 9930+{ 9931+ return (MMAL_RECT_T){ 9932+ .x = fmt->i_x_offset, 9933+ .y = fmt->i_y_offset, 9934+ .width = fmt->i_visible_width, 9935+ .height = fmt->i_visible_height 9936+ }; 9937+} 9938+ 9939+int cma_pic_set_data(picture_t * const pic, 9940+ const MMAL_ES_FORMAT_T * const mm_esfmt, 9941+ const MMAL_BUFFER_HEADER_T * const buf); 9942+ 9943+// Attaches cma buf to pic 9944+// Marks in_flight if not all_in_flight anyway 9945+int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic); 9946+// Returns a pointer to the cma_buf attached to the pic 9947+// Just a pointer - doesn't add a ref 9948+cma_buf_t * cma_buf_pic_get(picture_t * const pic); 9949+ 9950+static inline bool is_cma_buf_pic_chroma(const uint32_t chroma) 9951+{ 9952+ return chroma == VLC_CODEC_MMAL_ZC_RGB32 || 9953+ chroma == VLC_CODEC_MMAL_ZC_SAND8 || 9954+ chroma == VLC_CODEC_MMAL_ZC_SAND10 || 9955+ chroma == VLC_CODEC_MMAL_ZC_SAND30 || 9956+ chroma == VLC_CODEC_MMAL_ZC_I420; 9957+} 9958+ 9959+ 9960+int rpi_get_model_type(void); 9961+bool rpi_is_model_pi4(void); 9962+bool rpi_is_fkms_active(void); 9963+ 9964+typedef enum vcsm_init_type_e { 9965+ VCSM_INIT_NONE = 0, 9966+ VCSM_INIT_LEGACY, 9967+ VCSM_INIT_CMA 9968+} vcsm_init_type_t; 9969+ 9970+vcsm_init_type_t cma_vcsm_init(void); 9971+void cma_vcsm_exit(const vcsm_init_type_t init_mode); 9972+vcsm_init_type_t cma_vcsm_type(void); 9973+const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode); 9974+ 9975 9976- MMAL_BUFFER_HEADER_T *buffer; 9977- bool displayed; 9978-}; 9979+#define VOUT_DISPLAY_CHANGE_MMAL_BASE 1024 9980+#define VOUT_DISPLAY_CHANGE_MMAL_HIDE (VOUT_DISPLAY_CHANGE_MMAL_BASE + 0) 9981 9982-int mmal_picture_lock(picture_t *picture); 9983+#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize" 9984+#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp" 9985+#define MMAL_COMPONENT_HVS "vc.ril.hvs" 9986 9987 #endif 9988--- /dev/null 9989+++ b/modules/hw/mmal/rpi_prof.h 9990@@ -0,0 +1,110 @@ 9991+#ifndef RPI_PROFILE_H 9992+#define RPI_PROFILE_H 9993+ 9994+#include <stdint.h> 9995+#include <inttypes.h> 9996+ 9997+#ifndef RPI_PROFILE 9998+#define RPI_PROFILE 0 9999+#endif 10000+ 10001+#if RPI_PROFILE 10002+ 10003+#include "v7_pmu.h" 10004+ 10005+#ifdef RPI_PROC_ALLOC 10006+#define X volatile 10007+#define Z =0 10008+#else 10009+#define X extern volatile 10010+#define Z 10011+#endif 10012+ 10013+X uint64_t av_rpi_prof0_cycles Z; 10014+X unsigned int av_rpi_prof0_cnt Z; 10015+#define RPI_prof0_MAX_DURATION 100000 10016+ 10017+X uint64_t av_rpi_prof1_cycles Z; 10018+X unsigned int av_rpi_prof1_cnt Z; 10019+#define RPI_prof1_MAX_DURATION 100000 10020+ 10021+X uint64_t av_rpi_prof2_cycles Z; 10022+X unsigned int av_rpi_prof2_cnt Z; 10023+#define RPI_prof2_MAX_DURATION 10000 10024+ 10025+X uint64_t av_rpi_prof_n_cycles[128]; 10026+X unsigned int av_rpi_prof_n_cnt[128]; 10027+#define RPI_prof_n_MAX_DURATION 10000 10028+ 10029+ 10030+#undef X 10031+#undef Z 10032+ 10033+#define PROFILE_INIT()\ 10034+do {\ 10035+ enable_pmu();\ 10036+ enable_ccnt();\ 10037+} while (0) 10038+ 10039+#define PROFILE_START()\ 10040+do {\ 10041+ volatile uint32_t perf_1 = read_ccnt();\ 10042+ volatile uint32_t perf_2 10043+ 10044+ 10045+#define PROFILE_ACC(x)\ 10046+ perf_2 = read_ccnt();\ 10047+ {\ 10048+ const uint32_t duration = perf_2 - perf_1;\ 10049+ if (duration < RPI_##x##_MAX_DURATION)\ 10050+ {\ 10051+ av_rpi_##x##_cycles += duration;\ 10052+ av_rpi_##x##_cnt += 1;\ 10053+ }\ 10054+ }\ 10055+} while(0) 10056+ 10057+ 10058+#define PROFILE_ACC_N(n)\ 10059+ if ((n) >= 0) {\ 10060+ perf_2 = read_ccnt();\ 10061+ {\ 10062+ const uint32_t duration = perf_2 - perf_1;\ 10063+ if (duration < RPI_prof_n_MAX_DURATION)\ 10064+ {\ 10065+ av_rpi_prof_n_cycles[n] += duration;\ 10066+ av_rpi_prof_n_cnt[n] += 1;\ 10067+ }\ 10068+ }\ 10069+ }\ 10070+} while(0) 10071+ 10072+#define PROFILE_PRINTF(x)\ 10073+ printf("%-20s cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", #x, av_rpi_##x##_cycles, av_rpi_##x##_cnt,\ 10074+ av_rpi_##x##_cnt == 0 ? (uint64_t)0 : av_rpi_##x##_cycles / (uint64_t)av_rpi_##x##_cnt) 10075+ 10076+#define PROFILE_PRINTF_N(n)\ 10077+ printf("prof[%d] cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", (n), av_rpi_prof_n_cycles[n], av_rpi_prof_n_cnt[n],\ 10078+ av_rpi_prof_n_cnt[n] == 0 ? (uint64_t)0 : av_rpi_prof_n_cycles[n] / (uint64_t)av_rpi_prof_n_cnt[n]) 10079+ 10080+#define PROFILE_CLEAR_N(n) \ 10081+do {\ 10082+ av_rpi_prof_n_cycles[n] = 0;\ 10083+ av_rpi_prof_n_cnt[n] = 0;\ 10084+} while(0) 10085+ 10086+#else 10087+ 10088+// No profile 10089+#define PROFILE_INIT() 10090+#define PROFILE_START() 10091+#define PROFILE_ACC(x) 10092+#define PROFILE_ACC_N(x) 10093+#define PROFILE_PRINTF(x) 10094+#define PROFILE_PRINTF_N(x) 10095+#define PROFILE_CLEAR_N(n) 10096+ 10097+#endif 10098+ 10099+#endif 10100+ 10101--- /dev/null 10102+++ b/modules/hw/mmal/subpic.c 10103@@ -0,0 +1,257 @@ 10104+/***************************************************************************** 10105+ * mmal.c: MMAL-based decoder plugin for Raspberry Pi 10106+ ***************************************************************************** 10107+ * Authors: jc@kynesim.co.uk 10108+ * 10109+ * This program is free software; you can redistribute it and/or modify it 10110+ * under the terms of the GNU Lesser General Public License as published by 10111+ * the Free Software Foundation; either version 2.1 of the License, or 10112+ * (at your option) any later version. 10113+ * 10114+ * This program is distributed in the hope that it will be useful, 10115+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 10116+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10117+ * GNU Lesser General Public License for more details. 10118+ * 10119+ * You should have received a copy of the GNU Lesser General Public License 10120+ * along with this program; if not, write to the Free Software Foundation, 10121+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 10122+ *****************************************************************************/ 10123+ 10124+#ifdef HAVE_CONFIG_H 10125+#include "config.h" 10126+#endif 10127+ 10128+#include <stdatomic.h> 10129+ 10130+#include <vlc_common.h> 10131+#include <vlc_plugin.h> 10132+#include <vlc_codec.h> 10133+#include <vlc_filter.h> 10134+#include <vlc_threads.h> 10135+ 10136+#include <bcm_host.h> 10137+#include <interface/mmal/mmal.h> 10138+#include <interface/mmal/util/mmal_util.h> 10139+#include <interface/mmal/util/mmal_default_components.h> 10140+ 10141+#include "mmal_picture.h" 10142+#include "subpic.h" 10143+ 10144+ 10145+#define TRACE_ALL 0 10146+ 10147+static inline bool cmp_rect(const MMAL_RECT_T * const a, const MMAL_RECT_T * const b) 10148+{ 10149+ return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height; 10150+} 10151+ 10152+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const sub) 10153+{ 10154+ VLC_UNUSED(p_filter); 10155+ if (sub->port != NULL && sub->port->is_enabled) 10156+ mmal_port_disable(sub->port); 10157+ sub->seq = 0; 10158+} 10159+ 10160+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe) 10161+{ 10162+ hw_mmal_subpic_flush(p_filter, spe); 10163+ 10164+ if (spe->pool != NULL) 10165+ mmal_pool_destroy(spe->pool); 10166+ 10167+ // Zap to avoid any accidental reuse 10168+ *spe = (subpic_reg_stash_t){NULL}; 10169+} 10170+ 10171+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, 10172+ const int display_id, const unsigned int layer) 10173+{ 10174+ MMAL_STATUS_T err; 10175+ 10176+ // Start by zapping all to zero 10177+ *spe = (subpic_reg_stash_t){NULL}; 10178+ 10179+ if ((err = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) 10180+ { 10181+ msg_Err(p_filter, "Failed to set sub port zero copy"); 10182+ return err; 10183+ } 10184+ 10185+ if ((spe->pool = mmal_pool_create(30, 0)) == NULL) 10186+ { 10187+ msg_Err(p_filter, "Failed to create sub pool"); 10188+ return MMAL_ENOMEM; 10189+ } 10190+ 10191+ port->userdata = (void *)p_filter; 10192+ spe->port = port; 10193+ spe->display_id = display_id; 10194+ spe->layer = layer; 10195+ 10196+ return MMAL_SUCCESS; 10197+} 10198+ 10199+static void conv_subpic_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) 10200+{ 10201+#if TRACE_ALL 10202+ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, user=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld", 10203+ __func__, buf->cmd, buf->user_data, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts); 10204+#else 10205+ VLC_UNUSED(port); 10206+#endif 10207+ 10208+ mmal_buffer_header_release(buf); // Will extract & release pic in pool callback 10209+} 10210+ 10211+static int 10212+subpic_send_empty(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, const uint64_t pts) 10213+{ 10214+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(spe->pool->queue); 10215+ MMAL_STATUS_T err; 10216+ 10217+ if (buf == NULL) { 10218+ msg_Err(p_filter, "Buffer get for subpic failed"); 10219+ return -1; 10220+ } 10221+#if TRACE_ALL 10222+ msg_Dbg(p_filter, "Remove pic for sub %d", spe->seq); 10223+#endif 10224+ buf->cmd = 0; 10225+ buf->data = NULL; 10226+ buf->alloc_size = 0; 10227+ buf->offset = 0; 10228+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; 10229+ buf->pts = pts; 10230+ buf->dts = MMAL_TIME_UNKNOWN; 10231+ buf->user_data = NULL; 10232+ 10233+ if ((err = mmal_port_send_buffer(spe->port, buf)) != MMAL_SUCCESS) 10234+ { 10235+ msg_Err(p_filter, "Send buffer to subput failed"); 10236+ mmal_buffer_header_release(buf); 10237+ return -1; 10238+ } 10239+ return 0; 10240+} 10241+ 10242+// < 0 Error 10243+// 0 Done & stop 10244+// 1 Done & continue 10245+ 10246+int hw_mmal_subpic_update(vlc_object_t * const p_filter, 10247+ MMAL_BUFFER_HEADER_T * const sub_buf, 10248+ subpic_reg_stash_t * const spe, 10249+ const video_format_t * const fmt, 10250+ const MMAL_RECT_T * const scale_out, 10251+ const MMAL_DISPLAYTRANSFORM_T transform_out, 10252+ const uint64_t pts) 10253+{ 10254+ MMAL_STATUS_T err; 10255+ 10256+ if (sub_buf == NULL) 10257+ { 10258+ if (spe->port->is_enabled && spe->seq != 0) 10259+ { 10260+ subpic_send_empty(p_filter, spe, pts); 10261+ spe->seq = 0; 10262+ } 10263+ } 10264+ else 10265+ { 10266+ const unsigned int seq = hw_mmal_vzc_buf_seq(sub_buf); 10267+ bool needs_update = (spe->seq != seq); 10268+ 10269+ hw_mmal_vzc_buf_scale_dest_rect(sub_buf, scale_out, transform_out); 10270+ 10271+ if (hw_mmal_vzc_buf_set_format(sub_buf, spe->port->format)) 10272+ { 10273+ MMAL_DISPLAYREGION_T * const dreg = hw_mmal_vzc_buf_region(sub_buf); 10274+ MMAL_VIDEO_FORMAT_T *const v_fmt = &spe->port->format->es->video; 10275+ 10276+ v_fmt->frame_rate.den = fmt->i_frame_rate_base; 10277+ v_fmt->frame_rate.num = fmt->i_frame_rate; 10278+ v_fmt->par.den = fmt->i_sar_den; 10279+ v_fmt->par.num = fmt->i_sar_num; 10280+ v_fmt->color_space = MMAL_COLOR_SPACE_UNKNOWN; 10281+ 10282+ if (needs_update || dreg->alpha != spe->alpha || !cmp_rect(&dreg->dest_rect, &spe->dest_rect)) { 10283+ 10284+ spe->alpha = dreg->alpha; 10285+ spe->dest_rect = dreg->dest_rect; 10286+ needs_update = true; 10287+ 10288+ if (spe->display_id >= 0) 10289+ { 10290+ dreg->display_num = spe->display_id; 10291+ dreg->set |= MMAL_DISPLAY_SET_NUM; 10292+ } 10293+ dreg->layer = spe->layer; 10294+ dreg->set |= MMAL_DISPLAY_SET_LAYER; 10295+ 10296+#if TRACE_ALL 10297+ msg_Dbg(p_filter, "%s: Update region: Set=%x, dest=%dx%d @ (%d,%d), src=%dx%d @ (%d,%d), layer=%d, alpha=%#x", 10298+ __func__, dreg->set, 10299+ dreg->dest_rect.width, dreg->dest_rect.height, dreg->dest_rect.x, dreg->dest_rect.y, 10300+ dreg->src_rect.width, dreg->src_rect.height, dreg->src_rect.x, dreg->src_rect.y, 10301+ dreg->layer, dreg->alpha); 10302+#endif 10303+ 10304+ // If now completely offscreen just flush this & return 10305+ // We only do -ve as (a) that is easy and (b) it seems to be 10306+ // something that can confuse mmal 10307+ if (dreg->dest_rect.y + dreg->dest_rect.height <= 0 || 10308+ dreg->dest_rect.x + dreg->dest_rect.width <= 0) 10309+ { 10310+ if (spe->port->is_enabled) 10311+ subpic_send_empty(p_filter, spe, pts); 10312+ spe->seq = seq; 10313+ return 1; 10314+ } 10315+ 10316+ if ((err = mmal_port_parameter_set(spe->port, &dreg->hdr)) != MMAL_SUCCESS) 10317+ { 10318+ msg_Err(p_filter, "Set display region on subput failed"); 10319+ return -1; 10320+ } 10321+ 10322+ if ((err = mmal_port_format_commit(spe->port)) != MMAL_SUCCESS) 10323+ { 10324+ msg_Dbg(p_filter, "%s: Subpic commit fail: %d", __func__, err); 10325+ return -1; 10326+ } 10327+ } 10328+ } 10329+ 10330+ if (!spe->port->is_enabled) 10331+ { 10332+ spe->port->buffer_num = 30; 10333+ spe->port->buffer_size = spe->port->buffer_size_recommended; // Not used but shuts up the error checking 10334+ 10335+ if ((err = mmal_port_enable(spe->port, conv_subpic_cb)) != MMAL_SUCCESS) 10336+ { 10337+ msg_Dbg(p_filter, "%s: Subpic enable fail: %d", __func__, err); 10338+ return -1; 10339+ } 10340+ } 10341+ 10342+ if (needs_update) 10343+ { 10344+#if TRACE_ALL 10345+ msg_Dbg(p_filter, "Update pic for sub %d", spe->seq); 10346+#endif 10347+ if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS) 10348+ { 10349+ msg_Err(p_filter, "Send buffer to subput failed"); 10350+ return -1; 10351+ } 10352+ 10353+ spe->seq = seq; 10354+ } 10355+ } 10356+ return 1; 10357+} 10358+ 10359+ 10360+ 10361--- /dev/null 10362+++ b/modules/hw/mmal/subpic.h 10363@@ -0,0 +1,33 @@ 10364+#ifndef VLC_HW_MMAL_SUBPIC_H_ 10365+#define VLC_HW_MMAL_SUBPIC_H_ 10366+ 10367+typedef struct subpic_reg_stash_s 10368+{ 10369+ MMAL_PORT_T * port; 10370+ MMAL_POOL_T * pool; 10371+ int display_id; // -1 => do not set 10372+ unsigned int layer; 10373+ // Shadow vars so we can tell if stuff has changed 10374+ MMAL_RECT_T dest_rect; 10375+ unsigned int alpha; 10376+ unsigned int seq; 10377+} subpic_reg_stash_t; 10378+ 10379+int hw_mmal_subpic_update(vlc_object_t * const p_filter, 10380+ MMAL_BUFFER_HEADER_T * const sub_buf, 10381+ subpic_reg_stash_t * const spe, 10382+ const video_format_t * const fmt, 10383+ const MMAL_RECT_T * const scale_out, 10384+ const MMAL_DISPLAYTRANSFORM_T transform_out, 10385+ const uint64_t pts); 10386+ 10387+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe); 10388+ 10389+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe); 10390+ 10391+// If display id is -1 it will be unset 10392+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, 10393+ const int display_id, const unsigned int layer); 10394+ 10395+#endif 10396+ 10397--- /dev/null 10398+++ b/modules/hw/mmal/transform_ops.h 10399@@ -0,0 +1,99 @@ 10400+#ifndef VLC_MMAL_TRANSFORM_OPS_H 10401+#define VLC_MMAL_TRANSFORM_OPS_H 10402+ 10403+#include <vlc_common.h> 10404+#include <vlc_picture.h> 10405+#include <interface/mmal/mmal.h> 10406+ 10407+ 10408+// These are enums with the same order so simply coerce 10409+static inline MMAL_DISPLAYTRANSFORM_T vlc_to_mmal_transform(const video_orientation_t orientation){ 10410+ return (MMAL_DISPLAYTRANSFORM_T)orientation; 10411+} 10412+ 10413+// MMAL headers comment these (getting 2 a bit wrong) but do not give 10414+// defines 10415+#define XFORM_H_SHIFT 0 // Hflip 10416+#define XFORM_V_SHIFT 1 // Vflip 10417+#define XFORM_T_SHIFT 2 // Transpose 10418+#define XFORM_H_BIT (1 << XFORM_H_SHIFT) 10419+#define XFORM_V_BIT (1 << XFORM_V_SHIFT) 10420+#define XFORM_T_BIT (1 << XFORM_T_SHIFT) 10421+ 10422+static inline bool 10423+is_transform_transpose(const MMAL_DISPLAYTRANSFORM_T t) 10424+{ 10425+ return ((unsigned int)t & XFORM_T_BIT) != 0; 10426+} 10427+ 10428+static inline bool 10429+is_transform_hflip(const MMAL_DISPLAYTRANSFORM_T t) 10430+{ 10431+ return ((unsigned int)t & XFORM_H_BIT) != 0; 10432+} 10433+ 10434+static inline bool 10435+is_transform_vflip(const MMAL_DISPLAYTRANSFORM_T t) 10436+{ 10437+ return ((unsigned int)t & XFORM_V_BIT) != 0; 10438+} 10439+ 10440+static inline MMAL_DISPLAYTRANSFORM_T 10441+swap_transform_hv(const MMAL_DISPLAYTRANSFORM_T x) 10442+{ 10443+ return (((x >> XFORM_H_SHIFT) & 1) << XFORM_V_SHIFT) | 10444+ (((x >> XFORM_V_SHIFT) & 1) << XFORM_H_SHIFT) | 10445+ (x & XFORM_T_BIT); 10446+} 10447+ 10448+static inline MMAL_DISPLAYTRANSFORM_T 10449+transform_inverse(const MMAL_DISPLAYTRANSFORM_T x) 10450+{ 10451+ return is_transform_transpose(x) ? swap_transform_hv(x) : x; 10452+} 10453+ 10454+// Transform generated by A then B 10455+// All ops are self inverse so can simply be XORed on their own 10456+// H & V flips after a transpose need to be swapped 10457+static inline MMAL_DISPLAYTRANSFORM_T 10458+combine_transform(const MMAL_DISPLAYTRANSFORM_T a, const MMAL_DISPLAYTRANSFORM_T b) 10459+{ 10460+ return a ^ (is_transform_transpose(a) ? swap_transform_hv(b) : b); 10461+} 10462+ 10463+static inline MMAL_RECT_T 10464+rect_transpose(const MMAL_RECT_T s) 10465+{ 10466+ return (MMAL_RECT_T){ 10467+ .x = s.y, 10468+ .y = s.x, 10469+ .width = s.height, 10470+ .height = s.width 10471+ }; 10472+} 10473+ 10474+// hflip s in c 10475+static inline MMAL_RECT_T rect_hflip(const MMAL_RECT_T s, const MMAL_RECT_T c) 10476+{ 10477+ return (MMAL_RECT_T){ 10478+ .x = c.x + (c.x + c.width) - (s.x + s.width), 10479+ .y = s.y, 10480+ .width = s.width, 10481+ .height = s.height 10482+ }; 10483+} 10484+ 10485+// vflip s in c 10486+static inline MMAL_RECT_T rect_vflip(const MMAL_RECT_T s, const MMAL_RECT_T c) 10487+{ 10488+ return (MMAL_RECT_T){ 10489+ .x = s.x, 10490+ .y = (c.y + c.height) - (s.y - c.y) - s.height, 10491+ .width = s.width, 10492+ .height = s.height 10493+ }; 10494+} 10495+ 10496+ 10497+#endif 10498+ 10499--- /dev/null 10500+++ b/modules/hw/mmal/v7_pmu.S 10501@@ -0,0 +1,263 @@ 10502+/*------------------------------------------------------------ 10503+Performance Monitor Block 10504+------------------------------------------------------------*/ 10505+ .arm @ Make sure we are in ARM mode. 10506+ .text 10507+ .align 2 10508+ .global getPMN @ export this function for the linker 10509+ 10510+/* Returns the number of progammable counters uint32_t getPMN(void) */ 10511+ 10512+getPMN: 10513+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC Register */ 10514+ MOV r0, r0, LSR #11 /* Shift N field down to bit 0 */ 10515+ AND r0, r0, #0x1F /* Mask to leave just the 5 N bits */ 10516+ BX lr 10517+ 10518+ 10519+ 10520+ .global pmn_config @ export this function for the linker 10521+ /* Sets the event for a programmable counter to record */ 10522+ /* void pmn_config(unsigned counter, uint32_t event) */ 10523+ /* counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1 */ 10524+ /* event = r1 = The event code */ 10525+pmn_config: 10526+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ 10527+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ 10528+ MCR p15, 0, r1, c9, c13, 1 /* Write EVTSELx Register */ 10529+ BX lr 10530+ 10531+ 10532+ 10533+ .global ccnt_divider @ export this function for the linker 10534+ /* Enables/disables the divider (1/64) on CCNT */ 10535+ /* void ccnt_divider(int divider) */ 10536+ /* divider = r0 = If 0 disable divider, else enable dvider */ 10537+ccnt_divider: 10538+ MRC p15, 0, r1, c9, c12, 0 /* Read PMNC */ 10539+ 10540+ CMP r0, #0x0 /* IF (r0 == 0) */ 10541+ BICEQ r1, r1, #0x08 /* THEN: Clear the D bit (disables the */ 10542+ ORRNE r1, r1, #0x08 /* ELSE: Set the D bit (enables the di */ 10543+ 10544+ MCR p15, 0, r1, c9, c12, 0 /* Write PMNC */ 10545+ BX lr 10546+ 10547+ 10548+ /* --------------------------------------------------------------- */ 10549+ /* Enable/Disable */ 10550+ /* --------------------------------------------------------------- */ 10551+ 10552+ .global enable_pmu @ export this function for the linker 10553+ /* Global PMU enable */ 10554+ /* void enable_pmu(void) */ 10555+enable_pmu: 10556+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ 10557+ ORR r0, r0, #0x01 /* Set E bit */ 10558+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ 10559+ BX lr 10560+ 10561+ 10562+ 10563+ .global disable_pmu @ export this function for the linker 10564+ /* Global PMU disable */ 10565+ /* void disable_pmu(void) */ 10566+disable_pmu: 10567+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ 10568+ BIC r0, r0, #0x01 /* Clear E bit */ 10569+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ 10570+ BX lr 10571+ 10572+ 10573+ 10574+ .global enable_ccnt @ export this function for the linker 10575+ /* Enable the CCNT */ 10576+ /* void enable_ccnt(void) */ 10577+enable_ccnt: 10578+ MOV r0, #0x80000000 /* Set C bit */ 10579+ MCR p15, 0, r0, c9, c12, 1 /* Write CNTENS Register */ 10580+ BX lr 10581+ 10582+ 10583+ 10584+ .global disable_ccnt @ export this function for the linker 10585+ /* Disable the CCNT */ 10586+ /* void disable_ccnt(void) */ 10587+disable_ccnt: 10588+ MOV r0, #0x80000000 /* Clear C bit */ 10589+ MCR p15, 0, r0, c9, c12, 2 /* Write CNTENC Register */ 10590+ BX lr 10591+ 10592+ 10593+ 10594+ .global enable_pmn @ export this function for the linker 10595+ /* Enable PMN{n} */ 10596+ /* void enable_pmn(uint32_t counter) */ 10597+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) 10598+enable_pmn: */ 10599+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ 10600+ MOV r1, r1, LSL r0 10601+ 10602+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ 10603+ BX lr 10604+ 10605+ 10606+ 10607+ .global disable_pmn @ export this function for the linker 10608+ /* Enable PMN{n} */ 10609+ /* void disable_pmn(uint32_t counter) */ 10610+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) 10611+disable_pmn: */ 10612+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ 10613+ MOV r1, r1, LSL r0 10614+ 10615+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ 10616+ BX lr 10617+ 10618+ 10619+ 10620+ .global enable_pmu_user_access @ export this function for the linker 10621+ /* Enables User mode access to the PMU (must be called in a priviledge */ 10622+ /* void enable_pmu_user_access(void) */ 10623+enable_pmu_user_access: 10624+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ 10625+ ORR r0, r0, #0x01 /* Set EN bit (bit 0) */ 10626+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ 10627+ BX lr 10628+ 10629+ 10630+ 10631+ .global disable_pmu_user_access @ export this function for the linke 10632+ /* Disables User mode access to the PMU (must be called in a priviledg */ 10633+ /* void disable_pmu_user_access(void) */ 10634+disable_pmu_user_access: 10635+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ 10636+ BIC r0, r0, #0x01 /* Clear EN bit (bit 0) */ 10637+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ 10638+ BX lr 10639+ 10640+ 10641+ /* --------------------------------------------------------------- */ 10642+ /* Counter read registers */ 10643+ /* --------------------------------------------------------------- */ 10644+ 10645+ .global read_ccnt @ export this function for the linker 10646+ /* Returns the value of CCNT */ 10647+ /* uint32_t read_ccnt(void) */ 10648+read_ccnt: 10649+ MRC p15, 0, r0, c9, c13, 0 /* Read CCNT Register */ 10650+ BX lr 10651+ 10652+ 10653+ .global read_pmn @ export this function for the linker 10654+ /* Returns the value of PMN{n} */ 10655+ /* uint32_t read_pmn(uint32_t counter) */ 10656+ /* counter = r0 = The counter to read (e.g. 0 for PMN0, 1 for PMN1) * 10657+read_pmn: */ 10658+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ 10659+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ 10660+ MRC p15, 0, r0, c9, c13, 2 /* Read current PMNx Register */ 10661+ BX lr 10662+ 10663+ 10664+ /* --------------------------------------------------------------- */ 10665+ /* Software Increment */ 10666+ /* --------------------------------------------------------------- */ 10667+ 10668+ .global pmu_software_increment @ export this function for the linker 10669+ /* Writes to software increment register */ 10670+ /* void pmu_software_increment(uint32_t counter) */ 10671+ /* counter = r0 = The counter to increment (e.g. 0 for PMN0, 1 for PMN 10672+pmu_software_increment: */ 10673+ MOV r1, #0x01 10674+ MOV r1, r1, LSL r0 10675+ MCR p15, 0, r1, c9, c12, 4 /* Write SWINCR Register */ 10676+ BX lr 10677+ 10678+ /* --------------------------------------------------------------- */ 10679+ /* Overflow & Interrupt Generation */ 10680+ /* --------------------------------------------------------------- */ 10681+ 10682+ .global read_flags @ export this function for the linker 10683+ /* Returns the value of the overflow flags */ 10684+ /* uint32_t read_flags(void) */ 10685+read_flags: 10686+ MRC p15, 0, r0, c9, c12, 3 /* Read FLAG Register */ 10687+ BX lr 10688+ 10689+ 10690+ .global write_flags @ export this function for the linker 10691+ /* Writes the overflow flags */ 10692+ /* void write_flags(uint32_t flags) */ 10693+write_flags: 10694+ MCR p15, 0, r0, c9, c12, 3 /* Write FLAG Register */ 10695+ BX lr 10696+ 10697+ 10698+ .global enable_ccnt_irq @ export this function for the linker 10699+ /* Enables interrupt generation on overflow of the CCNT */ 10700+ /* void enable_ccnt_irq(void) */ 10701+enable_ccnt_irq: 10702+ MOV r0, #0x80000000 10703+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ 10704+ BX lr 10705+ 10706+ .global disable_ccnt_irq @ export this function for the linker 10707+ /* Disables interrupt generation on overflow of the CCNT */ 10708+ /* void disable_ccnt_irq(void) */ 10709+disable_ccnt_irq: 10710+ MOV r0, #0x80000000 10711+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ 10712+ BX lr 10713+ 10714+ 10715+ .global enable_pmn_irq @ export this function for the linker 10716+ /* Enables interrupt generation on overflow of PMN{x} */ 10717+ /* void enable_pmn_irq(uint32_t counter) */ 10718+ /* counter = r0 = The counter to enable the interrupt for (e.g. 0 for 10719+enable_pmn_irq: */ 10720+ MOV r1, #0x1 /* Use arg (r0) to set which counter */ 10721+ MOV r0, r1, LSL r0 10722+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ 10723+ BX lr 10724+ 10725+ .global disable_pmn_irq @ export this function for the linker 10726+ /* Disables interrupt generation on overflow of PMN{x} */ 10727+ /* void disable_pmn_irq(uint32_t counter) */ 10728+ /* counter = r0 = The counter to disable the interrupt for (e.g. 0 fo 10729+disable_pmn_irq: */ 10730+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */ 10731+ MOV r0, r1, LSL r0 10732+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ 10733+ BX lr 10734+ 10735+ /* --------------------------------------------------------------- */ 10736+ /* Reset Functions */ 10737+ /* --------------------------------------------------------------- */ 10738+ 10739+ .global reset_pmn @ export this function for the linker 10740+ /* Resets the programmable counters */ 10741+ /* void reset_pmn(void) */ 10742+reset_pmn: 10743+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ 10744+ ORR r0, r0, #0x02 /* Set P bit (Event Counter Reset) */ 10745+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ 10746+ BX lr 10747+ 10748+ 10749+ .global reset_ccnt @ export this function for the linker 10750+ /* Resets the CCNT */ 10751+ /* void reset_ccnt(void) */ 10752+reset_ccnt: 10753+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ 10754+ ORR r0, r0, #0x04 /* Set C bit (Event Counter Reset) */ 10755+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ 10756+ BX lr 10757+ 10758+ 10759+ .end @end of code, this line is optional. 10760+/* ------------------------------------------------------------ */ 10761+/* End of v7_pmu.s */ 10762+/* ------------------------------------------------------------ */ 10763+ 10764+ 10765--- /dev/null 10766+++ b/modules/hw/mmal/v7_pmu.h 10767@@ -0,0 +1,113 @@ 10768+// ------------------------------------------------------------ 10769+// PMU for Cortex-A/R (v7-A/R) 10770+// ------------------------------------------------------------ 10771+ 10772+#ifndef _V7_PMU_H 10773+#define _V7_PMU_H 10774+ 10775+// Returns the number of progammable counters 10776+unsigned int getPMN(void); 10777+ 10778+// Sets the event for a programmable counter to record 10779+// counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1) 10780+// event = r1 = The event code (from appropiate TRM or ARM Architecture Reference Manual) 10781+void pmn_config(unsigned int counter, unsigned int event); 10782+ 10783+// Enables/disables the divider (1/64) on CCNT 10784+// divider = r0 = If 0 disable divider, else enable dvider 10785+void ccnt_divider(int divider); 10786+ 10787+// 10788+// Enables and disables 10789+// 10790+ 10791+// Global PMU enable 10792+// On ARM11 this enables the PMU, and the counters start immediately 10793+// On Cortex this enables the PMU, there are individual enables for the counters 10794+void enable_pmu(void); 10795+ 10796+// Global PMU disable 10797+// On Cortex, this overrides the enable state of the individual counters 10798+void disable_pmu(void); 10799+ 10800+// Enable the CCNT 10801+void enable_ccnt(void); 10802+ 10803+// Disable the CCNT 10804+void disable_ccnt(void); 10805+ 10806+// Enable PMN{n} 10807+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) 10808+void enable_pmn(unsigned int counter); 10809+ 10810+// Enable PMN{n} 10811+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) 10812+void disable_pmn(unsigned int counter); 10813+ 10814+// 10815+// Read counter values 10816+// 10817+ 10818+// Returns the value of CCNT 10819+unsigned int read_ccnt(void); 10820+ 10821+// Returns the value of PMN{n} 10822+// counter = The counter to read (e.g. 0 for PMN0, 1 for PMN1) 10823+unsigned int read_pmn(unsigned int counter); 10824+ 10825+// 10826+// Overflow and interrupts 10827+// 10828+ 10829+// Returns the value of the overflow flags 10830+unsigned int read_flags(void); 10831+ 10832+// Writes the overflow flags 10833+void write_flags(unsigned int flags); 10834+ 10835+// Enables interrupt generation on overflow of the CCNT 10836+void enable_ccnt_irq(void); 10837+ 10838+// Disables interrupt generation on overflow of the CCNT 10839+void disable_ccnt_irq(void); 10840+ 10841+// Enables interrupt generation on overflow of PMN{x} 10842+// counter = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) 10843+void enable_pmn_irq(unsigned int counter); 10844+ 10845+// Disables interrupt generation on overflow of PMN{x} 10846+// counter = r0 = The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) 10847+void disable_pmn_irq(unsigned int counter); 10848+ 10849+// 10850+// Counter reset functions 10851+// 10852+ 10853+// Resets the programmable counters 10854+void reset_pmn(void); 10855+ 10856+// Resets the CCNT 10857+void reset_ccnt(void); 10858+ 10859+// 10860+// Software Increment 10861+ 10862+// Writes to software increment register 10863+// counter = The counter to increment (e.g. 0 for PMN0, 1 for PMN1) 10864+void pmu_software_increment(unsigned int counter); 10865+ 10866+// 10867+// User mode access 10868+// 10869+ 10870+// Enables User mode access to the PMU (must be called in a priviledged mode) 10871+void enable_pmu_user_access(void); 10872+ 10873+// Disables User mode access to the PMU (must be called in a priviledged mode) 10874+void disable_pmu_user_access(void); 10875+ 10876+#endif 10877+// ------------------------------------------------------------ 10878+// End of v7_pmu.h 10879+// ------------------------------------------------------------ 10880+ 10881--- a/modules/hw/mmal/vout.c 10882+++ b/modules/hw/mmal/vout.c 10883@@ -27,21 +27,28 @@ 10884 #endif 10885 10886 #include <math.h> 10887+#include <stdatomic.h> 10888 10889 #include <vlc_common.h> 10890-#include <vlc_atomic.h> 10891 #include <vlc_plugin.h> 10892 #include <vlc_threads.h> 10893 #include <vlc_vout_display.h> 10894+#include <vlc_modules.h> 10895 10896-#include "mmal_picture.h" 10897- 10898+#pragma GCC diagnostic push 10899+#pragma GCC diagnostic ignored "-Wbad-function-cast" 10900 #include <bcm_host.h> 10901+#pragma GCC diagnostic pop 10902 #include <interface/mmal/mmal.h> 10903 #include <interface/mmal/util/mmal_util.h> 10904 #include <interface/mmal/util/mmal_default_components.h> 10905 #include <interface/vmcs_host/vc_tvservice.h> 10906-#include <interface/vmcs_host/vc_dispmanx.h> 10907+ 10908+#include "mmal_picture.h" 10909+#include "subpic.h" 10910+#include "transform_ops.h" 10911+ 10912+#define TRACE_ALL 0 10913 10914 #define MAX_BUFFERS_IN_TRANSIT 1 10915 #define VC_TV_MAX_MODE_IDS 127 10916@@ -50,10 +57,28 @@ 10917 #define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.") 10918 #define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.") 10919 10920-#define MMAL_BLANK_BACKGROUND_NAME "mmal-blank-background" 10921-#define MMAL_BLANK_BACKGROUND_TEXT N_("Blank screen below video.") 10922-#define MMAL_BLANK_BACKGROUND_LONGTEXT N_("Render blank screen below video. " \ 10923- "Increases VideoCore load.") 10924+#define MMAL_DISPLAY_NAME "mmal-display" 10925+#define MMAL_DISPLAY_TEXT N_("Output device for Rpi fullscreen.") 10926+#define MMAL_DISPLAY_LONGTEXT N_("Output device for Rpi fullscreen. " \ 10927+"Valid values are HDMI-1,HDMI-2. By default if qt-fullscreen-screennumber " \ 10928+"is specified (or set by Fullscreen Output Device in Preferences) " \ 10929+"HDMI-<qt-fullscreen-screennumber+1> will be used, otherwise HDMI-1.") 10930+ 10931+#define MMAL_VOUT_TRANSFORM_NAME "mmal-vout-transform" 10932+#define MMAL_VOUT_TRANSFORM_TEXT N_("Video transform for Rpi fullscreen.") 10933+#define MMAL_VOUT_TRANSFORM_LONGTEXT N_("Video transform for Rpi fullscreen."\ 10934+"Transforms availible: auto, 0, 90, 180, 270, hflip, vflip, transpose, antitranspose") 10935+ 10936+#define MMAL_VOUT_WINDOW_NAME "mmal-vout-window" 10937+#define MMAL_VOUT_WINDOW_TEXT N_("Display window for Rpi fullscreen") 10938+#define MMAL_VOUT_WINDOW_LONGTEXT N_("Display window for Rpi fullscreen."\ 10939+"fullscreen|<width>x<height>+<x>+<y>") 10940+ 10941+#define MMAL_VOUT_TRANSPARENT_NAME "mmal-vout-transparent" 10942+#define MMAL_VOUT_TRANSPARENT_TEXT N_("Enable layers beneeth the vodeo layer.") 10943+#define MMAL_VOUT_TRANSPARENT_LONGTEXT N_("Enable layers beneath the video layer."\ 10944+" By default these are disabled."\ 10945+" Having the lower layers enabled can impact video performance") 10946 10947 #define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate" 10948 #define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.") 10949@@ -68,332 +93,628 @@ 10950 #define PHASE_OFFSET_TARGET ((double)0.25) 10951 #define PHASE_CHECK_INTERVAL 100 10952 10953-static int Open(vlc_object_t *); 10954-static void Close(vlc_object_t *); 10955- 10956-vlc_module_begin() 10957- set_shortname(N_("MMAL vout")) 10958- set_description(N_("MMAL-based vout plugin for Raspberry Pi")) 10959- set_capability("vout display", 90) 10960- add_shortcut("mmal_vout") 10961- add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false) 10962- add_bool(MMAL_BLANK_BACKGROUND_NAME, true, MMAL_BLANK_BACKGROUND_TEXT, 10963- MMAL_BLANK_BACKGROUND_LONGTEXT, true); 10964- add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT, 10965- MMAL_ADJUST_REFRESHRATE_LONGTEXT, false) 10966- add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT, 10967- MMAL_NATIVE_INTERLACE_LONGTEXT, false) 10968- set_callbacks(Open, Close) 10969-vlc_module_end() 10970+#define SUBS_MAX 4 10971 10972-struct dmx_region_t { 10973- struct dmx_region_t *next; 10974- picture_t *picture; 10975- VC_RECT_T bmp_rect; 10976- VC_RECT_T src_rect; 10977- VC_RECT_T dst_rect; 10978- VC_DISPMANX_ALPHA_T alpha; 10979- DISPMANX_ELEMENT_HANDLE_T element; 10980- DISPMANX_RESOURCE_HANDLE_T resource; 10981- int32_t pos_x; 10982- int32_t pos_y; 10983-}; 10984+typedef struct vout_subpic_s { 10985+ MMAL_COMPONENT_T *component; 10986+ subpic_reg_stash_t sub; 10987+} vout_subpic_t; 10988 10989 struct vout_display_sys_t { 10990- vlc_cond_t buffer_cond; 10991- vlc_mutex_t buffer_mutex; 10992 vlc_mutex_t manage_mutex; 10993 10994- plane_t planes[3]; /* Depending on video format up to 3 planes are used */ 10995- picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */ 10996- picture_pool_t *picture_pool; 10997- 10998+ vcsm_init_type_t init_type; 10999 MMAL_COMPONENT_T *component; 11000 MMAL_PORT_T *input; 11001 MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/ 11002- struct dmx_region_t *dmx_region; 11003 int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */ 11004 11005- uint32_t buffer_size; /* size of actual mmal buffers */ 11006 int buffers_in_transit; /* number of buffers currently pushed to mmal component */ 11007 unsigned num_buffers; /* number of buffers allocated at mmal port */ 11008 11009- DISPMANX_DISPLAY_HANDLE_T dmx_handle; 11010- DISPMANX_ELEMENT_HANDLE_T bkg_element; 11011- DISPMANX_RESOURCE_HANDLE_T bkg_resource; 11012- unsigned display_width; 11013- unsigned display_height; 11014+ int display_id; 11015+ MMAL_RECT_T win_rect; // Window rect after transform(s) 11016+ MMAL_RECT_T display_rect; // Actual shape of display (x, y always 0) 11017+ MMAL_RECT_T req_win; // User requested window (w=0 => fullscreen) 11018+ 11019+ MMAL_RECT_T spu_rect; // Output rectangle in cfg coords (for subpic placement) 11020+ MMAL_RECT_T dest_rect; // Output rectangle in display coords 11021+ MMAL_DISPLAYTRANSFORM_T dest_transform; // Dest window coord transform 11022+ MMAL_DISPLAYTRANSFORM_T display_transform; // "Native" display transform 11023+ MMAL_DISPLAYTRANSFORM_T video_transform; // Combined config+native transform 11024 11025- int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */ 11026- int i_frame_rate; 11027+ unsigned int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */ 11028+ unsigned int i_frame_rate; 11029 11030 int next_phase_check; /* lowpass for phase check frequency */ 11031 int phase_offset; /* currently applied offset to presentation time in ns */ 11032 int layer; /* the dispman layer (z-index) used for video rendering */ 11033+ bool transparent; // Do not disable layers beneath ours 11034 11035 bool need_configure_display; /* indicates a required display reconfigure to main thread */ 11036 bool adjust_refresh_rate; 11037 bool native_interlaced; 11038 bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */ 11039 bool b_progressive; 11040- bool opaque; /* indicated use of opaque picture format (zerocopy) */ 11041-}; 11042+ bool force_config; 11043 11044-static const vlc_fourcc_t subpicture_chromas[] = { 11045- VLC_CODEC_RGBA, 11046- 0 11047-}; 11048+ vout_subpic_t subs[SUBS_MAX]; 11049+ // Stash for subpics derived from the passed subpicture rather than 11050+ // included with the main pic 11051+ MMAL_BUFFER_HEADER_T * subpic_bufs[SUBS_MAX]; 11052+ 11053+ picture_pool_t * pic_pool; 11054+ 11055+ struct vout_isp_conf_s { 11056+ MMAL_COMPONENT_T *component; 11057+ MMAL_PORT_T * input; 11058+ MMAL_PORT_T * output; 11059+ MMAL_QUEUE_T * out_q; 11060+ MMAL_POOL_T * in_pool; 11061+ MMAL_POOL_T * out_pool; 11062+ bool pending; 11063+ } isp; 11064 11065-/* Utility functions */ 11066-static inline uint32_t align(uint32_t x, uint32_t y); 11067-static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, 11068- const video_format_t *fmt); 11069+ MMAL_POOL_T * copy_pool; 11070+ MMAL_BUFFER_HEADER_T * copy_buf; 11071 11072-/* VLC vout display callbacks */ 11073-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count); 11074-static void vd_prepare(vout_display_t *vd, picture_t *picture, 11075- subpicture_t *subpicture); 11076-static void vd_display(vout_display_t *vd, picture_t *picture, 11077- subpicture_t *subpicture); 11078-static int vd_control(vout_display_t *vd, int query, va_list args); 11079-static void vd_manage(vout_display_t *vd); 11080- 11081-/* MMAL callbacks */ 11082-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); 11083-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); 11084+ // Subpic blend if we have to do it here 11085+ vzc_pool_ctl_t * vzc; 11086+}; 11087 11088-/* TV service */ 11089-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height); 11090-static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, 11091- uint32_t param2); 11092-static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt); 11093-static int set_latency_target(vout_display_t *vd, bool enable); 11094 11095-/* DispManX */ 11096-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture); 11097-static void close_dmx(vout_display_t *vd); 11098-static struct dmx_region_t *dmx_region_new(vout_display_t *vd, 11099- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region); 11100-static void dmx_region_update(struct dmx_region_t *dmx_region, 11101- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture); 11102-static void dmx_region_delete(struct dmx_region_t *dmx_region, 11103- DISPMANX_UPDATE_HANDLE_T update); 11104-static void show_background(vout_display_t *vd, bool enable); 11105-static void maintain_phase_sync(vout_display_t *vd); 11106+// ISP setup 11107 11108-static int Open(vlc_object_t *object) 11109+static inline bool want_isp(const vout_display_t * const vd) 11110 { 11111- vout_display_t *vd = (vout_display_t *)object; 11112- vout_display_sys_t *sys; 11113- uint32_t buffer_pitch, buffer_height; 11114- vout_display_place_t place; 11115- MMAL_DISPLAYREGION_T display_region; 11116- MMAL_STATUS_T status; 11117- int ret = VLC_SUCCESS; 11118- unsigned i; 11119+ return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10); 11120+} 11121 11122- if (vout_display_IsWindowed(vd)) 11123- return VLC_EGENERIC; 11124+static inline bool want_copy(const vout_display_t * const vd) 11125+{ 11126+ return (vd->fmt.i_chroma == VLC_CODEC_I420 || vd->fmt.i_chroma == VLC_CODEC_I420_10L); 11127+} 11128 11129- sys = calloc(1, sizeof(struct vout_display_sys_t)); 11130- if (!sys) 11131- return VLC_ENOMEM; 11132- vd->sys = sys; 11133+static inline vlc_fourcc_t req_chroma(const vout_display_t * const vd) 11134+{ 11135+ return !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma) && !want_copy(vd) ? 11136+ VLC_CODEC_I420 : 11137+ vd->fmt.i_chroma; 11138+} 11139 11140- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); 11141- bcm_host_init(); 11142+static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc) 11143+{ 11144+ switch (fcc){ 11145+ case VLC_CODEC_MMAL_OPAQUE: 11146+ return MMAL_ENCODING_OPAQUE; 11147+ case VLC_CODEC_MMAL_ZC_SAND8: 11148+ return MMAL_ENCODING_YUVUV128; 11149+ case VLC_CODEC_MMAL_ZC_SAND10: 11150+ return MMAL_ENCODING_YUVUV64_10; 11151+ case VLC_CODEC_MMAL_ZC_SAND30: 11152+ return MMAL_ENCODING_YUV10_COL; 11153+ case VLC_CODEC_MMAL_ZC_I420: 11154+ case VLC_CODEC_I420: 11155+ return MMAL_ENCODING_I420; 11156+ default: 11157+ break; 11158+ } 11159+ return MMAL_ENCODING_I420; 11160+} 11161 11162- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; 11163+static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate) 11164+{ 11165+ const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ; 11166+ const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height; 11167+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video; 11168 11169- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component); 11170- if (status != MMAL_SUCCESS) { 11171- msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)", 11172- MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status)); 11173- ret = VLC_EGENERIC; 11174- goto out; 11175+ es_fmt->type = MMAL_ES_TYPE_VIDEO; 11176+ es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma); 11177+ es_fmt->encoding_variant = 0; 11178+ 11179+ v_fmt->width = (w + 31) & ~31; 11180+ v_fmt->height = (h + 15) & ~15; 11181+ v_fmt->crop.x = 0; 11182+ v_fmt->crop.y = 0; 11183+ v_fmt->crop.width = w; 11184+ v_fmt->crop.height = h; 11185+ if (vd->fmt.i_sar_num == 0 || vd->fmt.i_sar_den == 0) { 11186+ v_fmt->par.num = 1; 11187+ v_fmt->par.den = 1; 11188+ } else { 11189+ v_fmt->par.num = vd->fmt.i_sar_num; 11190+ v_fmt->par.den = vd->fmt.i_sar_den; 11191 } 11192+ v_fmt->frame_rate.num = vd->fmt.i_frame_rate; 11193+ v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base; 11194+ v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space); 11195 11196- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; 11197- status = mmal_port_enable(sys->component->control, control_port_cb); 11198- if (status != MMAL_SUCCESS) { 11199- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)", 11200- sys->component->control->name, status, mmal_status_to_string(status)); 11201- ret = VLC_EGENERIC; 11202- goto out; 11203+ msg_Dbg(vd, "WxH: %dx%d, Crop: %dx%d", v_fmt->width, v_fmt->height, v_fmt->crop.width, v_fmt->crop.height); 11204+} 11205+ 11206+static MMAL_RECT_T 11207+display_src_rect(const vout_display_t * const vd, const video_format_t * const src) 11208+{ 11209+ const bool wants_isp = want_isp(vd); 11210+ 11211+ // Scale source derived cropping to actual picture shape 11212+ return (MMAL_RECT_T){ 11213+ .x = wants_isp ? 0 : src->i_x_offset * vd->fmt.i_width / src->i_width, 11214+ .y = wants_isp ? 0 : src->i_y_offset * vd->fmt.i_height / src->i_height, 11215+ .width = src->i_visible_width * vd->fmt.i_width / src->i_width, 11216+ .height = src->i_visible_height * vd->fmt.i_height / src->i_height 11217+ }; 11218+} 11219+ 11220+static void isp_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) 11221+{ 11222+#if TRACE_ALL 11223+ vout_display_t * const vd = (vout_display_t *)port->userdata; 11224+ pic_ctx_mmal_t * ctx = buf->user_data; 11225+ msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf, 11226+ buf->flags, (long long)buf->pts); 11227+#else 11228+ VLC_UNUSED(port); 11229+#endif 11230+ 11231+ mmal_buffer_header_release(buf); 11232+ 11233+#if TRACE_ALL 11234+ msg_Dbg(vd, ">>> %s", __func__); 11235+#endif 11236+} 11237+ 11238+static void isp_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 11239+{ 11240+ vout_display_t *vd = (vout_display_t *)port->userdata; 11241+ MMAL_STATUS_T status; 11242+ 11243+ if (buffer->cmd == MMAL_EVENT_ERROR) { 11244+ status = *(uint32_t *)buffer->data; 11245+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); 11246 } 11247 11248- sys->input = sys->component->input[0]; 11249- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd; 11250+ mmal_buffer_header_release(buffer); 11251+} 11252 11253- if (sys->opaque) { 11254- sys->input->format->encoding = MMAL_ENCODING_OPAQUE; 11255- sys->i_planes = 1; 11256- sys->buffer_size = sys->input->buffer_size_recommended; 11257- } else { 11258- sys->input->format->encoding = MMAL_ENCODING_I420; 11259- vd->fmt.i_chroma = VLC_CODEC_I420; 11260- buffer_pitch = align(vd->fmt.i_width, 32); 11261- buffer_height = align(vd->fmt.i_height, 16); 11262- sys->i_planes = 3; 11263- sys->buffer_size = 3 * buffer_pitch * buffer_height / 2; 11264- } 11265- 11266- sys->input->format->es->video.width = vd->fmt.i_width; 11267- sys->input->format->es->video.height = vd->fmt.i_height; 11268- sys->input->format->es->video.crop.x = 0; 11269- sys->input->format->es->video.crop.y = 0; 11270- sys->input->format->es->video.crop.width = vd->fmt.i_width; 11271- sys->input->format->es->video.crop.height = vd->fmt.i_height; 11272- sys->input->format->es->video.par.num = vd->source.i_sar_num; 11273- sys->input->format->es->video.par.den = vd->source.i_sar_den; 11274+static void isp_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) 11275+{ 11276+ if (buf->cmd == 0 && buf->length != 0) 11277+ { 11278+ // The filter structure etc. should always exist if we have contents 11279+ // but might not on later flushes as we shut down 11280+ vout_display_t * const vd = (vout_display_t *)port->userdata; 11281+ struct vout_isp_conf_s *const isp = &vd->sys->isp; 11282 11283- status = mmal_port_format_commit(sys->input); 11284- if (status != MMAL_SUCCESS) { 11285- msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)", 11286- sys->input->name, status, mmal_status_to_string(status)); 11287- ret = VLC_EGENERIC; 11288- goto out; 11289+#if TRACE_ALL 11290+ msg_Dbg(vd, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts); 11291+#endif 11292+ mmal_queue_put(isp->out_q, buf); 11293+#if TRACE_ALL 11294+ msg_Dbg(vd, ">>> %s: out Q len=%d", __func__, mmal_queue_length(isp->out_q)); 11295+#endif 11296 } 11297- sys->input->buffer_size = sys->input->buffer_size_recommended; 11298+ else 11299+ { 11300+ mmal_buffer_header_reset(buf); 11301+ mmal_buffer_header_release(buf); 11302+ } 11303+} 11304 11305- vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); 11306- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; 11307- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T); 11308- display_region.fullscreen = MMAL_FALSE; 11309- display_region.src_rect.x = vd->fmt.i_x_offset; 11310- display_region.src_rect.y = vd->fmt.i_y_offset; 11311- display_region.src_rect.width = vd->fmt.i_visible_width; 11312- display_region.src_rect.height = vd->fmt.i_visible_height; 11313- display_region.dest_rect.x = place.x; 11314- display_region.dest_rect.y = place.y; 11315- display_region.dest_rect.width = place.width; 11316- display_region.dest_rect.height = place.height; 11317- display_region.layer = sys->layer; 11318- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT | 11319- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER; 11320- status = mmal_port_parameter_set(sys->input, &display_region.hdr); 11321- if (status != MMAL_SUCCESS) { 11322- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", 11323- status, mmal_status_to_string(status)); 11324- ret = VLC_EGENERIC; 11325- goto out; 11326+static void isp_empty_out_q(struct vout_isp_conf_s * const isp) 11327+{ 11328+ MMAL_BUFFER_HEADER_T * buf; 11329+ // We can be called as part of error recovery so allow for missing Q 11330+ if (isp->out_q == NULL) 11331+ return; 11332+ 11333+ while ((buf = mmal_queue_get(isp->out_q)) != NULL) 11334+ mmal_buffer_header_release(buf); 11335+} 11336+ 11337+static void isp_flush(struct vout_isp_conf_s * const isp) 11338+{ 11339+ if (!isp->input->is_enabled) 11340+ mmal_port_disable(isp->input); 11341+ 11342+ if (isp->output->is_enabled) 11343+ mmal_port_disable(isp->output); 11344+ 11345+ isp_empty_out_q(isp); 11346+ isp->pending = false; 11347+} 11348+ 11349+static MMAL_STATUS_T isp_prepare(vout_display_t * const vd, struct vout_isp_conf_s * const isp) 11350+{ 11351+ MMAL_STATUS_T err; 11352+ MMAL_BUFFER_HEADER_T * buf; 11353+ 11354+ if (!isp->output->is_enabled) { 11355+ if ((err = mmal_port_enable(isp->output, isp_output_cb)) != MMAL_SUCCESS) 11356+ { 11357+ msg_Err(vd, "ISP output port enable failed"); 11358+ return err; 11359+ } 11360 } 11361 11362- for (i = 0; i < sys->i_planes; ++i) { 11363- sys->planes[i].i_lines = buffer_height; 11364- sys->planes[i].i_pitch = buffer_pitch; 11365- sys->planes[i].i_visible_lines = vd->fmt.i_visible_height; 11366- sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width; 11367+ while ((buf = mmal_queue_get(isp->out_pool->queue)) != NULL) { 11368+ if ((err = mmal_port_send_buffer(isp->output, buf)) != MMAL_SUCCESS) 11369+ { 11370+ msg_Err(vd, "ISP output port stuff failed"); 11371+ return err; 11372+ } 11373+ } 11374 11375- if (i > 0) { 11376- sys->planes[i].i_lines /= 2; 11377- sys->planes[i].i_pitch /= 2; 11378- sys->planes[i].i_visible_lines /= 2; 11379- sys->planes[i].i_visible_pitch /= 2; 11380+ if (!isp->input->is_enabled) { 11381+ if ((err = mmal_port_enable(isp->input, isp_input_cb)) != MMAL_SUCCESS) 11382+ { 11383+ msg_Err(vd, "ISP input port enable failed"); 11384+ return err; 11385 } 11386 } 11387+ return MMAL_SUCCESS; 11388+} 11389 11390- vlc_mutex_init(&sys->buffer_mutex); 11391- vlc_cond_init(&sys->buffer_cond); 11392- vlc_mutex_init(&sys->manage_mutex); 11393+static void isp_close(vout_display_t * const vd, vout_display_sys_t * const vd_sys) 11394+{ 11395+ struct vout_isp_conf_s * const isp = &vd_sys->isp; 11396+ VLC_UNUSED(vd); 11397 11398- vd->pool = vd_pool; 11399- vd->prepare = vd_prepare; 11400- vd->display = vd_display; 11401- vd->control = vd_control; 11402- vd->manage = vd_manage; 11403+ if (isp->component == NULL) 11404+ return; 11405 11406- vc_tv_register_callback(tvservice_cb, vd); 11407+ isp_flush(isp); 11408 11409- if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) { 11410- vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height); 11411- } else { 11412- sys->display_width = vd->cfg->display.width; 11413- sys->display_height = vd->cfg->display.height; 11414+ if (isp->component->control->is_enabled) 11415+ mmal_port_disable(isp->component->control); 11416+ 11417+ if (isp->out_q != NULL) { 11418+ // 1st junk anything lying around 11419+ isp_empty_out_q(isp); 11420+ 11421+ mmal_queue_destroy(isp->out_q); 11422+ isp->out_q = NULL; 11423 } 11424 11425- sys->dmx_handle = vc_dispmanx_display_open(0); 11426- vd->info.subpicture_chromas = subpicture_chromas; 11427+ if (isp->out_pool != NULL) { 11428+ mmal_port_pool_destroy(isp->output, isp->out_pool); 11429+ isp->out_pool = NULL; 11430+ } 11431 11432- vout_display_DeleteWindow(vd, NULL); 11433+ isp->input = NULL; 11434+ isp->output = NULL; 11435 11436-out: 11437- if (ret != VLC_SUCCESS) 11438- Close(object); 11439+ mmal_component_release(isp->component); 11440+ isp->component = NULL; 11441 11442- return ret; 11443+ return; 11444 } 11445 11446-static void Close(vlc_object_t *object) 11447+// Restuff into output rather than return to pool is we can 11448+static MMAL_BOOL_T isp_out_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata) 11449 { 11450- vout_display_t *vd = (vout_display_t *)object; 11451- vout_display_sys_t *sys = vd->sys; 11452- char response[20]; /* answer is hvs_update_fields=%1d */ 11453- unsigned i; 11454+ struct vout_isp_conf_s * const isp = userdata; 11455+ VLC_UNUSED(pool); 11456+ if (isp->output->is_enabled) { 11457+ mmal_buffer_header_reset(buffer); 11458+ if (mmal_port_send_buffer(isp->output, buffer) == MMAL_SUCCESS) 11459+ return MMAL_FALSE; 11460+ } 11461+ return MMAL_TRUE; 11462+} 11463 11464- vc_tv_unregister_callback_full(tvservice_cb, vd); 11465+static MMAL_STATUS_T isp_setup(vout_display_t * const vd, vout_display_sys_t * const vd_sys) 11466+{ 11467+ struct vout_isp_conf_s * const isp = &vd_sys->isp; 11468+ MMAL_STATUS_T err; 11469 11470- if (sys->dmx_handle) 11471- close_dmx(vd); 11472+ if ((err = mmal_component_create(MMAL_COMPONENT_ISP_RESIZER, &isp->component)) != MMAL_SUCCESS) { 11473+ msg_Err(vd, "Cannot create ISP component"); 11474+ return err; 11475+ } 11476+ isp->input = isp->component->input[0]; 11477+ isp->output = isp->component->output[0]; 11478 11479- if (sys->component && sys->component->control->is_enabled) 11480- mmal_port_disable(sys->component->control); 11481+ isp->component->control->userdata = (void *)vd; 11482+ if ((err = mmal_port_enable(isp->component->control, isp_control_port_cb)) != MMAL_SUCCESS) { 11483+ msg_Err(vd, "Failed to enable ISP control port"); 11484+ goto fail; 11485+ } 11486 11487- if (sys->input && sys->input->is_enabled) 11488- mmal_port_disable(sys->input); 11489+ isp->input->userdata = (void *)vd; 11490+ display_set_format(vd, isp->input->format, false); 11491 11492- if (sys->component && sys->component->is_enabled) 11493- mmal_component_disable(sys->component); 11494+ if ((err = port_parameter_set_bool(isp->input, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) 11495+ goto fail; 11496 11497- if (sys->pool) 11498- mmal_port_pool_destroy(sys->input, sys->pool); 11499+ if ((err = mmal_port_format_commit(isp->input)) != MMAL_SUCCESS) { 11500+ msg_Err(vd, "Failed to set ISP input format"); 11501+ goto fail; 11502+ } 11503 11504- if (sys->component) 11505- mmal_component_release(sys->component); 11506+ isp->input->buffer_size = isp->input->buffer_size_recommended; 11507+ isp->input->buffer_num = 30; 11508 11509- if (sys->picture_pool) 11510- picture_pool_Release(sys->picture_pool); 11511- else 11512- for (i = 0; i < sys->num_buffers; ++i) 11513- if (sys->pictures[i]) { 11514- mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer); 11515- picture_Release(sys->pictures[i]); 11516- } 11517+ if ((isp->in_pool = mmal_pool_create(isp->input->buffer_num, 0)) == NULL) 11518+ { 11519+ msg_Err(vd, "Failed to create input pool"); 11520+ goto fail; 11521+ } 11522 11523- vlc_mutex_destroy(&sys->buffer_mutex); 11524- vlc_cond_destroy(&sys->buffer_cond); 11525- vlc_mutex_destroy(&sys->manage_mutex); 11526+ if ((isp->out_q = mmal_queue_create()) == NULL) 11527+ { 11528+ err = MMAL_ENOMEM; 11529+ goto fail; 11530+ } 11531 11532- if (sys->native_interlaced) { 11533- if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 || 11534- response[18] != '0') 11535- msg_Warn(vd, "Could not reset hvs field mode"); 11536+ display_set_format(vd, isp->output->format, true); 11537+ 11538+ if ((err = port_parameter_set_bool(isp->output, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) 11539+ goto fail; 11540+ 11541+ if ((err = mmal_port_format_commit(isp->output)) != MMAL_SUCCESS) { 11542+ msg_Err(vd, "Failed to set ISP input format"); 11543+ goto fail; 11544 } 11545 11546- free(sys->pictures); 11547- free(sys); 11548+ isp->output->buffer_size = isp->output->buffer_size_recommended; 11549+ isp->output->buffer_num = 2; 11550+ isp->output->userdata = (void *)vd; 11551+ 11552+ if ((isp->out_pool = mmal_port_pool_create(isp->output, isp->output->buffer_num, isp->output->buffer_size)) == NULL) 11553+ { 11554+ msg_Err(vd, "Failed to make ISP port pool"); 11555+ goto fail; 11556+ } 11557+ 11558+ mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp); 11559+ 11560+ if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS) 11561+ goto fail; 11562+ 11563+ return MMAL_SUCCESS; 11564 11565- bcm_host_deinit(); 11566+fail: 11567+ isp_close(vd, vd_sys); 11568+ return err; 11569 } 11570 11571-static inline uint32_t align(uint32_t x, uint32_t y) { 11572- uint32_t mod = x % y; 11573- if (mod == 0) 11574- return x; 11575+static MMAL_STATUS_T isp_check(vout_display_t * const vd, vout_display_sys_t * const vd_sys) 11576+{ 11577+ struct vout_isp_conf_s *const isp = &vd_sys->isp; 11578+ const bool has_isp = (isp->component != NULL); 11579+ const bool wants_isp = want_isp(vd); 11580+ 11581+ if (has_isp == wants_isp) 11582+ { 11583+ // All OK - do nothing 11584+ } 11585+ else if (has_isp) 11586+ { 11587+ // ISP active but we don't want it 11588+ isp_flush(isp); 11589+ 11590+ // Check we have everything back and then kill it 11591+ if (mmal_queue_length(isp->out_pool->queue) == isp->output->buffer_num) 11592+ isp_close(vd, vd_sys); 11593+ } 11594 else 11595- return x + y - mod; 11596+ { 11597+ // ISP closed but we want it 11598+ return isp_setup(vd, vd_sys); 11599+ } 11600+ 11601+ return MMAL_SUCCESS; 11602+} 11603+ 11604+/* TV service */ 11605+static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, 11606+ uint32_t param2); 11607+static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt); 11608+static int set_latency_target(vout_display_t *vd, bool enable); 11609+ 11610+// Mmal 11611+static void maintain_phase_sync(vout_display_t *vd); 11612+ 11613+ 11614+ 11615+static void vd_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) 11616+{ 11617+#if TRACE_ALL 11618+ vout_display_t * const vd = (vout_display_t *)port->userdata; 11619+ pic_ctx_mmal_t * ctx = buf->user_data; 11620+ msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf, 11621+ buf->flags, (long long)buf->pts); 11622+#else 11623+ VLC_UNUSED(port); 11624+#endif 11625+ 11626+ mmal_buffer_header_release(buf); 11627+ 11628+#if TRACE_ALL 11629+ msg_Dbg(vd, ">>> %s", __func__); 11630+#endif 11631+} 11632+ 11633+static int query_resolution(vout_display_t *vd, const int display_id, unsigned *width, unsigned *height) 11634+{ 11635+ TV_DISPLAY_STATE_T display_state = {0}; 11636+ int ret = 0; 11637+ 11638+ if (vc_tv_get_display_state_id(display_id, &display_state) == 0) { 11639+ msg_Dbg(vd, "State=%#x", display_state.state); 11640+ if (display_state.state & 0xFF) { 11641+ msg_Dbg(vd, "HDMI: %dx%d", display_state.display.hdmi.width, display_state.display.hdmi.height); 11642+ *width = display_state.display.hdmi.width; 11643+ *height = display_state.display.hdmi.height; 11644+ } else if (display_state.state & 0xFF00) { 11645+ msg_Dbg(vd, "SDTV: %dx%d", display_state.display.sdtv.width, display_state.display.sdtv.height); 11646+ *width = display_state.display.sdtv.width; 11647+ *height = display_state.display.sdtv.height; 11648+ } else { 11649+ msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state); 11650+ ret = -1; 11651+ } 11652+ } else { 11653+ msg_Warn(vd, "Failed to query display resolution"); 11654+ ret = -1; 11655+ } 11656+ 11657+ return ret; 11658+} 11659+ 11660+static inline MMAL_RECT_T 11661+place_to_mmal_rect(const vout_display_place_t place) 11662+{ 11663+ return (MMAL_RECT_T){ 11664+ .x = place.x, 11665+ .y = place.y, 11666+ .width = place.width, 11667+ .height = place.height 11668+ }; 11669+} 11670+ 11671+static MMAL_RECT_T 11672+place_out(const vout_display_cfg_t * cfg, 11673+ const video_format_t * fmt, 11674+ const MMAL_RECT_T r) 11675+{ 11676+ video_format_t tfmt; 11677+ vout_display_cfg_t tcfg; 11678+ vout_display_place_t place; 11679+ 11680+ // Fix SAR if unknown 11681+ if (fmt->i_sar_den == 0 || fmt->i_sar_num == 0) { 11682+ tfmt = *fmt; 11683+ tfmt.i_sar_den = 1; 11684+ tfmt.i_sar_num = 1; 11685+ fmt = &tfmt; 11686+ } 11687+ 11688+ // Override what VLC thinks might be going on with display size 11689+ // if we know better 11690+ if (r.width != 0 && r.height != 0) 11691+ { 11692+ tcfg = *cfg; 11693+ tcfg.display.width = r.width; 11694+ tcfg.display.height = r.height; 11695+ cfg = &tcfg; 11696+ } 11697+ 11698+ vout_display_PlacePicture(&place, fmt, cfg, false); 11699+ 11700+ place.x += r.x; 11701+ place.y += r.y; 11702+ 11703+ return place_to_mmal_rect(place); 11704+} 11705+ 11706+static MMAL_RECT_T 11707+rect_transform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t) 11708+{ 11709+ if (is_transform_transpose(t)) 11710+ s = rect_transpose(s); 11711+ if (is_transform_hflip(t)) 11712+ s = rect_hflip(s, c); 11713+ if (is_transform_vflip(t) != 0) 11714+ s = rect_vflip(s, c); 11715+ return s; 11716+} 11717+ 11718+static void 11719+place_dest_rect(vout_display_t * const vd, 11720+ const vout_display_cfg_t * const cfg, 11721+ const video_format_t * fmt) 11722+{ 11723+ vout_display_sys_t * const sys = vd->sys; 11724+ sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect), 11725+ sys->display_rect, sys->dest_transform); 11726+} 11727+ 11728+static void 11729+place_spu_rect(vout_display_t * const vd, 11730+ const vout_display_cfg_t * const cfg, 11731+ const video_format_t * fmt) 11732+{ 11733+ vout_display_sys_t * const sys = vd->sys; 11734+ static const MMAL_RECT_T r0 = {0}; 11735+ 11736+ sys->spu_rect = place_out(cfg, fmt, r0); 11737+ sys->spu_rect.x = 0; 11738+ sys->spu_rect.y = 0; 11739+ 11740+ // Copy place override logic for spu pos from video_output.c 11741+ // This info doesn't appear to reside anywhere natively 11742+ 11743+ if (fmt->i_width * fmt->i_height >= (unsigned int)(sys->spu_rect.width * sys->spu_rect.height)) { 11744+ sys->spu_rect.width = fmt->i_visible_width; 11745+ sys->spu_rect.height = fmt->i_visible_height; 11746+ } 11747+ 11748+ if (ORIENT_IS_SWAP(fmt->orientation)) 11749+ sys->spu_rect = rect_transpose(sys->spu_rect); 11750+} 11751+ 11752+static void 11753+place_rects(vout_display_t * const vd, 11754+ const vout_display_cfg_t * const cfg, 11755+ const video_format_t * fmt) 11756+{ 11757+ place_dest_rect(vd, cfg, fmt); 11758+ place_spu_rect(vd, cfg, fmt); 11759+} 11760+ 11761+static int 11762+set_input_region(vout_display_t * const vd, const video_format_t * const fmt) 11763+{ 11764+ const vout_display_sys_t * const sys = vd->sys; 11765+ MMAL_DISPLAYREGION_T display_region = { 11766+ .hdr = { 11767+ .id = MMAL_PARAMETER_DISPLAYREGION, 11768+ .size = sizeof(MMAL_DISPLAYREGION_T) 11769+ }, 11770+ .display_num = sys->display_id, 11771+ .fullscreen = MMAL_FALSE, 11772+ .transform = sys->video_transform, 11773+ .dest_rect = sys->dest_rect, 11774+ .src_rect = display_src_rect(vd, fmt), 11775+ .noaspect = MMAL_TRUE, 11776+ .mode = MMAL_DISPLAY_MODE_FILL, 11777+ .layer = sys->layer, 11778+ .alpha = 0xff | (sys->transparent ? 0 : (1 << 29)), 11779+ .set = 11780+ MMAL_DISPLAY_SET_NUM | 11781+ MMAL_DISPLAY_SET_FULLSCREEN | 11782+ MMAL_DISPLAY_SET_TRANSFORM | 11783+ MMAL_DISPLAY_SET_DEST_RECT | 11784+ MMAL_DISPLAY_SET_SRC_RECT | 11785+ MMAL_DISPLAY_SET_NOASPECT | 11786+ MMAL_DISPLAY_SET_MODE | 11787+ MMAL_DISPLAY_SET_LAYER | 11788+ MMAL_DISPLAY_SET_ALPHA 11789+ }; 11790+ MMAL_STATUS_T status = mmal_port_parameter_set(sys->input, &display_region.hdr); 11791+ if (status != MMAL_SUCCESS) { 11792+ msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", 11793+ status, mmal_status_to_string(status)); 11794+ return -EINVAL; 11795+ } 11796+ return 0; 11797 } 11798 11799 static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, 11800 const video_format_t *fmt) 11801 { 11802- vout_display_sys_t *sys = vd->sys; 11803- vout_display_place_t place; 11804- MMAL_DISPLAYREGION_T display_region; 11805+ vout_display_sys_t * const sys = vd->sys; 11806 MMAL_STATUS_T status; 11807 11808 if (!cfg && !fmt) 11809+ { 11810+ msg_Err(vd, "%s: Missing cfg & fmt", __func__); 11811 return -EINVAL; 11812+ } 11813+ 11814+ isp_check(vd, sys); 11815 11816 if (fmt) { 11817 sys->input->format->es->video.par.num = fmt->i_sar_num; 11818@@ -412,30 +733,14 @@ static int configure_display(vout_displa 11819 if (!cfg) 11820 cfg = vd->cfg; 11821 11822- vout_display_PlacePicture(&place, fmt, cfg, false); 11823+ sys->video_transform = combine_transform( 11824+ vlc_to_mmal_transform(fmt->orientation), sys->display_transform); 11825 11826- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; 11827- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T); 11828- display_region.fullscreen = MMAL_FALSE; 11829- display_region.src_rect.x = fmt->i_x_offset; 11830- display_region.src_rect.y = fmt->i_y_offset; 11831- display_region.src_rect.width = fmt->i_visible_width; 11832- display_region.src_rect.height = fmt->i_visible_height; 11833- display_region.dest_rect.x = place.x; 11834- display_region.dest_rect.y = place.y; 11835- display_region.dest_rect.width = place.width; 11836- display_region.dest_rect.height = place.height; 11837- display_region.layer = sys->layer; 11838- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT | 11839- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER; 11840- status = mmal_port_parameter_set(sys->input, &display_region.hdr); 11841- if (status != MMAL_SUCCESS) { 11842- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", 11843- status, mmal_status_to_string(status)); 11844+ place_rects(vd, cfg, fmt); 11845+ 11846+ if (set_input_region(vd, fmt) != 0) 11847 return -EINVAL; 11848- } 11849 11850- show_background(vd, var_InheritBool(vd, MMAL_BLANK_BACKGROUND_NAME)); 11851 sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME); 11852 sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED); 11853 if (sys->adjust_refresh_rate) { 11854@@ -446,204 +751,217 @@ static int configure_display(vout_displa 11855 return 0; 11856 } 11857 11858+static void kill_pool(vout_display_sys_t * const sys) 11859+{ 11860+ if (sys->pic_pool != NULL) { 11861+ picture_pool_Release(sys->pic_pool); 11862+ sys->pic_pool = NULL; 11863+ } 11864+} 11865+ 11866+// Actual picture pool for MMAL opaques is just a set of trivial containers 11867 static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count) 11868 { 11869- vout_display_sys_t *sys = vd->sys; 11870- picture_resource_t picture_res; 11871- picture_pool_configuration_t picture_pool_cfg; 11872- video_format_t fmt = vd->fmt; 11873- MMAL_STATUS_T status; 11874- unsigned i; 11875+ vout_display_sys_t * const sys = vd->sys; 11876 11877- if (sys->picture_pool) { 11878- if (sys->num_buffers < count) 11879- msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures", 11880- count, sys->num_buffers); 11881+ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__, 11882+ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height); 11883 11884- goto out; 11885+ if (sys->pic_pool == NULL) { 11886+ sys->pic_pool = picture_pool_NewFromFormat(&vd->fmt, count); 11887 } 11888+ return sys->pic_pool; 11889+} 11890 11891- if (sys->opaque) { 11892- if (count <= NUM_ACTUAL_OPAQUE_BUFFERS) 11893- count = NUM_ACTUAL_OPAQUE_BUFFERS; 11894+static inline bool 11895+check_shape(vout_display_t * const vd, const picture_t * const p_pic) 11896+{ 11897+ if (vd->fmt.i_width == p_pic->format.i_width && 11898+ vd->fmt.i_height == p_pic->format.i_height) 11899+ return true; 11900+ return false; 11901+} 11902 11903- MMAL_PARAMETER_BOOLEAN_T zero_copy = { 11904- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, 11905- 1 11906- }; 11907+static void vd_display(vout_display_t *vd, picture_t *p_pic, 11908+ subpicture_t *subpicture) 11909+{ 11910+ vout_display_sys_t * const sys = vd->sys; 11911+ MMAL_STATUS_T err; 11912 11913- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr); 11914- if (status != MMAL_SUCCESS) { 11915- msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", 11916- sys->input->name, status, mmal_status_to_string(status)); 11917- goto out; 11918- } 11919+#if TRACE_ALL 11920+ { 11921+ char dbuf0[5]; 11922+ msg_Dbg(vd, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %dx%d@%d,%d", __func__, 11923+ str_fourcc(dbuf0, p_pic->format.i_chroma), p_pic->format.i_width, p_pic->format.i_height, 11924+ p_pic->format.i_x_offset, p_pic->format.i_y_offset, 11925+ p_pic->format.i_visible_width, p_pic->format.i_visible_height, 11926+ p_pic->format.i_sar_num, p_pic->format.i_sar_den, 11927+ sys->dest_rect.width, sys->dest_rect.height, sys->dest_rect.x, sys->dest_rect.y); 11928 } 11929- 11930- if (count < sys->input->buffer_num_recommended) 11931- count = sys->input->buffer_num_recommended; 11932- 11933-#ifndef NDEBUG 11934- msg_Dbg(vd, "Creating picture pool with %u pictures", count); 11935 #endif 11936 11937- sys->input->buffer_num = count; 11938- status = mmal_port_enable(sys->input, input_port_cb); 11939- if (status != MMAL_SUCCESS) { 11940- msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)", 11941- sys->input->name, status, mmal_status_to_string(status)); 11942- goto out; 11943+ // If we had subpics then we have attached them to the main pic in prepare 11944+ // so all we have to do here is delete the refs 11945+ if (subpicture != NULL) { 11946+ subpicture_Delete(subpicture); 11947 } 11948 11949- status = mmal_component_enable(sys->component); 11950- if (status != MMAL_SUCCESS) { 11951- msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)", 11952- sys->component->name, status, mmal_status_to_string(status)); 11953- goto out; 11954+ if (!check_shape(vd, p_pic)) 11955+ { 11956+ msg_Err(vd, "Pic/fmt shape mismatch"); 11957+ goto fail; 11958+ } 11959+ 11960+ if (!sys->input->is_enabled && 11961+ (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS) 11962+ { 11963+ msg_Err(vd, "Input port enable failed"); 11964+ goto fail; 11965+ } 11966+ // Stuff into input 11967+ // We assume the BH is already set up with values reflecting pic date etc. 11968+ if (sys->copy_buf != NULL) { 11969+ MMAL_BUFFER_HEADER_T *const buf = sys->copy_buf; 11970+ sys->copy_buf = NULL; 11971+#if TRACE_ALL 11972+ msg_Dbg(vd, "--- %s: Copy stuff", __func__); 11973+#endif 11974+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS) 11975+ { 11976+ mmal_buffer_header_release(buf); 11977+ msg_Err(vd, "Send copy buffer to render input failed"); 11978+ goto fail; 11979+ } 11980 } 11981- 11982- sys->num_buffers = count; 11983- sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers, 11984- sys->input->buffer_size); 11985- if (!sys->pool) { 11986- msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32, 11987- count, sys->input->buffer_size); 11988- goto out; 11989+ else if (sys->isp.pending) { 11990+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q); 11991+ sys->isp.pending = false; 11992+#if TRACE_ALL 11993+ msg_Dbg(vd, "--- %s: ISP stuff", __func__); 11994+#endif 11995+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS) 11996+ { 11997+ mmal_buffer_header_release(buf); 11998+ msg_Err(vd, "Send ISP buffer to render input failed"); 11999+ goto fail; 12000+ } 12001 } 12002- 12003- memset(&picture_res, 0, sizeof(picture_resource_t)); 12004- sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *)); 12005- for (i = 0; i < sys->num_buffers; ++i) { 12006- picture_res.p_sys = calloc(1, sizeof(picture_sys_t)); 12007- picture_res.p_sys->owner = (vlc_object_t *)vd; 12008- picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue); 12009- 12010- sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res); 12011- if (!sys->pictures[i]) { 12012- msg_Err(vd, "Failed to create picture"); 12013- free(picture_res.p_sys); 12014- goto out; 12015+ else 12016+ { 12017+ MMAL_BUFFER_HEADER_T *const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->pool); 12018+ if (pic_buf == NULL) 12019+ { 12020+ msg_Err(vd, "Replicated buffer get fail"); 12021+ goto fail; 12022 } 12023 12024- sys->pictures[i]->i_planes = sys->i_planes; 12025- memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t)); 12026- } 12027 12028- memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t)); 12029- picture_pool_cfg.picture_count = sys->num_buffers; 12030- picture_pool_cfg.picture = sys->pictures; 12031- picture_pool_cfg.lock = mmal_picture_lock; 12032+ // If dimensions have chnaged then fix that 12033+ if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic)) 12034+ { 12035+ msg_Dbg(vd, "Reset port format"); 12036+ 12037+ // HVS can deal with on-line dimension changes 12038+ if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS) 12039+ msg_Warn(vd, "Input format commit failed"); 12040+ } 12041 12042- sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg); 12043- if (!sys->picture_pool) { 12044- msg_Err(vd, "Failed to create picture pool"); 12045- goto out; 12046+ if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS) 12047+ { 12048+ mmal_buffer_header_release(pic_buf); 12049+ msg_Err(vd, "Send buffer to input failed"); 12050+ goto fail; 12051+ } 12052 } 12053 12054-out: 12055- return sys->picture_pool; 12056-} 12057- 12058-static void vd_prepare(vout_display_t *vd, picture_t *picture, 12059- subpicture_t *subpicture) 12060-{ 12061- vout_display_sys_t *sys = vd->sys; 12062- picture_sys_t *pic_sys = picture->p_sys; 12063- 12064- if (!sys->adjust_refresh_rate || pic_sys->displayed) 12065- return; 12066- 12067- /* Apply the required phase_offset to the picture, so that vd_display() 12068- * will be called at the corrected time from the core */ 12069- picture->date += sys->phase_offset; 12070-} 12071- 12072-static void vd_display(vout_display_t *vd, picture_t *picture, 12073- subpicture_t *subpicture) 12074-{ 12075- vout_display_sys_t *sys = vd->sys; 12076- picture_sys_t *pic_sys = picture->p_sys; 12077- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; 12078- MMAL_STATUS_T status; 12079- 12080- if (picture->format.i_frame_rate != sys->i_frame_rate || 12081- picture->format.i_frame_rate_base != sys->i_frame_rate_base || 12082- picture->b_progressive != sys->b_progressive || 12083- picture->b_top_field_first != sys->b_top_field_first) { 12084- sys->b_top_field_first = picture->b_top_field_first; 12085- sys->b_progressive = picture->b_progressive; 12086- sys->i_frame_rate = picture->format.i_frame_rate; 12087- sys->i_frame_rate_base = picture->format.i_frame_rate_base; 12088- configure_display(vd, NULL, &picture->format); 12089- } 12090- 12091- if (!pic_sys->displayed || !sys->opaque) { 12092- buffer->cmd = 0; 12093- buffer->length = sys->input->buffer_size; 12094- buffer->user_data = picture; 12095- 12096- status = mmal_port_send_buffer(sys->input, buffer); 12097- if (status == MMAL_SUCCESS) 12098- atomic_fetch_add(&sys->buffers_in_transit, 1); 12099- 12100- if (status != MMAL_SUCCESS) { 12101- msg_Err(vd, "Failed to send buffer to input port. Frame dropped"); 12102- picture_Release(picture); 12103+ { 12104+ unsigned int sub_no = 0; 12105+ MMAL_BUFFER_HEADER_T **psub_bufs2 = sys->subpic_bufs; 12106+ const bool is_mmal_pic = hw_mmal_pic_is_mmal(p_pic); 12107+ 12108+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { 12109+ int rv; 12110+ MMAL_BUFFER_HEADER_T * const sub_buf = !is_mmal_pic ? NULL : 12111+ hw_mmal_pic_sub_buf_get(p_pic, sub_no); 12112+ 12113+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd), 12114+ sub_buf != NULL ? sub_buf : *psub_bufs2++, 12115+ &sys->subs[sub_no].sub, 12116+ &p_pic->format, 12117+ &sys->dest_rect, 12118+ sys->display_transform, 12119+ p_pic->date)) == 0) 12120+ break; 12121+ else if (rv < 0) 12122+ goto fail; 12123 } 12124- 12125- pic_sys->displayed = true; 12126- } else { 12127- picture_Release(picture); 12128 } 12129 12130- display_subpicture(vd, subpicture); 12131+fail: 12132+ for (unsigned int i = 0; i != SUBS_MAX && sys->subpic_bufs[i] != NULL; ++i) { 12133+ mmal_buffer_header_release(sys->subpic_bufs[i]); 12134+ sys->subpic_bufs[i] = NULL; 12135+ } 12136 12137- if (subpicture) 12138- subpicture_Delete(subpicture); 12139+ picture_Release(p_pic); 12140 12141 if (sys->next_phase_check == 0 && sys->adjust_refresh_rate) 12142 maintain_phase_sync(vd); 12143 sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL; 12144- 12145- if (sys->opaque) { 12146- vlc_mutex_lock(&sys->buffer_mutex); 12147- while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT) 12148- vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex); 12149- vlc_mutex_unlock(&sys->buffer_mutex); 12150- } 12151 } 12152 12153 static int vd_control(vout_display_t *vd, int query, va_list args) 12154 { 12155- vout_display_sys_t *sys = vd->sys; 12156- vout_display_cfg_t cfg; 12157- const vout_display_cfg_t *tmp_cfg; 12158+ vout_display_sys_t * const sys = vd->sys; 12159 int ret = VLC_EGENERIC; 12160+ VLC_UNUSED(args); 12161 12162 switch (query) { 12163- case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: 12164- tmp_cfg = va_arg(args, const vout_display_cfg_t *); 12165- if (tmp_cfg->display.width == sys->display_width && 12166- tmp_cfg->display.height == sys->display_height) { 12167- cfg = *vd->cfg; 12168- cfg.display.width = sys->display_width; 12169- cfg.display.height = sys->display_height; 12170- if (configure_display(vd, &cfg, NULL) >= 0) 12171- ret = VLC_SUCCESS; 12172- } 12173- break; 12174- 12175 case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: 12176 case VOUT_DISPLAY_CHANGE_SOURCE_CROP: 12177- if (configure_display(vd, NULL, &vd->source) >= 0) 12178+ if (configure_display(vd, vd->cfg, &vd->source) >= 0) 12179 ret = VLC_SUCCESS; 12180 break; 12181 12182- case VOUT_DISPLAY_RESET_PICTURES: 12183- vlc_assert_unreachable(); 12184 case VOUT_DISPLAY_CHANGE_ZOOM: 12185- msg_Warn(vd, "Unsupported control query %d", query); 12186+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: 12187+ case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: 12188+ { 12189+ const vout_display_cfg_t * const cfg = va_arg(args, const vout_display_cfg_t *); 12190+ 12191+ if (configure_display(vd, cfg, &vd->source) >= 0) 12192+ ret = VLC_SUCCESS; 12193+ break; 12194+ } 12195+ 12196+ case VOUT_DISPLAY_RESET_PICTURES: 12197+ msg_Warn(vd, "Reset Pictures"); 12198+ kill_pool(sys); 12199+ vd->fmt = vd->source; // Take (nearly) whatever source wants to give us 12200+ vd->fmt.i_chroma = req_chroma(vd); // Adjust chroma to something we can actaully deal with 12201+ ret = VLC_SUCCESS; 12202+ break; 12203+ 12204+ case VOUT_DISPLAY_CHANGE_MMAL_HIDE: 12205+ { 12206+ MMAL_STATUS_T err; 12207+ unsigned int i; 12208+ 12209+ msg_Dbg(vd, "Hide display"); 12210+ 12211+ for (i = 0; i != SUBS_MAX; ++i) 12212+ hw_mmal_subpic_flush(VLC_OBJECT(vd), &sys->subs[i].sub); 12213+ 12214+ if (sys->input->is_enabled && 12215+ (err = mmal_port_disable(sys->input)) != MMAL_SUCCESS) 12216+ { 12217+ msg_Err(vd, "Unable to disable port: err=%d", err); 12218+ break; 12219+ } 12220+ sys->force_config = true; 12221+ ret = VLC_SUCCESS; 12222 break; 12223+ } 12224 12225 default: 12226 msg_Warn(vd, "Unknown control query %d", query); 12227@@ -653,79 +971,207 @@ static int vd_control(vout_display_t *vd 12228 return ret; 12229 } 12230 12231+static void set_display_windows(vout_display_t *const vd, vout_display_sys_t *const sys) 12232+{ 12233+ unsigned int width, height; 12234+ if (query_resolution(vd, sys->display_id, &width, &height) < 0) { 12235+ width = vd->cfg->display.width; 12236+ height = vd->cfg->display.height; 12237+ } 12238+ sys->display_rect = (MMAL_RECT_T){0, 0, width, height}; 12239+ 12240+ sys->win_rect = (sys->req_win.width != 0) ? 12241+ sys->req_win : 12242+ is_transform_transpose(sys->display_transform) ? 12243+ rect_transpose(sys->display_rect) : sys->display_rect; 12244+} 12245+ 12246 static void vd_manage(vout_display_t *vd) 12247 { 12248- vout_display_sys_t *sys = vd->sys; 12249- unsigned width, height; 12250+ vout_display_sys_t *const sys = vd->sys; 12251 12252 vlc_mutex_lock(&sys->manage_mutex); 12253 12254 if (sys->need_configure_display) { 12255- close_dmx(vd); 12256- sys->dmx_handle = vc_dispmanx_display_open(0); 12257- 12258- if (query_resolution(vd, &width, &height) >= 0) { 12259- sys->display_width = width; 12260- sys->display_height = height; 12261- vout_display_SendEventDisplaySize(vd, width, height); 12262- } 12263- 12264 sys->need_configure_display = false; 12265+ set_display_windows(vd, sys); 12266 } 12267 12268 vlc_mutex_unlock(&sys->manage_mutex); 12269 } 12270 12271-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 12272+ 12273+static int attach_subpics(vout_display_t * const vd, vout_display_sys_t * const sys, 12274+ subpicture_t * const subpicture) 12275 { 12276- vout_display_t *vd = (vout_display_t *)port->userdata; 12277- MMAL_STATUS_T status; 12278+ unsigned int n = 0; 12279 12280- if (buffer->cmd == MMAL_EVENT_ERROR) { 12281- status = *(uint32_t *)buffer->data; 12282- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); 12283+ if (sys->vzc == NULL) { 12284+ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL) 12285+ { 12286+ msg_Err(vd, "Failed to allocate VZC"); 12287+ return VLC_ENOMEM; 12288+ } 12289 } 12290 12291- mmal_buffer_header_release(buffer); 12292+ // Attempt to import the subpics 12293+ for (subpicture_t * spic = subpicture; spic != NULL; spic = spic->p_next) 12294+ { 12295+ for (subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) { 12296+ picture_t *const src = sreg->p_picture; 12297+ 12298+#if TRACE_ALL 12299+ char dbuf0[5]; 12300+ msg_Dbg(vd, " [%p:%p] Pos=%d,%d max=%dx%d, src=%dx%d/%dx%d o:%d, spu=%d,%d:%dx%d, vd->fmt=%dx%d/%dx%d, vd->source=%dx%d/%dx%d, cfg=%dx%d, zoom=%d/%d, Alpha=%d, Fmt=%s", src, src->p[0].p_pixels, 12301+ sreg->i_x, sreg->i_y, 12302+ sreg->i_max_width, sreg->i_max_height, 12303+ src->format.i_visible_width, src->format.i_visible_height, 12304+ src->format.i_width, src->format.i_height, 12305+ src->format.orientation, 12306+ sys->spu_rect.x, sys->spu_rect.y, sys->spu_rect.width, sys->spu_rect.height, 12307+ vd->fmt.i_visible_width, vd->fmt.i_visible_height, 12308+ vd->fmt.i_width, vd->fmt.i_height, 12309+ vd->source.i_visible_width, vd->source.i_visible_height, 12310+ vd->source.i_width, vd->source.i_height, 12311+ vd->cfg->display.width, vd->cfg->display.height, 12312+ vd->cfg->zoom.num, vd->cfg->zoom.den, 12313+ sreg->i_alpha, 12314+ str_fourcc(dbuf0, src->format.i_chroma)); 12315+#endif 12316+ 12317+ // At this point I think the subtitles are being placed in the 12318+ // coord space of the placed rectangle in the cfg display space 12319+ if ((sys->subpic_bufs[n] = hw_mmal_vzc_buf_from_pic(sys->vzc, 12320+ src, 12321+ (MMAL_RECT_T){.width = sys->spu_rect.width, .height=sys->spu_rect.height}, 12322+ sreg->i_x, sreg->i_y, 12323+ sreg->i_alpha, 12324+ n == 0)) == NULL) 12325+ { 12326+ msg_Err(vd, "Failed to allocate vzc buffer for subpic"); 12327+ return VLC_ENOMEM; 12328+ } 12329+ 12330+ if (++n == SUBS_MAX) 12331+ return VLC_SUCCESS; 12332+ } 12333+ } 12334+ return VLC_SUCCESS; 12335 } 12336 12337-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 12338+ 12339+static void vd_prepare(vout_display_t *vd, picture_t *p_pic, 12340+#if VLC_VER_3 12341+ subpicture_t *subpicture 12342+#else 12343+ subpicture_t *subpicture, vlc_tick_t date 12344+#endif 12345+ ) 12346 { 12347- vout_display_t *vd = (vout_display_t *)port->userdata; 12348+ MMAL_STATUS_T err; 12349+ vout_display_sys_t * const sys = vd->sys; 12350+ 12351+ vd_manage(vd); 12352+ 12353+ if (!check_shape(vd, p_pic)) 12354+ return; 12355+ 12356+ if (sys->force_config || 12357+ p_pic->format.i_frame_rate != sys->i_frame_rate || 12358+ p_pic->format.i_frame_rate_base != sys->i_frame_rate_base || 12359+ p_pic->b_progressive != sys->b_progressive || 12360+ p_pic->b_top_field_first != sys->b_top_field_first) 12361+ { 12362+ sys->force_config = false; 12363+ sys->b_top_field_first = p_pic->b_top_field_first; 12364+ sys->b_progressive = p_pic->b_progressive; 12365+ sys->i_frame_rate = p_pic->format.i_frame_rate; 12366+ sys->i_frame_rate_base = p_pic->format.i_frame_rate_base; 12367+ configure_display(vd, NULL, &vd->source); 12368+ } 12369+ 12370+ // Subpics can either turn up attached to the main pic or in the 12371+ // subpic list here - if they turn up here then process into temp 12372+ // buffers 12373+ if (subpicture != NULL) { 12374+ attach_subpics(vd, sys, subpicture); 12375+ } 12376+ 12377+ // ***** 12378+ if (want_copy(vd)) { 12379+ if (sys->copy_buf != NULL) { 12380+ msg_Err(vd, "Copy buf not NULL"); 12381+ mmal_buffer_header_release(sys->copy_buf); 12382+ sys->copy_buf = NULL; 12383+ } 12384+ 12385+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->copy_pool->queue); 12386+ // Copy 2d 12387+ hw_mmal_copy_pic_to_buf(buf->data, &buf->length, sys->input->format, p_pic); 12388+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; 12389+ 12390+ sys->copy_buf = buf; 12391+ } 12392+ 12393+ if (isp_check(vd, sys) != MMAL_SUCCESS) { 12394+ return; 12395+ } 12396+ 12397+ if (want_isp(vd)) 12398+ { 12399+ struct vout_isp_conf_s * const isp = &sys->isp; 12400+ MMAL_BUFFER_HEADER_T * buf; 12401+ 12402+ // This should be empty - make it so if it isn't 12403+ isp_empty_out_q(isp); 12404+ isp->pending = false; 12405+ 12406+ // Stuff output 12407+ if (isp_prepare(vd, isp) != MMAL_SUCCESS) 12408+ return; 12409+ 12410+ if ((buf = hw_mmal_pic_buf_replicated(p_pic, isp->in_pool)) == NULL) 12411+ { 12412+ msg_Err(vd, "Pic has no attached buffer"); 12413+ return; 12414+ } 12415+ 12416+ if ((err = mmal_port_send_buffer(isp->input, buf)) != MMAL_SUCCESS) 12417+ { 12418+ msg_Err(vd, "Send buffer to input failed"); 12419+ mmal_buffer_header_release(buf); 12420+ return; 12421+ } 12422+ 12423+ isp->pending = true; 12424+ } 12425+ 12426+#if 0 12427+ VLC_UNUSED(date); 12428 vout_display_sys_t *sys = vd->sys; 12429- picture_t *picture = (picture_t *)buffer->user_data; 12430+ picture_sys_t *pic_sys = picture->p_sys; 12431 12432- if (picture) 12433- picture_Release(picture); 12434+ if (!sys->adjust_refresh_rate || pic_sys->displayed) 12435+ return; 12436 12437- vlc_mutex_lock(&sys->buffer_mutex); 12438- atomic_fetch_sub(&sys->buffers_in_transit, 1); 12439- vlc_cond_signal(&sys->buffer_cond); 12440- vlc_mutex_unlock(&sys->buffer_mutex); 12441+ /* Apply the required phase_offset to the picture, so that vd_display() 12442+ * will be called at the corrected time from the core */ 12443+ picture->date += sys->phase_offset; 12444+#endif 12445 } 12446 12447-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height) 12448+ 12449+static void vd_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) 12450 { 12451- TV_DISPLAY_STATE_T display_state; 12452- int ret = 0; 12453+ vout_display_t *vd = (vout_display_t *)port->userdata; 12454+ MMAL_STATUS_T status; 12455 12456- if (vc_tv_get_display_state(&display_state) == 0) { 12457- if (display_state.state & 0xFF) { 12458- *width = display_state.display.hdmi.width; 12459- *height = display_state.display.hdmi.height; 12460- } else if (display_state.state & 0xFF00) { 12461- *width = display_state.display.sdtv.width; 12462- *height = display_state.display.sdtv.height; 12463- } else { 12464- msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state); 12465- ret = -1; 12466- } 12467- } else { 12468- msg_Warn(vd, "Failed to query display resolution"); 12469- ret = -1; 12470+ if (buffer->cmd == MMAL_EVENT_ERROR) { 12471+ status = *(uint32_t *)buffer->data; 12472+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); 12473 } 12474 12475- return ret; 12476+ mmal_buffer_header_release(buffer); 12477 } 12478 12479 static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2) 12480@@ -780,9 +1226,9 @@ static void adjust_refresh_rate(vout_dis 12481 double best_score, score; 12482 int i; 12483 12484- vc_tv_get_display_state(&display_state); 12485+ vc_tv_get_display_state_id(sys->display_id, &display_state); 12486 if(display_state.display.hdmi.mode != HDMI_MODE_OFF) { 12487- num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group, 12488+ num_modes = vc_tv_hdmi_get_supported_modes_new_id(sys->display_id, display_state.display.hdmi.group, 12489 supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL); 12490 12491 for (i = 0; i < num_modes; ++i) { 12492@@ -810,7 +1256,7 @@ static void adjust_refresh_rate(vout_dis 12493 if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) { 12494 msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32, 12495 supported_modes[best_id].frame_rate); 12496- vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, 12497+ vc_tv_hdmi_power_on_explicit_new_id(sys->display_id, HDMI_MODE_HDMI, 12498 supported_modes[best_id].group, 12499 supported_modes[best_id].code); 12500 } 12501@@ -828,148 +1274,12 @@ static void adjust_refresh_rate(vout_dis 12502 } 12503 } 12504 12505-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture) 12506-{ 12507- vout_display_sys_t *sys = vd->sys; 12508- struct dmx_region_t **dmx_region = &sys->dmx_region; 12509- struct dmx_region_t *unused_dmx_region; 12510- DISPMANX_UPDATE_HANDLE_T update = 0; 12511- picture_t *picture; 12512- video_format_t *fmt; 12513- struct dmx_region_t *dmx_region_next; 12514- 12515- if(subpicture) { 12516- subpicture_region_t *region = subpicture->p_region; 12517- while(region) { 12518- picture = region->p_picture; 12519- fmt = ®ion->fmt; 12520- 12521- if(!*dmx_region) { 12522- if(!update) 12523- update = vc_dispmanx_update_start(10); 12524- *dmx_region = dmx_region_new(vd, update, region); 12525- } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) || 12526- ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) || 12527- ((*dmx_region)->pos_x != region->i_x) || 12528- ((*dmx_region)->pos_y != region->i_y) || 12529- ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) { 12530- dmx_region_next = (*dmx_region)->next; 12531- if(!update) 12532- update = vc_dispmanx_update_start(10); 12533- dmx_region_delete(*dmx_region, update); 12534- *dmx_region = dmx_region_new(vd, update, region); 12535- (*dmx_region)->next = dmx_region_next; 12536- } else if((*dmx_region)->picture != picture) { 12537- if(!update) 12538- update = vc_dispmanx_update_start(10); 12539- dmx_region_update(*dmx_region, update, picture); 12540- } 12541- 12542- dmx_region = &(*dmx_region)->next; 12543- region = region->p_next; 12544- } 12545- } 12546- 12547- /* Remove remaining regions */ 12548- unused_dmx_region = *dmx_region; 12549- while(unused_dmx_region) { 12550- dmx_region_next = unused_dmx_region->next; 12551- if(!update) 12552- update = vc_dispmanx_update_start(10); 12553- dmx_region_delete(unused_dmx_region, update); 12554- unused_dmx_region = dmx_region_next; 12555- } 12556- *dmx_region = NULL; 12557- 12558- if(update) 12559- vc_dispmanx_update_submit_sync(update); 12560-} 12561- 12562-static void close_dmx(vout_display_t *vd) 12563-{ 12564- vout_display_sys_t *sys = vd->sys; 12565- DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10); 12566- struct dmx_region_t *dmx_region = sys->dmx_region; 12567- struct dmx_region_t *dmx_region_next; 12568- 12569- while(dmx_region) { 12570- dmx_region_next = dmx_region->next; 12571- dmx_region_delete(dmx_region, update); 12572- dmx_region = dmx_region_next; 12573- } 12574- 12575- vc_dispmanx_update_submit_sync(update); 12576- sys->dmx_region = NULL; 12577- 12578- show_background(vd, false); 12579- 12580- vc_dispmanx_display_close(sys->dmx_handle); 12581- sys->dmx_handle = DISPMANX_NO_HANDLE; 12582-} 12583- 12584-static struct dmx_region_t *dmx_region_new(vout_display_t *vd, 12585- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region) 12586-{ 12587- vout_display_sys_t *sys = vd->sys; 12588- video_format_t *fmt = ®ion->fmt; 12589- struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t)); 12590- uint32_t image_handle; 12591- 12592- dmx_region->pos_x = region->i_x; 12593- dmx_region->pos_y = region->i_y; 12594- 12595- vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width, 12596- fmt->i_visible_height); 12597- vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16, 12598- fmt->i_visible_height << 16); 12599- vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y, 12600- fmt->i_visible_width, fmt->i_visible_height); 12601- 12602- dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 12603- dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16), 12604- dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16), 12605- &image_handle); 12606- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32, 12607- region->p_picture->p[0].i_pitch, 12608- region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect); 12609- 12610- dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX; 12611- dmx_region->alpha.opacity = region->i_alpha; 12612- dmx_region->alpha.mask = DISPMANX_NO_HANDLE; 12613- dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle, 12614- sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource, 12615- &dmx_region->src_rect, DISPMANX_PROTECTION_NONE, 12616- &dmx_region->alpha, NULL, VC_IMAGE_ROT0); 12617- 12618- dmx_region->next = NULL; 12619- dmx_region->picture = region->p_picture; 12620- 12621- return dmx_region; 12622-} 12623- 12624-static void dmx_region_update(struct dmx_region_t *dmx_region, 12625- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture) 12626-{ 12627- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32, 12628- picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect); 12629- vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource); 12630- dmx_region->picture = picture; 12631-} 12632- 12633-static void dmx_region_delete(struct dmx_region_t *dmx_region, 12634- DISPMANX_UPDATE_HANDLE_T update) 12635-{ 12636- vc_dispmanx_element_remove(update, dmx_region->element); 12637- vc_dispmanx_resource_delete(dmx_region->resource); 12638- free(dmx_region); 12639-} 12640- 12641 static void maintain_phase_sync(vout_display_t *vd) 12642 { 12643 MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = { 12644 .hdr = { MMAL_PARAMETER_VIDEO_RENDER_STATS, sizeof(render_stats) }, 12645 }; 12646- int32_t frame_duration = 1000000 / 12647+ int32_t frame_duration = CLOCK_FREQ / 12648 ((double)vd->sys->i_frame_rate / 12649 vd->sys->i_frame_rate_base); 12650 vout_display_sys_t *sys = vd->sys; 12651@@ -1012,32 +1322,436 @@ static void maintain_phase_sync(vout_dis 12652 } 12653 } 12654 12655-static void show_background(vout_display_t *vd, bool enable) 12656+static void CloseMmalVout(vlc_object_t *object) 12657 { 12658- vout_display_sys_t *sys = vd->sys; 12659- uint32_t image_ptr, color = 0xFF000000; 12660- VC_RECT_T dst_rect, src_rect; 12661- DISPMANX_UPDATE_HANDLE_T update; 12662- 12663- if (enable && !sys->bkg_element) { 12664- sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1, 12665- &image_ptr); 12666- vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1); 12667- vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32, 12668- sizeof(color), &color, &dst_rect); 12669- vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16); 12670- vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0); 12671- update = vc_dispmanx_update_start(0); 12672- sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle, 12673- sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect, 12674- DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0); 12675- vc_dispmanx_update_submit_sync(update); 12676- } else if (!enable && sys->bkg_element) { 12677- update = vc_dispmanx_update_start(0); 12678- vc_dispmanx_element_remove(update, sys->bkg_element); 12679- vc_dispmanx_resource_delete(sys->bkg_resource); 12680- vc_dispmanx_update_submit_sync(update); 12681- sys->bkg_element = DISPMANX_NO_HANDLE; 12682- sys->bkg_resource = DISPMANX_NO_HANDLE; 12683+ vout_display_t * const vd = (vout_display_t *)object; 12684+ vout_display_sys_t * const sys = vd->sys; 12685+ char response[20]; /* answer is hvs_update_fields=%1d */ 12686+ 12687+#if TRACE_ALL 12688+ msg_Dbg(vd, "<<< %s", __func__); 12689+#endif 12690+ 12691+ kill_pool(sys); 12692+ 12693+ vc_tv_unregister_callback_full(tvservice_cb, vd); 12694+ 12695+ // Shouldn't be anything here - but just in case 12696+ for (unsigned int i = 0; i != SUBS_MAX; ++i) 12697+ if (sys->subpic_bufs[i] != NULL) 12698+ mmal_buffer_header_release(sys->subpic_bufs[i]); 12699+ 12700+ for (unsigned int i = 0; i != SUBS_MAX; ++i) { 12701+ vout_subpic_t * const sub = sys->subs + i; 12702+ if (sub->component != NULL) { 12703+ hw_mmal_subpic_close(VLC_OBJECT(vd), &sub->sub); 12704+ if (sub->component->control->is_enabled) 12705+ mmal_port_disable(sub->component->control); 12706+ if (sub->component->is_enabled) 12707+ mmal_component_disable(sub->component); 12708+ mmal_component_release(sub->component); 12709+ sub->component = NULL; 12710+ } 12711 } 12712+ 12713+ if (sys->input && sys->input->is_enabled) 12714+ mmal_port_disable(sys->input); 12715+ 12716+ if (sys->component && sys->component->control->is_enabled) 12717+ mmal_port_disable(sys->component->control); 12718+ 12719+ if (sys->copy_buf != NULL) 12720+ mmal_buffer_header_release(sys->copy_buf); 12721+ 12722+ if (sys->input != NULL && sys->copy_pool != NULL) 12723+ mmal_port_pool_destroy(sys->input, sys->copy_pool); 12724+ 12725+ if (sys->component && sys->component->is_enabled) 12726+ mmal_component_disable(sys->component); 12727+ 12728+ if (sys->pool) 12729+ mmal_pool_destroy(sys->pool); 12730+ 12731+ if (sys->component) 12732+ mmal_component_release(sys->component); 12733+ 12734+ isp_close(vd, sys); 12735+ 12736+ hw_mmal_vzc_pool_release(sys->vzc); 12737+ 12738+ vlc_mutex_destroy(&sys->manage_mutex); 12739+ 12740+ if (sys->native_interlaced) { 12741+ if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 || 12742+ response[18] != '0') 12743+ msg_Warn(vd, "Could not reset hvs field mode"); 12744+ } 12745+ 12746+ cma_vcsm_exit(sys->init_type);; 12747+ 12748+ free(sys); 12749+ 12750+#if TRACE_ALL 12751+ msg_Dbg(vd, ">>> %s", __func__); 12752+#endif 12753+} 12754+ 12755+ 12756+static const struct { 12757+ const char * name; 12758+ int num; 12759+} display_name_to_num[] = { 12760+ {"auto", -1}, 12761+ {"hdmi-1", DISPMANX_ID_HDMI0}, 12762+ {"hdmi-2", DISPMANX_ID_HDMI1}, 12763+ {NULL, -2} 12764+}; 12765+ 12766+static const struct { 12767+ const char * name; 12768+ int transform_num; 12769+} transform_name_to_num[] = { 12770+ {"auto", -1}, 12771+ {"0", MMAL_DISPLAY_ROT0}, 12772+ {"hflip", MMAL_DISPLAY_MIRROR_ROT0}, 12773+ {"vflip", MMAL_DISPLAY_MIRROR_ROT180}, 12774+ {"180", MMAL_DISPLAY_ROT180}, 12775+ {"transpose", MMAL_DISPLAY_MIRROR_ROT90}, 12776+ {"270", MMAL_DISPLAY_ROT270}, 12777+ {"90", MMAL_DISPLAY_ROT90}, 12778+ {"antitranspose", MMAL_DISPLAY_MIRROR_ROT270}, 12779+ {NULL, -2} 12780+}; 12781+ 12782+static int find_display_num(const char * const name) 12783+{ 12784+ unsigned int i; 12785+ for (i = 0; display_name_to_num[i].name != NULL && strcasecmp(display_name_to_num[i].name, name) != 0; ++i) 12786+ /* Loop */; 12787+ return display_name_to_num[i].num; 12788+} 12789+ 12790+static int find_transform_num(const char * const name) 12791+{ 12792+ unsigned int i; 12793+ for (i = 0; transform_name_to_num[i].name != NULL && strcasecmp(transform_name_to_num[i].name, name) != 0; ++i) 12794+ /* Loop */; 12795+ return transform_name_to_num[i].transform_num; 12796+} 12797+ 12798+#if HAVE_X11_XLIB_H 12799+#include <X11/Xlib.h> 12800+#include <X11/extensions/Xrandr.h> 12801+static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd) 12802+{ 12803+ Display * const x = XOpenDisplay(NULL); 12804+ Rotation cur_rot = 0; 12805+ MMAL_DISPLAYTRANSFORM_T trans; 12806+ 12807+ if (x == NULL) 12808+ return MMAL_DISPLAY_ROT0; 12809+ 12810+ XRRRotations(x, 0, &cur_rot); 12811+ XCloseDisplay(x); 12812+ 12813+ // Convert to MMAL 12814+ // xrandr seems to rotate the other way to mmal 12815+ 12816+ switch (cur_rot) 12817+ { 12818+ case 0: 12819+ case RR_Rotate_0: 12820+ trans = MMAL_DISPLAY_ROT0; 12821+ break; 12822+ case RR_Rotate_90: 12823+ trans = MMAL_DISPLAY_ROT270; 12824+ break; 12825+ case RR_Rotate_180: 12826+ trans = MMAL_DISPLAY_ROT180; 12827+ break; 12828+ case RR_Rotate_270: 12829+ trans = MMAL_DISPLAY_ROT90; 12830+ break; 12831+ case RR_Reflect_X: 12832+ trans = MMAL_DISPLAY_MIRROR_ROT0; 12833+ break; 12834+ case RR_Reflect_Y: 12835+ trans = MMAL_DISPLAY_MIRROR_ROT180; 12836+ break; 12837+ default: 12838+ msg_Info(vd, "Unexpected X rotation value: %#x", cur_rot); 12839+ trans = MMAL_DISPLAY_ROT0; 12840+ break; 12841+ } 12842+ 12843+ return trans; 12844+} 12845+#else 12846+static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd) 12847+{ 12848+ VLC_UNUSED(vd); 12849+ return MMAL_DISPLAY_ROT0; 12850+} 12851+#endif 12852+ 12853+static MMAL_RECT_T str_to_rect(const char * s) 12854+{ 12855+ MMAL_RECT_T rect = {0}; 12856+ rect.width = strtoul(s, (char**)&s, 0); 12857+ if (*s == '\0') 12858+ return rect; 12859+ if (*s++ != 'x') 12860+ goto fail; 12861+ rect.height = strtoul(s, (char**)&s, 0); 12862+ if (*s == '\0') 12863+ return rect; 12864+ if (*s++ != '+') 12865+ goto fail; 12866+ rect.x = strtoul(s, (char**)&s, 0); 12867+ if (*s == '\0') 12868+ return rect; 12869+ if (*s++ != '+') 12870+ goto fail; 12871+ rect.y = strtoul(s, (char**)&s, 0); 12872+ if (*s != '\0') 12873+ goto fail; 12874+ return rect; 12875+ 12876+fail: 12877+ return (MMAL_RECT_T){0,0,0,0}; 12878+} 12879+ 12880+static int OpenMmalVout(vlc_object_t *object) 12881+{ 12882+ vout_display_t *vd = (vout_display_t *)object; 12883+ vout_display_sys_t *sys; 12884+ MMAL_STATUS_T status; 12885+ int ret = VLC_EGENERIC; 12886+ // At the moment all copy is via I420 12887+ const bool needs_copy = !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma); 12888+ const MMAL_FOURCC_T enc_in = needs_copy ? MMAL_ENCODING_I420 : 12889+ vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma); 12890+ 12891+#if TRACE_ALL 12892+ msg_Dbg(vd, "<<< %s: o:%d", __func__, (int)vd->fmt.orientation); 12893+#endif 12894+ 12895+ get_xrandr_rotation(vd); 12896+ 12897+ sys = calloc(1, sizeof(struct vout_display_sys_t)); 12898+ if (!sys) 12899+ return VLC_ENOMEM; 12900+ vd->sys = sys; 12901+ 12902+ vlc_mutex_init(&sys->manage_mutex); 12903+ 12904+ if ((sys->init_type = cma_vcsm_init()) == VCSM_INIT_NONE) 12905+ { 12906+ msg_Err(vd, "VCSM init fail"); 12907+ goto fail; 12908+ } 12909+ 12910+ vc_tv_register_callback(tvservice_cb, vd); 12911+ 12912+ sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); 12913+ sys->transparent = var_InheritBool(vd, MMAL_VOUT_TRANSPARENT_NAME); 12914+ 12915+ { 12916+ const char *display_name = var_InheritString(vd, MMAL_DISPLAY_NAME); 12917+ int qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber" ); 12918+ int display_id = find_display_num(display_name); 12919+// sys->display_id = display_id < 0 ? vc_tv_get_default_display_id() : display_id; 12920+ sys->display_id = display_id >= 0 ? display_id : 12921+ qt_num == 1 ? DISPMANX_ID_HDMI1 : DISPMANX_ID_HDMI; 12922+ if (display_id < -1) 12923+ msg_Warn(vd, "Unknown display device: '%s'", display_name); 12924+ else 12925+ msg_Dbg(vd, "Display device: %s, qt=%d id=%d display=%d", display_name, 12926+ qt_num, display_id, sys->display_id); 12927+ } 12928+ 12929+ { 12930+ const char *window_str = var_InheritString(vd, MMAL_VOUT_WINDOW_NAME); 12931+ sys->req_win = str_to_rect(window_str); 12932+ if (sys->req_win.width != 0) 12933+ msg_Dbg(vd, "Window: %dx%d @ %d,%d", 12934+ sys->req_win.width, sys->req_win.height, 12935+ sys->req_win.x, sys->req_win.y); 12936+ } 12937+ 12938+ { 12939+ const char *transform_name = var_InheritString(vd, MMAL_VOUT_TRANSFORM_NAME); 12940+ int transform_num = find_transform_num(transform_name); 12941+ sys->display_transform = transform_num < 0 ? 12942+ get_xrandr_rotation(vd) : 12943+ (MMAL_DISPLAYTRANSFORM_T)transform_num; 12944+ 12945+ if (transform_num < -1) 12946+ msg_Warn(vd, "Unknown vout transform: '%s'", transform_name); 12947+ else 12948+ msg_Dbg(vd, "Display transform: %s, mmal_display_transform=%d", 12949+ transform_name, (int)sys->display_transform); 12950+ 12951+ sys->video_transform = combine_transform( 12952+ vlc_to_mmal_transform(vd->fmt.orientation), sys->display_transform); 12953+ sys->dest_transform = transform_inverse(sys->display_transform); 12954+ } 12955+ 12956+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component); 12957+ if (status != MMAL_SUCCESS) { 12958+ msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)", 12959+ MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status)); 12960+ goto fail; 12961+ } 12962+ 12963+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; 12964+ status = mmal_port_enable(sys->component->control, vd_control_port_cb); 12965+ if (status != MMAL_SUCCESS) { 12966+ msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)", 12967+ sys->component->control->name, status, mmal_status_to_string(status)); 12968+ goto fail; 12969+ } 12970+ 12971+ sys->input = sys->component->input[0]; 12972+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd; 12973+ 12974+ sys->input->format->encoding = enc_in; 12975+ sys->input->format->encoding_variant = 0; 12976+ sys->i_planes = 1; 12977+ 12978+ display_set_format(vd, sys->input->format, want_isp(vd)); 12979+ 12980+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true); 12981+ if (status != MMAL_SUCCESS) { 12982+ msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", 12983+ sys->input->name, status, mmal_status_to_string(status)); 12984+ goto fail; 12985+ } 12986+ 12987+ status = mmal_port_format_commit(sys->input); 12988+ if (status != MMAL_SUCCESS) { 12989+ msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)", 12990+ sys->input->name, status, mmal_status_to_string(status)); 12991+ goto fail; 12992+ } 12993+ 12994+ sys->input->buffer_size = sys->input->buffer_size_recommended; 12995+ 12996+ if (!needs_copy) { 12997+ sys->input->buffer_num = 30; 12998+ } 12999+ else { 13000+ sys->input->buffer_num = 2; 13001+ if ((sys->copy_pool = mmal_port_pool_create(sys->input, 2, sys->input->buffer_size)) == NULL) 13002+ { 13003+ msg_Err(vd, "Cannot create copy pool"); 13004+ goto fail; 13005+ } 13006+ } 13007+ 13008+ set_display_windows(vd, sys); 13009+ 13010+ configure_display(vd, vd->cfg, &vd->source); 13011+ 13012+ status = mmal_port_enable(sys->input, vd_input_port_cb); 13013+ if (status != MMAL_SUCCESS) { 13014+ msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)", 13015+ sys->input->name, status, mmal_status_to_string(status)); 13016+ goto fail; 13017+ } 13018+ 13019+ status = mmal_component_enable(sys->component); 13020+ if (status != MMAL_SUCCESS) { 13021+ msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)", 13022+ sys->component->name, status, mmal_status_to_string(status)); 13023+ goto fail; 13024+ } 13025+ 13026+ if ((sys->pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) 13027+ { 13028+ msg_Err(vd, "Failed to create input pool"); 13029+ goto fail; 13030+ } 13031+ 13032+ for (unsigned int i = 0; i != SUBS_MAX; ++i) { 13033+ vout_subpic_t * const sub = sys->subs + i; 13034+ if ((status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sub->component)) != MMAL_SUCCESS) 13035+ { 13036+ msg_Dbg(vd, "Failed to create subpic component %d", i); 13037+ goto fail; 13038+ } 13039+ sub->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; 13040+ if ((status = mmal_port_enable(sub->component->control, vd_control_port_cb)) != MMAL_SUCCESS) { 13041+ msg_Err(vd, "Failed to enable control port %s on sub %d (status=%"PRIx32" %s)", 13042+ sys->component->control->name, i, status, mmal_status_to_string(status)); 13043+ goto fail; 13044+ } 13045+ if ((status = hw_mmal_subpic_open(VLC_OBJECT(vd), &sub->sub, sub->component->input[0], 13046+ sys->display_id, sys->layer + i + 1)) != MMAL_SUCCESS) { 13047+ msg_Dbg(vd, "Failed to open subpic %d", i); 13048+ goto fail; 13049+ } 13050+ if ((status = mmal_component_enable(sub->component)) != MMAL_SUCCESS) 13051+ { 13052+ msg_Dbg(vd, "Failed to enable subpic component %d", i); 13053+ goto fail; 13054+ } 13055+ } 13056+ 13057+ // If we can't deal with it directly ask for I420 13058+ vd->fmt.i_chroma = req_chroma(vd); 13059+ 13060+ vd->info = (vout_display_info_t){ 13061+ .is_slow = false, 13062+ .has_double_click = false, 13063+ .needs_hide_mouse = false, 13064+ .has_pictures_invalid = true, 13065+ .subpicture_chromas = hw_mmal_vzc_subpicture_chromas 13066+ }; 13067+ 13068+ vd->pool = vd_pool; 13069+ vd->prepare = vd_prepare; 13070+ vd->display = vd_display; 13071+ vd->control = vd_control; 13072+ 13073+ 13074+ msg_Dbg(vd, ">>> %s: ok", __func__); 13075+ return VLC_SUCCESS; 13076+ 13077+fail: 13078+ CloseMmalVout(object); 13079+ 13080+ msg_Dbg(vd, ">>> %s: rv=%d", __func__, ret); 13081+ 13082+ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret; 13083 } 13084+ 13085+vlc_module_begin() 13086+ 13087+ add_submodule() 13088+ 13089+ set_shortname(N_("MMAL vout")) 13090+ set_description(N_("MMAL-based vout plugin for Raspberry Pi")) 13091+ set_capability("vout display", 16) // 1 point better than ASCII art 13092+ add_shortcut("mmal_vout") 13093+ set_category( CAT_VIDEO ) 13094+ set_subcategory( SUBCAT_VIDEO_VOUT ) 13095+ 13096+ add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false) 13097+ add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT, 13098+ MMAL_ADJUST_REFRESHRATE_LONGTEXT, false) 13099+ add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT, 13100+ MMAL_NATIVE_INTERLACE_LONGTEXT, false) 13101+ add_string(MMAL_DISPLAY_NAME, "auto", MMAL_DISPLAY_TEXT, 13102+ MMAL_DISPLAY_LONGTEXT, false) 13103+ add_string(MMAL_VOUT_TRANSFORM_NAME, "auto", MMAL_VOUT_TRANSFORM_TEXT, 13104+ MMAL_VOUT_TRANSFORM_LONGTEXT, false) 13105+ add_string(MMAL_VOUT_WINDOW_NAME, "fullscreen", MMAL_VOUT_WINDOW_TEXT, 13106+ MMAL_VOUT_WINDOW_LONGTEXT, false) 13107+ add_bool(MMAL_VOUT_TRANSPARENT_NAME, false, MMAL_VOUT_TRANSPARENT_TEXT, 13108+ MMAL_VOUT_TRANSPARENT_LONGTEXT, false) 13109+ set_callbacks(OpenMmalVout, CloseMmalVout) 13110+ 13111+vlc_module_end() 13112+ 13113+ 13114--- /dev/null 13115+++ b/modules/hw/mmal/xsplitter.c 13116@@ -0,0 +1,584 @@ 13117+#ifdef HAVE_CONFIG_H 13118+#include "config.h" 13119+#endif 13120+ 13121+#include <stdatomic.h> 13122+ 13123+#include <vlc_common.h> 13124+#include <vlc_plugin.h> 13125+#include <vlc_threads.h> 13126+#include <vlc_vout_display.h> 13127+#include <vlc_modules.h> 13128+ 13129+#include <bcm_host.h> 13130+#include <interface/mmal/mmal.h> 13131+#include <interface/mmal/util/mmal_util.h> 13132+#include <interface/mmal/util/mmal_default_components.h> 13133+ 13134+#include "mmal_picture.h" 13135+ 13136+#define TRACE_ALL 0 13137+ 13138+typedef struct display_desc_s 13139+{ 13140+ vout_display_t * vout; 13141+ unsigned int max_pels; 13142+} display_desc_t; 13143+ 13144+typedef struct mmal_x11_sys_s 13145+{ 13146+ bool use_mmal; 13147+ display_desc_t * cur_desc; 13148+ display_desc_t mmal_desc; 13149+ display_desc_t x_desc; 13150+ uint32_t changed; 13151+ vlc_fourcc_t subpicture_chromas[16]; 13152+} mmal_x11_sys_t; 13153+ 13154+#define MAX_GL_PELS (1920*1080) 13155+#define MAX_MMAL_PELS (4096*4096) // Should never be hit 13156+ 13157+#if 0 13158+// Gen prog for the following table 13159+// Not done inline in case we end up pulling in FP libs we don't want 13160+#include <math.h> 13161+#include <stdio.h> 13162+ 13163+int main(int argc, char *argv[]) 13164+{ 13165+ unsigned int i; 13166+ for (i = 0; i != 64; ++i) 13167+ { 13168+ printf(" [%2u]=%5u,", i, (unsigned int)(0.5 + (1/sqrt((i + 5)/4.0) * 65536.0))); 13169+ if (i % 4 == 3) 13170+ printf("\n"); 13171+ } 13172+} 13173+#endif 13174+ 13175+static const uint16_t sqrt_tab[64] = { 13176+ [ 0]=58617, [ 1]=53510, [ 2]=49541, [ 3]=46341, 13177+ [ 4]=43691, [ 5]=41449, [ 6]=39520, [ 7]=37837, 13178+ [ 8]=36353, [ 9]=35030, [10]=33843, [11]=32768, 13179+ [12]=31790, [13]=30894, [14]=30070, [15]=29309, 13180+ [16]=28602, [17]=27945, [18]=27330, [19]=26755, 13181+ [20]=26214, [21]=25705, [22]=25225, [23]=24770, 13182+ [24]=24339, [25]=23930, [26]=23541, [27]=23170, 13183+ [28]=22817, [29]=22479, [30]=22155, [31]=21845, 13184+ [32]=21548, [33]=21263, [34]=20988, [35]=20724, 13185+ [36]=20470, [37]=20225, [38]=19988, [39]=19760, 13186+ [40]=19539, [41]=19326, [42]=19119, [43]=18919, 13187+ [44]=18725, [45]=18536, [46]=18354, [47]=18176, 13188+ [48]=18004, [49]=17837, [50]=17674, [51]=17515, 13189+ [52]=17361, [53]=17211, [54]=17064, [55]=16921, 13190+ [56]=16782, [57]=16646, [58]=16514, [59]=16384, 13191+ [60]=16257, [61]=16134, [62]=16013, [63]=15895 13192+}; 13193+#define SQRT_MAX (sizeof(sqrt_tab)/sizeof(sqrt_tab[0]) - 1) 13194+ 13195+static bool cpy_fmt_limit_size(const display_desc_t * const dd, 13196+ video_format_t * const dst, 13197+ const video_format_t * const src) 13198+{ 13199+ const unsigned int src_pel = src->i_visible_width * src->i_visible_height; 13200+ 13201+ *dst = *src; 13202+ 13203+ if (src_pel <= dd->max_pels) 13204+ return false; 13205+ 13206+ // scaling factor sqrt(max_pel/cur_pel) 13207+ // sqrt done by lookup & 16 bit fixed-point maths - not exactly accurate but 13208+ // easily good enough & avoids floating point (which may be slow) 13209+ // src_pel > max_pel so n >= 0 13210+ // Rounding should be such that exact sqrts work and everything else rounds 13211+ // down 13212+ unsigned int n = ((src_pel * 4 - 1) / dd->max_pels) - 4; 13213+ unsigned int scale = sqrt_tab[n >= SQRT_MAX ? SQRT_MAX : n]; 13214+ 13215+ // Rescale width - rounding up to 16 13216+ unsigned int width = ((src->i_visible_width * scale + (16 << 16) - 1) >> 16) & ~15; 13217+ // Rescale height based on new width 13218+ unsigned int height = (src->i_visible_height * width + src->i_visible_width/2) / src->i_visible_width; 13219+ 13220+// fprintf(stderr, "%dx%d -> %dx%d\n", src->i_visible_width, src->i_visible_height, width, height); 13221+ 13222+ dst->i_width = width; 13223+ dst->i_visible_width = width; 13224+ dst->i_height = height; 13225+ dst->i_visible_height = height; 13226+ return true; 13227+} 13228+ 13229+static void unload_display_module(vout_display_t * const x_vout) 13230+{ 13231+ if (x_vout != NULL) { 13232+ if (x_vout->module != NULL) { 13233+ module_unneed(x_vout, x_vout->module); 13234+ } 13235+ vlc_object_release(x_vout); 13236+ } 13237+} 13238+ 13239+static void CloseMmalX11(vlc_object_t *object) 13240+{ 13241+ vout_display_t * const vd = (vout_display_t *)object; 13242+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; 13243+ 13244+ msg_Dbg(vd, "<<< %s", __func__); 13245+ 13246+ if (sys == NULL) 13247+ return; 13248+ 13249+ unload_display_module(sys->x_desc.vout); 13250+ 13251+ unload_display_module(sys->mmal_desc.vout); 13252+ 13253+ free(sys); 13254+ 13255+ msg_Dbg(vd, ">>> %s", __func__); 13256+} 13257+ 13258+static void mmal_x11_event(vout_display_t * x_vd, int cmd, va_list args) 13259+{ 13260+ vout_display_t * const vd = x_vd->owner.sys; 13261+#if TRACE_ALL 13262+ msg_Dbg(vd, "<<< %s (cmd=%d)", __func__, cmd); 13263+#endif 13264+ 13265+ // Do not fall into the display assert if Invalid not supported 13266+ if (cmd == VOUT_DISPLAY_EVENT_PICTURES_INVALID && 13267+ !vd->info.has_pictures_invalid) 13268+ return; 13269+ 13270+ vd->owner.event(vd, cmd, args); 13271+} 13272+ 13273+static vout_window_t * mmal_x11_window_new(vout_display_t * x_vd, unsigned type) 13274+{ 13275+ vout_display_t * const vd = x_vd->owner.sys; 13276+#if TRACE_ALL 13277+ msg_Dbg(vd, "<<< %s (type=%d)", __func__, type); 13278+#endif 13279+ return vd->owner.window_new(vd, type); 13280+} 13281+ 13282+static void mmal_x11_window_del(vout_display_t * x_vd, vout_window_t * win) 13283+{ 13284+ vout_display_t * const vd = x_vd->owner.sys; 13285+#if TRACE_ALL 13286+ msg_Dbg(vd, "<<< %s", __func__); 13287+#endif 13288+ vd->owner.window_del(vd, win); 13289+} 13290+ 13291+ 13292+static int load_display_module(vout_display_t * const vd, 13293+ display_desc_t * const dd, 13294+ const char * const cap, 13295+ const char * const module_name) 13296+{ 13297+ vout_display_t * const x_vout = vlc_object_create(vd, sizeof(*x_vout)); 13298+ 13299+ dd->vout = NULL; 13300+ if (!x_vout) 13301+ return -1; 13302+ 13303+ x_vout->owner.sys = vd; 13304+ x_vout->owner.event = mmal_x11_event; 13305+ x_vout->owner.window_new = mmal_x11_window_new; 13306+ x_vout->owner.window_del = mmal_x11_window_del; 13307+ 13308+ x_vout->cfg = vd->cfg; 13309+ x_vout->info = vd->info; 13310+ cpy_fmt_limit_size(dd, &x_vout->source, &vd->source); 13311+ cpy_fmt_limit_size(dd, &x_vout->fmt, &vd->fmt); 13312+ 13313+ if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL) 13314+ { 13315+ msg_Err(vd, "Failed to open Xsplitter:%s module", module_name); 13316+ goto fail; 13317+ } 13318+ 13319+ msg_Dbg(vd, "R/G/B: %08x/%08x/%08x", x_vout->fmt.i_rmask, x_vout->fmt.i_gmask, x_vout->fmt.i_bmask); 13320+ 13321+ dd->vout = x_vout; 13322+ return 0; 13323+ 13324+fail: 13325+ vlc_object_release(x_vout); 13326+ return -1; 13327+} 13328+ 13329+ 13330+/* Return a pointer over the current picture_pool_t* (mandatory). 13331+ * 13332+ * For performance reasons, it is best to provide at least count 13333+ * pictures but it is not mandatory. 13334+ * You can return NULL when you cannot/do not want to allocate 13335+ * pictures. 13336+ * The vout display module keeps the ownership of the pool and can 13337+ * destroy it only when closing or on invalid pictures control. 13338+ */ 13339+static picture_pool_t * mmal_x11_pool(vout_display_t * vd, unsigned count) 13340+{ 13341+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; 13342+ vout_display_t * const x_vd = sys->cur_desc->vout; 13343+#if TRACE_ALL 13344+ char buf0[5]; 13345+ msg_Dbg(vd, "<<< %s (count=%d) %s:%dx%d->%s:%dx%d", __func__, count, 13346+ str_fourcc(buf0, vd->fmt.i_chroma), 13347+ vd->fmt.i_width, vd->fmt.i_height, 13348+ str_fourcc(buf0, x_vd->fmt.i_chroma), 13349+ x_vd->fmt.i_width, x_vd->fmt.i_height); 13350+#endif 13351+ picture_pool_t * pool = x_vd->pool(x_vd, count); 13352+#if TRACE_ALL 13353+ msg_Dbg(vd, ">>> %s: %p", __func__, pool); 13354+#endif 13355+ return pool; 13356+} 13357+ 13358+/* Prepare a picture and an optional subpicture for display (optional). 13359+ * 13360+ * It is called before the next pf_display call to provide as much 13361+ * time as possible to prepare the given picture and the subpicture 13362+ * for display. 13363+ * You are guaranted that pf_display will always be called and using 13364+ * the exact same picture_t and subpicture_t. 13365+ * You cannot change the pixel content of the picture_t or of the 13366+ * subpicture_t. 13367+ */ 13368+static void mmal_x11_prepare(vout_display_t * vd, picture_t * pic, subpicture_t * sub) 13369+{ 13370+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; 13371+ vout_display_t * const x_vd = sys->cur_desc->vout; 13372+#if TRACE_ALL 13373+ msg_Dbg(vd, "<<< %s", __func__); 13374+#endif 13375+ if (x_vd->prepare) 13376+ x_vd->prepare(x_vd, pic, sub); 13377+} 13378+ 13379+/* Display a picture and an optional subpicture (mandatory). 13380+ * 13381+ * The picture and the optional subpicture must be displayed as soon as 13382+ * possible. 13383+ * You cannot change the pixel content of the picture_t or of the 13384+ * subpicture_t. 13385+ * 13386+ * This function gives away the ownership of the picture and of the 13387+ * subpicture, so you must release them as soon as possible. 13388+ */ 13389+static void mmal_x11_display(vout_display_t * vd, picture_t * pic, subpicture_t * sub) 13390+{ 13391+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; 13392+ vout_display_t * const x_vd = sys->cur_desc->vout; 13393+ 13394+#if TRACE_ALL 13395+ const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic); 13396+ msg_Dbg(vd, "<<< %s: fmt: %dx%d/%dx%d, pic:%dx%d, pts=%lld, mmal=%d/%d", __func__, vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height, pic->format.i_width, pic->format.i_height, (long long)pic->date, 13397+ is_mmal_pic, sys->use_mmal); 13398+#endif 13399+ 13400+ if (x_vd->fmt.i_chroma != pic->format.i_chroma || 13401+ x_vd->fmt.i_width != pic->format.i_width || 13402+ x_vd->fmt.i_height != pic->format.i_height) 13403+ { 13404+ msg_Dbg(vd, "%s: Picture dropped", __func__); 13405+ picture_Release(pic); 13406+ if (sub != NULL) 13407+ subpicture_Delete(sub); 13408+ return; 13409+ } 13410+ 13411+ x_vd->display(x_vd, pic, sub); 13412+} 13413+ 13414+ 13415+static int vout_display_Control(const display_desc_t * const dd, int query, ...) 13416+{ 13417+ va_list args; 13418+ int result; 13419+ 13420+ va_start(args, query); 13421+ result = dd->vout->control(dd->vout, query, args); 13422+ va_end(args); 13423+ 13424+ return result; 13425+} 13426+ 13427+static bool want_mmal_vout(vout_display_t * const vd, const mmal_x11_sys_t * const sys) 13428+{ 13429+ return sys->mmal_desc.vout != NULL && 13430+ (sys->x_desc.vout == NULL || var_InheritBool(vd, "fullscreen")); 13431+} 13432+ 13433+static inline int 13434+up_rv(const int a, const int b) 13435+{ 13436+ return a != 0 ? a : b; 13437+} 13438+ 13439+static int 13440+reset_pictures(vout_display_t * const vd, const display_desc_t * const desc) 13441+{ 13442+ int rv = 0; 13443+ VLC_UNUSED(vd); 13444+ if (desc->vout) 13445+ { 13446+ // If the display doesn't have has_pictures_invalid then it doesn't 13447+ // expect RESET_PICTURES 13448+ if (desc->vout->info.has_pictures_invalid) 13449+ vout_display_Control(desc, VOUT_DISPLAY_RESET_PICTURES); 13450+ } 13451+ return rv; 13452+} 13453+ 13454+static int 13455+replay_controls(vout_display_t * const vd, const display_desc_t * const desc, const int32_t changed) 13456+{ 13457+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_DISPLAY_FILLED)) != 0) 13458+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, vd->cfg); 13459+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_ZOOM)) != 0) 13460+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg); 13461+ if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) | 13462+ (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0) 13463+ cpy_fmt_limit_size(desc, &desc->vout->source, &vd->source); 13464+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT)) != 0) 13465+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT); 13466+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP)) != 0) 13467+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_CROP); 13468+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_VIEWPOINT)) != 0) 13469+ vout_display_Control(desc, VOUT_DISPLAY_CHANGE_VIEWPOINT, vd->cfg); 13470+ return 0; 13471+} 13472+ 13473+/* Control on the module (mandatory) */ 13474+static int mmal_x11_control(vout_display_t * vd, int ctl, va_list va) 13475+{ 13476+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; 13477+ display_desc_t *x_desc = sys->cur_desc; 13478+ int rv; 13479+#if TRACE_ALL 13480+ msg_Dbg(vd, "<<< %s[%d] (ctl=%d)", __func__, sys->use_mmal, ctl); 13481+#endif 13482+ // Remember what we've told this vd - unwanted ctls ignored on replay 13483+ if (ctl >= 0 && ctl <= 31) 13484+ sys->changed |= (1 << ctl); 13485+ 13486+ switch (ctl) { 13487+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: 13488+ { 13489+ const vout_display_cfg_t * const cfg = va_arg(va, const vout_display_cfg_t *); 13490+ const bool want_mmal = want_mmal_vout(vd, sys); 13491+ const bool swap_vout = (sys->use_mmal != want_mmal); 13492+ display_desc_t * const new_desc = want_mmal ? &sys->mmal_desc : &sys->x_desc; 13493+ 13494+ msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d", 13495+ cfg->display.width, cfg->display.height, sys->mmal_desc.vout, want_mmal, 13496+ var_InheritBool(vd, "fullscreen")); 13497+ 13498+ // Repeat any control calls that we sent to the previous vd 13499+ if (swap_vout && sys->changed != 0) { 13500+ const uint32_t changed = sys->changed; 13501+ sys->changed = 0; 13502+ replay_controls(vd, new_desc, changed); 13503+ } 13504+ 13505+ if (swap_vout) { 13506+ if (sys->use_mmal) { 13507+ vout_display_Control(x_desc, VOUT_DISPLAY_CHANGE_MMAL_HIDE); 13508+ } 13509+ vout_display_SendEventPicturesInvalid(vd); 13510+ } 13511+ 13512+ rv = vout_display_Control(new_desc, ctl, cfg); 13513+ if (rv == VLC_SUCCESS) { 13514+ vd->fmt = new_desc->vout->fmt; 13515+ sys->cur_desc = new_desc; 13516+ sys->use_mmal = want_mmal; 13517+ } 13518+ 13519+ 13520+ break; 13521+ } 13522+ 13523+ case VOUT_DISPLAY_RESET_PICTURES: 13524+ { 13525+ char dbuf0[5], dbuf1[5], dbuf2[5]; 13526+ msg_Dbg(vd, "<<< %s: Pic reset: fmt: %s,%dx%d<-%s,%dx%d, source: %s,%dx%d/%dx%d", __func__, 13527+ str_fourcc(dbuf0, vd->fmt.i_chroma), vd->fmt.i_width, vd->fmt.i_height, 13528+ str_fourcc(dbuf1, x_desc->vout->fmt.i_chroma), x_desc->vout->fmt.i_width, x_desc->vout->fmt.i_height, 13529+ str_fourcc(dbuf2, vd->source.i_chroma), vd->source.i_width, vd->source.i_height, x_desc->vout->source.i_width, 13530+ x_desc->vout->source.i_height); 13531+ } 13532+ rv = reset_pictures(vd, &sys->x_desc); 13533+ rv = up_rv(rv, reset_pictures(vd, &sys->mmal_desc)); 13534+ 13535+ vd->fmt = x_desc->vout->fmt; 13536+ break; 13537+ 13538+ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: 13539+ case VOUT_DISPLAY_CHANGE_SOURCE_CROP: 13540+ cpy_fmt_limit_size(x_desc, &x_desc->vout->source, &vd->source); 13541+ 13542+ /* FALLTHRU */ 13543+ default: 13544+ rv = x_desc->vout->control(x_desc->vout, ctl, va); 13545+// vd->fmt = x_vd->fmt; 13546+ break; 13547+ } 13548+#if TRACE_ALL 13549+ msg_Dbg(vd, ">>> %s (rv=%d)", __func__, rv); 13550+#endif 13551+ return rv; 13552+} 13553+ 13554+#define DO_MANAGE 0 13555+ 13556+#if DO_MANAGE 13557+/* Manage pending event (optional) */ 13558+static void mmal_x11_manage(vout_display_t * vd) 13559+{ 13560+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; 13561+ vout_display_t * const x_vd = sys->cur_desc->vout; 13562+#if TRACE_ALL 13563+ msg_Dbg(vd, "<<< %s", __func__); 13564+#endif 13565+ x_vd->manage(x_vd); 13566+} 13567+#endif 13568+ 13569+static int OpenMmalX11(vlc_object_t *object) 13570+{ 13571+ vout_display_t * const vd = (vout_display_t *)object; 13572+ mmal_x11_sys_t * const sys = calloc(1, sizeof(*sys)); 13573+ int ret = VLC_SUCCESS; 13574+ 13575+ if (sys == NULL) { 13576+ return VLC_EGENERIC; 13577+ } 13578+ vd->sys = (vout_display_sys_t *)sys; 13579+ 13580+ vd->info = (vout_display_info_t){ 13581+ .is_slow = false, 13582+ .has_double_click = false, 13583+ .needs_hide_mouse = false, 13584+ .has_pictures_invalid = true, 13585+ .subpicture_chromas = NULL 13586+ }; 13587+ 13588+ { 13589+ char dbuf0[5]; 13590+ msg_Dbg(vd, ">>> %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__, 13591+ str_fourcc(dbuf0, vd->fmt.i_chroma), 13592+ vd->fmt.i_width, vd->fmt.i_height, 13593+ vd->fmt.i_x_offset, vd->fmt.i_y_offset, 13594+ vd->fmt.i_visible_width, vd->fmt.i_visible_height, 13595+ vd->fmt.i_sar_num, vd->fmt.i_sar_den); 13596+ } 13597+ 13598+ sys->x_desc.max_pels = MAX_GL_PELS; 13599+ sys->mmal_desc.max_pels = MAX_MMAL_PELS; 13600+ 13601+ if (load_display_module(vd, &sys->x_desc, "vout display", "opengles2") == 0) 13602+ { 13603+ msg_Dbg(vd, "Opengles2 output found"); 13604+ } 13605+ else 13606+ { 13607+ sys->x_desc.max_pels = MAX_MMAL_PELS; 13608+ if (load_display_module(vd, &sys->x_desc, "vout display", "xcb_x11") == 0) 13609+ msg_Dbg(vd, "X11 XCB output found"); 13610+ } 13611+ 13612+ if ((load_display_module(vd, &sys->mmal_desc, "vout display", "mmal_vout")) == 0) 13613+ msg_Dbg(vd, "MMAL output found"); 13614+ 13615+ if (sys->mmal_desc.vout == NULL && sys->x_desc.vout == NULL) { 13616+ char dbuf0[5], dbuf1[5]; 13617+ msg_Info(vd, "No valid output found for vout (%s/%s)", str_fourcc(dbuf0, vd->fmt.i_chroma), str_fourcc(dbuf1, vd->source.i_chroma)); 13618+ goto fail; 13619+ } 13620+ 13621+ vd->pool = mmal_x11_pool; 13622+ vd->prepare = mmal_x11_prepare; 13623+ vd->display = mmal_x11_display; 13624+ vd->control = mmal_x11_control; 13625+#if DO_MANAGE 13626+ vd->manage = mmal_x11_manage; 13627+#endif 13628+ 13629+ if (want_mmal_vout(vd, sys)) { 13630+ sys->cur_desc = &sys->mmal_desc; 13631+ sys->use_mmal = true; 13632+ } 13633+ else { 13634+ sys->cur_desc = &sys->x_desc; 13635+ sys->use_mmal = false; 13636+ } 13637+ 13638+ if (sys->mmal_desc.vout == NULL || sys->x_desc.vout == NULL) { 13639+ vd->info = sys->cur_desc->vout->info; 13640+ vd->info.has_pictures_invalid = true; // Should make this unwanted 13641+ } 13642+ else { 13643+ // We have both - construct a combination 13644+ vd->info = (vout_display_info_t){ 13645+ .is_slow = false, 13646+ .has_double_click = sys->mmal_desc.vout->info.has_double_click || sys->x_desc.vout->info.has_double_click, 13647+ .needs_hide_mouse = sys->mmal_desc.vout->info.needs_hide_mouse || sys->x_desc.vout->info.needs_hide_mouse, 13648+ .has_pictures_invalid = true, 13649+ }; 13650+ // Construct intersection of subpicture chromas 13651+ // sys calloced so no need to add the terminating zero 13652+ if (sys->mmal_desc.vout->info.subpicture_chromas != NULL && sys->x_desc.vout->info.subpicture_chromas != NULL) { 13653+ unsigned int n = 0; 13654+ // N^2 - fix if we ever care 13655+ for (const vlc_fourcc_t * p1 = sys->mmal_desc.vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) { 13656+ for (const vlc_fourcc_t * p2 = sys->x_desc.vout->info.subpicture_chromas; *p2 != 0; ++p2) { 13657+ if (*p1 == *p2) { 13658+ sys->subpicture_chromas[n++] = *p1; 13659+ break; 13660+ } 13661+ } 13662+ } 13663+ if (n != 0) 13664+ vd->info.subpicture_chromas = sys->subpicture_chromas; 13665+ } 13666+ } 13667+ vd->fmt = sys->cur_desc->vout->fmt; 13668+ 13669+#if TRACE_ALL 13670+ { 13671+ char dbuf0[5]; 13672+ msg_Dbg(vd, ">>> %s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__, 13673+ module_get_name(sys->cur_desc->vout->module, false), 13674+ str_fourcc(dbuf0, vd->fmt.i_chroma), 13675+ vd->fmt.i_width, vd->fmt.i_height, 13676+ vd->fmt.i_x_offset, vd->fmt.i_y_offset, 13677+ vd->fmt.i_visible_width, vd->fmt.i_visible_height, 13678+ vd->fmt.i_sar_num, vd->fmt.i_sar_den); 13679+ } 13680+#endif 13681+ return VLC_SUCCESS; 13682+ 13683+fail: 13684+ CloseMmalX11(VLC_OBJECT(vd)); 13685+ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret; 13686+} 13687+ 13688+ 13689+ 13690+ 13691+vlc_module_begin() 13692+ set_shortname(N_("MMAL x11 splitter")) 13693+ set_description(N_("MMAL x11 splitter for Raspberry Pi")) 13694+ set_capability("vout display", 300) // Between GLES & GL 13695+ add_shortcut("mmal_x11") 13696+ set_category( CAT_VIDEO ) 13697+ set_subcategory( SUBCAT_VIDEO_VOUT ) 13698+ set_callbacks(OpenMmalX11, CloseMmalX11) 13699+vlc_module_end() 13700+ 13701--- a/modules/video_output/opengl/egl.c 13702+++ b/modules/video_output/opengl/egl.c 13703@@ -43,6 +43,8 @@ 13704 # include "../android/utils.h" 13705 #endif 13706 13707+#define REQUIRE_DMA_BUF_IMPORT 1 13708+ 13709 typedef struct vlc_gl_sys_t 13710 { 13711 EGLDisplay display; 13712@@ -355,6 +357,14 @@ static int Open (vlc_object_t *obj, cons 13713 goto error; 13714 } 13715 13716+#if REQUIRE_DMA_BUF_IMPORT 13717+ if (!CheckToken(ext, "EGL_EXT_image_dma_buf_import")) 13718+ { 13719+ msg_Dbg(obj, "No dma_buf_import - fall back to X"); 13720+ goto error; 13721+ } 13722+#endif 13723+ 13724 const EGLint conf_attr[] = { 13725 EGL_RED_SIZE, 5, 13726 EGL_GREEN_SIZE, 5, 13727--- a/src/input/decoder.c 13728+++ b/src/input/decoder.c 13729@@ -1995,6 +1995,7 @@ void input_DecoderDelete( decoder_t *p_d 13730 vlc_mutex_lock( &p_owner->lock ); 13731 p_owner->b_waiting = false; 13732 vlc_cond_signal( &p_owner->wait_request ); 13733+ vlc_mutex_unlock( &p_owner->lock ); 13734 13735 /* If the video output is paused or slow, or if the picture pool size was 13736 * under-estimated (e.g. greedy video filter, buggy decoder...), the 13737@@ -2005,7 +2006,6 @@ void input_DecoderDelete( decoder_t *p_d 13738 * worker threads (if any) and the decoder thread to terminate. */ 13739 if( p_owner->p_vout != NULL ) 13740 vout_Cancel( p_owner->p_vout, true ); 13741- vlc_mutex_unlock( &p_owner->lock ); 13742 13743 vlc_join( p_owner->thread, NULL ); 13744 13745--- a/src/misc/fourcc.c 13746+++ b/src/misc/fourcc.c 13747@@ -755,8 +755,13 @@ static const struct 13748 { { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422, 13749 VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT }, 13750 FAKE_FMT() }, 13751- { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, 13752- VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE }, 13753+ { { VLC_CODEC_ANDROID_OPAQUE }, FAKE_FMT() }, 13754+ { { VLC_CODEC_MMAL_OPAQUE, VLC_CODEC_MMAL_ZC_SAND30 }, 13755+ FAKE_FMT() }, 13756+ { { VLC_CODEC_MMAL_ZC_I420, VLC_CODEC_MMAL_ZC_SAND8, 13757+ VLC_CODEC_MMAL_ZC_SAND10, VLC_CODEC_MMAL_ZC_RGB32 }, 13758+ FAKE_FMT() }, 13759+ { { VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE }, 13760 FAKE_FMT() }, 13761 { { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B }, 13762 FAKE_FMT() }, 13763--- a/src/misc/picture.c 13764+++ b/src/misc/picture.c 13765@@ -365,10 +365,30 @@ void picture_CopyProperties( picture_t * 13766 p_dst->b_top_field_first = p_src->b_top_field_first; 13767 } 13768 13769+static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma) 13770+{ 13771+ return i_chroma == VLC_CODEC_MMAL_OPAQUE || 13772+ i_chroma == VLC_CODEC_MMAL_ZC_I420 || 13773+ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 || 13774+ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 || 13775+ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 || 13776+ i_chroma == VLC_CODEC_MMAL_ZC_SAND8; 13777+} 13778+ 13779 void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src ) 13780 { 13781- for( int i = 0; i < p_src->i_planes ; i++ ) 13782- plane_CopyPixels( p_dst->p+i, p_src->p+i ); 13783+ if( is_zc_chroma(p_src->format.i_chroma) ) 13784+ { 13785+ assert(p_dst->i_planes == 0); 13786+ p_dst->i_planes = p_src->i_planes; 13787+ for( int i = 0; i < p_src->i_planes; i++ ) 13788+ p_dst->p[i] = p_src->p[i]; 13789+ } 13790+ else 13791+ { 13792+ for( int i = 0; i < p_src->i_planes; i++ ) 13793+ plane_CopyPixels( p_dst->p+i, p_src->p+i ); 13794+ } 13795 13796 assert( p_dst->context == NULL ); 13797 13798--- a/src/video_output/video_output.c 13799+++ b/src/video_output/video_output.c 13800@@ -964,6 +964,17 @@ static picture_t *ConvertRGB32AndBlend(v 13801 return NULL; 13802 } 13803 13804+ 13805+static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma) 13806+{ 13807+ return i_chroma == VLC_CODEC_MMAL_OPAQUE || 13808+ i_chroma == VLC_CODEC_MMAL_ZC_I420 || 13809+ i_chroma == VLC_CODEC_MMAL_ZC_RGB32 || 13810+ i_chroma == VLC_CODEC_MMAL_ZC_SAND10 || 13811+ i_chroma == VLC_CODEC_MMAL_ZC_SAND30 || 13812+ i_chroma == VLC_CODEC_MMAL_ZC_SAND8; 13813+} 13814+ 13815 static int ThreadDisplayRenderPicture(vout_thread_t *vout, bool is_forced) 13816 { 13817 vout_thread_sys_t *sys = vout->p; 13818@@ -1098,7 +1109,7 @@ static int ThreadDisplayRenderPicture(vo 13819 } 13820 13821 assert(vout_IsDisplayFiltered(vd) == !sys->display.use_dr); 13822- if (sys->display.use_dr && !is_direct) { 13823+ if (sys->display.use_dr && !is_direct && !is_zc_chroma(todisplay->format.i_chroma)) { 13824 picture_t *direct = NULL; 13825 if (likely(vout->p->display_pool != NULL)) 13826 direct = picture_pool_Get(vout->p->display_pool); 13827