Andrew Geissler | 87f5cff | 2022-09-30 13:13:31 -0500 | [diff] [blame] | 1 | Upstream-status: Pending |
| 2 | |
| 3 | --- a/configure.ac |
| 4 | +++ b/configure.ac |
| 5 | @@ -3478,6 +3478,9 @@ dnl |
| 6 | AC_ARG_ENABLE(mmal, |
| 7 | AS_HELP_STRING([--enable-mmal], |
| 8 | [Multi-Media Abstraction Layer (MMAL) hardware plugin (default enable)])) |
| 9 | +AC_ARG_ENABLE(mmal_avcodec, |
| 10 | + AS_HELP_STRING([--enable-mmal-avcodec], |
| 11 | + [Use MMAL enabled avcodec libs (default disable)])) |
| 12 | if test "${enable_mmal}" != "no"; then |
| 13 | VLC_SAVE_FLAGS |
| 14 | LDFLAGS="${LDFLAGS} -L/opt/vc/lib -lvchostif" |
| 15 | @@ -3488,7 +3491,7 @@ if test "${enable_mmal}" != "no"; then |
| 16 | VLC_ADD_PLUGIN([mmal]) |
| 17 | VLC_ADD_LDFLAGS([mmal],[ -L/opt/vc/lib ]) |
| 18 | VLC_ADD_CFLAGS([mmal],[ -isystem /opt/vc/include -isystem /opt/vc/include/interface/vcos/pthreads -isystem /opt/vc/include/interface/vmcs_host/linux ]) |
| 19 | - VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif ]) ], [ |
| 20 | + VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif -lvchiq_arm -lvcsm ]) ], [ |
| 21 | AS_IF([test "${enable_mmal}" = "yes"], |
| 22 | [ AC_MSG_ERROR([Cannot find bcm library...]) ], |
| 23 | [ AC_MSG_WARN([Cannot find bcm library...]) ]) |
| 24 | @@ -3500,6 +3503,7 @@ if test "${enable_mmal}" != "no"; then |
| 25 | VLC_RESTORE_FLAGS |
| 26 | fi |
| 27 | AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"]) |
| 28 | +AM_CONDITIONAL([HAVE_MMAL_AVCODEC], [test "${enable_mmal_avcodec}" = "yes"]) |
| 29 | |
| 30 | dnl |
| 31 | dnl evas plugin |
| 32 | --- a/include/vlc_fourcc.h |
| 33 | +++ b/include/vlc_fourcc.h |
| 34 | @@ -365,6 +365,11 @@ |
| 35 | |
| 36 | /* Broadcom MMAL opaque buffer type */ |
| 37 | #define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L') |
| 38 | +#define VLC_CODEC_MMAL_ZC_SAND8 VLC_FOURCC('Z','S','D','8') |
| 39 | +#define VLC_CODEC_MMAL_ZC_SAND10 VLC_FOURCC('Z','S','D','0') |
| 40 | +#define VLC_CODEC_MMAL_ZC_SAND30 VLC_FOURCC('Z','S','D','3') |
| 41 | +#define VLC_CODEC_MMAL_ZC_I420 VLC_FOURCC('Z','4','2','0') |
| 42 | +#define VLC_CODEC_MMAL_ZC_RGB32 VLC_FOURCC('Z','R','G','B') |
| 43 | |
| 44 | /* DXVA2 opaque video surface for use with D3D9 */ |
| 45 | #define VLC_CODEC_D3D9_OPAQUE VLC_FOURCC('D','X','A','9') /* 4:2:0 8 bpc */ |
| 46 | --- a/modules/hw/mmal/Makefile.am |
| 47 | +++ b/modules/hw/mmal/Makefile.am |
| 48 | @@ -1,23 +1,57 @@ |
| 49 | include $(top_srcdir)/modules/common.am |
| 50 | mmaldir = $(pluginsdir)/mmal |
| 51 | |
| 52 | -AM_CFLAGS += $(CFLAGS_mmal) |
| 53 | -AM_LDFLAGS += -rpath '$(mmaldir)' $(LDFLAGS_mmal) |
| 54 | +AM_CFLAGS += -pthread $(CFLAGS_mmal) |
| 55 | +AM_LDFLAGS += -pthread -rpath '$(mmaldir)' $(LDFLAGS_mmal) |
| 56 | |
| 57 | -libmmal_vout_plugin_la_SOURCES = vout.c mmal_picture.c mmal_picture.h |
| 58 | +libmmal_vout_plugin_la_SOURCES = vout.c mmal_cma.c mmal_picture.c subpic.c\ |
| 59 | + mmal_cma.h mmal_picture.h subpic.h transform_ops.h\ |
| 60 | + mmal_piccpy_neon.S |
| 61 | libmmal_vout_plugin_la_CFLAGS = $(AM_CFLAGS) |
| 62 | -libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm |
| 63 | +libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm -lX11 -lXrandr |
| 64 | libmmal_vout_plugin_la_LIBADD = $(LIBS_mmal) |
| 65 | mmal_LTLIBRARIES = libmmal_vout_plugin.la |
| 66 | |
| 67 | -libmmal_codec_plugin_la_SOURCES = codec.c |
| 68 | +libmmal_codec_plugin_la_SOURCES = codec.c mmal_cma.c mmal_picture.c subpic.c\ |
| 69 | + mmal_cma.h mmal_picture.h subpic.h transform_ops.h\ |
| 70 | + blend_rgba_neon.S mmal_piccpy_neon.S |
| 71 | libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS) |
| 72 | libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS) |
| 73 | libmmal_codec_plugin_la_LIBADD = $(LIBS_mmal) |
| 74 | mmal_LTLIBRARIES += libmmal_codec_plugin.la |
| 75 | |
| 76 | -libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c |
| 77 | +libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c mmal_cma.c\ |
| 78 | + mmal_cma.h mmal_picture.h transform_ops.h\ |
| 79 | + mmal_piccpy_neon.S |
| 80 | libmmal_deinterlace_plugin_la_CFLAGS = $(AM_CFLAGS) |
| 81 | libmmal_deinterlace_plugin_la_LDFLAGS = $(AM_LDFLAGS) |
| 82 | libmmal_deinterlace_plugin_la_LIBADD = $(LIBS_mmal) |
| 83 | mmal_LTLIBRARIES += libmmal_deinterlace_plugin.la |
| 84 | + |
| 85 | +libmmal_xsplitter_plugin_la_SOURCES = xsplitter.c mmal_picture.c mmal_cma.c\ |
| 86 | + mmal_cma.h mmal_picture.h transform_ops.h\ |
| 87 | + mmal_piccpy_neon.S |
| 88 | +libmmal_xsplitter_plugin_la_CFLAGS = $(AM_CFLAGS) |
| 89 | +libmmal_xsplitter_plugin_la_LDFLAGS = $(AM_LDFLAGS) |
| 90 | +libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal) |
| 91 | +mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la |
| 92 | + |
| 93 | +libmmal_converter_plugin_la_SOURCES = converter_mmal.c mmal_cma.c mmal_picture.c\ |
| 94 | + mmal_cma.h mmal_picture.h transform_ops.h\ |
| 95 | + mmal_piccpy_neon.S |
| 96 | +libmmal_converter_plugin_la_CFLAGS = $(AM_CFLAGS) |
| 97 | +libmmal_converter_plugin_la_LDFLAGS = $(AM_LDFLAGS) |
| 98 | +libmmal_converter_plugin_la_LIBADD = $(LIBS_mmal) |
| 99 | +mmal_LTLIBRARIES += libmmal_converter_plugin.la |
| 100 | + |
| 101 | +if HAVE_MMAL_AVCODEC |
| 102 | +libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_cma.c mmal_picture.c\ |
| 103 | + mmal_cma.h mmal_picture.h transform_ops.h\ |
| 104 | + mmal_piccpy_neon.S |
| 105 | +libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS) |
| 106 | +libmmal_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS) |
| 107 | +libmmal_avcodec_plugin_la_LIBADD = $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(LIBS_mmal) |
| 108 | +mmal_LTLIBRARIES += libmmal_avcodec_plugin.la |
| 109 | +endif |
| 110 | + |
| 111 | + |
| 112 | --- /dev/null |
| 113 | +++ b/modules/hw/mmal/blend_rgba_neon.S |
| 114 | @@ -0,0 +1,197 @@ |
| 115 | + .syntax unified |
| 116 | + .arm |
| 117 | +// .thumb |
| 118 | + .text |
| 119 | + .align 16 |
| 120 | + .arch armv7-a |
| 121 | + .fpu neon-vfpv4 |
| 122 | + |
| 123 | +@ blend_rgbx_rgba_neon |
| 124 | + |
| 125 | +@ Implements /255 as ((x * 257) + 0x8000) >> 16 |
| 126 | +@ This generates something in the range [(x+126)/255, (x+127)/255] which is good enough |
| 127 | + |
| 128 | +@ There is advantage to aligning src and/or dest - dest gives a bit more due to being used twice |
| 129 | + |
| 130 | + |
| 131 | + |
| 132 | +@ [r0] RGBx dest loaded into d20-d23 |
| 133 | +@ [r1] RGBA src merge loaded into d16-d19 |
| 134 | +@ r2 plane alpha |
| 135 | +@ r3 count (pixels) |
| 136 | + |
| 137 | +.macro blend_main sR, sG, sB, sA, dR, dG, dB, dA |
| 138 | + |
| 139 | + push { r4, lr } |
| 140 | + |
| 141 | + vdup.u8 d7, r2 |
| 142 | + |
| 143 | + subs r3, #8 |
| 144 | + vmov.u8 d6, #0xff |
| 145 | + |
| 146 | + blt 2f |
| 147 | + |
| 148 | + @ If < 16 bytes to move then don't bother trying to align |
| 149 | + @ (a) This means the the align doesn't need to worry about r3 underflow |
| 150 | + @ (b) The overhead would be greater than any gain |
| 151 | + cmp r3, #8 |
| 152 | + mov r4, r3 |
| 153 | + ble 1f |
| 154 | + |
| 155 | + @ Align r1 on a 32 byte boundary |
| 156 | + neg r3, r0 |
| 157 | + ubfx r3, r3, #2, #3 |
| 158 | + |
| 159 | + cmp r3, #0 |
| 160 | + blne 10f |
| 161 | + |
| 162 | + sub r3, r4, r3 |
| 163 | + |
| 164 | +1: |
| 165 | + vld4.8 {d16, d17, d18, d19}, [r1] |
| 166 | + |
| 167 | +1: |
| 168 | + vmull.u8 q15, \sA, d7 |
| 169 | + |
| 170 | + vld4.8 {d20, d21, d22, d23}, [r0] |
| 171 | + |
| 172 | + vsra.u16 q15, q15, #8 |
| 173 | + subs r3, #8 |
| 174 | + vrshrn.u16 d31, q15, #8 |
| 175 | + vsub.u8 d30, d6, d31 |
| 176 | + |
| 177 | + vmull.u8 q12, \sR, d31 |
| 178 | + vmull.u8 q13, \sG, d31 |
| 179 | + vmull.u8 q14, \sB, d31 |
| 180 | + addge r1, #32 |
| 181 | + |
| 182 | + vmlal.u8 q12, \dR, d30 |
| 183 | + vmlal.u8 q13, \dG, d30 |
| 184 | + vmlal.u8 q14, \dB, d30 |
| 185 | + vld4.8 {d16, d17, d18, d19}, [r1] |
| 186 | + |
| 187 | + vsra.u16 q12, q12, #8 @ * 257/256 |
| 188 | + vsra.u16 q13, q13, #8 |
| 189 | + vsra.u16 q14, q14, #8 |
| 190 | + |
| 191 | + vrshrn.u16 \dR, q12, #8 |
| 192 | + vrshrn.u16 \dG, q13, #8 |
| 193 | + vrshrn.u16 \dB, q14, #8 |
| 194 | + vmov.u8 \dA, #0xff |
| 195 | + |
| 196 | + vst4.8 {d20, d21, d22, d23}, [r0]! |
| 197 | + bge 1b |
| 198 | + add r1, #32 |
| 199 | + |
| 200 | +2: |
| 201 | + cmp r3, #-8 |
| 202 | + blgt 10f |
| 203 | + |
| 204 | + pop { r4, pc } |
| 205 | + |
| 206 | + |
| 207 | +// Partial version |
| 208 | +// Align @ start & deal with tail |
| 209 | +10: |
| 210 | + lsls r2, r3, #30 @ b2 -> C, b1 -> N |
| 211 | + mov r2, r0 |
| 212 | + bcc 1f |
| 213 | + vld4.8 {d16[0], d17[0], d18[0], d19[0]}, [r1]! |
| 214 | + vld4.8 {d20[0], d21[0], d22[0], d23[0]}, [r2]! |
| 215 | + vld4.8 {d16[1], d17[1], d18[1], d19[1]}, [r1]! |
| 216 | + vld4.8 {d20[1], d21[1], d22[1], d23[1]}, [r2]! |
| 217 | + vld4.8 {d16[2], d17[2], d18[2], d19[2]}, [r1]! |
| 218 | + vld4.8 {d20[2], d21[2], d22[2], d23[2]}, [r2]! |
| 219 | + vld4.8 {d16[3], d17[3], d18[3], d19[3]}, [r1]! |
| 220 | + vld4.8 {d20[3], d21[3], d22[3], d23[3]}, [r2]! |
| 221 | +1: |
| 222 | + bpl 1f |
| 223 | + vld4.8 {d16[4], d17[4], d18[4], d19[4]}, [r1]! |
| 224 | + vld4.8 {d20[4], d21[4], d22[4], d23[4]}, [r2]! |
| 225 | + vld4.8 {d16[5], d17[5], d18[5], d19[5]}, [r1]! |
| 226 | + vld4.8 {d20[5], d21[5], d22[5], d23[5]}, [r2]! |
| 227 | +1: |
| 228 | + tst r3, #1 |
| 229 | + beq 1f |
| 230 | + vld4.8 {d16[6], d17[6], d18[6], d19[6]}, [r1]! |
| 231 | + vld4.8 {d20[6], d21[6], d22[6], d23[6]}, [r2]! |
| 232 | +1: |
| 233 | + @ Set conditions for later |
| 234 | + lsls r2, r3, #30 @ b2 -> C, b1 -> N |
| 235 | + |
| 236 | + vmull.u8 q15, \sA, d7 |
| 237 | + vsra.u16 q15, q15, #8 |
| 238 | + vrshrn.u16 d31, q15, #8 |
| 239 | + vsub.u8 d30, d6, d31 |
| 240 | + |
| 241 | + vmull.u8 q12, \sR, d31 |
| 242 | + vmull.u8 q13, \sG, d31 |
| 243 | + vmull.u8 q14, \sB, d31 |
| 244 | + |
| 245 | + vmlal.u8 q12, \dR, d30 |
| 246 | + vmlal.u8 q13, \dG, d30 |
| 247 | + vmlal.u8 q14, \dB, d30 |
| 248 | + |
| 249 | + vsra.u16 q12, q12, #8 |
| 250 | + vsra.u16 q13, q13, #8 |
| 251 | + vsra.u16 q14, q14, #8 |
| 252 | + |
| 253 | + vrshrn.u16 \dR, q12, #8 |
| 254 | + vrshrn.u16 \dG, q13, #8 |
| 255 | + vrshrn.u16 \dB, q14, #8 |
| 256 | + vmov.u8 \dA, #0xff |
| 257 | + |
| 258 | + bcc 1f |
| 259 | + vst4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]! |
| 260 | + vst4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]! |
| 261 | + vst4.8 {d20[2], d21[2], d22[2], d23[2]}, [r0]! |
| 262 | + vst4.8 {d20[3], d21[3], d22[3], d23[3]}, [r0]! |
| 263 | +1: |
| 264 | + bpl 1f |
| 265 | + vst4.8 {d20[4], d21[4], d22[4], d23[4]}, [r0]! |
| 266 | + vst4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]! |
| 267 | +1: |
| 268 | + tst r3, #1 |
| 269 | + bxeq lr |
| 270 | + vst4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]! |
| 271 | + |
| 272 | + bx lr |
| 273 | + |
| 274 | +.endm |
| 275 | + |
| 276 | + |
| 277 | +@ [r0] RGBx dest (Byte order: R, G, B, x) |
| 278 | +@ [r1] RGBA src merge (Byte order: R, G, B, A) |
| 279 | +@ r2 plane alpha |
| 280 | +@ r3 count (pixels) |
| 281 | + |
| 282 | +@ Whilst specified as RGBx+RGBA the only important part is the position of |
| 283 | +@ alpha, the other components are all treated the same |
| 284 | + |
| 285 | +@ [r0] RGBx dest (Byte order: R, G, B, x) |
| 286 | +@ [r1] RGBA src merge (Byte order: R, G, B, A) - same as above |
| 287 | +@ r2 plane alpha |
| 288 | +@ r3 count (pixels) |
| 289 | + .align 16 |
| 290 | + .global blend_rgbx_rgba_neon |
| 291 | +#ifdef __ELF__ |
| 292 | + .type blend_rgbx_rgba_neon, %function |
| 293 | +#endif |
| 294 | +blend_rgbx_rgba_neon: |
| 295 | + blend_main d16, d17, d18, d19, d20, d21, d22, d23 |
| 296 | + |
| 297 | + |
| 298 | +@ [r0] RGBx dest (Byte order: R, G, B, x) |
| 299 | +@ [r1] RGBA src merge (Byte order: B, G, R, A) - B / R swapped |
| 300 | +@ r2 plane alpha |
| 301 | +@ r3 count (pixels) |
| 302 | + .align 16 |
| 303 | + .global blend_bgrx_rgba_neon |
| 304 | +#ifdef __ELF__ |
| 305 | + .type blend_bgrx_rgba_neon, %function |
| 306 | +#endif |
| 307 | +blend_bgrx_rgba_neon: |
| 308 | + blend_main d18, d17, d16, d19, d20, d21, d22, d23 |
| 309 | + |
| 310 | + |
| 311 | + |
| 312 | --- /dev/null |
| 313 | +++ b/modules/hw/mmal/blend_rgba_neon.h |
| 314 | @@ -0,0 +1,17 @@ |
| 315 | +#ifndef HW_MMAL_BLEND_RGBA_NEON_H |
| 316 | +#define HW_MMAL_BLEND_RGBA_NEON_H |
| 317 | + |
| 318 | +#ifdef __cplusplus |
| 319 | +extern "C" { |
| 320 | +#endif |
| 321 | + |
| 322 | +typedef void blend_neon_fn(void * dest, const void * src, int alpha, unsigned int n); |
| 323 | +extern blend_neon_fn blend_rgbx_rgba_neon; |
| 324 | +extern blend_neon_fn blend_bgrx_rgba_neon; |
| 325 | + |
| 326 | +#ifdef __cplusplus |
| 327 | +} |
| 328 | +#endif |
| 329 | + |
| 330 | +#endif |
| 331 | + |
| 332 | --- /dev/null |
| 333 | +++ b/modules/hw/mmal/blend_test.c |
| 334 | @@ -0,0 +1,180 @@ |
| 335 | +#include <stdio.h> |
| 336 | +#include <stdint.h> |
| 337 | +#include <memory.h> |
| 338 | + |
| 339 | +#include "blend_rgba_neon.h" |
| 340 | + |
| 341 | +#define RPI_PROFILE 1 |
| 342 | +#define RPI_PROC_ALLOC 1 |
| 343 | +#include "rpi_prof.h" |
| 344 | + |
| 345 | +static inline unsigned div255(unsigned v) |
| 346 | +{ |
| 347 | + // This models what we we do in the asm for / 255 |
| 348 | + // It generates something in the range [(i+126)/255, (i+127)/255] which is good enough |
| 349 | + return ((v * 257) + 0x8000) >> 16; |
| 350 | +} |
| 351 | + |
| 352 | +static inline unsigned int a_merge(unsigned int dst, unsigned src, unsigned f) |
| 353 | +{ |
| 354 | + return div255((255 - f) * (dst) + src * f); |
| 355 | +} |
| 356 | + |
| 357 | + |
| 358 | +static void merge_line(void * dest, const void * src, int alpha, unsigned int n) |
| 359 | +{ |
| 360 | + unsigned int i; |
| 361 | + const uint8_t * s_data = src; |
| 362 | + uint8_t * d_data = dest; |
| 363 | + |
| 364 | + for (i = 0; i != n; ++i) { |
| 365 | + const uint32_t s_pel = ((const uint32_t *)s_data)[i]; |
| 366 | + const uint32_t d_pel = ((const uint32_t *)d_data)[i]; |
| 367 | + const unsigned int a = div255(alpha * (s_pel >> 24)); |
| 368 | + ((uint32_t *)d_data)[i] = 0xff000000 | |
| 369 | + (a_merge((d_pel >> 16) & 0xff, (s_pel >> 16) & 0xff, a) << 16) | |
| 370 | + (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) | |
| 371 | + (a_merge((d_pel >> 0) & 0xff, (s_pel >> 0) & 0xff, a) << 0 ); |
| 372 | + } |
| 373 | +} |
| 374 | + |
| 375 | + |
| 376 | +// Merge RGBA with BGRA |
| 377 | +static void merge_line2(void * dest, const void * src, int alpha, unsigned int n) |
| 378 | +{ |
| 379 | + unsigned int i; |
| 380 | + const uint8_t * s_data = src; |
| 381 | + uint8_t * d_data = dest; |
| 382 | + |
| 383 | + for (i = 0; i != n; ++i) { |
| 384 | + const uint32_t s_pel = ((const uint32_t *)s_data)[i]; |
| 385 | + const uint32_t d_pel = ((const uint32_t *)d_data)[i]; |
| 386 | + const unsigned int a = div255(alpha * (s_pel >> 24)); |
| 387 | + ((uint32_t *)d_data)[i] = 0xff000000 | |
| 388 | + (a_merge((d_pel >> 0) & 0xff, (s_pel >> 16) & 0xff, a) << 0 ) | |
| 389 | + (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) | |
| 390 | + (a_merge((d_pel >> 16) & 0xff, (s_pel >> 0) & 0xff, a) << 16); |
| 391 | + } |
| 392 | +} |
| 393 | + |
| 394 | +#define BUF_SIZE 256 |
| 395 | +#define BUF_SLACK 16 |
| 396 | +#define BUF_ALIGN 64 |
| 397 | +#define BUF_ALLOC (BUF_SIZE + 2*BUF_SLACK + BUF_ALIGN) |
| 398 | + |
| 399 | +static void test_line(const uint32_t * const dx, const unsigned int d_off, |
| 400 | + const uint32_t * const sx, const unsigned int s_off, |
| 401 | + const unsigned int alpha, const unsigned int len, const int prof_no) |
| 402 | +{ |
| 403 | + uint32_t d0_buf[BUF_ALLOC]; |
| 404 | + uint32_t d1_buf[BUF_ALLOC]; |
| 405 | + const uint32_t * const s0 = sx + s_off; |
| 406 | + |
| 407 | + uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off; |
| 408 | + uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off; |
| 409 | + unsigned int i; |
| 410 | + |
| 411 | + memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4); |
| 412 | + memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4); |
| 413 | + |
| 414 | + merge_line(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len); |
| 415 | + |
| 416 | + PROFILE_START(); |
| 417 | + blend_rgbx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len); |
| 418 | + PROFILE_ACC_N(prof_no); |
| 419 | + |
| 420 | + for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) { |
| 421 | + if (d0[i] != d1[i]) { |
| 422 | + printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len); |
| 423 | + } |
| 424 | + } |
| 425 | +} |
| 426 | + |
| 427 | +static void test_line2(const uint32_t * const dx, const unsigned int d_off, |
| 428 | + const uint32_t * const sx, const unsigned int s_off, |
| 429 | + const unsigned int alpha, const unsigned int len, const int prof_no) |
| 430 | +{ |
| 431 | + uint32_t d0_buf[BUF_ALLOC]; |
| 432 | + uint32_t d1_buf[BUF_ALLOC]; |
| 433 | + const uint32_t * const s0 = sx + s_off; |
| 434 | + |
| 435 | + uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off; |
| 436 | + uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off; |
| 437 | + unsigned int i; |
| 438 | + |
| 439 | + memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4); |
| 440 | + memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4); |
| 441 | + |
| 442 | + merge_line2(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len); |
| 443 | + |
| 444 | + PROFILE_START(); |
| 445 | + blend_bgrx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len); |
| 446 | + PROFILE_ACC_N(prof_no); |
| 447 | + |
| 448 | + for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) { |
| 449 | + if (d0[i] != d1[i]) { |
| 450 | + printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len); |
| 451 | + } |
| 452 | + } |
| 453 | +} |
| 454 | + |
| 455 | + |
| 456 | + |
| 457 | +int main(int argc, char *argv[]) |
| 458 | +{ |
| 459 | + unsigned int i, j; |
| 460 | + uint32_t d0_buf[BUF_ALLOC]; |
| 461 | + uint32_t s0_buf[BUF_ALLOC]; |
| 462 | + |
| 463 | + uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + 63) & ~63) + 0; |
| 464 | + uint32_t * const s0 = (uint32_t *)(((uintptr_t)s0_buf + 63) & ~63) + 0; |
| 465 | + |
| 466 | + PROFILE_INIT(); |
| 467 | + |
| 468 | + for (i = 0; i != 255*255; ++i) { |
| 469 | + unsigned int a = div255(i); |
| 470 | + unsigned int b = (i + 127)/255; |
| 471 | + unsigned int c = (i + 126)/255; |
| 472 | + if (a != b && a != c) |
| 473 | + printf("%d/255: %d != %d/%d\n", i, a, b, c); |
| 474 | + } |
| 475 | + |
| 476 | + for (i = 0; i != BUF_ALLOC; ++i) { |
| 477 | + d0_buf[i] = 0xff00 | i; |
| 478 | + s0_buf[i] = (i << 24) | 0x40ffc0; |
| 479 | + } |
| 480 | + |
| 481 | + for (i = 0; i != 256; ++i) { |
| 482 | + test_line(d0, 0, s0, 0, i, 256, -1); |
| 483 | + } |
| 484 | + for (i = 0; i != 256; ++i) { |
| 485 | + test_line(d0, 0, s0, 0, 128, i, -1); |
| 486 | + } |
| 487 | + |
| 488 | + for (j = 0; j != 16; ++j) { |
| 489 | + for (i = 0; i != 256; ++i) { |
| 490 | + test_line(d0, j & 3, s0, j >> 2, i, 256, j); |
| 491 | + } |
| 492 | + PROFILE_PRINTF_N(j); |
| 493 | + PROFILE_CLEAR_N(j); |
| 494 | + } |
| 495 | + printf("Done 1\n"); |
| 496 | + |
| 497 | + for (i = 0; i != 256; ++i) { |
| 498 | + test_line2(d0, 0, s0, 0, i, 256, -1); |
| 499 | + } |
| 500 | + for (i = 0; i != 256; ++i) { |
| 501 | + test_line2(d0, 0, s0, 0, 128, i, -1); |
| 502 | + } |
| 503 | + |
| 504 | + for (j = 0; j != 16; ++j) { |
| 505 | + for (i = 0; i != 256; ++i) { |
| 506 | + test_line2(d0, j & 3, s0, j >> 2, i, 256, j); |
| 507 | + } |
| 508 | + PROFILE_PRINTF_N(j); |
| 509 | + } |
| 510 | + printf("Done 2\n"); |
| 511 | + |
| 512 | + return 0; |
| 513 | +} |
| 514 | + |
| 515 | --- a/modules/hw/mmal/codec.c |
| 516 | +++ b/modules/hw/mmal/codec.c |
| 517 | @@ -26,267 +26,443 @@ |
| 518 | #include "config.h" |
| 519 | #endif |
| 520 | |
| 521 | +#include <stdatomic.h> |
| 522 | + |
| 523 | #include <vlc_common.h> |
| 524 | -#include <vlc_atomic.h> |
| 525 | #include <vlc_plugin.h> |
| 526 | #include <vlc_codec.h> |
| 527 | +#include <vlc_filter.h> |
| 528 | #include <vlc_threads.h> |
| 529 | |
| 530 | -#include <bcm_host.h> |
| 531 | #include <interface/mmal/mmal.h> |
| 532 | #include <interface/mmal/util/mmal_util.h> |
| 533 | #include <interface/mmal/util/mmal_default_components.h> |
| 534 | |
| 535 | +#include <interface/vcsm/user-vcsm.h> |
| 536 | + |
| 537 | +#include "mmal_cma.h" |
| 538 | #include "mmal_picture.h" |
| 539 | |
| 540 | +#include "subpic.h" |
| 541 | +#include "blend_rgba_neon.h" |
| 542 | + |
| 543 | +#define TRACE_ALL 0 |
| 544 | + |
| 545 | +#define OPT_TO_FROM_ZC 0 |
| 546 | + |
| 547 | /* |
| 548 | * This seems to be a bit high, but reducing it causes instabilities |
| 549 | */ |
| 550 | #define NUM_EXTRA_BUFFERS 5 |
| 551 | +//#define NUM_EXTRA_BUFFERS 10 |
| 552 | #define NUM_DECODER_BUFFER_HEADERS 30 |
| 553 | |
| 554 | -#define MIN_NUM_BUFFERS_IN_TRANSIT 2 |
| 555 | +#define CONVERTER_BUFFERS 4 // Buffers on the output of the converter |
| 556 | + |
| 557 | +#define MMAL_SLICE_HEIGHT 16 |
| 558 | +#define MMAL_ALIGN_W 32 |
| 559 | +#define MMAL_ALIGN_H 16 |
| 560 | |
| 561 | #define MMAL_OPAQUE_NAME "mmal-opaque" |
| 562 | #define MMAL_OPAQUE_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.") |
| 563 | #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.") |
| 564 | |
| 565 | -static int OpenDecoder(decoder_t *dec); |
| 566 | -static void CloseDecoder(decoder_t *dec); |
| 567 | - |
| 568 | -vlc_module_begin() |
| 569 | - set_shortname(N_("MMAL decoder")) |
| 570 | - set_description(N_("MMAL-based decoder plugin for Raspberry Pi")) |
| 571 | - set_capability("video decoder", 90) |
| 572 | - add_shortcut("mmal_decoder") |
| 573 | - add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false) |
| 574 | - set_callbacks(OpenDecoder, CloseDecoder) |
| 575 | -vlc_module_end() |
| 576 | +#define MMAL_RESIZE_NAME "mmal-resize" |
| 577 | +#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.") |
| 578 | +#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.") |
| 579 | + |
| 580 | +#define MMAL_ISP_NAME "mmal-isp" |
| 581 | +#define MMAL_ISP_TEXT N_("Use mmal isp rather than hvs.") |
| 582 | +#define MMAL_ISP_LONGTEXT N_("Use mmal isp rather than hvs. This may be faster but has no blend.") |
| 583 | |
| 584 | -struct decoder_sys_t { |
| 585 | - bool opaque; |
| 586 | +typedef struct decoder_sys_t |
| 587 | +{ |
| 588 | MMAL_COMPONENT_T *component; |
| 589 | MMAL_PORT_T *input; |
| 590 | MMAL_POOL_T *input_pool; |
| 591 | MMAL_PORT_T *output; |
| 592 | - MMAL_POOL_T *output_pool; /* only used for non-opaque mode */ |
| 593 | + hw_mmal_port_pool_ref_t *ppr; |
| 594 | MMAL_ES_FORMAT_T *output_format; |
| 595 | - vlc_sem_t sem; |
| 596 | |
| 597 | + MMAL_STATUS_T err_stream; |
| 598 | bool b_top_field_first; |
| 599 | bool b_progressive; |
| 600 | |
| 601 | + bool b_flushed; |
| 602 | + |
| 603 | + vcsm_init_type_t vcsm_init_type; |
| 604 | + |
| 605 | + // Lock to avoid pic update & allocate happenening simultainiously |
| 606 | + // * We should be able to arrange life s.t. this isn't needed |
| 607 | + // but while we are confused apply belt & braces |
| 608 | + vlc_mutex_t pic_lock; |
| 609 | + |
| 610 | /* statistics */ |
| 611 | - int output_in_transit; |
| 612 | - int input_in_transit; |
| 613 | atomic_bool started; |
| 614 | -}; |
| 615 | +} decoder_sys_t; |
| 616 | |
| 617 | -/* Utilities */ |
| 618 | -static int change_output_format(decoder_t *dec); |
| 619 | -static int send_output_buffer(decoder_t *dec); |
| 620 | -static void fill_output_port(decoder_t *dec); |
| 621 | - |
| 622 | -/* VLC decoder callback */ |
| 623 | -static int decode(decoder_t *dec, block_t *block); |
| 624 | -static void flush_decoder(decoder_t *dec); |
| 625 | - |
| 626 | -/* MMAL callbacks */ |
| 627 | -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
| 628 | -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
| 629 | -static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
| 630 | |
| 631 | -static int OpenDecoder(decoder_t *dec) |
| 632 | -{ |
| 633 | - int ret = VLC_SUCCESS; |
| 634 | - decoder_sys_t *sys; |
| 635 | - MMAL_PARAMETER_UINT32_T extra_buffers; |
| 636 | - MMAL_STATUS_T status; |
| 637 | +typedef struct supported_mmal_enc_s { |
| 638 | + struct { |
| 639 | + MMAL_PARAMETER_HEADER_T header; |
| 640 | + MMAL_FOURCC_T encodings[64]; |
| 641 | + } supported; |
| 642 | + int n; |
| 643 | +} supported_mmal_enc_t; |
| 644 | + |
| 645 | +#define SUPPORTED_MMAL_ENC_INIT \ |
| 646 | +{ \ |
| 647 | + {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}}, \ |
| 648 | + -1 \ |
| 649 | +} |
| 650 | |
| 651 | - if (dec->fmt_in.i_codec != VLC_CODEC_MPGV && |
| 652 | - dec->fmt_in.i_codec != VLC_CODEC_H264) |
| 653 | - return VLC_EGENERIC; |
| 654 | +static supported_mmal_enc_t supported_decode_in_enc = SUPPORTED_MMAL_ENC_INIT; |
| 655 | |
| 656 | - sys = calloc(1, sizeof(decoder_sys_t)); |
| 657 | - if (!sys) { |
| 658 | - ret = VLC_ENOMEM; |
| 659 | - goto out; |
| 660 | +static bool is_enc_supported(supported_mmal_enc_t * const support, const MMAL_FOURCC_T fcc) |
| 661 | +{ |
| 662 | + int i; |
| 663 | + |
| 664 | + if (fcc == 0) |
| 665 | + return false; |
| 666 | + if (support->n == -1) |
| 667 | + return true; // Unknown - say OK |
| 668 | + for (i = 0; i < support->n; ++i) { |
| 669 | + if (support->supported.encodings[i] == fcc) |
| 670 | + return true; |
| 671 | } |
| 672 | - dec->p_sys = sys; |
| 673 | + return false; |
| 674 | +} |
| 675 | |
| 676 | - sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME); |
| 677 | - bcm_host_init(); |
| 678 | +static bool set_and_test_enc_supported(supported_mmal_enc_t * const support, MMAL_PORT_T * port, const MMAL_FOURCC_T fcc) |
| 679 | +{ |
| 680 | + if (support->n >= 0) |
| 681 | + /* already done */; |
| 682 | + else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&support->supported) != MMAL_SUCCESS) |
| 683 | + support->n = 0; |
| 684 | + else |
| 685 | + support->n = (support->supported.header.size - sizeof(support->supported.header)) / |
| 686 | + sizeof(support->supported.encodings[0]); |
| 687 | |
| 688 | - status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component); |
| 689 | - if (status != MMAL_SUCCESS) { |
| 690 | - msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)", |
| 691 | - MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status)); |
| 692 | - ret = VLC_EGENERIC; |
| 693 | - goto out; |
| 694 | - } |
| 695 | + return is_enc_supported(support, fcc); |
| 696 | +} |
| 697 | |
| 698 | - sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec; |
| 699 | - status = mmal_port_enable(sys->component->control, control_port_cb); |
| 700 | - if (status != MMAL_SUCCESS) { |
| 701 | - msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)", |
| 702 | - sys->component->control->name, status, mmal_status_to_string(status)); |
| 703 | - ret = VLC_EGENERIC; |
| 704 | - goto out; |
| 705 | +static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc) |
| 706 | +{ |
| 707 | + switch (fcc){ |
| 708 | + case VLC_CODEC_MJPG: |
| 709 | + return MMAL_ENCODING_MJPEG; |
| 710 | + case VLC_CODEC_MP1V: |
| 711 | + return MMAL_ENCODING_MP1V; |
| 712 | + case VLC_CODEC_MPGV: |
| 713 | + case VLC_CODEC_MP2V: |
| 714 | + return MMAL_ENCODING_MP2V; |
| 715 | + case VLC_CODEC_H263: |
| 716 | + return MMAL_ENCODING_H263; |
| 717 | + case VLC_CODEC_MP4V: |
| 718 | + return MMAL_ENCODING_MP4V; |
| 719 | + case VLC_CODEC_H264: |
| 720 | + return MMAL_ENCODING_H264; |
| 721 | + case VLC_CODEC_VP6: |
| 722 | + return MMAL_ENCODING_VP6; |
| 723 | + case VLC_CODEC_VP8: |
| 724 | + return MMAL_ENCODING_VP8; |
| 725 | + case VLC_CODEC_WMV1: |
| 726 | + return MMAL_ENCODING_WMV1; |
| 727 | + case VLC_CODEC_WMV2: |
| 728 | + return MMAL_ENCODING_WMV2; |
| 729 | + case VLC_CODEC_WMV3: |
| 730 | + return MMAL_ENCODING_WMV3; |
| 731 | + case VLC_CODEC_VC1: |
| 732 | + return MMAL_ENCODING_WVC1; |
| 733 | + case VLC_CODEC_THEORA: |
| 734 | + return MMAL_ENCODING_THEORA; |
| 735 | + default: |
| 736 | + break; |
| 737 | } |
| 738 | + return 0; |
| 739 | +} |
| 740 | |
| 741 | - sys->input = sys->component->input[0]; |
| 742 | - sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec; |
| 743 | - if (dec->fmt_in.i_codec == VLC_CODEC_MPGV) |
| 744 | - sys->input->format->encoding = MMAL_ENCODING_MP2V; |
| 745 | - else |
| 746 | - sys->input->format->encoding = MMAL_ENCODING_H264; |
| 747 | +static MMAL_FOURCC_T pic_to_slice_mmal_fourcc(const MMAL_FOURCC_T fcc) |
| 748 | +{ |
| 749 | + switch (fcc){ |
| 750 | + case MMAL_ENCODING_I420: |
| 751 | + return MMAL_ENCODING_I420_SLICE; |
| 752 | + case MMAL_ENCODING_I422: |
| 753 | + return MMAL_ENCODING_I422_SLICE; |
| 754 | + case MMAL_ENCODING_ARGB: |
| 755 | + return MMAL_ENCODING_ARGB_SLICE; |
| 756 | + case MMAL_ENCODING_RGBA: |
| 757 | + return MMAL_ENCODING_RGBA_SLICE; |
| 758 | + case MMAL_ENCODING_ABGR: |
| 759 | + return MMAL_ENCODING_ABGR_SLICE; |
| 760 | + case MMAL_ENCODING_BGRA: |
| 761 | + return MMAL_ENCODING_BGRA_SLICE; |
| 762 | + case MMAL_ENCODING_RGB16: |
| 763 | + return MMAL_ENCODING_RGB16_SLICE; |
| 764 | + case MMAL_ENCODING_RGB24: |
| 765 | + return MMAL_ENCODING_RGB24_SLICE; |
| 766 | + case MMAL_ENCODING_RGB32: |
| 767 | + return MMAL_ENCODING_RGB32_SLICE; |
| 768 | + case MMAL_ENCODING_BGR16: |
| 769 | + return MMAL_ENCODING_BGR16_SLICE; |
| 770 | + case MMAL_ENCODING_BGR24: |
| 771 | + return MMAL_ENCODING_BGR24_SLICE; |
| 772 | + case MMAL_ENCODING_BGR32: |
| 773 | + return MMAL_ENCODING_BGR32_SLICE; |
| 774 | + default: |
| 775 | + break; |
| 776 | + } |
| 777 | + return 0; |
| 778 | +} |
| 779 | |
| 780 | - if (dec->fmt_in.i_codec == VLC_CODEC_H264) { |
| 781 | - if (dec->fmt_in.i_extra > 0) { |
| 782 | - status = mmal_format_extradata_alloc(sys->input->format, |
| 783 | - dec->fmt_in.i_extra); |
| 784 | - if (status == MMAL_SUCCESS) { |
| 785 | - memcpy(sys->input->format->extradata, dec->fmt_in.p_extra, |
| 786 | - dec->fmt_in.i_extra); |
| 787 | - sys->input->format->extradata_size = dec->fmt_in.i_extra; |
| 788 | - } else { |
| 789 | - msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)", |
| 790 | - sys->input->name, status, mmal_status_to_string(status)); |
| 791 | - } |
| 792 | +#define DEBUG_SQUARES 0 |
| 793 | +#if DEBUG_SQUARES |
| 794 | +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) |
| 795 | +{ |
| 796 | + uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x; |
| 797 | + unsigned int i; |
| 798 | + for (i = 0; i != h; ++i) { |
| 799 | + unsigned int j; |
| 800 | + for (j = 0; j != w; ++j) { |
| 801 | + p[j] = val; |
| 802 | } |
| 803 | + p += pic_stride; |
| 804 | } |
| 805 | +} |
| 806 | +#endif |
| 807 | |
| 808 | - status = mmal_port_format_commit(sys->input); |
| 809 | - if (status != MMAL_SUCCESS) { |
| 810 | - msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)", |
| 811 | - sys->input->name, status, mmal_status_to_string(status)); |
| 812 | - ret = VLC_EGENERIC; |
| 813 | - goto out; |
| 814 | +#if 0 |
| 815 | +static inline void draw_line(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int len, int inc) |
| 816 | +{ |
| 817 | + uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x; |
| 818 | + while (len-- != 0) { |
| 819 | + *p = ~0U; |
| 820 | + p += inc; |
| 821 | } |
| 822 | - sys->input->buffer_size = sys->input->buffer_size_recommended; |
| 823 | - sys->input->buffer_num = sys->input->buffer_num_recommended; |
| 824 | +} |
| 825 | |
| 826 | - status = mmal_port_enable(sys->input, input_port_cb); |
| 827 | - if (status != MMAL_SUCCESS) { |
| 828 | - msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)", |
| 829 | - sys->input->name, status, mmal_status_to_string(status)); |
| 830 | - ret = VLC_EGENERIC; |
| 831 | - goto out; |
| 832 | - } |
| 833 | |
| 834 | - sys->output = sys->component->output[0]; |
| 835 | - sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec; |
| 836 | +static void draw_corners(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h) |
| 837 | +{ |
| 838 | + const unsigned int len = 20; |
| 839 | + draw_line(pic_buf, pic_stride, x, y, len, 1); |
| 840 | + draw_line(pic_buf, pic_stride, x, y, len, pic_stride); |
| 841 | + draw_line(pic_buf, pic_stride, x + w - 1, y, len, -1); |
| 842 | + draw_line(pic_buf, pic_stride, x + w - 1, y, len, pic_stride); |
| 843 | + draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -1); |
| 844 | + draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -(int)pic_stride); |
| 845 | + draw_line(pic_buf, pic_stride, x, y + h - 1, len, 1); |
| 846 | + draw_line(pic_buf, pic_stride, x, y + h - 1, len, -(int)pic_stride); |
| 847 | +} |
| 848 | +#endif |
| 849 | |
| 850 | - if (sys->opaque) { |
| 851 | - extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS; |
| 852 | - extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T); |
| 853 | - extra_buffers.value = NUM_EXTRA_BUFFERS; |
| 854 | - status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr); |
| 855 | - if (status != MMAL_SUCCESS) { |
| 856 | - msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)", |
| 857 | - status, mmal_status_to_string(status)); |
| 858 | - ret = VLC_EGENERIC; |
| 859 | - goto out; |
| 860 | - } |
| 861 | +static MMAL_RATIONAL_T |
| 862 | +rationalize_sar(unsigned int num, unsigned int den) |
| 863 | +{ |
| 864 | + static const unsigned int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 0}; |
| 865 | + const unsigned int * p = primes; |
| 866 | |
| 867 | - msg_Dbg(dec, "Activate zero-copy for output port"); |
| 868 | - MMAL_PARAMETER_BOOLEAN_T zero_copy = { |
| 869 | - { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, |
| 870 | - 1 |
| 871 | - }; |
| 872 | + // If either num or den is 0 then return a well formed "unknown" |
| 873 | + if (num == 0 || den == 0) { |
| 874 | + return (MMAL_RATIONAL_T){.num = 0, .den = 0}; |
| 875 | + } |
| 876 | |
| 877 | - status = mmal_port_parameter_set(sys->output, &zero_copy.hdr); |
| 878 | - if (status != MMAL_SUCCESS) { |
| 879 | - msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", |
| 880 | - sys->output->name, status, mmal_status_to_string(status)); |
| 881 | - goto out; |
| 882 | + while (*p != 0 && num >= *p && den >= *p) { |
| 883 | + if (num % *p != 0 || den % *p != 0) |
| 884 | + ++p; |
| 885 | + else { |
| 886 | + num /= *p; |
| 887 | + den /= *p; |
| 888 | } |
| 889 | } |
| 890 | + return (MMAL_RATIONAL_T){.num = num, .den = den}; |
| 891 | +} |
| 892 | |
| 893 | - status = mmal_port_enable(sys->output, output_port_cb); |
| 894 | - if (status != MMAL_SUCCESS) { |
| 895 | - msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)", |
| 896 | - sys->output->name, status, mmal_status_to_string(status)); |
| 897 | - ret = VLC_EGENERIC; |
| 898 | - goto out; |
| 899 | - } |
| 900 | +// Buffer either attached to pic or released |
| 901 | +static picture_t * alloc_opaque_pic(decoder_t * const dec, MMAL_BUFFER_HEADER_T * const buf) |
| 902 | +{ |
| 903 | + decoder_sys_t *const dec_sys = dec->p_sys; |
| 904 | |
| 905 | - status = mmal_component_enable(sys->component); |
| 906 | - if (status != MMAL_SUCCESS) { |
| 907 | - msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)", |
| 908 | - sys->component->name, status, mmal_status_to_string(status)); |
| 909 | - ret = VLC_EGENERIC; |
| 910 | - goto out; |
| 911 | + vlc_mutex_lock(&dec_sys->pic_lock); |
| 912 | + picture_t * const pic = decoder_NewPicture(dec); |
| 913 | + vlc_mutex_unlock(&dec_sys->pic_lock); |
| 914 | + |
| 915 | + if (pic == NULL) |
| 916 | + goto fail1; |
| 917 | + |
| 918 | + if (buf->length == 0) { |
| 919 | + msg_Err(dec, "%s: Empty buffer", __func__); |
| 920 | + goto fail2; |
| 921 | } |
| 922 | |
| 923 | - sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0); |
| 924 | + if ((pic->context = hw_mmal_gen_context(buf, dec_sys->ppr)) == NULL) |
| 925 | + goto fail2; |
| 926 | |
| 927 | - if (sys->opaque) { |
| 928 | - dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE; |
| 929 | - dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE; |
| 930 | - } else { |
| 931 | - dec->fmt_out.i_codec = VLC_CODEC_I420; |
| 932 | - dec->fmt_out.video.i_chroma = VLC_CODEC_I420; |
| 933 | + buf_to_pic_copy_props(pic, buf); |
| 934 | + |
| 935 | +#if TRACE_ALL |
| 936 | + msg_Dbg(dec, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date); |
| 937 | +#endif |
| 938 | + |
| 939 | + return pic; |
| 940 | + |
| 941 | +fail2: |
| 942 | + picture_Release(pic); |
| 943 | +fail1: |
| 944 | + // Recycle rather than release to avoid buffer starvation if NewPic fails |
| 945 | + hw_mmal_port_pool_ref_recycle(dec_sys->ppr, buf); |
| 946 | + return NULL; |
| 947 | +} |
| 948 | + |
| 949 | +static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 950 | +{ |
| 951 | + decoder_t *dec = (decoder_t *)port->userdata; |
| 952 | + MMAL_STATUS_T status; |
| 953 | + |
| 954 | +#if TRACE_ALL |
| 955 | + msg_Dbg(dec, "<<< %s: cmd=%d, data=%p", __func__, buffer->cmd, buffer->data); |
| 956 | +#endif |
| 957 | + |
| 958 | + if (buffer->cmd == MMAL_EVENT_ERROR) { |
| 959 | + status = *(uint32_t *)buffer->data; |
| 960 | + dec->p_sys->err_stream = status; |
| 961 | + msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status, |
| 962 | + mmal_status_to_string(status)); |
| 963 | } |
| 964 | |
| 965 | - dec->pf_decode = decode; |
| 966 | - dec->pf_flush = flush_decoder; |
| 967 | + mmal_buffer_header_release(buffer); |
| 968 | +} |
| 969 | |
| 970 | - vlc_sem_init(&sys->sem, 0); |
| 971 | +static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 972 | +{ |
| 973 | + block_t * const block = (block_t *)buffer->user_data; |
| 974 | |
| 975 | -out: |
| 976 | - if (ret != VLC_SUCCESS) |
| 977 | - CloseDecoder(dec); |
| 978 | + (void)port; // Unused |
| 979 | |
| 980 | - return ret; |
| 981 | +#if TRACE_ALL |
| 982 | + msg_Dbg((decoder_t *)port->userdata, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__, |
| 983 | + buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts); |
| 984 | +#endif |
| 985 | + |
| 986 | + mmal_buffer_header_reset(buffer); |
| 987 | + mmal_buffer_header_release(buffer); |
| 988 | + |
| 989 | + if (block != NULL) |
| 990 | + block_Release(block); |
| 991 | } |
| 992 | |
| 993 | -static void CloseDecoder(decoder_t *dec) |
| 994 | +static void decoder_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 995 | { |
| 996 | - decoder_sys_t *sys = dec->p_sys; |
| 997 | - MMAL_BUFFER_HEADER_T *buffer; |
| 998 | + decoder_t * const dec = (decoder_t *)port->userdata; |
| 999 | |
| 1000 | - if (!sys) |
| 1001 | + if (buffer->cmd == 0 && buffer->length != 0) |
| 1002 | + { |
| 1003 | +#if TRACE_ALL |
| 1004 | + msg_Dbg(dec, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__, |
| 1005 | + buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts); |
| 1006 | +#endif |
| 1007 | + |
| 1008 | + picture_t *pic = alloc_opaque_pic(dec, buffer); |
| 1009 | +#if TRACE_ALL |
| 1010 | + msg_Dbg(dec, "flags=%#x, video flags=%#x", buffer->flags, buffer->type->video.flags); |
| 1011 | +#endif |
| 1012 | + if (pic == NULL) |
| 1013 | + msg_Err(dec, "Failed to allocate new picture"); |
| 1014 | + else |
| 1015 | + decoder_QueueVideo(dec, pic); |
| 1016 | + // Buffer released or attached to pic - do not release again |
| 1017 | return; |
| 1018 | + } |
| 1019 | |
| 1020 | - if (sys->component && sys->component->control->is_enabled) |
| 1021 | - mmal_port_disable(sys->component->control); |
| 1022 | + if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) |
| 1023 | + { |
| 1024 | + decoder_sys_t * const sys = dec->p_sys; |
| 1025 | + MMAL_EVENT_FORMAT_CHANGED_T * const fmt = mmal_event_format_changed_get(buffer); |
| 1026 | + MMAL_ES_FORMAT_T * const format = mmal_format_alloc(); |
| 1027 | |
| 1028 | - if (sys->input && sys->input->is_enabled) |
| 1029 | - mmal_port_disable(sys->input); |
| 1030 | + if (format == NULL) |
| 1031 | + msg_Err(dec, "Failed to allocate new format"); |
| 1032 | + else |
| 1033 | + { |
| 1034 | + mmal_format_full_copy(format, fmt->format); |
| 1035 | + format->encoding = MMAL_ENCODING_OPAQUE; |
| 1036 | |
| 1037 | - if (sys->output && sys->output->is_enabled) |
| 1038 | - mmal_port_disable(sys->output); |
| 1039 | + // If no PAR in the stream - see if we've got one from the demux |
| 1040 | + if (format->es->video.par.den <= 0 || format->es->video.par.num <= 0) { |
| 1041 | + unsigned int n = dec->fmt_in.video.i_sar_num; |
| 1042 | + unsigned int d = dec->fmt_in.video.i_sar_den; |
| 1043 | + |
| 1044 | + if (n == 0 || d == 0) { |
| 1045 | + // Guesswork required |
| 1046 | + const unsigned int w = format->es->video.width; |
| 1047 | + const unsigned int h = format->es->video.height; |
| 1048 | + if ((w == 704 || w == 720) && (h == 480 || h == 576)) { |
| 1049 | + // Very likely SD 4:3 |
| 1050 | + n = w * 3; |
| 1051 | + d = h * 4; |
| 1052 | + } |
| 1053 | + else |
| 1054 | + { |
| 1055 | + // Otherwise guess SAR 1:1 |
| 1056 | + n = 1; |
| 1057 | + d = 1; |
| 1058 | + } |
| 1059 | + } |
| 1060 | |
| 1061 | - if (sys->component && sys->component->is_enabled) |
| 1062 | - mmal_component_disable(sys->component); |
| 1063 | + format->es->video.par = rationalize_sar(n, d); |
| 1064 | + } |
| 1065 | |
| 1066 | - if (sys->input_pool) |
| 1067 | - mmal_pool_destroy(sys->input_pool); |
| 1068 | + if (sys->output_format != NULL) |
| 1069 | + mmal_format_free(sys->output_format); |
| 1070 | |
| 1071 | - if (sys->output_format) |
| 1072 | - mmal_format_free(sys->output_format); |
| 1073 | + sys->output_format = format; |
| 1074 | + } |
| 1075 | + } |
| 1076 | + else if (buffer->cmd != 0) { |
| 1077 | + char buf0[5]; |
| 1078 | + msg_Warn(dec, "Unexpected output cb event: %s", str_fourcc(buf0, buffer->cmd)); |
| 1079 | + } |
| 1080 | |
| 1081 | - if (sys->output_pool) |
| 1082 | - mmal_pool_destroy(sys->output_pool); |
| 1083 | + // If we get here then we were flushing (cmd == 0 && len == 0) or |
| 1084 | + // that was an EVENT - in either case we want to release the buffer |
| 1085 | + // back to its pool rather than recycle it. |
| 1086 | + mmal_buffer_header_reset(buffer); |
| 1087 | + buffer->user_data = NULL; |
| 1088 | + mmal_buffer_header_release(buffer); |
| 1089 | +} |
| 1090 | |
| 1091 | - if (sys->component) |
| 1092 | - mmal_component_release(sys->component); |
| 1093 | |
| 1094 | - vlc_sem_destroy(&sys->sem); |
| 1095 | - free(sys); |
| 1096 | |
| 1097 | - bcm_host_deinit(); |
| 1098 | +static void fill_output_port(decoder_t *dec) |
| 1099 | +{ |
| 1100 | + decoder_sys_t *sys = dec->p_sys; |
| 1101 | + |
| 1102 | + if (decoder_UpdateVideoFormat(dec) != 0) |
| 1103 | + { |
| 1104 | + // If we have a new format don't bother stuffing the buffer |
| 1105 | + // We should get a reset RSN |
| 1106 | +#if TRACE_ALL |
| 1107 | + msg_Dbg(dec, "%s: Updated", __func__); |
| 1108 | +#endif |
| 1109 | + |
| 1110 | + return; |
| 1111 | + } |
| 1112 | + |
| 1113 | + hw_mmal_port_pool_ref_fill(sys->ppr); |
| 1114 | + return; |
| 1115 | } |
| 1116 | |
| 1117 | static int change_output_format(decoder_t *dec) |
| 1118 | { |
| 1119 | MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type; |
| 1120 | - decoder_sys_t *sys = dec->p_sys; |
| 1121 | + decoder_sys_t * const sys = dec->p_sys; |
| 1122 | MMAL_STATUS_T status; |
| 1123 | - int pool_size; |
| 1124 | int ret = 0; |
| 1125 | |
| 1126 | +#if TRACE_ALL |
| 1127 | + msg_Dbg(dec, "%s: <<<", __func__); |
| 1128 | +#endif |
| 1129 | + |
| 1130 | if (atomic_load(&sys->started)) { |
| 1131 | mmal_format_full_copy(sys->output->format, sys->output_format); |
| 1132 | status = mmal_port_format_commit(sys->output); |
| 1133 | @@ -300,7 +476,9 @@ static int change_output_format(decoder_ |
| 1134 | } |
| 1135 | |
| 1136 | port_reset: |
| 1137 | +#if TRACE_ALL |
| 1138 | msg_Dbg(dec, "%s: Do full port reset", __func__); |
| 1139 | +#endif |
| 1140 | status = mmal_port_disable(sys->output); |
| 1141 | if (status != MMAL_SUCCESS) { |
| 1142 | msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)", |
| 1143 | @@ -310,6 +488,7 @@ port_reset: |
| 1144 | } |
| 1145 | |
| 1146 | mmal_format_full_copy(sys->output->format, sys->output_format); |
| 1147 | + |
| 1148 | status = mmal_port_format_commit(sys->output); |
| 1149 | if (status != MMAL_SUCCESS) { |
| 1150 | msg_Err(dec, "Failed to commit output format (status=%"PRIx32" %s)", |
| 1151 | @@ -318,18 +497,10 @@ port_reset: |
| 1152 | goto out; |
| 1153 | } |
| 1154 | |
| 1155 | - if (sys->opaque) { |
| 1156 | - sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS; |
| 1157 | - pool_size = NUM_DECODER_BUFFER_HEADERS; |
| 1158 | - } else { |
| 1159 | - sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended, |
| 1160 | - MIN_NUM_BUFFERS_IN_TRANSIT); |
| 1161 | - pool_size = sys->output->buffer_num; |
| 1162 | - } |
| 1163 | - |
| 1164 | + sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS; |
| 1165 | sys->output->buffer_size = sys->output->buffer_size_recommended; |
| 1166 | |
| 1167 | - status = mmal_port_enable(sys->output, output_port_cb); |
| 1168 | + status = mmal_port_enable(sys->output, decoder_output_cb); |
| 1169 | if (status != MMAL_SUCCESS) { |
| 1170 | msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)", |
| 1171 | status, mmal_status_to_string(status)); |
| 1172 | @@ -338,25 +509,14 @@ port_reset: |
| 1173 | } |
| 1174 | |
| 1175 | if (!atomic_load(&sys->started)) { |
| 1176 | - if (!sys->opaque) { |
| 1177 | - sys->output_pool = mmal_port_pool_create(sys->output, pool_size, 0); |
| 1178 | - msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num); |
| 1179 | - } |
| 1180 | - |
| 1181 | atomic_store(&sys->started, true); |
| 1182 | |
| 1183 | /* we need one picture from vout for each buffer header on the output |
| 1184 | * port */ |
| 1185 | - dec->i_extra_picture_buffers = pool_size; |
| 1186 | - |
| 1187 | - /* remove what VLC core reserves as it is part of the pool_size |
| 1188 | - * already */ |
| 1189 | - if (dec->fmt_in.i_codec == VLC_CODEC_H264) |
| 1190 | - dec->i_extra_picture_buffers -= 19; |
| 1191 | - else |
| 1192 | - dec->i_extra_picture_buffers -= 3; |
| 1193 | - |
| 1194 | + dec->i_extra_picture_buffers = 10; |
| 1195 | +#if TRACE_ALL |
| 1196 | msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers); |
| 1197 | +#endif |
| 1198 | } |
| 1199 | |
| 1200 | apply_fmt: |
| 1201 | @@ -366,8 +526,8 @@ apply_fmt: |
| 1202 | dec->fmt_out.video.i_y_offset = sys->output->format->es->video.crop.y; |
| 1203 | dec->fmt_out.video.i_visible_width = sys->output->format->es->video.crop.width; |
| 1204 | dec->fmt_out.video.i_visible_height = sys->output->format->es->video.crop.height; |
| 1205 | - dec->fmt_out.video.i_sar_num = sys->output->format->es->video.par.num; |
| 1206 | - dec->fmt_out.video.i_sar_den = sys->output->format->es->video.par.den; |
| 1207 | + dec->fmt_out.video.i_sar_num = sys->output_format->es->video.par.num; // SAR can be killed by commit |
| 1208 | + dec->fmt_out.video.i_sar_den = sys->output_format->es->video.par.den; |
| 1209 | dec->fmt_out.video.i_frame_rate = sys->output->format->es->video.frame_rate.num; |
| 1210 | dec->fmt_out.video.i_frame_rate_base = sys->output->format->es->video.frame_rate.den; |
| 1211 | |
| 1212 | @@ -382,12 +542,19 @@ apply_fmt: |
| 1213 | sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive); |
| 1214 | sys->b_top_field_first = sys->b_progressive ? true : |
| 1215 | (interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst); |
| 1216 | +#if TRACE_ALL |
| 1217 | msg_Dbg(dec, "Detected %s%s video (%d)", |
| 1218 | sys->b_progressive ? "progressive" : "interlaced", |
| 1219 | sys->b_progressive ? "" : (sys->b_top_field_first ? " tff" : " bff"), |
| 1220 | interlace_type.eMode); |
| 1221 | +#endif |
| 1222 | } |
| 1223 | |
| 1224 | + // Tell the rest of the world we have changed format |
| 1225 | + vlc_mutex_lock(&sys->pic_lock); |
| 1226 | + ret = decoder_UpdateVideoFormat(dec); |
| 1227 | + vlc_mutex_unlock(&sys->pic_lock); |
| 1228 | + |
| 1229 | out: |
| 1230 | mmal_format_free(sys->output_format); |
| 1231 | sys->output_format = NULL; |
| 1232 | @@ -395,144 +562,85 @@ out: |
| 1233 | return ret; |
| 1234 | } |
| 1235 | |
| 1236 | -static int send_output_buffer(decoder_t *dec) |
| 1237 | +static MMAL_STATUS_T |
| 1238 | +set_extradata_and_commit(decoder_t * const dec, decoder_sys_t * const sys) |
| 1239 | { |
| 1240 | - decoder_sys_t *sys = dec->p_sys; |
| 1241 | - MMAL_BUFFER_HEADER_T *buffer; |
| 1242 | - picture_sys_t *p_sys; |
| 1243 | - picture_t *picture = NULL; |
| 1244 | MMAL_STATUS_T status; |
| 1245 | - unsigned buffer_size = 0; |
| 1246 | - int ret = 0; |
| 1247 | |
| 1248 | - if (!sys->output->is_enabled) |
| 1249 | - return VLC_EGENERIC; |
| 1250 | - |
| 1251 | - /* If local output pool is allocated, use it - this is only the case for |
| 1252 | - * non-opaque modes */ |
| 1253 | - if (sys->output_pool) { |
| 1254 | - buffer = mmal_queue_get(sys->output_pool->queue); |
| 1255 | - if (!buffer) { |
| 1256 | - msg_Warn(dec, "Failed to get new buffer"); |
| 1257 | - return VLC_EGENERIC; |
| 1258 | - } |
| 1259 | - } |
| 1260 | - |
| 1261 | - if (!decoder_UpdateVideoFormat(dec)) |
| 1262 | - picture = decoder_NewPicture(dec); |
| 1263 | - if (!picture) { |
| 1264 | - msg_Warn(dec, "Failed to get new picture"); |
| 1265 | - ret = -1; |
| 1266 | - goto err; |
| 1267 | - } |
| 1268 | - |
| 1269 | - p_sys = picture->p_sys; |
| 1270 | - for (int i = 0; i < picture->i_planes; i++) |
| 1271 | - buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch; |
| 1272 | - |
| 1273 | - if (sys->output_pool) { |
| 1274 | - mmal_buffer_header_reset(buffer); |
| 1275 | - buffer->alloc_size = sys->output->buffer_size; |
| 1276 | - if (buffer_size < sys->output->buffer_size) { |
| 1277 | - msg_Err(dec, "Retrieved picture with too small data block (%d < %d)", |
| 1278 | - buffer_size, sys->output->buffer_size); |
| 1279 | - ret = VLC_EGENERIC; |
| 1280 | - goto err; |
| 1281 | - } |
| 1282 | - |
| 1283 | - if (!sys->opaque) |
| 1284 | - buffer->data = picture->p[0].p_pixels; |
| 1285 | - } else { |
| 1286 | - buffer = p_sys->buffer; |
| 1287 | - if (!buffer) { |
| 1288 | - msg_Warn(dec, "Picture has no buffer attached"); |
| 1289 | - picture_Release(picture); |
| 1290 | - return VLC_EGENERIC; |
| 1291 | - } |
| 1292 | - buffer->data = p_sys->buffer->data; |
| 1293 | - } |
| 1294 | - buffer->user_data = picture; |
| 1295 | - buffer->cmd = 0; |
| 1296 | - |
| 1297 | - status = mmal_port_send_buffer(sys->output, buffer); |
| 1298 | + status = mmal_port_format_commit(sys->input); |
| 1299 | if (status != MMAL_SUCCESS) { |
| 1300 | - msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)", |
| 1301 | - status, mmal_status_to_string(status)); |
| 1302 | - ret = -1; |
| 1303 | - goto err; |
| 1304 | - } |
| 1305 | - atomic_fetch_add(&sys->output_in_transit, 1); |
| 1306 | - |
| 1307 | - return ret; |
| 1308 | - |
| 1309 | -err: |
| 1310 | - if (picture) |
| 1311 | - picture_Release(picture); |
| 1312 | - if (sys->output_pool && buffer) { |
| 1313 | - buffer->data = NULL; |
| 1314 | - mmal_buffer_header_release(buffer); |
| 1315 | + msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)", |
| 1316 | + sys->input->name, status, mmal_status_to_string(status)); |
| 1317 | } |
| 1318 | - return ret; |
| 1319 | + return status; |
| 1320 | } |
| 1321 | |
| 1322 | -static void fill_output_port(decoder_t *dec) |
| 1323 | +static MMAL_STATUS_T decoder_send_extradata(decoder_t * const dec, decoder_sys_t *const sys) |
| 1324 | { |
| 1325 | - decoder_sys_t *sys = dec->p_sys; |
| 1326 | - |
| 1327 | - unsigned max_buffers_in_transit = 0; |
| 1328 | - int buffers_available = 0; |
| 1329 | - int buffers_to_send = 0; |
| 1330 | - int i; |
| 1331 | + if (dec->fmt_in.i_codec == VLC_CODEC_H264 && |
| 1332 | + dec->fmt_in.i_extra > 0) |
| 1333 | + { |
| 1334 | + MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->input_pool->queue); |
| 1335 | + MMAL_STATUS_T status; |
| 1336 | + |
| 1337 | + mmal_buffer_header_reset(buf); |
| 1338 | + buf->cmd = 0; |
| 1339 | + buf->user_data = NULL; |
| 1340 | + buf->alloc_size = sys->input->buffer_size; |
| 1341 | + buf->length = dec->fmt_in.i_extra; |
| 1342 | + buf->data = dec->fmt_in.p_extra; |
| 1343 | + buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG; |
| 1344 | |
| 1345 | - if (sys->output_pool) { |
| 1346 | - max_buffers_in_transit = __MAX(sys->output_pool->headers_num, |
| 1347 | - MIN_NUM_BUFFERS_IN_TRANSIT); |
| 1348 | - buffers_available = mmal_queue_length(sys->output_pool->queue); |
| 1349 | - } else { |
| 1350 | - max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS; |
| 1351 | - buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit); |
| 1352 | + status = mmal_port_send_buffer(sys->input, buf); |
| 1353 | + if (status != MMAL_SUCCESS) { |
| 1354 | + msg_Err(dec, "Failed to send extradata buffer to input port (status=%"PRIx32" %s)", |
| 1355 | + status, mmal_status_to_string(status)); |
| 1356 | + return status; |
| 1357 | + } |
| 1358 | } |
| 1359 | - buffers_to_send = max_buffers_in_transit - atomic_load(&sys->output_in_transit); |
| 1360 | |
| 1361 | - if (buffers_to_send > buffers_available) |
| 1362 | - buffers_to_send = buffers_available; |
| 1363 | - |
| 1364 | -#ifndef NDEBUG |
| 1365 | - msg_Dbg(dec, "Send %d buffers to output port (available: %d, " |
| 1366 | - "in_transit: %d, buffer_num: %d)", |
| 1367 | - buffers_to_send, buffers_available, |
| 1368 | - atomic_load(&sys->output_in_transit), |
| 1369 | - sys->output->buffer_num); |
| 1370 | -#endif |
| 1371 | - for (i = 0; i < buffers_to_send; ++i) |
| 1372 | - if (send_output_buffer(dec) < 0) |
| 1373 | - break; |
| 1374 | + return MMAL_SUCCESS; |
| 1375 | } |
| 1376 | |
| 1377 | static void flush_decoder(decoder_t *dec) |
| 1378 | { |
| 1379 | - decoder_sys_t *sys = dec->p_sys; |
| 1380 | - MMAL_BUFFER_HEADER_T *buffer; |
| 1381 | - MMAL_STATUS_T status; |
| 1382 | + decoder_sys_t *const sys = dec->p_sys; |
| 1383 | |
| 1384 | - msg_Dbg(dec, "Flushing decoder ports..."); |
| 1385 | - mmal_port_flush(sys->output); |
| 1386 | - mmal_port_flush(sys->input); |
| 1387 | - |
| 1388 | - while (atomic_load(&sys->output_in_transit) || |
| 1389 | - atomic_load(&sys->input_in_transit)) |
| 1390 | - vlc_sem_wait(&sys->sem); |
| 1391 | +#if TRACE_ALL |
| 1392 | + msg_Dbg(dec, "%s: <<<", __func__); |
| 1393 | +#endif |
| 1394 | + |
| 1395 | + if (!sys->b_flushed) { |
| 1396 | + mmal_port_disable(sys->input); |
| 1397 | + mmal_port_disable(sys->output); |
| 1398 | + // We can leave the input disabled, but we want the output enabled |
| 1399 | + // in order to sink any buffers returning from other modules |
| 1400 | + mmal_port_enable(sys->output, decoder_output_cb); |
| 1401 | + sys->b_flushed = true; |
| 1402 | + } |
| 1403 | +#if TRACE_ALL |
| 1404 | + msg_Dbg(dec, "%s: >>>", __func__); |
| 1405 | +#endif |
| 1406 | } |
| 1407 | |
| 1408 | static int decode(decoder_t *dec, block_t *block) |
| 1409 | { |
| 1410 | decoder_sys_t *sys = dec->p_sys; |
| 1411 | MMAL_BUFFER_HEADER_T *buffer; |
| 1412 | - bool need_flush = false; |
| 1413 | uint32_t len; |
| 1414 | - uint32_t flags = 0; |
| 1415 | + uint32_t flags = MMAL_BUFFER_HEADER_FLAG_FRAME_START; |
| 1416 | MMAL_STATUS_T status; |
| 1417 | |
| 1418 | +#if TRACE_ALL |
| 1419 | + msg_Dbg(dec, "<<< %s: %lld/%lld", __func__, block == NULL ? -1LL : block->i_dts, block == NULL ? -1LL : block->i_pts); |
| 1420 | +#endif |
| 1421 | + |
| 1422 | + if (sys->err_stream != MMAL_SUCCESS) { |
| 1423 | + msg_Err(dec, "MMAL error reported by ctrl"); |
| 1424 | + flush_decoder(dec); |
| 1425 | + return VLCDEC_ECRITICAL; /// I think they are all fatal |
| 1426 | + } |
| 1427 | + |
| 1428 | /* |
| 1429 | * Configure output port if necessary |
| 1430 | */ |
| 1431 | @@ -541,18 +649,50 @@ static int decode(decoder_t *dec, block_ |
| 1432 | msg_Err(dec, "Failed to change output port format"); |
| 1433 | } |
| 1434 | |
| 1435 | - if (!block) |
| 1436 | - goto out; |
| 1437 | + if (block == NULL) |
| 1438 | + return VLCDEC_SUCCESS; |
| 1439 | |
| 1440 | /* |
| 1441 | * Check whether full flush is required |
| 1442 | */ |
| 1443 | - if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) { |
| 1444 | + if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) { |
| 1445 | +#if TRACE_ALL |
| 1446 | + msg_Dbg(dec, "%s: >>> Discontinuity", __func__); |
| 1447 | +#endif |
| 1448 | flush_decoder(dec); |
| 1449 | + } |
| 1450 | + |
| 1451 | + if (block->i_buffer == 0) |
| 1452 | + { |
| 1453 | block_Release(block); |
| 1454 | return VLCDEC_SUCCESS; |
| 1455 | } |
| 1456 | |
| 1457 | + // Reenable stuff if the last thing we did was flush |
| 1458 | + if (!sys->output->is_enabled && |
| 1459 | + (status = mmal_port_enable(sys->output, decoder_output_cb)) != MMAL_SUCCESS) |
| 1460 | + { |
| 1461 | + msg_Err(dec, "Output port enable failed"); |
| 1462 | + goto fail; |
| 1463 | + } |
| 1464 | + |
| 1465 | + if (!sys->input->is_enabled) |
| 1466 | + { |
| 1467 | + if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS) |
| 1468 | + goto fail; |
| 1469 | + |
| 1470 | + if ((status = mmal_port_enable(sys->input, input_port_cb)) != MMAL_SUCCESS) |
| 1471 | + { |
| 1472 | + msg_Err(dec, "Input port enable failed"); |
| 1473 | + goto fail; |
| 1474 | + } |
| 1475 | + |
| 1476 | + if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS) |
| 1477 | + goto fail; |
| 1478 | + } |
| 1479 | + |
| 1480 | + // *** We cannot get a picture to put the result in 'till we have |
| 1481 | + // reported the size & the output stages have been set up |
| 1482 | if (atomic_load(&sys->started)) |
| 1483 | fill_output_port(dec); |
| 1484 | |
| 1485 | @@ -563,18 +703,21 @@ static int decode(decoder_t *dec, block_ |
| 1486 | if (block->i_flags & BLOCK_FLAG_CORRUPTED) |
| 1487 | flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED; |
| 1488 | |
| 1489 | - while (block && block->i_buffer > 0) { |
| 1490 | - buffer = mmal_queue_timedwait(sys->input_pool->queue, 100); |
| 1491 | + while (block != NULL) |
| 1492 | + { |
| 1493 | + buffer = mmal_queue_wait(sys->input_pool->queue); |
| 1494 | if (!buffer) { |
| 1495 | msg_Err(dec, "Failed to retrieve buffer header for input data"); |
| 1496 | - need_flush = true; |
| 1497 | - break; |
| 1498 | + goto fail; |
| 1499 | } |
| 1500 | + |
| 1501 | mmal_buffer_header_reset(buffer); |
| 1502 | buffer->cmd = 0; |
| 1503 | - buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts; |
| 1504 | + buffer->pts = block->i_pts != VLC_TICK_INVALID ? block->i_pts : |
| 1505 | + block->i_dts != VLC_TICK_INVALID ? block->i_dts : MMAL_TIME_UNKNOWN; |
| 1506 | buffer->dts = block->i_dts; |
| 1507 | buffer->alloc_size = sys->input->buffer_size; |
| 1508 | + buffer->user_data = NULL; |
| 1509 | |
| 1510 | len = block->i_buffer; |
| 1511 | if (len > buffer->alloc_size) |
| 1512 | @@ -585,94 +728,1808 @@ static int decode(decoder_t *dec, block_ |
| 1513 | block->i_buffer -= len; |
| 1514 | buffer->length = len; |
| 1515 | if (block->i_buffer == 0) { |
| 1516 | + flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; |
| 1517 | + if (block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE) { |
| 1518 | + msg_Dbg(dec, "EOS sent"); |
| 1519 | + flags |= MMAL_BUFFER_HEADER_FLAG_EOS; |
| 1520 | + } |
| 1521 | buffer->user_data = block; |
| 1522 | block = NULL; |
| 1523 | } |
| 1524 | buffer->flags = flags; |
| 1525 | |
| 1526 | +#if TRACE_ALL |
| 1527 | + msg_Dbg(dec, "%s: -- Send buffer: cmd=%d, data=%p, size=%d, len=%d, offset=%d, flags=%#x, pts=%lld, dts=%lld", __func__,\ |
| 1528 | + buffer->cmd, buffer->data, buffer->alloc_size, buffer->length, buffer->offset, |
| 1529 | + buffer->flags, (long long)buffer->pts, (long long)buffer->dts); |
| 1530 | +#endif |
| 1531 | status = mmal_port_send_buffer(sys->input, buffer); |
| 1532 | if (status != MMAL_SUCCESS) { |
| 1533 | msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)", |
| 1534 | status, mmal_status_to_string(status)); |
| 1535 | - break; |
| 1536 | + goto fail; |
| 1537 | } |
| 1538 | - atomic_fetch_add(&sys->input_in_transit, 1); |
| 1539 | + |
| 1540 | + // Reset flushed flag once we have sent a buf |
| 1541 | + sys->b_flushed = false; |
| 1542 | + flags &= ~MMAL_BUFFER_HEADER_FLAG_FRAME_START; |
| 1543 | } |
| 1544 | + return VLCDEC_SUCCESS; |
| 1545 | |
| 1546 | -out: |
| 1547 | - if (need_flush) |
| 1548 | - flush_decoder(dec); |
| 1549 | +fail: |
| 1550 | + flush_decoder(dec); |
| 1551 | + return VLCDEC_ECRITICAL; |
| 1552 | |
| 1553 | - return VLCDEC_SUCCESS; |
| 1554 | } |
| 1555 | |
| 1556 | -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 1557 | + |
| 1558 | +static void CloseDecoder(decoder_t *dec) |
| 1559 | { |
| 1560 | - decoder_t *dec = (decoder_t *)port->userdata; |
| 1561 | + decoder_sys_t *sys = dec->p_sys; |
| 1562 | + |
| 1563 | +#if TRACE_ALL |
| 1564 | + msg_Dbg(dec, "%s: <<<", __func__); |
| 1565 | +#endif |
| 1566 | + |
| 1567 | + if (!sys) |
| 1568 | + return; |
| 1569 | + |
| 1570 | + if (sys->component != NULL) { |
| 1571 | + if (sys->input->is_enabled) |
| 1572 | + mmal_port_disable(sys->input); |
| 1573 | + |
| 1574 | + if (sys->output->is_enabled) |
| 1575 | + mmal_port_disable(sys->output); |
| 1576 | + |
| 1577 | + if (sys->component->control->is_enabled) |
| 1578 | + mmal_port_disable(sys->component->control); |
| 1579 | + |
| 1580 | + if (sys->component->is_enabled) |
| 1581 | + mmal_component_disable(sys->component); |
| 1582 | + |
| 1583 | + mmal_component_release(sys->component); |
| 1584 | + } |
| 1585 | + |
| 1586 | + if (sys->input_pool != NULL) |
| 1587 | + mmal_pool_destroy(sys->input_pool); |
| 1588 | + |
| 1589 | + if (sys->output_format != NULL) |
| 1590 | + mmal_format_free(sys->output_format); |
| 1591 | + |
| 1592 | + hw_mmal_port_pool_ref_release(sys->ppr, false); |
| 1593 | + |
| 1594 | + cma_vcsm_exit(sys->vcsm_init_type); |
| 1595 | + |
| 1596 | + vlc_mutex_destroy(&sys->pic_lock); |
| 1597 | + free(sys); |
| 1598 | +} |
| 1599 | + |
| 1600 | +static int OpenDecoder(decoder_t *dec) |
| 1601 | +{ |
| 1602 | + int ret = VLC_EGENERIC; |
| 1603 | + decoder_sys_t *sys; |
| 1604 | MMAL_STATUS_T status; |
| 1605 | + const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec); |
| 1606 | + |
| 1607 | +#if TRACE_ALL || 1 |
| 1608 | + { |
| 1609 | + char buf1[5], buf2[5], buf2a[5]; |
| 1610 | + char buf3[5], buf4[5]; |
| 1611 | + MMAL_RATIONAL_T r = rationalize_sar(dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den); |
| 1612 | + |
| 1613 | + msg_Dbg(dec, "%s: <<< (%s/%s)[%s] %dx%d %d/%d=%d/%d o:%#x -> (%s/%s) %dx%d %d/%d o:%#x", __func__, |
| 1614 | + str_fourcc(buf1, dec->fmt_in.i_codec), |
| 1615 | + str_fourcc(buf2, dec->fmt_in.video.i_chroma), |
| 1616 | + str_fourcc(buf2a, in_fcc), |
| 1617 | + dec->fmt_in.video.i_width, dec->fmt_in.video.i_height, |
| 1618 | + dec->fmt_in.video.i_sar_num, dec->fmt_in.video.i_sar_den, |
| 1619 | + r.num, r.den, |
| 1620 | + (int)dec->fmt_in.video.orientation, |
| 1621 | + str_fourcc(buf3, dec->fmt_out.i_codec), |
| 1622 | + str_fourcc(buf4, dec->fmt_out.video.i_chroma), |
| 1623 | + dec->fmt_out.video.i_width, dec->fmt_out.video.i_height, |
| 1624 | + dec->fmt_out.video.i_sar_num, dec->fmt_out.video.i_sar_den, |
| 1625 | + (int)dec->fmt_out.video.orientation); |
| 1626 | + } |
| 1627 | +#endif |
| 1628 | + |
| 1629 | + if (!is_enc_supported(&supported_decode_in_enc, in_fcc)) |
| 1630 | + return VLC_EGENERIC; |
| 1631 | + |
| 1632 | + sys = calloc(1, sizeof(decoder_sys_t)); |
| 1633 | + if (!sys) { |
| 1634 | + ret = VLC_ENOMEM; |
| 1635 | + goto fail; |
| 1636 | + } |
| 1637 | + dec->p_sys = sys; |
| 1638 | + vlc_mutex_init(&sys->pic_lock); |
| 1639 | + |
| 1640 | + if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { |
| 1641 | + msg_Err(dec, "VCSM init failed"); |
| 1642 | + goto fail; |
| 1643 | + } |
| 1644 | + msg_Info(dec, "VCSM init succeeded: %s", cma_vcsm_init_str(sys->vcsm_init_type)); |
| 1645 | + |
| 1646 | + sys->err_stream = MMAL_SUCCESS; |
| 1647 | + |
| 1648 | + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component); |
| 1649 | + if (status != MMAL_SUCCESS) { |
| 1650 | + msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)", |
| 1651 | + MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status)); |
| 1652 | + goto fail; |
| 1653 | + } |
| 1654 | + |
| 1655 | + sys->input = sys->component->input[0]; |
| 1656 | + sys->output = sys->component->output[0]; |
| 1657 | + |
| 1658 | + sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec; |
| 1659 | + sys->input->format->encoding = in_fcc; |
| 1660 | + |
| 1661 | + if (!set_and_test_enc_supported(&supported_decode_in_enc, sys->input, in_fcc)) { |
| 1662 | +#if TRACE_ALL |
| 1663 | + char cbuf[5]; |
| 1664 | + msg_Dbg(dec, "Format not supported: %s", str_fourcc(cbuf, in_fcc)); |
| 1665 | +#endif |
| 1666 | + goto fail; |
| 1667 | + } |
| 1668 | + |
| 1669 | + sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec; |
| 1670 | + status = mmal_port_enable(sys->component->control, control_port_cb); |
| 1671 | + if (status != MMAL_SUCCESS) { |
| 1672 | + msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)", |
| 1673 | + sys->component->control->name, status, mmal_status_to_string(status)); |
| 1674 | + goto fail; |
| 1675 | + } |
| 1676 | + |
| 1677 | + if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS) |
| 1678 | + goto fail; |
| 1679 | + |
| 1680 | + sys->input->buffer_size = sys->input->buffer_size_recommended; |
| 1681 | + sys->input->buffer_num = sys->input->buffer_num_recommended; |
| 1682 | + |
| 1683 | + status = mmal_port_enable(sys->input, input_port_cb); |
| 1684 | + if (status != MMAL_SUCCESS) { |
| 1685 | + msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)", |
| 1686 | + sys->input->name, status, mmal_status_to_string(status)); |
| 1687 | + goto fail; |
| 1688 | + } |
| 1689 | + |
| 1690 | + // Set vanishingly unlikely shape (or at least crop) |
| 1691 | + // to ensure that we get a resolution changed event |
| 1692 | + // Small wxh are rejected (128x128 is rejected) so pick a |
| 1693 | + // plausible size. |
| 1694 | + // Crop doesn't seem to be checked for being constrained by wxh |
| 1695 | + // so we could place it outside the pic to be sure that it is |
| 1696 | + // never matched but stick with something legal in case it is ever |
| 1697 | + // actually checked |
| 1698 | + sys->output->format->es->video.height = 256; |
| 1699 | + sys->output->format->es->video.width = 256; |
| 1700 | + sys->output->format->es->video.crop.height = 4; |
| 1701 | + sys->output->format->es->video.crop.width = 2; |
| 1702 | + sys->output->format->es->video.crop.x = 66; |
| 1703 | + sys->output->format->es->video.crop.y = 88; |
| 1704 | + |
| 1705 | + if ((status = hw_mmal_opaque_output(VLC_OBJECT(dec), &sys->ppr, |
| 1706 | + sys->output, NUM_EXTRA_BUFFERS, decoder_output_cb)) != MMAL_SUCCESS) |
| 1707 | + goto fail; |
| 1708 | + |
| 1709 | + status = mmal_component_enable(sys->component); |
| 1710 | + if (status != MMAL_SUCCESS) { |
| 1711 | + msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)", |
| 1712 | + sys->component->name, status, mmal_status_to_string(status)); |
| 1713 | + goto fail; |
| 1714 | + } |
| 1715 | + |
| 1716 | + if ((sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) |
| 1717 | + { |
| 1718 | + msg_Err(dec, "Failed to create input pool"); |
| 1719 | + goto fail; |
| 1720 | + } |
| 1721 | + |
| 1722 | + sys->b_flushed = true; |
| 1723 | + |
| 1724 | + if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS) |
| 1725 | + goto fail; |
| 1726 | + |
| 1727 | + // Given no better ideas at this point copy input format to output |
| 1728 | + // This also copies container stuff (such as orientation) that we do not |
| 1729 | + // decode from the ES but may be important to display |
| 1730 | + video_format_Copy(&dec->fmt_out.video, &dec->fmt_in.video); |
| 1731 | + dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE; |
| 1732 | + dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE; |
| 1733 | + |
| 1734 | + |
| 1735 | + dec->pf_decode = decode; |
| 1736 | + dec->pf_flush = flush_decoder; |
| 1737 | + |
| 1738 | +#if TRACE_ALL |
| 1739 | + msg_Dbg(dec, ">>> %s: ok", __func__); |
| 1740 | +#endif |
| 1741 | + return 0; |
| 1742 | + |
| 1743 | +fail: |
| 1744 | + CloseDecoder(dec); |
| 1745 | +#if TRACE_ALL |
| 1746 | +msg_Dbg(dec, ">>> %s: FAIL: ret=%d", __func__, ret); |
| 1747 | +#endif |
| 1748 | + return ret; |
| 1749 | +} |
| 1750 | + |
| 1751 | +// ---------------------------- |
| 1752 | + |
| 1753 | +#define CONV_MAX_LATENCY 1 // In frames |
| 1754 | + |
| 1755 | +typedef struct pic_fifo_s { |
| 1756 | + picture_t * head; |
| 1757 | + picture_t * tail; |
| 1758 | +} pic_fifo_t; |
| 1759 | + |
| 1760 | +static inline picture_t * pic_fifo_get(pic_fifo_t * const pf) |
| 1761 | +{ |
| 1762 | + picture_t * const pic = pf->head;; |
| 1763 | + if (pic != NULL) { |
| 1764 | + pf->head = pic->p_next; |
| 1765 | + pic->p_next = NULL; |
| 1766 | + } |
| 1767 | + return pic; |
| 1768 | +} |
| 1769 | + |
| 1770 | +static inline picture_t * pic_fifo_get_all(pic_fifo_t * const pf) |
| 1771 | +{ |
| 1772 | + picture_t * const pic = pf->head;; |
| 1773 | + pf->head = NULL; |
| 1774 | + return pic; |
| 1775 | +} |
| 1776 | + |
| 1777 | +static inline void pic_fifo_release_all(pic_fifo_t * const pf) |
| 1778 | +{ |
| 1779 | + picture_t * pic; |
| 1780 | + while ((pic = pic_fifo_get(pf)) != NULL) { |
| 1781 | + picture_Release(pic); |
| 1782 | + } |
| 1783 | +} |
| 1784 | + |
| 1785 | +static inline void pic_fifo_init(pic_fifo_t * const pf) |
| 1786 | +{ |
| 1787 | + pf->head = NULL; |
| 1788 | + pf->tail = NULL; // Not strictly needed |
| 1789 | +} |
| 1790 | + |
| 1791 | +static inline void pic_fifo_put(pic_fifo_t * const pf, picture_t * pic) |
| 1792 | +{ |
| 1793 | + pic->p_next = NULL; |
| 1794 | + if (pf->head == NULL) |
| 1795 | + pf->head = pic; |
| 1796 | + else |
| 1797 | + pf->tail->p_next = pic; |
| 1798 | + pf->tail = pic; |
| 1799 | +} |
| 1800 | + |
| 1801 | +#define SUBS_MAX 3 |
| 1802 | + |
| 1803 | +typedef enum filter_resizer_e { |
| 1804 | + FILTER_RESIZER_RESIZER, |
| 1805 | + FILTER_RESIZER_ISP, |
| 1806 | + FILTER_RESIZER_HVS |
| 1807 | +} filter_resizer_t; |
| 1808 | + |
| 1809 | +typedef struct conv_frame_stash_s |
| 1810 | +{ |
| 1811 | + mtime_t pts; |
| 1812 | + MMAL_BUFFER_HEADER_T * sub_bufs[SUBS_MAX]; |
| 1813 | +} conv_frame_stash_t; |
| 1814 | + |
| 1815 | +typedef struct filter_sys_t { |
| 1816 | + filter_resizer_t resizer_type; |
| 1817 | + MMAL_COMPONENT_T *component; |
| 1818 | + MMAL_PORT_T *input; |
| 1819 | + MMAL_PORT_T *output; |
| 1820 | + MMAL_POOL_T *out_pool; // Free output buffers |
| 1821 | + MMAL_POOL_T *in_pool; // Input pool to get BH for replication |
| 1822 | + |
| 1823 | + cma_buf_pool_t * cma_in_pool; |
| 1824 | + cma_buf_pool_t * cma_out_pool; |
| 1825 | + |
| 1826 | + subpic_reg_stash_t subs[SUBS_MAX]; |
| 1827 | + |
| 1828 | + pic_fifo_t ret_pics; |
| 1829 | + |
| 1830 | + unsigned int pic_n; |
| 1831 | + vlc_sem_t sem; |
| 1832 | + vlc_mutex_t lock; |
| 1833 | + |
| 1834 | + MMAL_STATUS_T err_stream; |
| 1835 | + |
| 1836 | + bool needs_copy_in; |
| 1837 | + bool is_cma; |
| 1838 | + bool is_sliced; |
| 1839 | + bool out_fmt_set; |
| 1840 | + const char * component_name; |
| 1841 | + MMAL_PORT_BH_CB_T in_port_cb_fn; |
| 1842 | + MMAL_PORT_BH_CB_T out_port_cb_fn; |
| 1843 | + |
| 1844 | + uint64_t frame_seq; |
| 1845 | + conv_frame_stash_t stash[16]; |
| 1846 | + |
| 1847 | + // Slice specific tracking stuff |
| 1848 | + struct { |
| 1849 | + pic_fifo_t pics; |
| 1850 | + unsigned int line; // Lines filled |
| 1851 | + } slice; |
| 1852 | + |
| 1853 | + vcsm_init_type_t vcsm_init_type; |
| 1854 | +} filter_sys_t; |
| 1855 | + |
| 1856 | + |
| 1857 | +static MMAL_STATUS_T pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic) |
| 1858 | +{ |
| 1859 | + unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3; |
| 1860 | + MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video; |
| 1861 | + |
| 1862 | + es_fmt->type = MMAL_ES_TYPE_VIDEO; |
| 1863 | + es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format); |
| 1864 | + es_fmt->encoding_variant = 0; |
| 1865 | + |
| 1866 | + // Fill in crop etc. |
| 1867 | + hw_mmal_vlc_fmt_to_mmal_fmt(es_fmt, &pic->format); |
| 1868 | + // Override width / height with strides if appropriate |
| 1869 | + if (bpp != 0) { |
| 1870 | + v_fmt->width = pic->p[0].i_pitch / bpp; |
| 1871 | + v_fmt->height = pic->p[0].i_lines; |
| 1872 | + } |
| 1873 | + return MMAL_SUCCESS; |
| 1874 | +} |
| 1875 | + |
| 1876 | + |
| 1877 | +static MMAL_STATUS_T conv_enable_in(filter_t * const p_filter, filter_sys_t * const sys) |
| 1878 | +{ |
| 1879 | + MMAL_STATUS_T err = MMAL_SUCCESS; |
| 1880 | + |
| 1881 | + if (!sys->input->is_enabled && |
| 1882 | + (err = mmal_port_enable(sys->input, sys->in_port_cb_fn)) != MMAL_SUCCESS) |
| 1883 | + { |
| 1884 | + msg_Err(p_filter, "Failed to enable input port %s (status=%"PRIx32" %s)", |
| 1885 | + sys->input->name, err, mmal_status_to_string(err)); |
| 1886 | + } |
| 1887 | + return err; |
| 1888 | +} |
| 1889 | + |
| 1890 | +static MMAL_STATUS_T conv_enable_out(filter_t * const p_filter, filter_sys_t * const sys) |
| 1891 | +{ |
| 1892 | + MMAL_STATUS_T err = MMAL_SUCCESS; |
| 1893 | + |
| 1894 | + if (sys->is_cma) |
| 1895 | + { |
| 1896 | + if (sys->cma_out_pool == NULL && |
| 1897 | + (sys->cma_out_pool = cma_buf_pool_new(CONVERTER_BUFFERS, CONVERTER_BUFFERS, true, "mmal_resizer")) == NULL) |
| 1898 | + { |
| 1899 | + msg_Err(p_filter, "Failed to alloc cma buf pool"); |
| 1900 | + return MMAL_ENOMEM; |
| 1901 | + } |
| 1902 | + } |
| 1903 | + else |
| 1904 | + { |
| 1905 | + cma_buf_pool_deletez(&sys->cma_out_pool); |
| 1906 | + } |
| 1907 | + |
| 1908 | + if (!sys->output->is_enabled && |
| 1909 | + (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS) |
| 1910 | + { |
| 1911 | + msg_Err(p_filter, "Failed to enable output port %s (status=%"PRIx32" %s)", |
| 1912 | + sys->output->name, err, mmal_status_to_string(err)); |
| 1913 | + } |
| 1914 | + return err; |
| 1915 | +} |
| 1916 | + |
| 1917 | +static void conv_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 1918 | +{ |
| 1919 | + filter_t * const p_filter = (filter_t *)port->userdata; |
| 1920 | + |
| 1921 | +#if TRACE_ALL |
| 1922 | + msg_Dbg(p_filter, "%s: <<< cmd=%d, data=%p, pic=%p", __func__, buffer->cmd, buffer->data, buffer->user_data); |
| 1923 | +#endif |
| 1924 | |
| 1925 | if (buffer->cmd == MMAL_EVENT_ERROR) { |
| 1926 | - status = *(uint32_t *)buffer->data; |
| 1927 | - msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status, |
| 1928 | + MMAL_STATUS_T status = *(uint32_t *)buffer->data; |
| 1929 | + |
| 1930 | + p_filter->p_sys->err_stream = status; |
| 1931 | + |
| 1932 | + msg_Err(p_filter, "MMAL error %"PRIx32" \"%s\"", status, |
| 1933 | mmal_status_to_string(status)); |
| 1934 | } |
| 1935 | |
| 1936 | mmal_buffer_header_release(buffer); |
| 1937 | } |
| 1938 | |
| 1939 | -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 1940 | +static void conv_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) |
| 1941 | { |
| 1942 | - block_t *block = (block_t *)buffer->user_data; |
| 1943 | - decoder_t *dec = (decoder_t *)port->userdata; |
| 1944 | - decoder_sys_t *sys = dec->p_sys; |
| 1945 | - buffer->user_data = NULL; |
| 1946 | +#if TRACE_ALL |
| 1947 | + picture_context_t * ctx = buf->user_data; |
| 1948 | +// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys; |
| 1949 | + |
| 1950 | + msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, ctx=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld", |
| 1951 | + __func__, buf->cmd, ctx, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts); |
| 1952 | +#else |
| 1953 | + VLC_UNUSED(port); |
| 1954 | +#endif |
| 1955 | + |
| 1956 | + mmal_buffer_header_release(buf); |
| 1957 | + |
| 1958 | +#if TRACE_ALL |
| 1959 | + msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__); |
| 1960 | +#endif |
| 1961 | +} |
| 1962 | + |
| 1963 | +static void conv_out_q_pic(filter_sys_t * const sys, picture_t * const pic) |
| 1964 | +{ |
| 1965 | + pic->p_next = NULL; |
| 1966 | + |
| 1967 | + vlc_mutex_lock(&sys->lock); |
| 1968 | + pic_fifo_put(&sys->ret_pics, pic); |
| 1969 | + vlc_mutex_unlock(&sys->lock); |
| 1970 | |
| 1971 | - mmal_buffer_header_release(buffer); |
| 1972 | - if (block) |
| 1973 | - block_Release(block); |
| 1974 | - atomic_fetch_sub(&sys->input_in_transit, 1); |
| 1975 | vlc_sem_post(&sys->sem); |
| 1976 | } |
| 1977 | |
| 1978 | -static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 1979 | +static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) |
| 1980 | { |
| 1981 | - decoder_t *dec = (decoder_t *)port->userdata; |
| 1982 | - decoder_sys_t *sys = dec->p_sys; |
| 1983 | - picture_t *picture; |
| 1984 | - MMAL_EVENT_FORMAT_CHANGED_T *fmt; |
| 1985 | - MMAL_ES_FORMAT_T *format; |
| 1986 | - |
| 1987 | - if (buffer->cmd == 0) { |
| 1988 | - picture = (picture_t *)buffer->user_data; |
| 1989 | - if (buffer->length > 0) { |
| 1990 | - picture->date = buffer->pts; |
| 1991 | - picture->b_progressive = sys->b_progressive; |
| 1992 | - picture->b_top_field_first = sys->b_top_field_first; |
| 1993 | - decoder_QueueVideo(dec, picture); |
| 1994 | - } else { |
| 1995 | - picture_Release(picture); |
| 1996 | - if (sys->output_pool) { |
| 1997 | - buffer->user_data = NULL; |
| 1998 | - buffer->alloc_size = 0; |
| 1999 | - buffer->data = NULL; |
| 2000 | - mmal_buffer_header_release(buffer); |
| 2001 | - } |
| 2002 | - } |
| 2003 | - atomic_fetch_sub(&sys->output_in_transit, 1); |
| 2004 | - vlc_sem_post(&sys->sem); |
| 2005 | - } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { |
| 2006 | - fmt = mmal_event_format_changed_get(buffer); |
| 2007 | + filter_t * const p_filter = (filter_t *)port->userdata; |
| 2008 | + filter_sys_t * const sys = p_filter->p_sys; |
| 2009 | |
| 2010 | - format = mmal_format_alloc(); |
| 2011 | - mmal_format_full_copy(format, fmt->format); |
| 2012 | +#if TRACE_ALL |
| 2013 | + msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld/%lld", __func__, |
| 2014 | + buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, |
| 2015 | + (long long)buf->pts, (long long)sys->stash[(unsigned int)(buf->pts & 0xf)].pts); |
| 2016 | +#endif |
| 2017 | + if (buf->cmd == 0) { |
| 2018 | + picture_t * const pic = (picture_t *)buf->user_data; |
| 2019 | |
| 2020 | - if (sys->opaque) |
| 2021 | - format->encoding = MMAL_ENCODING_OPAQUE; |
| 2022 | + if (pic == NULL) { |
| 2023 | + msg_Err(p_filter, "%s: Buffer has no attached picture", __func__); |
| 2024 | + } |
| 2025 | + else if (buf->data == NULL || buf->length == 0) |
| 2026 | + { |
| 2027 | +#if TRACE_ALL |
| 2028 | + msg_Dbg(p_filter, "%s: Buffer has no data", __func__); |
| 2029 | +#endif |
| 2030 | + } |
| 2031 | + else |
| 2032 | + { |
| 2033 | + buf_to_pic_copy_props(pic, buf); |
| 2034 | + |
| 2035 | + // Set pic data pointers from buf aux info now it has it |
| 2036 | + if (sys->is_cma) { |
| 2037 | + if (cma_pic_set_data(pic, sys->output->format, buf) != VLC_SUCCESS) |
| 2038 | + msg_Err(p_filter, "Failed to set data"); |
| 2039 | + } |
| 2040 | + |
| 2041 | +// 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); |
| 2042 | +#if DEBUG_SQUARES |
| 2043 | + draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, 32, 32, 0xffff0000); |
| 2044 | + draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 32, 0, 32, 32, 0xff00ff00); |
| 2045 | + draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 64, 0, 32, 32, 0xff0000ff); |
| 2046 | +#endif |
| 2047 | + |
| 2048 | + buf->user_data = NULL; // Responsability for this pic no longer with buffer |
| 2049 | + conv_out_q_pic(sys, pic); |
| 2050 | + } |
| 2051 | + } |
| 2052 | + |
| 2053 | + mmal_buffer_header_release(buf); |
| 2054 | +} |
| 2055 | + |
| 2056 | + |
| 2057 | +static void slice_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) |
| 2058 | +{ |
| 2059 | + filter_t * const p_filter = (filter_t *)port->userdata; |
| 2060 | + filter_sys_t * const sys = p_filter->p_sys; |
| 2061 | + |
| 2062 | +#if TRACE_ALL |
| 2063 | + msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld", __func__, |
| 2064 | + buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, (long long)buf->pts); |
| 2065 | +#endif |
| 2066 | + |
| 2067 | + if (buf->cmd != 0) |
| 2068 | + { |
| 2069 | + mmal_buffer_header_release(buf); |
| 2070 | + return; |
| 2071 | + } |
| 2072 | + |
| 2073 | + if (buf->data == NULL || buf->length == 0) |
| 2074 | + { |
| 2075 | +#if TRACE_ALL |
| 2076 | + msg_Dbg(p_filter, "%s: Buffer has no data", __func__); |
| 2077 | +#endif |
| 2078 | + } |
| 2079 | + else |
| 2080 | + { |
| 2081 | + // Got slice |
| 2082 | + picture_t *pic = sys->slice.pics.head; |
| 2083 | + const unsigned int scale_lines = sys->output->format->es->video.height; // Expected lines of callback |
| 2084 | + |
| 2085 | + if (pic == NULL) { |
| 2086 | + msg_Err(p_filter, "No output picture"); |
| 2087 | + goto fail; |
| 2088 | + } |
| 2089 | + |
| 2090 | + // Copy lines |
| 2091 | + // * single plane only - fix for I420 |
| 2092 | + { |
| 2093 | + const unsigned int scale_n = __MIN(scale_lines - sys->slice.line, MMAL_SLICE_HEIGHT); |
| 2094 | + const unsigned int pic_lines = pic->p[0].i_lines; |
| 2095 | + const unsigned int copy_n = sys->slice.line + scale_n <= pic_lines ? scale_n : |
| 2096 | + sys->slice.line >= pic_lines ? 0 : |
| 2097 | + pic_lines - sys->slice.line; |
| 2098 | + |
| 2099 | + const unsigned int src_stride = buf->type->video.pitch[0]; |
| 2100 | + const unsigned int dst_stride = pic->p[0].i_pitch; |
| 2101 | + uint8_t *dst = pic->p[0].p_pixels + sys->slice.line * dst_stride; |
| 2102 | + const uint8_t *src = buf->data + buf->type->video.offset[0]; |
| 2103 | + |
| 2104 | + if (src_stride == dst_stride) { |
| 2105 | + if (copy_n != 0) |
| 2106 | + memcpy(dst, src, src_stride * copy_n); |
| 2107 | + } |
| 2108 | + else { |
| 2109 | + unsigned int i; |
| 2110 | + for (i = 0; i != copy_n; ++i) { |
| 2111 | + memcpy(dst, src, __MIN(dst_stride, src_stride)); |
| 2112 | + dst += dst_stride; |
| 2113 | + src += src_stride; |
| 2114 | + } |
| 2115 | + } |
| 2116 | + sys->slice.line += scale_n; |
| 2117 | + } |
| 2118 | + |
| 2119 | + if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) { |
| 2120 | + |
| 2121 | + if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) { |
| 2122 | + // Stuff doesn't add up... |
| 2123 | + msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags); |
| 2124 | + goto fail; |
| 2125 | + } |
| 2126 | + else { |
| 2127 | + sys->slice.line = 0; |
| 2128 | + |
| 2129 | + vlc_mutex_lock(&sys->lock); |
| 2130 | + pic_fifo_get(&sys->slice.pics); // Remove head from Q |
| 2131 | + vlc_mutex_unlock(&sys->lock); |
| 2132 | + |
| 2133 | + buf_to_pic_copy_props(pic, buf); |
| 2134 | + conv_out_q_pic(sys, pic); |
| 2135 | + } |
| 2136 | + } |
| 2137 | + } |
| 2138 | + |
| 2139 | + // Put back |
| 2140 | + buf->user_data = NULL; // Zap here to make sure we can't reuse later |
| 2141 | + mmal_buffer_header_reset(buf); |
| 2142 | + |
| 2143 | + if (mmal_port_send_buffer(sys->output, buf) != MMAL_SUCCESS) { |
| 2144 | + mmal_buffer_header_release(buf); |
| 2145 | + } |
| 2146 | + return; |
| 2147 | + |
| 2148 | +fail: |
| 2149 | + sys->err_stream = MMAL_EIO; |
| 2150 | + vlc_sem_post(&sys->sem); // If we were waiting then break us out - the flush should fix sem values |
| 2151 | +} |
| 2152 | + |
| 2153 | + |
| 2154 | +static void conv_flush(filter_t * p_filter) |
| 2155 | +{ |
| 2156 | + filter_sys_t * const sys = p_filter->p_sys; |
| 2157 | + unsigned int i; |
| 2158 | + |
| 2159 | +#if TRACE_ALL |
| 2160 | + msg_Dbg(p_filter, "<<< %s", __func__); |
| 2161 | +#endif |
| 2162 | + |
| 2163 | + if (sys->resizer_type == FILTER_RESIZER_HVS) |
| 2164 | + { |
| 2165 | + for (i = 0; i != SUBS_MAX; ++i) { |
| 2166 | + hw_mmal_subpic_flush(VLC_OBJECT(p_filter), sys->subs + i); |
| 2167 | + } |
| 2168 | + } |
| 2169 | + |
| 2170 | + if (sys->input != NULL && sys->input->is_enabled) |
| 2171 | + mmal_port_disable(sys->input); |
| 2172 | + |
| 2173 | + if (sys->output != NULL && sys->output->is_enabled) |
| 2174 | + mmal_port_disable(sys->output); |
| 2175 | + |
| 2176 | +// cma_buf_pool_deletez(&sys->cma_out_pool); |
| 2177 | + |
| 2178 | + // Free up anything we may have already lying around |
| 2179 | + // Don't need lock as the above disables should have prevented anything |
| 2180 | + // happening in the background |
| 2181 | + |
| 2182 | + for (i = 0; i != 16; ++i) { |
| 2183 | + conv_frame_stash_t *const stash = sys->stash + i; |
| 2184 | + unsigned int sub_no; |
| 2185 | + |
| 2186 | + stash->pts = MMAL_TIME_UNKNOWN; |
| 2187 | + for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { |
| 2188 | + if (stash->sub_bufs[sub_no] != NULL) { |
| 2189 | + mmal_buffer_header_release(stash->sub_bufs[sub_no]); |
| 2190 | + stash->sub_bufs[sub_no] = NULL; |
| 2191 | + } |
| 2192 | + } |
| 2193 | + } |
| 2194 | + |
| 2195 | + pic_fifo_release_all(&sys->slice.pics); |
| 2196 | + pic_fifo_release_all(&sys->ret_pics); |
| 2197 | + |
| 2198 | + // Reset sem values - easiest & most reliable way is to just kill & re-init |
| 2199 | + vlc_sem_destroy(&sys->sem); |
| 2200 | + vlc_sem_init(&sys->sem, 0); |
| 2201 | + sys->pic_n = 0; |
| 2202 | + |
| 2203 | + // Reset error status |
| 2204 | + sys->err_stream = MMAL_SUCCESS; |
| 2205 | + |
| 2206 | +#if TRACE_ALL |
| 2207 | + msg_Dbg(p_filter, ">>> %s", __func__); |
| 2208 | +#endif |
| 2209 | +} |
| 2210 | + |
| 2211 | +static void conv_stash_fixup(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const p_pic) |
| 2212 | +{ |
| 2213 | + conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf); |
| 2214 | + unsigned int sub_no; |
| 2215 | + VLC_UNUSED(p_filter); |
| 2216 | + |
| 2217 | + p_pic->date = stash->pts; |
| 2218 | + for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { |
| 2219 | + if (stash->sub_bufs[sub_no] != NULL) { |
| 2220 | + // **** Do stashed blend |
| 2221 | + // **** Aaargh, bother... need to rescale subs too |
| 2222 | + |
| 2223 | + mmal_buffer_header_release(stash->sub_bufs[sub_no]); |
| 2224 | + stash->sub_bufs[sub_no] = NULL; |
| 2225 | + } |
| 2226 | + } |
| 2227 | +} |
| 2228 | + |
| 2229 | +// Output buffers may contain a pic ref on error or flush |
| 2230 | +// Free it |
| 2231 | +static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata) |
| 2232 | +{ |
| 2233 | + VLC_UNUSED(userdata); |
| 2234 | + |
| 2235 | + picture_t * const pic = header->user_data; |
| 2236 | + header->user_data = NULL; |
| 2237 | + |
| 2238 | + if (pic != NULL) |
| 2239 | + picture_Release(pic); |
| 2240 | + |
| 2241 | + return MMAL_FALSE; |
| 2242 | +} |
| 2243 | + |
| 2244 | +static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const pic) |
| 2245 | +{ |
| 2246 | + MMAL_STATUS_T status; |
| 2247 | + |
| 2248 | + sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter; |
| 2249 | + sys->output->format->type = MMAL_ES_TYPE_VIDEO; |
| 2250 | + sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video); |
| 2251 | + sys->output->format->encoding_variant = 0; |
| 2252 | + hw_mmal_vlc_fmt_to_mmal_fmt(sys->output->format, &p_filter->fmt_out.video); |
| 2253 | + |
| 2254 | + if (pic != NULL) |
| 2255 | + { |
| 2256 | + // Override default format width/height if we have a pic we need to match |
| 2257 | + if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS) |
| 2258 | + { |
| 2259 | + char cbuf[5]; |
| 2260 | + 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); |
| 2261 | + return status; |
| 2262 | + } |
| 2263 | + |
| 2264 | + MMAL_VIDEO_FORMAT_T *fmt = &sys->output->format->es->video; |
| 2265 | + msg_Dbg(p_filter, "%s: %dx%d [(0,0) %dx%d]", __func__, fmt->width, fmt->height, fmt->crop.width, fmt->crop.height); |
| 2266 | + } |
| 2267 | + |
| 2268 | + if (sys->is_sliced) { |
| 2269 | + // Override height for slice |
| 2270 | + sys->output->format->es->video.height = MMAL_SLICE_HEIGHT; |
| 2271 | + } |
| 2272 | + |
| 2273 | + mmal_log_dump_format(sys->output->format); |
| 2274 | + |
| 2275 | + status = mmal_port_format_commit(sys->output); |
| 2276 | + if (status != MMAL_SUCCESS) { |
| 2277 | + msg_Err(p_filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)", |
| 2278 | + sys->output->name, status, mmal_status_to_string(status)); |
| 2279 | + return status; |
| 2280 | + } |
| 2281 | + |
| 2282 | + sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended); |
| 2283 | + sys->output->buffer_size = sys->output->buffer_size_recommended; |
| 2284 | + |
| 2285 | + if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS) |
| 2286 | + return status; |
| 2287 | + |
| 2288 | + return MMAL_SUCCESS; |
| 2289 | +} |
| 2290 | + |
| 2291 | + |
| 2292 | +static picture_t *conv_get_out_pics(filter_sys_t * const sys) |
| 2293 | +{ |
| 2294 | + picture_t * ret_pics; |
| 2295 | + |
| 2296 | + vlc_sem_wait(&sys->sem); |
| 2297 | + |
| 2298 | + // Return a single pending buffer |
| 2299 | + vlc_mutex_lock(&sys->lock); |
| 2300 | + ret_pics = pic_fifo_get(&sys->ret_pics); |
| 2301 | + vlc_mutex_unlock(&sys->lock); |
| 2302 | + |
| 2303 | + return ret_pics; |
| 2304 | +} |
| 2305 | + |
| 2306 | +static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic) |
| 2307 | +{ |
| 2308 | + filter_sys_t * const sys = p_filter->p_sys; |
| 2309 | + picture_t * ret_pics = NULL; |
| 2310 | + MMAL_STATUS_T err; |
| 2311 | + const uint64_t frame_seq = ++sys->frame_seq; |
| 2312 | + conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf); |
| 2313 | + MMAL_BUFFER_HEADER_T * out_buf = NULL; |
| 2314 | + |
| 2315 | +#if TRACE_ALL |
| 2316 | + { |
| 2317 | + char dbuf0[5], dbuf1[5]; |
| 2318 | + 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__, |
| 2319 | + str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, |
| 2320 | + p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, |
| 2321 | + p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, |
| 2322 | + p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den, |
| 2323 | + str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, |
| 2324 | + p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, |
| 2325 | + p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, |
| 2326 | + p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den); |
| 2327 | + } |
| 2328 | +#endif |
| 2329 | + |
| 2330 | + if (sys->err_stream != MMAL_SUCCESS) { |
| 2331 | + goto stream_fail; |
| 2332 | + } |
| 2333 | + |
| 2334 | + // Check pic fmt corresponds to what we have set up |
| 2335 | + if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic)) |
| 2336 | + { |
| 2337 | + msg_Dbg(p_filter, "Reset input port format"); |
| 2338 | + |
| 2339 | + // HVS can take new formats without disable, others need it |
| 2340 | + if (sys->resizer_type != FILTER_RESIZER_HVS) { |
| 2341 | + // Extract any pending pic |
| 2342 | + if (sys->pic_n >= 2) { |
| 2343 | + ret_pics = conv_get_out_pics(sys); |
| 2344 | + // If pic_n == 1 then we return without trying to get stuff |
| 2345 | + sys->pic_n = 1; |
| 2346 | + } |
| 2347 | + if (sys->input->is_enabled) { |
| 2348 | + if ((err = mmal_port_disable(sys->input)) != MMAL_SUCCESS) |
| 2349 | + msg_Warn(p_filter, "Format update disable failed: %s", mmal_status_to_string(err)); |
| 2350 | + } |
| 2351 | + } |
| 2352 | + |
| 2353 | +// mmal_log_dump_port(sys->input); |
| 2354 | + if ((err = mmal_port_format_commit(sys->input)) != MMAL_SUCCESS) |
| 2355 | + msg_Warn(p_filter, "Format update commit failed: %s", mmal_status_to_string(err)); |
| 2356 | + |
| 2357 | + // (Re)enable if required will be done later |
| 2358 | + } |
| 2359 | + |
| 2360 | + if (p_pic->context == NULL) { |
| 2361 | + // Can't have stashed subpics if not one of our pics |
| 2362 | + if (!sys->needs_copy_in) |
| 2363 | + msg_Dbg(p_filter, "%s: No context", __func__); |
| 2364 | + } |
| 2365 | + else if (sys->resizer_type == FILTER_RESIZER_HVS) |
| 2366 | + { |
| 2367 | + unsigned int sub_no = 0; |
| 2368 | + |
| 2369 | + for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { |
| 2370 | + int rv; |
| 2371 | + if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter), |
| 2372 | + hw_mmal_pic_sub_buf_get(p_pic, sub_no), |
| 2373 | + sys->subs + sub_no, |
| 2374 | + &p_pic->format, |
| 2375 | + &sys->output->format->es->video.crop, |
| 2376 | + MMAL_DISPLAY_ROT0, |
| 2377 | + frame_seq)) == 0) |
| 2378 | + break; |
| 2379 | + else if (rv < 0) |
| 2380 | + goto fail; |
| 2381 | + } |
| 2382 | + } |
| 2383 | + else |
| 2384 | + { |
| 2385 | + unsigned int sub_no = 0; |
| 2386 | + for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { |
| 2387 | + if ((stash->sub_bufs[sub_no] = hw_mmal_pic_sub_buf_get(p_pic, sub_no)) != NULL) { |
| 2388 | + mmal_buffer_header_acquire(stash->sub_bufs[sub_no]); |
| 2389 | + } |
| 2390 | + } |
| 2391 | + } |
| 2392 | + |
| 2393 | + if (!sys->out_fmt_set) { |
| 2394 | + sys->out_fmt_set = true; |
| 2395 | + |
| 2396 | + if (sys->is_sliced) { |
| 2397 | + // If zc then we will do stride conversion when we copy to arm side |
| 2398 | + // so no need to worry about actual pic dimensions here |
| 2399 | + if ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS) |
| 2400 | + goto fail; |
| 2401 | + |
| 2402 | + sys->out_pool = mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size); |
| 2403 | + } |
| 2404 | + else { |
| 2405 | + picture_t *pic = filter_NewPicture(p_filter); |
| 2406 | + err = conv_set_output(p_filter, sys, pic); |
| 2407 | + picture_Release(pic); |
| 2408 | + if (err != MMAL_SUCCESS) |
| 2409 | + goto fail; |
| 2410 | + |
| 2411 | + sys->out_pool = mmal_pool_create(sys->output->buffer_num, 0); |
| 2412 | + } |
| 2413 | + |
| 2414 | + if (sys->out_pool == NULL) { |
| 2415 | + msg_Err(p_filter, "Failed to create output pool"); |
| 2416 | + goto fail; |
| 2417 | + } |
| 2418 | + } |
| 2419 | + |
| 2420 | + // Reenable stuff if the last thing we did was flush |
| 2421 | + if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS || |
| 2422 | + (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS) |
| 2423 | + goto fail; |
| 2424 | + |
| 2425 | + // We attach pic to buf before stuffing the output port |
| 2426 | + // We could attach the pic on output for cma, but it is a lot easier to keep |
| 2427 | + // the code common. |
| 2428 | + { |
| 2429 | + picture_t * const out_pic = filter_NewPicture(p_filter); |
| 2430 | + |
| 2431 | + if (out_pic == NULL) |
| 2432 | + { |
| 2433 | + msg_Err(p_filter, "Failed to alloc required filter output pic"); |
| 2434 | + goto fail; |
| 2435 | + } |
| 2436 | + |
| 2437 | + out_pic->format.i_sar_den = p_filter->fmt_out.video.i_sar_den; |
| 2438 | + out_pic->format.i_sar_num = p_filter->fmt_out.video.i_sar_num; |
| 2439 | + |
| 2440 | + if (sys->is_sliced) { |
| 2441 | + vlc_mutex_lock(&sys->lock); |
| 2442 | + pic_fifo_put(&sys->slice.pics, out_pic); |
| 2443 | + vlc_mutex_unlock(&sys->lock); |
| 2444 | + |
| 2445 | + // Poke any returned pic buffers into output |
| 2446 | + // In general this should only happen immediately after enable |
| 2447 | + while ((out_buf = mmal_queue_get(sys->out_pool->queue)) != NULL) |
| 2448 | + mmal_port_send_buffer(sys->output, out_buf); |
| 2449 | + } |
| 2450 | + else |
| 2451 | + { |
| 2452 | + // 1 in - 1 out |
| 2453 | + if ((out_buf = mmal_queue_wait(sys->out_pool->queue)) == NULL) |
| 2454 | + { |
| 2455 | + msg_Err(p_filter, "Failed to get output buffer"); |
| 2456 | + picture_Release(out_pic); |
| 2457 | + goto fail; |
| 2458 | + } |
| 2459 | + mmal_buffer_header_reset(out_buf); |
| 2460 | + |
| 2461 | + // Attach out_pic to the buffer & ensure it is freed when the buffer is released |
| 2462 | + // On a good send callback the pic will be extracted to avoid this |
| 2463 | + out_buf->user_data = out_pic; |
| 2464 | + mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, NULL); |
| 2465 | + |
| 2466 | +#if 0 |
| 2467 | + { |
| 2468 | + char dbuf0[5]; |
| 2469 | + msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", |
| 2470 | + str_fourcc(dbuf0, out_pic->format.i_chroma), |
| 2471 | + out_pic->format.i_width, out_pic->format.i_height, |
| 2472 | + out_pic->format.i_x_offset, out_pic->format.i_y_offset, |
| 2473 | + out_pic->format.i_visible_width, out_pic->format.i_visible_height, |
| 2474 | + out_pic->format.i_sar_num, out_pic->format.i_sar_den); |
| 2475 | + } |
| 2476 | +#endif |
| 2477 | + |
| 2478 | + if (sys->is_cma) { |
| 2479 | + int rv; |
| 2480 | + |
| 2481 | + cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size); |
| 2482 | + if (cb == NULL) { |
| 2483 | + char dbuf0[5]; |
| 2484 | + msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d", |
| 2485 | + str_fourcc(dbuf0, out_pic->format.i_chroma), |
| 2486 | + sys->output->buffer_size); |
| 2487 | + goto fail; |
| 2488 | + } |
| 2489 | + const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable |
| 2490 | + out_buf->data = (uint8_t *)vc_h; |
| 2491 | + out_buf->alloc_size = sys->output->buffer_size; |
| 2492 | + |
| 2493 | + if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS) |
| 2494 | + { |
| 2495 | + char dbuf0[5]; |
| 2496 | + msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d", |
| 2497 | + str_fourcc(dbuf0, out_pic->format.i_chroma), |
| 2498 | + rv); |
| 2499 | + cma_buf_unref(cb); |
| 2500 | + goto fail; |
| 2501 | + } |
| 2502 | + } |
| 2503 | + else { |
| 2504 | + out_buf->data = out_pic->p[0].p_pixels; |
| 2505 | + out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines; |
| 2506 | + //**** stride ???? |
| 2507 | + } |
| 2508 | + |
| 2509 | +#if TRACE_ALL |
| 2510 | + msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld", |
| 2511 | + p_pic, out_buf->data, out_buf->user_data, out_buf->flags, |
| 2512 | + out_buf->length, out_buf->alloc_size, (long long)out_buf->pts); |
| 2513 | +#endif |
| 2514 | + |
| 2515 | + if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) |
| 2516 | + { |
| 2517 | + msg_Err(p_filter, "Send buffer to output failed"); |
| 2518 | + goto fail; |
| 2519 | + } |
| 2520 | + out_buf = NULL; |
| 2521 | + } |
| 2522 | + } |
| 2523 | + |
| 2524 | + |
| 2525 | + // Stuff into input |
| 2526 | + // We assume the BH is already set up with values reflecting pic date etc. |
| 2527 | + stash->pts = p_pic->date; |
| 2528 | + { |
| 2529 | + MMAL_BUFFER_HEADER_T *const pic_buf = sys->needs_copy_in ? |
| 2530 | + hw_mmal_pic_buf_copied(p_pic, sys->in_pool, sys->input, sys->cma_in_pool) : |
| 2531 | + hw_mmal_pic_buf_replicated(p_pic, sys->in_pool); |
| 2532 | + |
| 2533 | + // Whether or not we extracted the pic_buf we are done with the picture |
| 2534 | + picture_Release(p_pic); |
| 2535 | + p_pic = NULL; |
| 2536 | + |
| 2537 | + if (pic_buf == NULL) { |
| 2538 | + msg_Err(p_filter, "Pic has no attached buffer"); |
| 2539 | + goto fail; |
| 2540 | + } |
| 2541 | + |
| 2542 | + pic_buf->pts = frame_seq; |
| 2543 | + |
| 2544 | +#if TRACE_ALL |
| 2545 | + msg_Dbg(p_filter, "In buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d/%d, pts=%lld", |
| 2546 | + p_pic, pic_buf->data, pic_buf->user_data, pic_buf->flags, |
| 2547 | + pic_buf->length, pic_buf->alloc_size, sys->input->buffer_size, (long long)pic_buf->pts); |
| 2548 | +#endif |
| 2549 | + |
| 2550 | + if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS) |
| 2551 | + { |
| 2552 | + msg_Err(p_filter, "Send buffer to input failed"); |
| 2553 | + mmal_buffer_header_release(pic_buf); |
| 2554 | + goto fail; |
| 2555 | + } |
| 2556 | + } |
| 2557 | + |
| 2558 | + // We have a 1 pic latency for everything except the 1st pic which we |
| 2559 | + // wait for. |
| 2560 | + // This means we get a single static pic out |
| 2561 | + if (sys->pic_n++ == 1) { |
| 2562 | +#if TRACE_ALL |
| 2563 | + msg_Dbg(p_filter, ">>> %s: Pic1=%p", __func__, ret_pics); |
| 2564 | +#endif |
| 2565 | + return ret_pics; |
| 2566 | + } |
| 2567 | + |
| 2568 | + ret_pics = conv_get_out_pics(sys); |
| 2569 | + |
| 2570 | + if (sys->err_stream != MMAL_SUCCESS) |
| 2571 | + goto stream_fail; |
| 2572 | + |
| 2573 | + conv_stash_fixup(p_filter, sys, ret_pics); |
| 2574 | + |
| 2575 | +#if TRACE_ALL |
| 2576 | + msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics); |
| 2577 | +#endif |
| 2578 | + |
| 2579 | + return ret_pics; |
| 2580 | + |
| 2581 | +stream_fail: |
| 2582 | + msg_Err(p_filter, "MMAL error reported by callback"); |
| 2583 | +fail: |
| 2584 | +#if TRACE_ALL |
| 2585 | + msg_Err(p_filter, ">>> %s: FAIL", __func__); |
| 2586 | +#endif |
| 2587 | + if (ret_pics != NULL) |
| 2588 | + picture_Release(ret_pics); |
| 2589 | + if (out_buf != NULL) |
| 2590 | + mmal_buffer_header_release(out_buf); |
| 2591 | + if (p_pic != NULL) |
| 2592 | + picture_Release(p_pic); |
| 2593 | + conv_flush(p_filter); |
| 2594 | + return NULL; |
| 2595 | +} |
| 2596 | + |
| 2597 | +static void CloseConverter(vlc_object_t * obj) |
| 2598 | +{ |
| 2599 | + filter_t * const p_filter = (filter_t *)obj; |
| 2600 | + filter_sys_t * const sys = p_filter->p_sys; |
| 2601 | + unsigned int i; |
| 2602 | + |
| 2603 | +#if TRACE_ALL |
| 2604 | + msg_Dbg(obj, "<<< %s", __func__); |
| 2605 | +#endif |
| 2606 | + |
| 2607 | + if (sys == NULL) |
| 2608 | + return; |
| 2609 | + |
| 2610 | + // Disables input & output ports |
| 2611 | + conv_flush(p_filter); |
| 2612 | + |
| 2613 | + cma_buf_pool_deletez(&sys->cma_in_pool); |
| 2614 | + cma_buf_pool_deletez(&sys->cma_out_pool); |
| 2615 | + |
| 2616 | + if (sys->component && sys->component->control->is_enabled) |
| 2617 | + mmal_port_disable(sys->component->control); |
| 2618 | + |
| 2619 | + if (sys->component && sys->component->is_enabled) |
| 2620 | + mmal_component_disable(sys->component); |
| 2621 | + |
| 2622 | + if (sys->resizer_type == FILTER_RESIZER_HVS) |
| 2623 | + { |
| 2624 | + for (i = 0; i != SUBS_MAX; ++i) { |
| 2625 | + hw_mmal_subpic_close(VLC_OBJECT(p_filter), sys->subs + i); |
| 2626 | + } |
| 2627 | + } |
| 2628 | + |
| 2629 | + if (sys->out_pool) |
| 2630 | + { |
| 2631 | + if (sys->is_sliced) |
| 2632 | + mmal_port_pool_destroy(sys->output, sys->out_pool); |
| 2633 | + else |
| 2634 | + mmal_pool_destroy(sys->out_pool); |
| 2635 | + } |
| 2636 | + |
| 2637 | + if (sys->in_pool != NULL) |
| 2638 | + mmal_pool_destroy(sys->in_pool); |
| 2639 | + |
| 2640 | + if (sys->component) |
| 2641 | + mmal_component_release(sys->component); |
| 2642 | + |
| 2643 | + cma_vcsm_exit(sys->vcsm_init_type); |
| 2644 | + |
| 2645 | + vlc_sem_destroy(&sys->sem); |
| 2646 | + vlc_mutex_destroy(&sys->lock); |
| 2647 | + |
| 2648 | + p_filter->p_sys = NULL; |
| 2649 | + free(sys); |
| 2650 | +} |
| 2651 | + |
| 2652 | + |
| 2653 | +static inline MMAL_FOURCC_T filter_enc_in(const video_format_t * const fmt) |
| 2654 | +{ |
| 2655 | + if (hw_mmal_chroma_is_mmal(fmt->i_chroma)) |
| 2656 | + return vlc_to_mmal_video_fourcc(fmt); |
| 2657 | + |
| 2658 | + if (fmt->i_chroma == VLC_CODEC_I420 || |
| 2659 | + fmt->i_chroma == VLC_CODEC_I420_10L) |
| 2660 | + return MMAL_ENCODING_I420; |
| 2661 | + |
| 2662 | + return 0; |
| 2663 | +} |
| 2664 | + |
| 2665 | +static inline MMAL_FOURCC_T filter_enc_out(const video_format_t * const fmt) |
| 2666 | +{ |
| 2667 | + const MMAL_FOURCC_T mmes = vlc_to_mmal_video_fourcc(fmt); |
| 2668 | + // Can only copy out single plane stuff currently - this could be fixed! |
| 2669 | + return hw_mmal_chroma_is_mmal(fmt->i_chroma) || mmes != MMAL_ENCODING_I420 ? mmes : 0; |
| 2670 | +} |
| 2671 | + |
| 2672 | + |
| 2673 | +static int OpenConverter(vlc_object_t * obj) |
| 2674 | +{ |
| 2675 | + filter_t * const p_filter = (filter_t *)obj; |
| 2676 | + int ret = VLC_EGENERIC; |
| 2677 | + filter_sys_t *sys; |
| 2678 | + MMAL_STATUS_T status; |
| 2679 | + MMAL_FOURCC_T enc_out = filter_enc_out(&p_filter->fmt_out.video); |
| 2680 | + const MMAL_FOURCC_T enc_in = filter_enc_in(&p_filter->fmt_in.video); |
| 2681 | + bool use_resizer; |
| 2682 | + bool use_isp; |
| 2683 | + int gpu_mem; |
| 2684 | + |
| 2685 | + // At least in principle we should deal with any mmal format as input |
| 2686 | + if (enc_in == 0 || enc_out == 0) |
| 2687 | + return VLC_EGENERIC; |
| 2688 | + |
| 2689 | + // Can't transform |
| 2690 | + if (p_filter->fmt_in.video.orientation != p_filter->fmt_out.video.orientation) |
| 2691 | + return VLC_EGENERIC; |
| 2692 | + |
| 2693 | + use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME); |
| 2694 | + use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME); |
| 2695 | + |
| 2696 | +retry: |
| 2697 | + // ** Make more generic by checking supported encs |
| 2698 | + // |
| 2699 | + // Must use ISP - HVS can't do this, nor can resizer |
| 2700 | + if (enc_in == MMAL_ENCODING_YUVUV64_10) { |
| 2701 | + // If resizer selected then just give up |
| 2702 | + if (use_resizer) |
| 2703 | + return VLC_EGENERIC; |
| 2704 | + // otherwise downgrade HVS to ISP |
| 2705 | + use_isp = true; |
| 2706 | + } |
| 2707 | + // HVS can't do I420 |
| 2708 | + if (enc_out == MMAL_ENCODING_I420) { |
| 2709 | + use_isp = true; |
| 2710 | + } |
| 2711 | + // Only HVS can deal with SAND30 |
| 2712 | + if (enc_in == MMAL_ENCODING_YUV10_COL) { |
| 2713 | + if (use_isp || use_resizer) |
| 2714 | + return VLC_EGENERIC; |
| 2715 | + } |
| 2716 | |
| 2717 | - sys->output_format = format; |
| 2718 | |
| 2719 | - mmal_buffer_header_release(buffer); |
| 2720 | + if (use_resizer) { |
| 2721 | + // use resizer overrides use_isp |
| 2722 | + use_isp = false; |
| 2723 | + } |
| 2724 | + |
| 2725 | + // Check we have a sliced version of the fourcc if we want the resizer |
| 2726 | + if (use_resizer && |
| 2727 | + (enc_out = pic_to_slice_mmal_fourcc(enc_out)) == 0) { |
| 2728 | + return VLC_EGENERIC; |
| 2729 | + } |
| 2730 | + |
| 2731 | + gpu_mem = hw_mmal_get_gpu_mem(); |
| 2732 | + |
| 2733 | + { |
| 2734 | + char dbuf0[5], dbuf1[5], dbuf2[5], dbuf3[5]; |
| 2735 | + 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__, |
| 2736 | + use_resizer ? "resize" : use_isp ? "isp" : "hvs", |
| 2737 | + str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), str_fourcc(dbuf2, enc_in), |
| 2738 | + p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, |
| 2739 | + p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, |
| 2740 | + p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, |
| 2741 | + p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den, |
| 2742 | + str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), str_fourcc(dbuf3, enc_out), |
| 2743 | + p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, |
| 2744 | + p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, |
| 2745 | + p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, |
| 2746 | + p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask, |
| 2747 | + p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den, |
| 2748 | + gpu_mem); |
| 2749 | + } |
| 2750 | + |
| 2751 | + sys = calloc(1, sizeof(filter_sys_t)); |
| 2752 | + if (!sys) { |
| 2753 | + ret = VLC_ENOMEM; |
| 2754 | + goto fail; |
| 2755 | + } |
| 2756 | + p_filter->p_sys = sys; |
| 2757 | + |
| 2758 | + // Init stuff the we destroy unconditionaly in Close first |
| 2759 | + vlc_mutex_init(&sys->lock); |
| 2760 | + vlc_sem_init(&sys->sem, 0); |
| 2761 | + sys->err_stream = MMAL_SUCCESS; |
| 2762 | + pic_fifo_init(&sys->ret_pics); |
| 2763 | + pic_fifo_init(&sys->slice.pics); |
| 2764 | + |
| 2765 | + sys->needs_copy_in = !hw_mmal_chroma_is_mmal(p_filter->fmt_in.video.i_chroma); |
| 2766 | + sys->in_port_cb_fn = conv_input_port_cb; |
| 2767 | + |
| 2768 | + if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { |
| 2769 | + msg_Err(p_filter, "VCSM init failed"); |
| 2770 | + goto fail; |
| 2771 | + } |
| 2772 | + |
| 2773 | + if (use_resizer) { |
| 2774 | + sys->resizer_type = FILTER_RESIZER_RESIZER; |
| 2775 | + sys->is_sliced = true; |
| 2776 | + sys->component_name = MMAL_COMPONENT_DEFAULT_RESIZER; |
| 2777 | + sys->out_port_cb_fn = slice_output_port_cb; |
| 2778 | + } |
| 2779 | + else if (use_isp) { |
| 2780 | + sys->resizer_type = FILTER_RESIZER_ISP; |
| 2781 | + sys->is_sliced = false; // Copy directly into filter picture |
| 2782 | + sys->component_name = MMAL_COMPONENT_ISP_RESIZER; |
| 2783 | + sys->out_port_cb_fn = conv_output_port_cb; |
| 2784 | } else { |
| 2785 | - mmal_buffer_header_release(buffer); |
| 2786 | + sys->resizer_type = FILTER_RESIZER_HVS; |
| 2787 | + sys->is_sliced = false; // Copy directly into filter picture |
| 2788 | + sys->component_name = MMAL_COMPONENT_HVS; |
| 2789 | + sys->out_port_cb_fn = conv_output_port_cb; |
| 2790 | + } |
| 2791 | + sys->is_cma = is_cma_buf_pic_chroma(p_filter->fmt_out.video.i_chroma); |
| 2792 | + |
| 2793 | + status = mmal_component_create(sys->component_name, &sys->component); |
| 2794 | + if (status != MMAL_SUCCESS) { |
| 2795 | + if (!use_isp && !use_resizer) { |
| 2796 | + msg_Warn(p_filter, "Failed to rcreate HVS resizer - retrying with ISP"); |
| 2797 | + CloseConverter(obj); |
| 2798 | + use_isp = true; |
| 2799 | + goto retry; |
| 2800 | + } |
| 2801 | + msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)", |
| 2802 | + MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status)); |
| 2803 | + goto fail; |
| 2804 | } |
| 2805 | + sys->output = sys->component->output[0]; |
| 2806 | + sys->input = sys->component->input[0]; |
| 2807 | + |
| 2808 | + sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter; |
| 2809 | + status = mmal_port_enable(sys->component->control, conv_control_port_cb); |
| 2810 | + if (status != MMAL_SUCCESS) { |
| 2811 | + msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)", |
| 2812 | + sys->component->control->name, status, mmal_status_to_string(status)); |
| 2813 | + goto fail; |
| 2814 | + } |
| 2815 | + |
| 2816 | + if (sys->needs_copy_in && |
| 2817 | + (sys->cma_in_pool = cma_buf_pool_new(2, 2, true, "conv-copy-in")) == NULL) |
| 2818 | + { |
| 2819 | + msg_Err(p_filter, "Failed to allocate input CMA pool"); |
| 2820 | + goto fail; |
| 2821 | + } |
| 2822 | + |
| 2823 | + sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter; |
| 2824 | + sys->input->format->type = MMAL_ES_TYPE_VIDEO; |
| 2825 | + sys->input->format->encoding = enc_in; |
| 2826 | + sys->input->format->encoding_variant = MMAL_ENCODING_I420; |
| 2827 | + hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &p_filter->fmt_in.video); |
| 2828 | + port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, 1); |
| 2829 | + |
| 2830 | + mmal_log_dump_format(sys->input->format); |
| 2831 | + |
| 2832 | + status = mmal_port_format_commit(sys->input); |
| 2833 | + if (status != MMAL_SUCCESS) { |
| 2834 | + msg_Err(p_filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)", |
| 2835 | + sys->input->name, status, mmal_status_to_string(status)); |
| 2836 | + goto fail; |
| 2837 | + } |
| 2838 | + sys->input->buffer_size = sys->input->buffer_size_recommended; |
| 2839 | + sys->input->buffer_num = NUM_DECODER_BUFFER_HEADERS; |
| 2840 | + |
| 2841 | + if ((status = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS) |
| 2842 | + goto fail; |
| 2843 | + |
| 2844 | + port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced || sys->is_cma); |
| 2845 | + |
| 2846 | + status = mmal_component_enable(sys->component); |
| 2847 | + if (status != MMAL_SUCCESS) { |
| 2848 | + msg_Err(p_filter, "Failed to enable component %s (status=%"PRIx32" %s)", |
| 2849 | + sys->component->name, status, mmal_status_to_string(status)); |
| 2850 | + goto fail; |
| 2851 | + } |
| 2852 | + |
| 2853 | + if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) |
| 2854 | + { |
| 2855 | + msg_Err(p_filter, "Failed to create input pool"); |
| 2856 | + goto fail; |
| 2857 | + } |
| 2858 | + |
| 2859 | + if (sys->resizer_type == FILTER_RESIZER_HVS) |
| 2860 | + { |
| 2861 | + unsigned int i; |
| 2862 | + for (i = 0; i != SUBS_MAX; ++i) { |
| 2863 | + if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], -1, i + 1) != MMAL_SUCCESS) |
| 2864 | + { |
| 2865 | + msg_Err(p_filter, "Failed to open subpic %d", i); |
| 2866 | + goto fail; |
| 2867 | + } |
| 2868 | + } |
| 2869 | + } |
| 2870 | + |
| 2871 | + p_filter->pf_video_filter = conv_filter; |
| 2872 | + p_filter->pf_flush = conv_flush; |
| 2873 | + // video_drain NIF in filter structure |
| 2874 | + |
| 2875 | +#if TRACE_ALL |
| 2876 | + msg_Dbg(p_filter, ">>> %s: ok", __func__); |
| 2877 | +#endif |
| 2878 | + |
| 2879 | + return VLC_SUCCESS; |
| 2880 | + |
| 2881 | +fail: |
| 2882 | + CloseConverter(obj); |
| 2883 | + |
| 2884 | + if (!use_resizer && status == MMAL_ENOMEM) { |
| 2885 | + use_resizer = true; |
| 2886 | + msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer"); |
| 2887 | + goto retry; |
| 2888 | + } |
| 2889 | + |
| 2890 | +#if TRACE_ALL |
| 2891 | + msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret); |
| 2892 | +#endif |
| 2893 | + return ret; |
| 2894 | +} |
| 2895 | + |
| 2896 | +#if OPT_TO_FROM_ZC |
| 2897 | +//---------------------------------------------------------------------------- |
| 2898 | +// |
| 2899 | +// Simple copy in to ZC |
| 2900 | + |
| 2901 | +typedef struct to_zc_sys_s { |
| 2902 | + vcsm_init_type_t vcsm_init_type; |
| 2903 | + cma_buf_pool_t * cma_out_pool; |
| 2904 | +} to_zc_sys_t; |
| 2905 | + |
| 2906 | + |
| 2907 | +static size_t buf_alloc_size(const vlc_fourcc_t i_chroma, const unsigned int width, const unsigned int height) |
| 2908 | +{ |
| 2909 | + const unsigned int pels = width * height; |
| 2910 | + |
| 2911 | + switch (i_chroma) |
| 2912 | + { |
| 2913 | + case VLC_CODEC_MMAL_ZC_RGB32: |
| 2914 | + return pels * 4; |
| 2915 | + case VLC_CODEC_MMAL_ZC_I420: |
| 2916 | + return pels * 3 / 2; |
| 2917 | + default: |
| 2918 | + break; |
| 2919 | + } |
| 2920 | + return 0; |
| 2921 | +} |
| 2922 | + |
| 2923 | + |
| 2924 | +static picture_t * |
| 2925 | +to_zc_filter(filter_t *p_filter, picture_t *in_pic) |
| 2926 | +{ |
| 2927 | + to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys; |
| 2928 | +#if TRACE_ALL |
| 2929 | + msg_Dbg(p_filter, "<<< %s", __func__); |
| 2930 | +#endif |
| 2931 | + |
| 2932 | + assert(p_filter->fmt_out.video.i_chroma == VLC_CODEC_MMAL_ZC_I420); |
| 2933 | + |
| 2934 | + picture_t * const out_pic = filter_NewPicture(p_filter); |
| 2935 | + if (out_pic == NULL) |
| 2936 | + goto fail0; |
| 2937 | + |
| 2938 | + MMAL_ES_SPECIFIC_FORMAT_T mm_vfmt = {.video={0}}; |
| 2939 | + MMAL_ES_FORMAT_T mm_esfmt = { |
| 2940 | + .encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video), |
| 2941 | + .es = &mm_vfmt}; |
| 2942 | + |
| 2943 | + hw_mmal_vlc_fmt_to_mmal_fmt(&mm_esfmt, &p_filter->fmt_out.video); |
| 2944 | + |
| 2945 | + const size_t buf_alloc = buf_alloc_size(p_filter->fmt_out.video.i_chroma, |
| 2946 | + mm_vfmt.video.width, mm_vfmt.video.height); |
| 2947 | + if (buf_alloc == 0) |
| 2948 | + goto fail1; |
| 2949 | + cma_buf_t *const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, buf_alloc); |
| 2950 | + if (cb == NULL) |
| 2951 | + goto fail1; |
| 2952 | + |
| 2953 | + if (cma_buf_pic_attach(cb, out_pic) != VLC_SUCCESS) |
| 2954 | + goto fail2; |
| 2955 | + cma_pic_set_data(out_pic, &mm_esfmt, NULL); |
| 2956 | + |
| 2957 | + hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), NULL, &mm_esfmt, in_pic); |
| 2958 | + |
| 2959 | + // Copy pic properties |
| 2960 | + out_pic->date = in_pic->date; |
| 2961 | + out_pic->b_force = in_pic->b_force; |
| 2962 | + out_pic->b_progressive = in_pic->b_progressive; |
| 2963 | + out_pic->b_top_field_first = in_pic->b_top_field_first; |
| 2964 | + out_pic->i_nb_fields = in_pic->i_nb_fields; |
| 2965 | + |
| 2966 | + picture_Release(in_pic); |
| 2967 | + |
| 2968 | + return out_pic; |
| 2969 | + |
| 2970 | +fail2: |
| 2971 | + cma_buf_unref(cb); |
| 2972 | +fail1: |
| 2973 | + picture_Release(out_pic); |
| 2974 | +fail0: |
| 2975 | + picture_Release(in_pic); |
| 2976 | + return NULL; |
| 2977 | +} |
| 2978 | + |
| 2979 | +static void to_zc_flush(filter_t * p_filter) |
| 2980 | +{ |
| 2981 | + VLC_UNUSED(p_filter); |
| 2982 | } |
| 2983 | + |
| 2984 | +static void CloseConverterToZc(vlc_object_t * obj) |
| 2985 | +{ |
| 2986 | + filter_t * const p_filter = (filter_t *)obj; |
| 2987 | + to_zc_sys_t * const sys = (to_zc_sys_t *)p_filter->p_sys; |
| 2988 | + |
| 2989 | + if (sys == NULL) |
| 2990 | + return; |
| 2991 | + |
| 2992 | + p_filter->p_sys = NULL; |
| 2993 | + |
| 2994 | + cma_buf_pool_deletez(&sys->cma_out_pool); |
| 2995 | + cma_vcsm_exit(sys->vcsm_init_type); |
| 2996 | + |
| 2997 | + free(sys); |
| 2998 | +} |
| 2999 | + |
| 3000 | +static bool to_zc_validate_fmt(const video_format_t * const f_in, const video_format_t * const f_out) |
| 3001 | +{ |
| 3002 | + if (!((f_in->i_chroma == VLC_CODEC_I420 || f_in->i_chroma == VLC_CODEC_I420_10L) && |
| 3003 | + f_out->i_chroma == VLC_CODEC_MMAL_ZC_I420)) |
| 3004 | + { |
| 3005 | + return false; |
| 3006 | + } |
| 3007 | + if (f_in->i_height != f_out->i_height || |
| 3008 | + f_in->i_width != f_out->i_width) |
| 3009 | + { |
| 3010 | + return false; |
| 3011 | + } |
| 3012 | + |
| 3013 | + return true; |
| 3014 | +} |
| 3015 | + |
| 3016 | +static int OpenConverterToZc(vlc_object_t * obj) |
| 3017 | +{ |
| 3018 | + int ret = VLC_EGENERIC; |
| 3019 | + filter_t * const p_filter = (filter_t *)obj; |
| 3020 | + |
| 3021 | + if (!to_zc_validate_fmt(&p_filter->fmt_in.video, &p_filter->fmt_out.video)) |
| 3022 | + goto fail; |
| 3023 | + |
| 3024 | + { |
| 3025 | + char dbuf0[5], dbuf1[5]; |
| 3026 | + 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__, |
| 3027 | + str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), |
| 3028 | + p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, |
| 3029 | + p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, |
| 3030 | + p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, |
| 3031 | + p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den, |
| 3032 | + str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), |
| 3033 | + p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, |
| 3034 | + p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, |
| 3035 | + p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height, |
| 3036 | + p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask, |
| 3037 | + p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den); |
| 3038 | + } |
| 3039 | + |
| 3040 | + to_zc_sys_t * const sys = calloc(1, sizeof(*sys)); |
| 3041 | + if (!sys) { |
| 3042 | + ret = VLC_ENOMEM; |
| 3043 | + goto fail; |
| 3044 | + } |
| 3045 | + p_filter->p_sys = (filter_sys_t *)sys; |
| 3046 | + |
| 3047 | + if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { |
| 3048 | + msg_Err(p_filter, "VCSM init failed"); |
| 3049 | + goto fail; |
| 3050 | + } |
| 3051 | + |
| 3052 | + if ((sys->cma_out_pool = cma_buf_pool_new(5, 5, true, "conv-to-zc")) == NULL) |
| 3053 | + { |
| 3054 | + msg_Err(p_filter, "Failed to allocate input CMA pool"); |
| 3055 | + goto fail; |
| 3056 | + } |
| 3057 | + |
| 3058 | + p_filter->pf_video_filter = to_zc_filter; |
| 3059 | + p_filter->pf_flush = to_zc_flush; |
| 3060 | + return VLC_SUCCESS; |
| 3061 | + |
| 3062 | +fail: |
| 3063 | + CloseConverterToZc(obj); |
| 3064 | + return ret; |
| 3065 | +} |
| 3066 | + |
| 3067 | +//---------------------------------------------------------------------------- |
| 3068 | +// |
| 3069 | +// Simple "copy" from ZC |
| 3070 | + |
| 3071 | +static void CloseConverterFromZc(vlc_object_t * obj) |
| 3072 | +{ |
| 3073 | + VLC_UNUSED(obj); |
| 3074 | +} |
| 3075 | + |
| 3076 | +static int OpenConverterFromZc(vlc_object_t * obj) |
| 3077 | +{ |
| 3078 | + return VLC_EGENERIC; |
| 3079 | +} |
| 3080 | +#endif |
| 3081 | +//---------------------------------------------------------------------------- |
| 3082 | + |
| 3083 | +typedef struct blend_sys_s { |
| 3084 | + vzc_pool_ctl_t * vzc; |
| 3085 | + const picture_t * last_dst; // Not a ref, just a hint that we have a new pic |
| 3086 | + vcsm_init_type_t vcsm_init_type; |
| 3087 | +} blend_sys_t; |
| 3088 | + |
| 3089 | +static void FilterBlendMmal(filter_t *p_filter, |
| 3090 | + picture_t *dst, const picture_t * src, |
| 3091 | + int x_offset, int y_offset, int alpha) |
| 3092 | +{ |
| 3093 | + blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys; |
| 3094 | +#if TRACE_ALL |
| 3095 | + 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); |
| 3096 | +#endif |
| 3097 | + // If nothing to do then do nothing |
| 3098 | + if (alpha == 0 || |
| 3099 | + src->format.i_visible_height == 0 || |
| 3100 | + src->format.i_visible_width == 0) |
| 3101 | + { |
| 3102 | + return; |
| 3103 | + } |
| 3104 | + |
| 3105 | + if (dst->context == NULL) |
| 3106 | + msg_Err(p_filter, "MMAL pic missing context"); |
| 3107 | + else |
| 3108 | + { |
| 3109 | + // cast away src const so we can ref it |
| 3110 | + MMAL_BUFFER_HEADER_T *buf = hw_mmal_vzc_buf_from_pic(sys->vzc, (picture_t *)src, |
| 3111 | + vis_mmal_rect(&dst->format), |
| 3112 | + x_offset, y_offset, |
| 3113 | + alpha, |
| 3114 | + dst != sys->last_dst || !hw_mmal_pic_has_sub_bufs(dst)); |
| 3115 | + if (buf == NULL) { |
| 3116 | + msg_Err(p_filter, "Failed to allocate vzc buffer for subpic"); |
| 3117 | + return; |
| 3118 | + } |
| 3119 | + |
| 3120 | + hw_mmal_pic_sub_buf_add(dst, buf); |
| 3121 | + |
| 3122 | + sys->last_dst = dst; |
| 3123 | + } |
| 3124 | +} |
| 3125 | + |
| 3126 | +static void FlushBlendMmal(filter_t * p_filter) |
| 3127 | +{ |
| 3128 | + blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys; |
| 3129 | + sys->last_dst = NULL; |
| 3130 | + hw_mmal_vzc_pool_flush(sys->vzc); |
| 3131 | +} |
| 3132 | + |
| 3133 | +static void CloseBlendMmal(vlc_object_t *object) |
| 3134 | +{ |
| 3135 | + filter_t * const p_filter = (filter_t *)object; |
| 3136 | + blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys; |
| 3137 | + |
| 3138 | + if (sys != NULL) { |
| 3139 | + p_filter->p_sys = NULL; |
| 3140 | + |
| 3141 | + hw_mmal_vzc_pool_release(sys->vzc); |
| 3142 | + cma_vcsm_exit(sys->vcsm_init_type); |
| 3143 | + free(sys); |
| 3144 | + } |
| 3145 | +} |
| 3146 | + |
| 3147 | +static int OpenBlendMmal(vlc_object_t *object) |
| 3148 | +{ |
| 3149 | + filter_t * const p_filter = (filter_t *)object; |
| 3150 | + const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma; |
| 3151 | + |
| 3152 | + if (!hw_mmal_chroma_is_mmal(vfcc_dst) || |
| 3153 | + !hw_mmal_vzc_subpic_fmt_valid(&p_filter->fmt_in.video)) |
| 3154 | + { |
| 3155 | + return VLC_EGENERIC; |
| 3156 | + } |
| 3157 | + |
| 3158 | + { |
| 3159 | + char dbuf0[5], dbuf1[5]; |
| 3160 | + msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %dx%d]->%s,%dx%d [(%d,%d) %dx%d]", __func__, |
| 3161 | + "blend", |
| 3162 | + str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, |
| 3163 | + p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, |
| 3164 | + p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, |
| 3165 | + str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, |
| 3166 | + p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, |
| 3167 | + p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height); |
| 3168 | + } |
| 3169 | + |
| 3170 | + { |
| 3171 | + blend_sys_t * const sys = calloc(1, sizeof (*sys)); |
| 3172 | + if (sys == NULL) |
| 3173 | + return VLC_ENOMEM; |
| 3174 | + |
| 3175 | + p_filter->p_sys = (filter_sys_t *)sys; |
| 3176 | + |
| 3177 | + if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { |
| 3178 | + msg_Err(p_filter, "VCSM init failed"); |
| 3179 | + goto fail; |
| 3180 | + } |
| 3181 | + |
| 3182 | + if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL) |
| 3183 | + goto fail; |
| 3184 | + } |
| 3185 | + |
| 3186 | + p_filter->pf_video_blend = FilterBlendMmal; |
| 3187 | + p_filter->pf_flush = FlushBlendMmal; |
| 3188 | + |
| 3189 | + return VLC_SUCCESS; |
| 3190 | + |
| 3191 | +fail: |
| 3192 | + CloseBlendMmal(VLC_OBJECT(p_filter)); |
| 3193 | + return VLC_ENOMEM; |
| 3194 | +} |
| 3195 | + |
| 3196 | +// --------------------------------------------------------------------------- |
| 3197 | + |
| 3198 | +static void FilterBlendNeon(filter_t *p_filter, |
| 3199 | + picture_t *dst_pic, const picture_t * src_pic, |
| 3200 | + int x_offset, int y_offset, int alpha) |
| 3201 | +{ |
| 3202 | + const uint8_t * s_data; |
| 3203 | + uint8_t * d_data; |
| 3204 | + int width = src_pic->format.i_visible_width; |
| 3205 | + int height = src_pic->format.i_visible_height; |
| 3206 | + blend_neon_fn *const blend_fn = (blend_neon_fn * )p_filter->p_sys; |
| 3207 | + |
| 3208 | +#if TRACE_ALL |
| 3209 | + 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); |
| 3210 | +#endif |
| 3211 | + |
| 3212 | + if (alpha == 0 || |
| 3213 | + src_pic->format.i_visible_height == 0 || |
| 3214 | + src_pic->format.i_visible_width == 0) |
| 3215 | + { |
| 3216 | + return; |
| 3217 | + } |
| 3218 | + |
| 3219 | + x_offset += dst_pic->format.i_x_offset; |
| 3220 | + y_offset += dst_pic->format.i_y_offset; |
| 3221 | + |
| 3222 | + // Deal with R/B overrun |
| 3223 | + if (x_offset + width >= (int)(dst_pic->format.i_x_offset + dst_pic->format.i_visible_width)) |
| 3224 | + width = dst_pic->format.i_x_offset + dst_pic->format.i_visible_width - x_offset; |
| 3225 | + if (y_offset + height >= (int)(dst_pic->format.i_y_offset + dst_pic->format.i_visible_height)) |
| 3226 | + height = dst_pic->format.i_y_offset + dst_pic->format.i_visible_height - y_offset; |
| 3227 | + |
| 3228 | + if (width <= 0 || height <= 0) { |
| 3229 | + return; |
| 3230 | + } |
| 3231 | + |
| 3232 | + // *** L/U overrun |
| 3233 | + |
| 3234 | + s_data = src_pic->p[0].p_pixels + |
| 3235 | + src_pic->p[0].i_pixel_pitch * src_pic->format.i_x_offset + |
| 3236 | + src_pic->p[0].i_pitch * src_pic->format.i_y_offset; |
| 3237 | + d_data = dst_pic->p[0].p_pixels + |
| 3238 | + dst_pic->p[0].i_pixel_pitch * x_offset + |
| 3239 | + dst_pic->p[0].i_pitch * y_offset; |
| 3240 | + |
| 3241 | + |
| 3242 | + do { |
| 3243 | + blend_fn(d_data, s_data, alpha, width); |
| 3244 | + s_data += src_pic->p[0].i_pitch; |
| 3245 | + d_data += dst_pic->p[0].i_pitch; |
| 3246 | + } while (--height > 0); |
| 3247 | +} |
| 3248 | + |
| 3249 | +static void CloseBlendNeon(vlc_object_t *object) |
| 3250 | +{ |
| 3251 | + VLC_UNUSED(object); |
| 3252 | +} |
| 3253 | + |
| 3254 | +static int OpenBlendNeon(vlc_object_t *object) |
| 3255 | +{ |
| 3256 | + filter_t * const p_filter = (filter_t *)object; |
| 3257 | + const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma; |
| 3258 | + MMAL_FOURCC_T mfcc_src = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video); |
| 3259 | + MMAL_FOURCC_T mfcc_dst = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video); |
| 3260 | + blend_neon_fn * blend_fn = (blend_neon_fn *)0; |
| 3261 | + |
| 3262 | + // Non-alpha RGB only for dest |
| 3263 | + if (vfcc_dst != VLC_CODEC_RGB32) |
| 3264 | + return VLC_EGENERIC; |
| 3265 | + |
| 3266 | + // Check we have appropriate blend fn (mmal doesn't have a non-alpha RGB32) |
| 3267 | + switch (mfcc_src) { |
| 3268 | + case MMAL_ENCODING_RGBA: |
| 3269 | + if (mfcc_dst == MMAL_ENCODING_RGBA) |
| 3270 | + blend_fn = blend_rgbx_rgba_neon; |
| 3271 | + else if (mfcc_dst == MMAL_ENCODING_BGRA) |
| 3272 | + blend_fn = blend_bgrx_rgba_neon; |
| 3273 | + break; |
| 3274 | + |
| 3275 | + case MMAL_ENCODING_BGRA: |
| 3276 | + if (mfcc_dst == MMAL_ENCODING_BGRA) |
| 3277 | + blend_fn = blend_rgbx_rgba_neon; |
| 3278 | + else if (mfcc_dst == MMAL_ENCODING_RGBA) |
| 3279 | + blend_fn = blend_bgrx_rgba_neon; |
| 3280 | + break; |
| 3281 | + |
| 3282 | + default: |
| 3283 | + break; |
| 3284 | + } |
| 3285 | + |
| 3286 | + if (blend_fn == (blend_neon_fn *)0) |
| 3287 | + { |
| 3288 | + return VLC_EGENERIC; |
| 3289 | + } |
| 3290 | + |
| 3291 | + p_filter->p_sys = (void *)blend_fn; |
| 3292 | + p_filter->pf_video_blend = FilterBlendNeon; |
| 3293 | + |
| 3294 | + { |
| 3295 | + char dbuf0[5], dbuf1[5]; |
| 3296 | + char dbuf0a[5], dbuf1a[5]; |
| 3297 | + msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %dx%d]->%s/%s,%dx%d [(%d,%d) %dx%d]", __func__, |
| 3298 | + "blend", |
| 3299 | + str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), |
| 3300 | + str_fourcc(dbuf0a, mfcc_src), |
| 3301 | + p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height, |
| 3302 | + p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset, |
| 3303 | + p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height, |
| 3304 | + str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), |
| 3305 | + str_fourcc(dbuf1a, mfcc_dst), |
| 3306 | + p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height, |
| 3307 | + p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset, |
| 3308 | + p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height); |
| 3309 | + } |
| 3310 | + |
| 3311 | + return VLC_SUCCESS; |
| 3312 | +} |
| 3313 | + |
| 3314 | +vlc_module_begin() |
| 3315 | + set_category( CAT_INPUT ) |
| 3316 | + set_subcategory( SUBCAT_INPUT_VCODEC ) |
| 3317 | + set_shortname(N_("MMAL decoder")) |
| 3318 | + set_description(N_("MMAL-based decoder plugin for Raspberry Pi")) |
| 3319 | + set_capability("video decoder", 90) |
| 3320 | + add_shortcut("mmal_decoder") |
| 3321 | + add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false) |
| 3322 | + set_callbacks(OpenDecoder, CloseDecoder) |
| 3323 | + |
| 3324 | + add_submodule() |
| 3325 | + set_category( CAT_VIDEO ) |
| 3326 | + set_subcategory( SUBCAT_VIDEO_VFILTER ) |
| 3327 | + set_shortname(N_("MMAL resizer")) |
| 3328 | + set_description(N_("MMAL resizing conversion filter")) |
| 3329 | + add_shortcut("mmal_converter") |
| 3330 | + set_capability( "video converter", 900 ) |
| 3331 | + add_bool(MMAL_RESIZE_NAME, /* default */ false, MMAL_RESIZE_TEXT, MMAL_RESIZE_LONGTEXT, /* advanced option */ false) |
| 3332 | + add_bool(MMAL_ISP_NAME, /* default */ false, MMAL_ISP_TEXT, MMAL_ISP_LONGTEXT, /* advanced option */ false) |
| 3333 | + set_callbacks(OpenConverter, CloseConverter) |
| 3334 | + |
| 3335 | +#if OPT_TO_FROM_ZC |
| 3336 | + add_submodule() |
| 3337 | + set_category( CAT_VIDEO ) |
| 3338 | + set_subcategory( SUBCAT_VIDEO_VFILTER ) |
| 3339 | + set_shortname(N_("MMAL to ZC")) |
| 3340 | + set_description(N_("MMAL conversion to ZC filter")) |
| 3341 | + add_shortcut("mmal_to_zc") |
| 3342 | + set_capability( "video converter", 901 ) |
| 3343 | + set_callbacks(OpenConverterToZc, CloseConverterToZc) |
| 3344 | + |
| 3345 | + add_submodule() |
| 3346 | + set_category( CAT_VIDEO ) |
| 3347 | + set_subcategory( SUBCAT_VIDEO_VFILTER ) |
| 3348 | + set_shortname(N_("MMAL from ZC")) |
| 3349 | + set_description(N_("MMAL conversion from ZC filter")) |
| 3350 | + add_shortcut("mmal_from_zc") |
| 3351 | + set_capability( "video converter", 902 ) |
| 3352 | + set_callbacks(OpenConverterFromZc, CloseConverterFromZc) |
| 3353 | +#endif |
| 3354 | + |
| 3355 | + add_submodule() |
| 3356 | + set_category( CAT_VIDEO ) |
| 3357 | + set_subcategory( SUBCAT_VIDEO_VFILTER ) |
| 3358 | + set_description(N_("Video pictures blending for MMAL")) |
| 3359 | + add_shortcut("mmal_blend") |
| 3360 | + set_capability("video blending", 120) |
| 3361 | + set_callbacks(OpenBlendMmal, CloseBlendMmal) |
| 3362 | + |
| 3363 | + add_submodule() |
| 3364 | + set_category( CAT_VIDEO ) |
| 3365 | + set_subcategory( SUBCAT_VIDEO_VFILTER ) |
| 3366 | + set_description(N_("Video pictures blending for neon")) |
| 3367 | + add_shortcut("neon_blend") |
| 3368 | + set_capability("video blending", 110) |
| 3369 | + set_callbacks(OpenBlendNeon, CloseBlendNeon) |
| 3370 | + |
| 3371 | +vlc_module_end() |
| 3372 | + |
| 3373 | + |
| 3374 | --- /dev/null |
| 3375 | +++ b/modules/hw/mmal/converter_mmal.c |
| 3376 | @@ -0,0 +1,479 @@ |
| 3377 | +#ifdef HAVE_CONFIG_H |
| 3378 | +# include "config.h" |
| 3379 | +#endif |
| 3380 | + |
| 3381 | +#include <unistd.h> |
| 3382 | +#include <fcntl.h> |
| 3383 | +#include <sys/ioctl.h> |
| 3384 | +#include <sys/mman.h> |
| 3385 | + |
| 3386 | +#include <interface/vcsm/user-vcsm.h> |
| 3387 | + |
| 3388 | +#include <vlc_common.h> |
| 3389 | +#include <vlc_picture.h> |
| 3390 | + |
| 3391 | +#include <libdrm/drm_fourcc.h> |
| 3392 | +#include <EGL/egl.h> |
| 3393 | +#include <EGL/eglext.h> |
| 3394 | +#include <GLES2/gl2.h> |
| 3395 | +#include <GLES2/gl2ext.h> |
| 3396 | + |
| 3397 | +#include "mmal_cma.h" |
| 3398 | + |
| 3399 | +#include "../../video_output/opengl/converter.h" |
| 3400 | + |
| 3401 | +#include "mmal_picture.h" |
| 3402 | + |
| 3403 | +#include <assert.h> |
| 3404 | + |
| 3405 | +#define TRACE_ALL 0 |
| 3406 | + |
| 3407 | +typedef struct mmal_gl_converter_s |
| 3408 | +{ |
| 3409 | + EGLint drm_fourcc; |
| 3410 | + vcsm_init_type_t vcsm_init_type; |
| 3411 | + cma_buf_t * last_cb; |
| 3412 | + |
| 3413 | + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; |
| 3414 | +} mmal_gl_converter_t; |
| 3415 | + |
| 3416 | + |
| 3417 | +static EGLint vlc_to_gl_fourcc(const video_format_t * const fmt) |
| 3418 | +{ |
| 3419 | + // Converting to mmal selects the right RGB32 varient |
| 3420 | + switch(vlc_to_mmal_video_fourcc(fmt)) |
| 3421 | + { |
| 3422 | + case MMAL_ENCODING_I420: |
| 3423 | + return MMAL_FOURCC('Y','U','1','2'); |
| 3424 | + case MMAL_ENCODING_YV12: |
| 3425 | + return MMAL_FOURCC('Y','V','1','2'); |
| 3426 | + case MMAL_ENCODING_I422: |
| 3427 | + return MMAL_FOURCC('Y','U','1','6'); |
| 3428 | +// case MMAL_ENCODING_YUVUV128: // Doesn't actually work yet |
| 3429 | + case MMAL_ENCODING_NV12: |
| 3430 | + return MMAL_FOURCC('N','V','1','2'); |
| 3431 | + case MMAL_ENCODING_NV21: |
| 3432 | + return MMAL_FOURCC('N','V','2','1'); |
| 3433 | + case MMAL_ENCODING_RGB16: |
| 3434 | + return MMAL_FOURCC('R','G','1','6'); |
| 3435 | + case MMAL_ENCODING_RGB24: |
| 3436 | + return MMAL_FOURCC('B','G','2','4'); |
| 3437 | + case MMAL_ENCODING_BGR24: |
| 3438 | + return MMAL_FOURCC('R','G','2','4'); |
| 3439 | + case MMAL_ENCODING_BGR32: |
| 3440 | + case MMAL_ENCODING_BGRA: |
| 3441 | + return MMAL_FOURCC('X','R','2','4'); |
| 3442 | + case MMAL_ENCODING_RGB32: |
| 3443 | + case MMAL_ENCODING_RGBA: |
| 3444 | + return MMAL_FOURCC('X','B','2','4'); |
| 3445 | + default: |
| 3446 | + break; |
| 3447 | + } |
| 3448 | + return 0; |
| 3449 | +} |
| 3450 | + |
| 3451 | +typedef struct tex_context_s { |
| 3452 | + picture_context_t cmn; |
| 3453 | + GLuint texture; |
| 3454 | + |
| 3455 | + PFNGLDELETETEXTURESPROC DeleteTextures; // Copy fn pointer so we don't need tc on delete |
| 3456 | +} tex_context_t; |
| 3457 | + |
| 3458 | +static void tex_context_delete(tex_context_t * const tex) |
| 3459 | +{ |
| 3460 | + tex->DeleteTextures(1, &tex->texture); |
| 3461 | + free(tex); |
| 3462 | +} |
| 3463 | + |
| 3464 | +static void tex_context_destroy(picture_context_t * pic_ctx) |
| 3465 | +{ |
| 3466 | + tex_context_delete((tex_context_t *)pic_ctx); |
| 3467 | +} |
| 3468 | + |
| 3469 | +static picture_context_t * tex_context_copy(picture_context_t * pic_ctx) |
| 3470 | +{ |
| 3471 | + return pic_ctx; |
| 3472 | +} |
| 3473 | + |
| 3474 | +static tex_context_t * get_tex_context(const opengl_tex_converter_t * const tc, picture_t * const pic, cma_buf_t * const cb) |
| 3475 | +{ |
| 3476 | + mmal_gl_converter_t * const sys = tc->priv; |
| 3477 | + tex_context_t * tex = (tex_context_t *)cma_buf_context2(cb); |
| 3478 | + if (tex != NULL) |
| 3479 | + return tex; |
| 3480 | + |
| 3481 | + if ((tex = malloc(sizeof(*tex))) == NULL) |
| 3482 | + return NULL; |
| 3483 | + |
| 3484 | + *tex = (tex_context_t){ |
| 3485 | + .cmn = { |
| 3486 | + .destroy = tex_context_destroy, |
| 3487 | + .copy = tex_context_copy |
| 3488 | + }, |
| 3489 | + .texture = 0, |
| 3490 | + .DeleteTextures = tc->vt->DeleteTextures |
| 3491 | + }; |
| 3492 | + |
| 3493 | + { |
| 3494 | + EGLint attribs[30]; |
| 3495 | + EGLint * a = attribs; |
| 3496 | + const int fd = cma_buf_fd(cb); |
| 3497 | + uint8_t * base_addr = cma_buf_addr(cb); |
| 3498 | + |
| 3499 | + if (pic->i_planes >= 4 || pic->i_planes <= 0) |
| 3500 | + { |
| 3501 | + msg_Err(tc, "%s: Bad planes: %d", __func__, pic->i_planes); |
| 3502 | + goto fail; |
| 3503 | + } |
| 3504 | + |
| 3505 | + *a++ = EGL_WIDTH; |
| 3506 | + *a++ = pic->format.i_visible_width; |
| 3507 | + *a++ = EGL_HEIGHT; |
| 3508 | + *a++ = pic->format.i_visible_height; |
| 3509 | + *a++ = EGL_LINUX_DRM_FOURCC_EXT; |
| 3510 | + *a++ = sys->drm_fourcc; |
| 3511 | + |
| 3512 | + if (pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8) |
| 3513 | + { |
| 3514 | + // Sand is its own very special bunny :-( |
| 3515 | + static const EGLint attnames[] = { |
| 3516 | + EGL_DMA_BUF_PLANE0_FD_EXT, |
| 3517 | + EGL_DMA_BUF_PLANE0_OFFSET_EXT, |
| 3518 | + EGL_DMA_BUF_PLANE0_PITCH_EXT, |
| 3519 | + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, |
| 3520 | + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, |
| 3521 | + EGL_DMA_BUF_PLANE1_FD_EXT, |
| 3522 | + EGL_DMA_BUF_PLANE1_OFFSET_EXT, |
| 3523 | + EGL_DMA_BUF_PLANE1_PITCH_EXT, |
| 3524 | + EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, |
| 3525 | + EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT |
| 3526 | + }; |
| 3527 | + |
| 3528 | + const EGLint * n = attnames; |
| 3529 | + |
| 3530 | + for (int i = 0; i < pic->i_planes; ++i) |
| 3531 | + { |
| 3532 | + const uint64_t mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(pic->p[i].i_pitch >> 7); |
| 3533 | + |
| 3534 | + *a++ = *n++; |
| 3535 | + *a++ = fd; |
| 3536 | + *a++ = *n++; |
| 3537 | + *a++ = pic->p[i].p_pixels - base_addr; |
| 3538 | + *a++ = *n++; |
| 3539 | + *a++ = pic->format.i_width; |
| 3540 | + *a++ = *n++; |
| 3541 | + *a++ = (EGLint)(mod >> 32); |
| 3542 | + *a++ = *n++; |
| 3543 | + *a++ = (EGLint)(mod & 0xffffffff); |
| 3544 | + } |
| 3545 | + } |
| 3546 | + else |
| 3547 | + { |
| 3548 | + static const EGLint attnames[] = { |
| 3549 | + EGL_DMA_BUF_PLANE0_FD_EXT, |
| 3550 | + EGL_DMA_BUF_PLANE0_OFFSET_EXT, |
| 3551 | + EGL_DMA_BUF_PLANE0_PITCH_EXT, |
| 3552 | + EGL_DMA_BUF_PLANE1_FD_EXT, |
| 3553 | + EGL_DMA_BUF_PLANE1_OFFSET_EXT, |
| 3554 | + EGL_DMA_BUF_PLANE1_PITCH_EXT, |
| 3555 | + EGL_DMA_BUF_PLANE2_FD_EXT, |
| 3556 | + EGL_DMA_BUF_PLANE2_OFFSET_EXT, |
| 3557 | + EGL_DMA_BUF_PLANE2_PITCH_EXT, |
| 3558 | + EGL_DMA_BUF_PLANE3_FD_EXT, |
| 3559 | + EGL_DMA_BUF_PLANE3_OFFSET_EXT, |
| 3560 | + EGL_DMA_BUF_PLANE3_PITCH_EXT |
| 3561 | + }; |
| 3562 | + |
| 3563 | + const EGLint * n = attnames; |
| 3564 | + |
| 3565 | + for (int i = 0; i < pic->i_planes; ++i) |
| 3566 | + { |
| 3567 | + *a++ = *n++; |
| 3568 | + *a++ = fd; |
| 3569 | + *a++ = *n++; |
| 3570 | + *a++ = pic->p[i].p_pixels - base_addr; |
| 3571 | + *a++ = *n++; |
| 3572 | + *a++ = pic->p[i].i_pitch; |
| 3573 | + } |
| 3574 | + } |
| 3575 | + |
| 3576 | + *a = EGL_NONE; |
| 3577 | + |
| 3578 | + const EGLImage image = tc->gl->egl.createImageKHR(tc->gl, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); |
| 3579 | + if (!image) { |
| 3580 | + msg_Err(tc, "Failed to import fd %d: Err=%#x", fd, tc->vt->GetError()); |
| 3581 | + goto fail; |
| 3582 | + } |
| 3583 | + |
| 3584 | + // ** ?? tc->tex_target |
| 3585 | + tc->vt->GenTextures(1, &tex->texture); |
| 3586 | + tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture); |
| 3587 | + tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 3588 | + tc->vt->TexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| 3589 | + sys->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); |
| 3590 | + |
| 3591 | + tc->gl->egl.destroyImageKHR(tc->gl, image); |
| 3592 | + } |
| 3593 | + |
| 3594 | + if (cma_buf_add_context2(cb, &tex->cmn) != VLC_SUCCESS) |
| 3595 | + { |
| 3596 | + msg_Err(tc, "%s: add_context2 failed", __func__); |
| 3597 | + goto fail; |
| 3598 | + } |
| 3599 | + return tex; |
| 3600 | + |
| 3601 | +fail: |
| 3602 | + tex_context_delete(tex); |
| 3603 | + return NULL; |
| 3604 | +} |
| 3605 | + |
| 3606 | + |
| 3607 | +static int |
| 3608 | +tc_mmal_update(const opengl_tex_converter_t *tc, GLuint *textures, |
| 3609 | + const GLsizei *tex_width, const GLsizei *tex_height, |
| 3610 | + picture_t *pic, const size_t *plane_offset) |
| 3611 | +{ |
| 3612 | + mmal_gl_converter_t * const sys = tc->priv; |
| 3613 | +#if TRACE_ALL |
| 3614 | + { |
| 3615 | + char cbuf[5]; |
| 3616 | + msg_Dbg(tc, "%s: %s %d*%dx%d : %d*%dx%d", __func__, |
| 3617 | + str_fourcc(cbuf, pic->format.i_chroma), |
| 3618 | + tc->tex_count, tex_width[0], tex_height[0], pic->i_planes, pic->p[0].i_pitch, pic->p[0].i_lines); |
| 3619 | + } |
| 3620 | +#endif |
| 3621 | + VLC_UNUSED(tex_width); |
| 3622 | + VLC_UNUSED(tex_height); |
| 3623 | + VLC_UNUSED(plane_offset); |
| 3624 | + |
| 3625 | + if (!is_cma_buf_pic_chroma(pic->format.i_chroma)) |
| 3626 | + { |
| 3627 | + char cbuf[5]; |
| 3628 | + msg_Err(tc, "Pic with unexpected chroma: %s", str_fourcc(cbuf, pic->format.i_chroma)); |
| 3629 | + return VLC_EGENERIC; |
| 3630 | + } |
| 3631 | + |
| 3632 | + cma_buf_t * const cb = cma_buf_pic_get(pic); |
| 3633 | + if (cb == NULL) |
| 3634 | + { |
| 3635 | + msg_Err(tc, "Pic missing cma buf"); |
| 3636 | + return VLC_EGENERIC; |
| 3637 | + } |
| 3638 | + |
| 3639 | + tex_context_t * const tex = get_tex_context(tc, pic, cb); |
| 3640 | + if (tex == NULL) |
| 3641 | + return VLC_EGENERIC; |
| 3642 | + |
| 3643 | +// tc->vt->BindTexture(GL_TEXTURE_EXTERNAL_OES, tex->texture); |
| 3644 | + |
| 3645 | + cma_buf_unref(sys->last_cb); |
| 3646 | + sys->last_cb = cma_buf_ref(cb); |
| 3647 | + |
| 3648 | + textures[0] = tex->texture; |
| 3649 | + return VLC_SUCCESS; |
| 3650 | +} |
| 3651 | + |
| 3652 | +static int |
| 3653 | +tc_mmal_fetch_locations(opengl_tex_converter_t *tc, GLuint program) |
| 3654 | +{ |
| 3655 | + tc->uloc.Texture[0] = tc->vt->GetUniformLocation(program, "Texture0"); |
| 3656 | + return tc->uloc.Texture[0] != -1 ? VLC_SUCCESS : VLC_EGENERIC; |
| 3657 | +} |
| 3658 | + |
| 3659 | +static void |
| 3660 | +tc_mmal_prepare_shader(const opengl_tex_converter_t *tc, |
| 3661 | + const GLsizei *tex_width, const GLsizei *tex_height, |
| 3662 | + float alpha) |
| 3663 | +{ |
| 3664 | + (void) tex_width; (void) tex_height; (void) alpha; |
| 3665 | + VLC_UNUSED(tc); |
| 3666 | +// tc->vt->Uniform1i(tc->uloc.Texture[0], 0); |
| 3667 | +} |
| 3668 | + |
| 3669 | +static GLuint |
| 3670 | +tc_fragment_shader_init(opengl_tex_converter_t * const tc, const GLenum tex_target, |
| 3671 | + const vlc_fourcc_t chroma, const video_color_space_t yuv_space) |
| 3672 | +{ |
| 3673 | + VLC_UNUSED(yuv_space); |
| 3674 | + |
| 3675 | + tc->tex_count = 1; |
| 3676 | + tc->tex_target = tex_target; |
| 3677 | + tc->texs[0] = (struct opengl_tex_cfg) { |
| 3678 | + { 1, 1 }, { 1, 1 }, GL_RGB, chroma, GL_UNSIGNED_SHORT //** ?? |
| 3679 | + }; |
| 3680 | + |
| 3681 | + tc->pf_fetch_locations = tc_mmal_fetch_locations; |
| 3682 | + tc->pf_prepare_shader = tc_mmal_prepare_shader; |
| 3683 | + |
| 3684 | + |
| 3685 | + const char fs[] = |
| 3686 | + "#extension GL_OES_EGL_image_external : enable\n" |
| 3687 | + "precision mediump float;\n" |
| 3688 | + "uniform samplerExternalOES Texture0;\n" |
| 3689 | + "varying vec2 TexCoord0;\n" |
| 3690 | + "void main() {\n" |
| 3691 | + " gl_FragColor = texture2D(Texture0, TexCoord0);\n" |
| 3692 | + "}\n"; |
| 3693 | + |
| 3694 | + |
| 3695 | + const char *code = fs; |
| 3696 | + |
| 3697 | + GLuint fragment_shader = tc->vt->CreateShader(GL_FRAGMENT_SHADER); |
| 3698 | + tc->vt->ShaderSource(fragment_shader, 1, &code, NULL); |
| 3699 | + tc->vt->CompileShader(fragment_shader); |
| 3700 | + return fragment_shader; |
| 3701 | +} |
| 3702 | + |
| 3703 | + |
| 3704 | +static void |
| 3705 | +CloseGLConverter(vlc_object_t *obj) |
| 3706 | +{ |
| 3707 | + opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj; |
| 3708 | + mmal_gl_converter_t * const sys = tc->priv; |
| 3709 | + |
| 3710 | + if (sys == NULL) |
| 3711 | + return; |
| 3712 | + |
| 3713 | + cma_buf_unref(sys->last_cb); |
| 3714 | + cma_vcsm_exit(sys->vcsm_init_type); |
| 3715 | + free(sys); |
| 3716 | +} |
| 3717 | + |
| 3718 | + |
| 3719 | +// Pick a chroma that we can convert to |
| 3720 | +// Prefer I420 as smallest |
| 3721 | +static vlc_fourcc_t chroma_in_out(const vlc_fourcc_t chroma_in) |
| 3722 | +{ |
| 3723 | + switch (chroma_in) |
| 3724 | + { |
| 3725 | + case VLC_CODEC_MMAL_OPAQUE: |
| 3726 | + case VLC_CODEC_MMAL_ZC_I420: |
| 3727 | + case VLC_CODEC_MMAL_ZC_SAND8: |
| 3728 | + case VLC_CODEC_MMAL_ZC_SAND10: // ISP only |
| 3729 | + return VLC_CODEC_MMAL_ZC_I420; |
| 3730 | + case VLC_CODEC_MMAL_ZC_SAND30: // HVS only |
| 3731 | + case VLC_CODEC_MMAL_ZC_RGB32: |
| 3732 | + return VLC_CODEC_MMAL_ZC_RGB32; // HVS can't generate YUV of any sort |
| 3733 | + default: |
| 3734 | + break; |
| 3735 | + } |
| 3736 | + return 0; |
| 3737 | +} |
| 3738 | + |
| 3739 | + |
| 3740 | +static int |
| 3741 | +OpenGLConverter(vlc_object_t *obj) |
| 3742 | +{ |
| 3743 | + opengl_tex_converter_t * const tc = (opengl_tex_converter_t *)obj; |
| 3744 | + int rv = VLC_EGENERIC; |
| 3745 | + const EGLint eglfmt = vlc_to_gl_fourcc(&tc->fmt); |
| 3746 | + const vlc_fourcc_t chroma_out = chroma_in_out(tc->fmt.i_chroma); |
| 3747 | + |
| 3748 | + // Do we know what to do with this? |
| 3749 | + if (chroma_out == 0) |
| 3750 | + return rv; |
| 3751 | + |
| 3752 | + { |
| 3753 | + char dbuf0[5], dbuf1[5], dbuf2[5]; |
| 3754 | + msg_Dbg(tc, "<<< %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__, |
| 3755 | + str_fourcc(dbuf0, tc->fmt.i_chroma), |
| 3756 | + str_fourcc(dbuf1, eglfmt), |
| 3757 | + tc->fmt.i_width, tc->fmt.i_height, |
| 3758 | + tc->fmt.i_x_offset, tc->fmt.i_y_offset, |
| 3759 | + tc->fmt.i_visible_width, tc->fmt.i_visible_height, |
| 3760 | + tc->fmt.i_sar_num, tc->fmt.i_sar_den, |
| 3761 | + str_fourcc(dbuf2, chroma_out)); |
| 3762 | + } |
| 3763 | + |
| 3764 | + if (tc->gl->ext != VLC_GL_EXT_EGL || |
| 3765 | + !tc->gl->egl.createImageKHR || !tc->gl->egl.destroyImageKHR) |
| 3766 | + { |
| 3767 | + // Missing an important callback |
| 3768 | + msg_Dbg(tc, "Missing EGL xxxImageKHR calls"); |
| 3769 | + return rv; |
| 3770 | + } |
| 3771 | + |
| 3772 | + if ((tc->priv = calloc(1, sizeof(mmal_gl_converter_t))) == NULL) |
| 3773 | + { |
| 3774 | + msg_Err(tc, "priv alloc failure"); |
| 3775 | + rv = VLC_ENOMEM; |
| 3776 | + goto fail; |
| 3777 | + } |
| 3778 | + mmal_gl_converter_t * const sys = tc->priv; |
| 3779 | + |
| 3780 | + sys->drm_fourcc = eglfmt; |
| 3781 | + |
| 3782 | + if ((sys->vcsm_init_type = cma_vcsm_init()) != VCSM_INIT_CMA) { |
| 3783 | + msg_Dbg(tc, "VCSM init failed"); |
| 3784 | + goto fail; |
| 3785 | + } |
| 3786 | + |
| 3787 | + if ((sys->glEGLImageTargetTexture2DOES = vlc_gl_GetProcAddress(tc->gl, "glEGLImageTargetTexture2DOES")) == NULL) |
| 3788 | + { |
| 3789 | + msg_Err(tc, "Failed to bind GL fns"); |
| 3790 | + goto fail; |
| 3791 | + } |
| 3792 | + |
| 3793 | + if ((tc->fshader = tc_fragment_shader_init(tc, GL_TEXTURE_EXTERNAL_OES, |
| 3794 | + eglfmt == 0 ? VLC_CODEC_RGB32 : tc->fmt.i_chroma, |
| 3795 | + eglfmt == 0 ? COLOR_SPACE_SRGB : tc->fmt.space)) == 0) |
| 3796 | + { |
| 3797 | + msg_Err(tc, "Failed to make shader"); |
| 3798 | + goto fail; |
| 3799 | + } |
| 3800 | + |
| 3801 | + if (eglfmt == 0) |
| 3802 | + { |
| 3803 | + tc->fmt.i_chroma = chroma_out; |
| 3804 | + tc->fmt.i_bits_per_pixel = 8; |
| 3805 | + if (tc->fmt.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) |
| 3806 | + { |
| 3807 | + tc->fmt.i_rmask = 0xff0000; |
| 3808 | + tc->fmt.i_gmask = 0xff00; |
| 3809 | + tc->fmt.i_bmask = 0xff; |
| 3810 | + tc->fmt.space = COLOR_SPACE_SRGB; |
| 3811 | + } |
| 3812 | + else |
| 3813 | + { |
| 3814 | + tc->fmt.i_rmask = 0; |
| 3815 | + tc->fmt.i_gmask = 0; |
| 3816 | + tc->fmt.i_bmask = 0; |
| 3817 | + tc->fmt.space = COLOR_SPACE_UNDEF; |
| 3818 | + } |
| 3819 | + sys->drm_fourcc = vlc_to_gl_fourcc(&tc->fmt); |
| 3820 | + } |
| 3821 | + |
| 3822 | + tc->handle_texs_gen = true; // We manage the texs |
| 3823 | + tc->pf_update = tc_mmal_update; |
| 3824 | + |
| 3825 | +#if TRACE_ALL |
| 3826 | + { |
| 3827 | + char dbuf0[5], dbuf1[5], dbuf2[5]; |
| 3828 | + msg_Dbg(tc, ">>> %s: V:%s/E:%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %s", __func__, |
| 3829 | + str_fourcc(dbuf0, tc->fmt.i_chroma), |
| 3830 | + str_fourcc(dbuf1, sys->drm_fourcc), |
| 3831 | + tc->fmt.i_width, tc->fmt.i_height, |
| 3832 | + tc->fmt.i_x_offset, tc->fmt.i_y_offset, |
| 3833 | + tc->fmt.i_visible_width, tc->fmt.i_visible_height, |
| 3834 | + tc->fmt.i_sar_num, tc->fmt.i_sar_den, |
| 3835 | + str_fourcc(dbuf2, chroma_out)); |
| 3836 | + } |
| 3837 | +#endif |
| 3838 | + |
| 3839 | + return VLC_SUCCESS; |
| 3840 | + |
| 3841 | +fail: |
| 3842 | + CloseGLConverter(obj); |
| 3843 | + return rv; |
| 3844 | +} |
| 3845 | + |
| 3846 | +vlc_module_begin () |
| 3847 | + set_description("MMAL OpenGL surface converter") |
| 3848 | + set_shortname (N_("MMALGLConverter")) |
| 3849 | + set_capability("glconv", 900) |
| 3850 | + set_callbacks(OpenGLConverter, CloseGLConverter) |
| 3851 | + set_category(CAT_VIDEO) |
| 3852 | + set_subcategory(SUBCAT_VIDEO_VOUT) |
| 3853 | + add_shortcut("mmal_gl_converter") |
| 3854 | +vlc_module_end () |
| 3855 | + |
| 3856 | --- a/modules/hw/mmal/deinterlace.c |
| 3857 | +++ b/modules/hw/mmal/deinterlace.c |
| 3858 | @@ -26,11 +26,12 @@ |
| 3859 | #include "config.h" |
| 3860 | #endif |
| 3861 | |
| 3862 | -#include <vlc_picture_pool.h> |
| 3863 | +#include <stdatomic.h> |
| 3864 | + |
| 3865 | #include <vlc_common.h> |
| 3866 | +#include <vlc_picture_pool.h> |
| 3867 | #include <vlc_plugin.h> |
| 3868 | #include <vlc_filter.h> |
| 3869 | -#include <vlc_atomic.h> |
| 3870 | |
| 3871 | #include "mmal_picture.h" |
| 3872 | |
| 3873 | @@ -39,468 +40,814 @@ |
| 3874 | #include <interface/mmal/util/mmal_util.h> |
| 3875 | #include <interface/mmal/util/mmal_default_components.h> |
| 3876 | |
| 3877 | -#define MIN_NUM_BUFFERS_IN_TRANSIT 2 |
| 3878 | +#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu" |
| 3879 | +#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.") |
| 3880 | +#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.") |
| 3881 | |
| 3882 | -#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu" |
| 3883 | -#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.") |
| 3884 | -#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.") |
| 3885 | +#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv" |
| 3886 | +#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace") |
| 3887 | +#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace") |
| 3888 | |
| 3889 | -static int Open(filter_t *filter); |
| 3890 | -static void Close(filter_t *filter); |
| 3891 | +#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast" |
| 3892 | +#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace") |
| 3893 | +#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace") |
| 3894 | |
| 3895 | -vlc_module_begin() |
| 3896 | - set_shortname(N_("MMAL deinterlace")) |
| 3897 | - set_description(N_("MMAL-based deinterlace filter plugin")) |
| 3898 | - set_capability("video filter", 0) |
| 3899 | - set_category(CAT_VIDEO) |
| 3900 | - set_subcategory(SUBCAT_VIDEO_VFILTER) |
| 3901 | - set_callbacks(Open, Close) |
| 3902 | - add_shortcut("deinterlace") |
| 3903 | - add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT, |
| 3904 | - MMAL_DEINTERLACE_QPU_LONGTEXT, true); |
| 3905 | -vlc_module_end() |
| 3906 | +#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none" |
| 3907 | +#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace") |
| 3908 | +#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\ |
| 3909 | + "This is the default for > SD if < 96M gpu-mem") |
| 3910 | + |
| 3911 | +#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate" |
| 3912 | +#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate") |
| 3913 | +#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input") |
| 3914 | + |
| 3915 | +#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate" |
| 3916 | +#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate") |
| 3917 | +#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input") |
| 3918 | |
| 3919 | -struct filter_sys_t { |
| 3920 | + |
| 3921 | +typedef struct filter_sys_t |
| 3922 | +{ |
| 3923 | MMAL_COMPONENT_T *component; |
| 3924 | MMAL_PORT_T *input; |
| 3925 | MMAL_PORT_T *output; |
| 3926 | + MMAL_POOL_T *in_pool; |
| 3927 | + |
| 3928 | + MMAL_QUEUE_T * out_q; |
| 3929 | + |
| 3930 | + // Bind this lot somehow into ppr???? |
| 3931 | + bool is_cma; |
| 3932 | + cma_buf_pool_t * cma_out_pool; |
| 3933 | + MMAL_POOL_T * out_pool; |
| 3934 | + |
| 3935 | + hw_mmal_port_pool_ref_t *out_ppr; |
| 3936 | + |
| 3937 | + bool half_rate; |
| 3938 | + bool use_qpu; |
| 3939 | + bool use_fast; |
| 3940 | + bool use_passthrough; |
| 3941 | + unsigned int seq_in; // Seq of next frame to submit (1-15) [Init=1] |
| 3942 | + unsigned int seq_out; // Seq of last frame received (1-15) [Init=15] |
| 3943 | |
| 3944 | - MMAL_QUEUE_T *filtered_pictures; |
| 3945 | - vlc_sem_t sem; |
| 3946 | + vcsm_init_type_t vcsm_init_type; |
| 3947 | |
| 3948 | - atomic_bool started; |
| 3949 | +} filter_sys_t; |
| 3950 | |
| 3951 | - /* statistics */ |
| 3952 | - int output_in_transit; |
| 3953 | - int input_in_transit; |
| 3954 | -}; |
| 3955 | - |
| 3956 | -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
| 3957 | -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
| 3958 | -static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
| 3959 | -static picture_t *deinterlace(filter_t *filter, picture_t *picture); |
| 3960 | -static void flush(filter_t *filter); |
| 3961 | |
| 3962 | #define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx" |
| 3963 | |
| 3964 | -static int Open(filter_t *filter) |
| 3965 | +#define TRACE_ALL 0 |
| 3966 | + |
| 3967 | + |
| 3968 | + |
| 3969 | +// Buffer attached to pic on success, is still valid on failure |
| 3970 | +static picture_t * di_alloc_opaque(filter_t * const p_filter, MMAL_BUFFER_HEADER_T * const buf) |
| 3971 | { |
| 3972 | - int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ? |
| 3973 | - (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base / |
| 3974 | - filter->fmt_in.video.i_frame_rate : 0; |
| 3975 | - bool use_qpu = var_InheritBool(filter, MMAL_DEINTERLACE_QPU); |
| 3976 | + filter_sys_t *const filter_sys = p_filter->p_sys; |
| 3977 | + picture_t * const pic = filter_NewPicture(p_filter); |
| 3978 | |
| 3979 | - MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = { |
| 3980 | - { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) }, |
| 3981 | - MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV, |
| 3982 | - 4, |
| 3983 | - { 3, frame_duration, 0, use_qpu } |
| 3984 | - }; |
| 3985 | + if (pic == NULL) |
| 3986 | + goto fail1; |
| 3987 | |
| 3988 | - int ret = VLC_SUCCESS; |
| 3989 | - MMAL_STATUS_T status; |
| 3990 | - filter_sys_t *sys; |
| 3991 | + if (buf->length == 0) { |
| 3992 | + msg_Err(p_filter, "%s: Empty buffer", __func__); |
| 3993 | + goto fail2; |
| 3994 | + } |
| 3995 | |
| 3996 | - msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!", |
| 3997 | - frame_duration, use_qpu ? "used" : "unused"); |
| 3998 | + if ((pic->context = hw_mmal_gen_context(buf, filter_sys->out_ppr)) == NULL) |
| 3999 | + goto fail2; |
| 4000 | |
| 4001 | - if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE) |
| 4002 | - return VLC_EGENERIC; |
| 4003 | + buf_to_pic_copy_props(pic, buf); |
| 4004 | |
| 4005 | - if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE) |
| 4006 | - return VLC_EGENERIC; |
| 4007 | +#if TRACE_ALL |
| 4008 | + msg_Dbg(p_filter, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date); |
| 4009 | +#endif |
| 4010 | |
| 4011 | - sys = calloc(1, sizeof(filter_sys_t)); |
| 4012 | - if (!sys) |
| 4013 | - return VLC_ENOMEM; |
| 4014 | - filter->p_sys = sys; |
| 4015 | + return pic; |
| 4016 | |
| 4017 | - bcm_host_init(); |
| 4018 | +fail2: |
| 4019 | + picture_Release(pic); |
| 4020 | +fail1: |
| 4021 | +// mmal_buffer_header_release(buf); |
| 4022 | + return NULL; |
| 4023 | +} |
| 4024 | |
| 4025 | - status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component); |
| 4026 | - if (status != MMAL_SUCCESS) { |
| 4027 | - msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)", |
| 4028 | - MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); |
| 4029 | - ret = VLC_EGENERIC; |
| 4030 | - goto out; |
| 4031 | - } |
| 4032 | +static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 4033 | +{ |
| 4034 | +#if TRACE_ALL |
| 4035 | + pic_ctx_mmal_t * ctx = buffer->user_data; |
| 4036 | +// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys; |
| 4037 | + |
| 4038 | + msg_Dbg((filter_t *)port->userdata, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buffer->cmd, ctx, buffer, |
| 4039 | + buffer->flags, (long long)buffer->pts); |
| 4040 | +#else |
| 4041 | + VLC_UNUSED(port); |
| 4042 | +#endif |
| 4043 | |
| 4044 | - status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr); |
| 4045 | - if (status != MMAL_SUCCESS) { |
| 4046 | - msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)", |
| 4047 | - MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); |
| 4048 | - ret = VLC_EGENERIC; |
| 4049 | - goto out; |
| 4050 | - } |
| 4051 | + mmal_buffer_header_release(buffer); |
| 4052 | |
| 4053 | - sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter; |
| 4054 | - status = mmal_port_enable(sys->component->control, control_port_cb); |
| 4055 | - if (status != MMAL_SUCCESS) { |
| 4056 | - msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)", |
| 4057 | - sys->component->control->name, status, mmal_status_to_string(status)); |
| 4058 | - ret = VLC_EGENERIC; |
| 4059 | - goto out; |
| 4060 | +#if TRACE_ALL |
| 4061 | + msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__); |
| 4062 | +#endif |
| 4063 | +} |
| 4064 | + |
| 4065 | +static void di_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) |
| 4066 | +{ |
| 4067 | + if (buf->cmd == 0 && buf->length != 0) |
| 4068 | + { |
| 4069 | + // The filter structure etc. should always exist if we have contents |
| 4070 | + // but might not on later flushes as we shut down |
| 4071 | + filter_t * const p_filter = (filter_t *)port->userdata; |
| 4072 | + filter_sys_t * const sys = p_filter->p_sys; |
| 4073 | + |
| 4074 | +#if TRACE_ALL |
| 4075 | + msg_Dbg(p_filter, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts); |
| 4076 | +#endif |
| 4077 | + mmal_queue_put(sys->out_q, buf); |
| 4078 | +#if TRACE_ALL |
| 4079 | + msg_Dbg(p_filter, ">>> %s: out Q len=%d", __func__, mmal_queue_length(sys->out_q)); |
| 4080 | +#endif |
| 4081 | + return; |
| 4082 | } |
| 4083 | |
| 4084 | - sys->input = sys->component->input[0]; |
| 4085 | - sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter; |
| 4086 | - if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) |
| 4087 | - sys->input->format->encoding = MMAL_ENCODING_OPAQUE; |
| 4088 | - sys->input->format->es->video.width = filter->fmt_in.video.i_width; |
| 4089 | - sys->input->format->es->video.height = filter->fmt_in.video.i_height; |
| 4090 | - sys->input->format->es->video.crop.x = 0; |
| 4091 | - sys->input->format->es->video.crop.y = 0; |
| 4092 | - sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width; |
| 4093 | - sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height; |
| 4094 | - sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num; |
| 4095 | - sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den; |
| 4096 | + mmal_buffer_header_reset(buf); // User data stays intact so release will kill pic |
| 4097 | + mmal_buffer_header_release(buf); |
| 4098 | +} |
| 4099 | |
| 4100 | - es_format_Copy(&filter->fmt_out, &filter->fmt_in); |
| 4101 | - filter->fmt_out.video.i_frame_rate *= 2; |
| 4102 | |
| 4103 | - status = mmal_port_format_commit(sys->input); |
| 4104 | - if (status != MMAL_SUCCESS) { |
| 4105 | - msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)", |
| 4106 | - sys->input->name, status, mmal_status_to_string(status)); |
| 4107 | - ret = VLC_EGENERIC; |
| 4108 | - goto out; |
| 4109 | - } |
| 4110 | - sys->input->buffer_size = sys->input->buffer_size_recommended; |
| 4111 | - sys->input->buffer_num = sys->input->buffer_num_recommended; |
| 4112 | |
| 4113 | - if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) { |
| 4114 | - MMAL_PARAMETER_BOOLEAN_T zero_copy = { |
| 4115 | - { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, |
| 4116 | - 1 |
| 4117 | - }; |
| 4118 | +static MMAL_STATUS_T fill_output_from_q(filter_t * const p_filter, filter_sys_t * const sys, MMAL_QUEUE_T * const q) |
| 4119 | +{ |
| 4120 | + MMAL_BUFFER_HEADER_T * out_buf; |
| 4121 | |
| 4122 | - status = mmal_port_parameter_set(sys->input, &zero_copy.hdr); |
| 4123 | - if (status != MMAL_SUCCESS) { |
| 4124 | - msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", |
| 4125 | - sys->input->name, status, mmal_status_to_string(status)); |
| 4126 | - goto out; |
| 4127 | + while ((out_buf = mmal_queue_get(q)) != NULL) |
| 4128 | + { |
| 4129 | + MMAL_STATUS_T err; |
| 4130 | + if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) |
| 4131 | + { |
| 4132 | + msg_Err(p_filter, "Send buffer to output failed"); |
| 4133 | + mmal_queue_put_back(q, out_buf); |
| 4134 | + return err; |
| 4135 | } |
| 4136 | } |
| 4137 | + return MMAL_SUCCESS; |
| 4138 | +} |
| 4139 | |
| 4140 | - status = mmal_port_enable(sys->input, input_port_cb); |
| 4141 | - if (status != MMAL_SUCCESS) { |
| 4142 | - msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)", |
| 4143 | - sys->input->name, status, mmal_status_to_string(status)); |
| 4144 | - ret = VLC_EGENERIC; |
| 4145 | - goto out; |
| 4146 | - } |
| 4147 | +// Output buffers may contain a pic ref on error or flush |
| 4148 | +// Free it |
| 4149 | +static MMAL_BOOL_T out_buffer_pre_release_cb(MMAL_BUFFER_HEADER_T *header, void *userdata) |
| 4150 | +{ |
| 4151 | + VLC_UNUSED(userdata); |
| 4152 | |
| 4153 | - sys->output = sys->component->output[0]; |
| 4154 | - sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter; |
| 4155 | - mmal_format_full_copy(sys->output->format, sys->input->format); |
| 4156 | + cma_buf_t * const cb = header->user_data; |
| 4157 | + header->user_data = NULL; |
| 4158 | + cma_buf_unref(cb); // Copes fine with NULL |
| 4159 | |
| 4160 | - status = mmal_port_format_commit(sys->output); |
| 4161 | - if (status != MMAL_SUCCESS) { |
| 4162 | - msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)", |
| 4163 | - sys->input->name, status, mmal_status_to_string(status)); |
| 4164 | - ret = VLC_EGENERIC; |
| 4165 | - goto out; |
| 4166 | + return MMAL_FALSE; |
| 4167 | +} |
| 4168 | + |
| 4169 | +static inline unsigned int seq_inc(unsigned int x) |
| 4170 | +{ |
| 4171 | + return x + 1 >= 16 ? 1 : x + 1; |
| 4172 | +} |
| 4173 | + |
| 4174 | +static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq) |
| 4175 | +{ |
| 4176 | + return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq); |
| 4177 | +} |
| 4178 | + |
| 4179 | +static picture_t *deinterlace(filter_t * p_filter, picture_t * p_pic) |
| 4180 | +{ |
| 4181 | + filter_sys_t * const sys = p_filter->p_sys; |
| 4182 | + picture_t *ret_pics = NULL; |
| 4183 | + MMAL_STATUS_T err; |
| 4184 | + MMAL_BUFFER_HEADER_T * out_buf = NULL; |
| 4185 | + |
| 4186 | +#if TRACE_ALL |
| 4187 | + msg_Dbg(p_filter, "<<< %s", __func__); |
| 4188 | +#endif |
| 4189 | + |
| 4190 | + if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic)) |
| 4191 | + { |
| 4192 | + // ****** Breaks on opaque (at least) |
| 4193 | + |
| 4194 | + if (sys->input->is_enabled) |
| 4195 | + mmal_port_disable(sys->input); |
| 4196 | +#if 0 |
| 4197 | + if (sys->output->is_enabled) |
| 4198 | + mmal_port_disable(sys->output); |
| 4199 | + |
| 4200 | + mmal_format_full_copy(sys->output->format, sys->input->format); |
| 4201 | + mmal_port_format_commit(sys->output); |
| 4202 | + sys->output->buffer_num = 30; |
| 4203 | + sys->output->buffer_size = sys->input->buffer_size_recommended; |
| 4204 | + mmal_port_enable(sys->output, di_output_port_cb); |
| 4205 | +#endif |
| 4206 | + if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS) |
| 4207 | + msg_Err(p_filter, "Failed to update pic format"); |
| 4208 | + sys->input->buffer_num = 30; |
| 4209 | + sys->input->buffer_size = sys->input->buffer_size_recommended; |
| 4210 | + mmal_log_dump_format(sys->input->format); |
| 4211 | + } |
| 4212 | + |
| 4213 | + // Reenable stuff if the last thing we did was flush |
| 4214 | + // Output should always be enabled |
| 4215 | + if (!sys->input->is_enabled && |
| 4216 | + (err = mmal_port_enable(sys->input, di_input_port_cb)) != MMAL_SUCCESS) |
| 4217 | + { |
| 4218 | + msg_Err(p_filter, "Input port reenable failed"); |
| 4219 | + goto fail; |
| 4220 | + } |
| 4221 | + |
| 4222 | + if (!sys->is_cma) |
| 4223 | + { |
| 4224 | + // Fill output from anything that has turned up in pool Q |
| 4225 | + if (hw_mmal_port_pool_ref_fill(sys->out_ppr) != MMAL_SUCCESS) |
| 4226 | + { |
| 4227 | + msg_Err(p_filter, "Out port fill fail"); |
| 4228 | + goto fail; |
| 4229 | + } |
| 4230 | } |
| 4231 | + else |
| 4232 | + { |
| 4233 | + // We are expecting one in - one out so simply wedge a new bufer |
| 4234 | + // into the output port. Flow control will happen on cma alloc. |
| 4235 | + |
| 4236 | + if ((out_buf = mmal_queue_get(sys->out_pool->queue)) == NULL) |
| 4237 | + { |
| 4238 | + // Should never happen |
| 4239 | + msg_Err(p_filter, "Failed to get output buffer"); |
| 4240 | + goto fail; |
| 4241 | + } |
| 4242 | + mmal_buffer_header_reset(out_buf); |
| 4243 | |
| 4244 | - sys->output->buffer_num = 3; |
| 4245 | + // Attach cma_buf to the buffer & ensure it is freed when the buffer is released |
| 4246 | + // On a good send callback the pic will be extracted to avoid this |
| 4247 | + mmal_buffer_header_pre_release_cb_set(out_buf, out_buffer_pre_release_cb, p_filter); |
| 4248 | + |
| 4249 | + cma_buf_t * const cb = cma_buf_pool_alloc_buf(sys->cma_out_pool, sys->output->buffer_size); |
| 4250 | + if ((out_buf->user_data = cb) == NULL) // Check & attach cb to buf |
| 4251 | + { |
| 4252 | + char dbuf0[5]; |
| 4253 | + msg_Err(p_filter, "Failed to alloc CMA buf: fmt=%s, size=%d", |
| 4254 | + str_fourcc(dbuf0, p_pic->format.i_chroma), |
| 4255 | + sys->output->buffer_size); |
| 4256 | + goto fail; |
| 4257 | + } |
| 4258 | + const unsigned int vc_h = cma_buf_vc_handle(cb); // Cannot coerce without going via variable |
| 4259 | + out_buf->data = (uint8_t *)vc_h; |
| 4260 | + out_buf->alloc_size = sys->output->buffer_size; |
| 4261 | + |
| 4262 | +#if TRACE_ALL |
| 4263 | + msg_Dbg(p_filter, "Out buf send: pic=%p, data=%p, user=%p, flags=%#x, len=%d/%d, pts=%lld", |
| 4264 | + p_pic, out_buf->data, out_buf->user_data, out_buf->flags, |
| 4265 | + out_buf->length, out_buf->alloc_size, (long long)out_buf->pts); |
| 4266 | +#endif |
| 4267 | |
| 4268 | - if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) { |
| 4269 | - MMAL_PARAMETER_UINT32_T extra_buffers = { |
| 4270 | - { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) }, |
| 4271 | - 5 |
| 4272 | - }; |
| 4273 | - status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr); |
| 4274 | - if (status != MMAL_SUCCESS) { |
| 4275 | - msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)", |
| 4276 | - status, mmal_status_to_string(status)); |
| 4277 | - goto out; |
| 4278 | + if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS) |
| 4279 | + { |
| 4280 | + msg_Err(p_filter, "Send buffer to output failed"); |
| 4281 | + goto fail; |
| 4282 | } |
| 4283 | + out_buf = NULL; |
| 4284 | + } |
| 4285 | |
| 4286 | - MMAL_PARAMETER_BOOLEAN_T zero_copy = { |
| 4287 | - { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, |
| 4288 | - 1 |
| 4289 | - }; |
| 4290 | + // Stuff into input |
| 4291 | + // We assume the BH is already set up with values reflecting pic date etc. |
| 4292 | + { |
| 4293 | + MMAL_BUFFER_HEADER_T * const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->in_pool); |
| 4294 | + |
| 4295 | + if (pic_buf == NULL) |
| 4296 | + { |
| 4297 | + msg_Err(p_filter, "Pic has not attached buffer"); |
| 4298 | + goto fail; |
| 4299 | + } |
| 4300 | |
| 4301 | - status = mmal_port_parameter_set(sys->output, &zero_copy.hdr); |
| 4302 | - if (status != MMAL_SUCCESS) { |
| 4303 | - msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", |
| 4304 | - sys->output->name, status, mmal_status_to_string(status)); |
| 4305 | - goto out; |
| 4306 | + picture_Release(p_pic); |
| 4307 | + |
| 4308 | + // Add a sequence to the flags so we can track what we have actually |
| 4309 | + // deinterlaced |
| 4310 | + pic_buf->flags = (pic_buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0)); |
| 4311 | + sys->seq_in = seq_inc(sys->seq_in); |
| 4312 | + |
| 4313 | + if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS) |
| 4314 | + { |
| 4315 | + msg_Err(p_filter, "Send buffer to input failed"); |
| 4316 | + mmal_buffer_header_release(pic_buf); |
| 4317 | + goto fail; |
| 4318 | } |
| 4319 | } |
| 4320 | |
| 4321 | - status = mmal_port_enable(sys->output, output_port_cb); |
| 4322 | - if (status != MMAL_SUCCESS) { |
| 4323 | - msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)", |
| 4324 | - sys->output->name, status, mmal_status_to_string(status)); |
| 4325 | - ret = VLC_EGENERIC; |
| 4326 | - goto out; |
| 4327 | + // Return anything that is in the out Q |
| 4328 | + { |
| 4329 | + picture_t ** pp_pic = &ret_pics; |
| 4330 | + |
| 4331 | + // Advanced di has a 3 frame latency, so if the seq delta is greater |
| 4332 | + // than that then we are expecting at least two frames of output. Wait |
| 4333 | + // for one of those. |
| 4334 | + // seq_in is seq of the next frame we are going to submit (1-15, no 0) |
| 4335 | + // seq_out is last frame we removed from Q |
| 4336 | + // So after 4 frames sent (1st time we want to wait), 0 rx seq_in=5, seq_out=15, delta=5 |
| 4337 | + |
| 4338 | + 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) |
| 4339 | + { |
| 4340 | + const unsigned int seq_out = (out_buf->flags / MMAL_BUFFER_HEADER_FLAG_USER0) & 0xf; |
| 4341 | + int rv; |
| 4342 | + |
| 4343 | + picture_t * out_pic; |
| 4344 | + |
| 4345 | + if (sys->is_cma) |
| 4346 | + { |
| 4347 | + // Alloc pic |
| 4348 | + if ((out_pic = filter_NewPicture(p_filter)) == NULL) |
| 4349 | + { |
| 4350 | + // Can't alloc pic - just stop extraction |
| 4351 | + mmal_queue_put_back(sys->out_q, out_buf); |
| 4352 | + out_buf = NULL; |
| 4353 | + msg_Warn(p_filter, "Failed to alloc new filter output pic"); |
| 4354 | + break; |
| 4355 | + } |
| 4356 | + |
| 4357 | + // Extract cma_buf from buf & attach to pic |
| 4358 | + cma_buf_t * const cb = (cma_buf_t *)out_buf->user_data; |
| 4359 | + if ((rv = cma_buf_pic_attach(cb, out_pic)) != VLC_SUCCESS) |
| 4360 | + { |
| 4361 | + char dbuf0[5]; |
| 4362 | + msg_Err(p_filter, "Failed to attach CMA to pic: fmt=%s err=%d", |
| 4363 | + str_fourcc(dbuf0, out_pic->format.i_chroma), |
| 4364 | + rv); |
| 4365 | + // cb still attached to buffer and will be freed with it |
| 4366 | + goto fail; |
| 4367 | + } |
| 4368 | + out_buf->user_data = NULL; |
| 4369 | + |
| 4370 | + buf_to_pic_copy_props(out_pic, out_buf); |
| 4371 | + |
| 4372 | + // Set pic data pointers from buf aux info now it has it |
| 4373 | + if ((rv = cma_pic_set_data(out_pic, sys->output->format, out_buf)) != VLC_SUCCESS) |
| 4374 | + { |
| 4375 | + char dbuf0[5]; |
| 4376 | + msg_Err(p_filter, "Failed to set data: fmt=%s, rv=%d", |
| 4377 | + str_fourcc(dbuf0, sys->output->format->encoding), |
| 4378 | + rv); |
| 4379 | + } |
| 4380 | + |
| 4381 | + out_buf->user_data = NULL; // Responsability for this pic no longer with buffer |
| 4382 | + mmal_buffer_header_release(out_buf); |
| 4383 | + } |
| 4384 | + else |
| 4385 | + { |
| 4386 | + out_pic = di_alloc_opaque(p_filter, out_buf); |
| 4387 | + |
| 4388 | + if (out_pic == NULL) { |
| 4389 | + msg_Warn(p_filter, "Failed to alloc new filter output pic"); |
| 4390 | + mmal_queue_put_back(sys->out_q, out_buf); // Wedge buf back into Q in the hope we can alloc a pic later |
| 4391 | + out_buf = NULL; |
| 4392 | + break; |
| 4393 | + } |
| 4394 | + } |
| 4395 | + out_buf = NULL; // Now attached to pic or recycled |
| 4396 | + |
| 4397 | +#if TRACE_ALL |
| 4398 | + 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)); |
| 4399 | +#endif |
| 4400 | + |
| 4401 | + *pp_pic = out_pic; |
| 4402 | + pp_pic = &out_pic->p_next; |
| 4403 | + |
| 4404 | + // Ignore 0 seqs |
| 4405 | + // Don't think these should actually happen |
| 4406 | + if (seq_out != 0) |
| 4407 | + sys->seq_out = seq_out; |
| 4408 | + } |
| 4409 | + |
| 4410 | + // Crash on lockup |
| 4411 | + assert(ret_pics != NULL || seq_delta(sys->seq_in, sys->seq_out) < 5); |
| 4412 | } |
| 4413 | |
| 4414 | - status = mmal_component_enable(sys->component); |
| 4415 | - if (status != MMAL_SUCCESS) { |
| 4416 | - msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)", |
| 4417 | - sys->component->name, status, mmal_status_to_string(status)); |
| 4418 | - ret = VLC_EGENERIC; |
| 4419 | - goto out; |
| 4420 | +#if TRACE_ALL |
| 4421 | + msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics); |
| 4422 | +#endif |
| 4423 | + |
| 4424 | + return ret_pics; |
| 4425 | + |
| 4426 | +fail: |
| 4427 | + if (out_buf != NULL) |
| 4428 | + mmal_buffer_header_release(out_buf); |
| 4429 | + picture_Release(p_pic); |
| 4430 | + return NULL; |
| 4431 | +} |
| 4432 | + |
| 4433 | +static void di_flush(filter_t *p_filter) |
| 4434 | +{ |
| 4435 | + filter_sys_t * const sys = p_filter->p_sys; |
| 4436 | + |
| 4437 | +#if TRACE_ALL |
| 4438 | + msg_Dbg(p_filter, "<<< %s", __func__); |
| 4439 | +#endif |
| 4440 | + |
| 4441 | + if (sys->input != NULL && sys->input->is_enabled) |
| 4442 | + mmal_port_disable(sys->input); |
| 4443 | + |
| 4444 | + if (sys->output != NULL && sys->output->is_enabled) |
| 4445 | + { |
| 4446 | + if (sys->is_cma) |
| 4447 | + { |
| 4448 | + MMAL_BUFFER_HEADER_T * buf; |
| 4449 | + mmal_port_disable(sys->output); |
| 4450 | + while ((buf = mmal_queue_get(sys->out_q)) != NULL) |
| 4451 | + mmal_buffer_header_release(buf); |
| 4452 | + } |
| 4453 | + else |
| 4454 | + { |
| 4455 | + // Wedge anything we've got into the output port as that will free the underlying buffers |
| 4456 | + fill_output_from_q(p_filter, sys, sys->out_q); |
| 4457 | + |
| 4458 | + mmal_port_disable(sys->output); |
| 4459 | + |
| 4460 | + // If that dumped anything real into the out_q then have another go |
| 4461 | + if (mmal_queue_length(sys->out_q) != 0) |
| 4462 | + { |
| 4463 | + mmal_port_enable(sys->output, di_output_port_cb); |
| 4464 | + fill_output_from_q(p_filter, sys, sys->out_q); |
| 4465 | + mmal_port_disable(sys->output); |
| 4466 | + // Out q should now be empty & should remain so until the input is reenabled |
| 4467 | + } |
| 4468 | + } |
| 4469 | + mmal_port_enable(sys->output, di_output_port_cb); |
| 4470 | + |
| 4471 | + // Leaving the input disabled is fine - but we want to leave the output enabled |
| 4472 | + // so we can retrieve buffers that are still bound to pictures |
| 4473 | } |
| 4474 | |
| 4475 | - sys->filtered_pictures = mmal_queue_create(); |
| 4476 | + sys->seq_in = 1; |
| 4477 | + sys->seq_out = 15; |
| 4478 | |
| 4479 | - filter->pf_video_filter = deinterlace; |
| 4480 | - filter->pf_flush = flush; |
| 4481 | +#if TRACE_ALL |
| 4482 | + msg_Dbg(p_filter, ">>> %s", __func__); |
| 4483 | +#endif |
| 4484 | +} |
| 4485 | |
| 4486 | - vlc_sem_init(&sys->sem, 0); |
| 4487 | |
| 4488 | -out: |
| 4489 | - if (ret != VLC_SUCCESS) |
| 4490 | - Close(filter); |
| 4491 | +static void pass_flush(filter_t *p_filter) |
| 4492 | +{ |
| 4493 | + // Nothing to do |
| 4494 | + VLC_UNUSED(p_filter); |
| 4495 | +} |
| 4496 | |
| 4497 | - return ret; |
| 4498 | +static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic) |
| 4499 | +{ |
| 4500 | + VLC_UNUSED(p_filter); |
| 4501 | + |
| 4502 | + p_pic->b_progressive = true; |
| 4503 | + return p_pic; |
| 4504 | } |
| 4505 | |
| 4506 | -static void Close(filter_t *filter) |
| 4507 | + |
| 4508 | +static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 4509 | { |
| 4510 | - filter_sys_t *sys = filter->p_sys; |
| 4511 | - MMAL_BUFFER_HEADER_T *buffer; |
| 4512 | + filter_t *filter = (filter_t *)port->userdata; |
| 4513 | + MMAL_STATUS_T status; |
| 4514 | |
| 4515 | - if (!sys) |
| 4516 | + if (buffer->cmd == MMAL_EVENT_ERROR) { |
| 4517 | + status = *(uint32_t *)buffer->data; |
| 4518 | + msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status, |
| 4519 | + mmal_status_to_string(status)); |
| 4520 | + } |
| 4521 | + |
| 4522 | + mmal_buffer_header_reset(buffer); |
| 4523 | + mmal_buffer_header_release(buffer); |
| 4524 | +} |
| 4525 | + |
| 4526 | +static void CloseMmalDeinterlace(filter_t *filter) |
| 4527 | +{ |
| 4528 | + filter_sys_t * const sys = filter->p_sys; |
| 4529 | + |
| 4530 | +#if TRACE_ALL |
| 4531 | + msg_Dbg(filter, "<<< %s", __func__); |
| 4532 | +#endif |
| 4533 | + |
| 4534 | + if (sys == NULL) |
| 4535 | return; |
| 4536 | |
| 4537 | - if (sys->component && sys->component->control->is_enabled) |
| 4538 | - mmal_port_disable(sys->component->control); |
| 4539 | + if (sys->use_passthrough) |
| 4540 | + { |
| 4541 | + free(sys); |
| 4542 | + return; |
| 4543 | + } |
| 4544 | |
| 4545 | - if (sys->input && sys->input->is_enabled) |
| 4546 | - mmal_port_disable(sys->input); |
| 4547 | + di_flush(filter); |
| 4548 | |
| 4549 | - if (sys->output && sys->output->is_enabled) |
| 4550 | - mmal_port_disable(sys->output); |
| 4551 | + if (sys->component && sys->component->control->is_enabled) |
| 4552 | + mmal_port_disable(sys->component->control); |
| 4553 | |
| 4554 | if (sys->component && sys->component->is_enabled) |
| 4555 | mmal_component_disable(sys->component); |
| 4556 | |
| 4557 | - while ((buffer = mmal_queue_get(sys->filtered_pictures))) { |
| 4558 | - picture_t *pic = (picture_t *)buffer->user_data; |
| 4559 | - picture_Release(pic); |
| 4560 | + if (sys->in_pool != NULL) |
| 4561 | + mmal_pool_destroy(sys->in_pool); |
| 4562 | + |
| 4563 | + hw_mmal_port_pool_ref_release(sys->out_ppr, false); |
| 4564 | + // Once we exit filter & sys are invalid so mark as such |
| 4565 | + if (sys->output != NULL) |
| 4566 | + sys->output->userdata = NULL; |
| 4567 | + |
| 4568 | + if (sys->is_cma) |
| 4569 | + { |
| 4570 | + if (sys->output && sys->output->is_enabled) |
| 4571 | + mmal_port_disable(sys->output); |
| 4572 | + |
| 4573 | + cma_buf_pool_deletez(&sys->cma_out_pool); |
| 4574 | + |
| 4575 | + if (sys->out_pool != NULL) |
| 4576 | + mmal_pool_destroy(sys->out_pool); |
| 4577 | } |
| 4578 | |
| 4579 | - if (sys->filtered_pictures) |
| 4580 | - mmal_queue_destroy(sys->filtered_pictures); |
| 4581 | + if (sys->out_q != NULL) |
| 4582 | + mmal_queue_destroy(sys->out_q); |
| 4583 | |
| 4584 | if (sys->component) |
| 4585 | mmal_component_release(sys->component); |
| 4586 | |
| 4587 | - vlc_sem_destroy(&sys->sem); |
| 4588 | + cma_vcsm_exit(sys->vcsm_init_type); |
| 4589 | + |
| 4590 | free(sys); |
| 4591 | +} |
| 4592 | + |
| 4593 | |
| 4594 | - bcm_host_deinit(); |
| 4595 | +static bool is_fmt_valid_in(const vlc_fourcc_t fmt) |
| 4596 | +{ |
| 4597 | + return fmt == VLC_CODEC_MMAL_OPAQUE || |
| 4598 | + fmt == VLC_CODEC_MMAL_ZC_I420 || |
| 4599 | + fmt == VLC_CODEC_MMAL_ZC_SAND8; |
| 4600 | } |
| 4601 | |
| 4602 | -static int send_output_buffer(filter_t *filter) |
| 4603 | +static int OpenMmalDeinterlace(filter_t *filter) |
| 4604 | { |
| 4605 | - filter_sys_t *sys = filter->p_sys; |
| 4606 | - MMAL_BUFFER_HEADER_T *buffer; |
| 4607 | + int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ? |
| 4608 | + CLOCK_FREQ * filter->fmt_in.video.i_frame_rate_base / |
| 4609 | + filter->fmt_in.video.i_frame_rate : 0; |
| 4610 | + |
| 4611 | + int ret = VLC_EGENERIC; |
| 4612 | MMAL_STATUS_T status; |
| 4613 | - picture_t *picture; |
| 4614 | - int ret = 0; |
| 4615 | + filter_sys_t *sys; |
| 4616 | + |
| 4617 | + msg_Dbg(filter, "<<< %s", __func__); |
| 4618 | + |
| 4619 | + if (!is_fmt_valid_in(filter->fmt_in.video.i_chroma) || |
| 4620 | + filter->fmt_out.video.i_chroma != filter->fmt_in.video.i_chroma) |
| 4621 | + return VLC_EGENERIC; |
| 4622 | |
| 4623 | - if (!sys->output->is_enabled) { |
| 4624 | - ret = VLC_EGENERIC; |
| 4625 | - goto out; |
| 4626 | + sys = calloc(1, sizeof(filter_sys_t)); |
| 4627 | + if (!sys) |
| 4628 | + return VLC_ENOMEM; |
| 4629 | + filter->p_sys = sys; |
| 4630 | + |
| 4631 | + sys->seq_in = 1; |
| 4632 | + sys->seq_out = 15; |
| 4633 | + sys->is_cma = is_cma_buf_pic_chroma(filter->fmt_out.video.i_chroma); |
| 4634 | + |
| 4635 | + if ((sys->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) { |
| 4636 | + msg_Err(filter, "VCSM init failed"); |
| 4637 | + goto fail; |
| 4638 | + } |
| 4639 | + |
| 4640 | + if (rpi_is_model_pi4()) |
| 4641 | + { |
| 4642 | + sys->half_rate = true; |
| 4643 | + sys->use_qpu = false; |
| 4644 | + sys->use_fast = true; |
| 4645 | + } |
| 4646 | + else |
| 4647 | + { |
| 4648 | + sys->half_rate = false; |
| 4649 | + sys->use_qpu = true; |
| 4650 | + sys->use_fast = false; |
| 4651 | + } |
| 4652 | + sys->use_passthrough = false; |
| 4653 | + |
| 4654 | + if (filter->fmt_in.video.i_width * filter->fmt_in.video.i_height > 768 * 576) |
| 4655 | + { |
| 4656 | + // We get stressed if we have to try too hard - so make life easier |
| 4657 | + sys->half_rate = true; |
| 4658 | + // Also check we actually have enough memory to do this |
| 4659 | + // Memory always comes from GPU if Opaque |
| 4660 | + // Assume we have plenty of memory if it comes from CMA |
| 4661 | + if ((!sys->is_cma || sys->vcsm_init_type == VCSM_INIT_LEGACY) && |
| 4662 | + hw_mmal_get_gpu_mem() < (96 << 20)) |
| 4663 | + { |
| 4664 | + sys->use_passthrough = true; |
| 4665 | + msg_Warn(filter, "Deinterlace bypassed due to lack of GPU memory"); |
| 4666 | + } |
| 4667 | } |
| 4668 | |
| 4669 | - picture = filter_NewPicture(filter); |
| 4670 | - if (!picture) { |
| 4671 | - msg_Warn(filter, "Failed to get new picture"); |
| 4672 | - ret = -1; |
| 4673 | - goto out; |
| 4674 | + if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU)) |
| 4675 | + sys->use_qpu = false; |
| 4676 | + if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV)) |
| 4677 | + { |
| 4678 | + sys->use_fast = false; |
| 4679 | + sys->use_passthrough = false; |
| 4680 | + } |
| 4681 | + if (var_InheritBool(filter, MMAL_DEINTERLACE_FAST)) |
| 4682 | + { |
| 4683 | + sys->use_fast = true; |
| 4684 | + sys->use_passthrough = false; |
| 4685 | + } |
| 4686 | + if (var_InheritBool(filter, MMAL_DEINTERLACE_NONE)) |
| 4687 | + sys->use_passthrough = true; |
| 4688 | + if (var_InheritBool(filter, MMAL_DEINTERLACE_FULL_RATE)) |
| 4689 | + sys->half_rate = false; |
| 4690 | + if (var_InheritBool(filter, MMAL_DEINTERLACE_HALF_RATE)) |
| 4691 | + sys->half_rate = true; |
| 4692 | + |
| 4693 | + if (sys->use_passthrough) |
| 4694 | + { |
| 4695 | + filter->pf_video_filter = pass_deinterlace; |
| 4696 | + filter->pf_flush = pass_flush; |
| 4697 | + // Don't need VCSM - get rid of it now |
| 4698 | + cma_vcsm_exit(sys->vcsm_init_type); |
| 4699 | + sys->vcsm_init_type = VCSM_INIT_NONE; |
| 4700 | + return 0; |
| 4701 | + } |
| 4702 | + |
| 4703 | + { |
| 4704 | + char dbuf0[5], dbuf1[5]; |
| 4705 | + msg_Dbg(filter, "%s: %s,%dx%d [(%d,%d) %d/%d] -> %s,%dx%d [(%d,%d) %dx%d]: %s %s %s", __func__, |
| 4706 | + str_fourcc(dbuf0, filter->fmt_in.video.i_chroma), |
| 4707 | + filter->fmt_in.video.i_width, filter->fmt_in.video.i_height, |
| 4708 | + filter->fmt_in.video.i_x_offset, filter->fmt_in.video.i_y_offset, |
| 4709 | + filter->fmt_in.video.i_visible_width, filter->fmt_in.video.i_visible_height, |
| 4710 | + str_fourcc(dbuf1, filter->fmt_out.video.i_chroma), |
| 4711 | + filter->fmt_out.video.i_width, filter->fmt_out.video.i_height, |
| 4712 | + filter->fmt_out.video.i_x_offset, filter->fmt_out.video.i_y_offset, |
| 4713 | + filter->fmt_out.video.i_visible_width, filter->fmt_out.video.i_visible_height, |
| 4714 | + sys->use_qpu ? "QPU" : "VPU", |
| 4715 | + sys->use_fast ? "FAST" : "ADV", |
| 4716 | + sys->use_passthrough ? "PASS" : sys->half_rate ? "HALF" : "FULL"); |
| 4717 | } |
| 4718 | - picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate; |
| 4719 | - picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base; |
| 4720 | |
| 4721 | - buffer = picture->p_sys->buffer; |
| 4722 | - buffer->user_data = picture; |
| 4723 | - buffer->cmd = 0; |
| 4724 | + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component); |
| 4725 | + if (status != MMAL_SUCCESS) { |
| 4726 | + msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)", |
| 4727 | + MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); |
| 4728 | + goto fail; |
| 4729 | + } |
| 4730 | |
| 4731 | - mmal_picture_lock(picture); |
| 4732 | + { |
| 4733 | + const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = { |
| 4734 | + { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) }, |
| 4735 | + sys->use_fast ? |
| 4736 | + MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST : |
| 4737 | + MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV, |
| 4738 | + 4, |
| 4739 | + { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu } |
| 4740 | + }; |
| 4741 | |
| 4742 | - status = mmal_port_send_buffer(sys->output, buffer); |
| 4743 | + status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr); |
| 4744 | + if (status != MMAL_SUCCESS) { |
| 4745 | + msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)", |
| 4746 | + MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status)); |
| 4747 | + goto fail; |
| 4748 | + } |
| 4749 | + } |
| 4750 | + |
| 4751 | + sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter; |
| 4752 | + status = mmal_port_enable(sys->component->control, control_port_cb); |
| 4753 | if (status != MMAL_SUCCESS) { |
| 4754 | - msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)", |
| 4755 | - status, mmal_status_to_string(status)); |
| 4756 | - mmal_buffer_header_release(buffer); |
| 4757 | - picture_Release(picture); |
| 4758 | - ret = -1; |
| 4759 | - } else { |
| 4760 | - atomic_fetch_add(&sys->output_in_transit, 1); |
| 4761 | - vlc_sem_post(&sys->sem); |
| 4762 | + msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)", |
| 4763 | + sys->component->control->name, status, mmal_status_to_string(status)); |
| 4764 | + goto fail; |
| 4765 | } |
| 4766 | |
| 4767 | -out: |
| 4768 | - return ret; |
| 4769 | -} |
| 4770 | + sys->input = sys->component->input[0]; |
| 4771 | + sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter; |
| 4772 | + sys->input->format->encoding = vlc_to_mmal_video_fourcc(&filter->fmt_in.video); |
| 4773 | + hw_mmal_vlc_fmt_to_mmal_fmt(sys->input->format, &filter->fmt_in.video); |
| 4774 | |
| 4775 | -static void fill_output_port(filter_t *filter) |
| 4776 | -{ |
| 4777 | - filter_sys_t *sys = filter->p_sys; |
| 4778 | - /* allow at least 2 buffers in transit */ |
| 4779 | - unsigned max_buffers_in_transit = __MAX(2, MIN_NUM_BUFFERS_IN_TRANSIT); |
| 4780 | - int buffers_available = sys->output->buffer_num - |
| 4781 | - atomic_load(&sys->output_in_transit) - |
| 4782 | - mmal_queue_length(sys->filtered_pictures); |
| 4783 | - int buffers_to_send = max_buffers_in_transit - sys->output_in_transit; |
| 4784 | - int i; |
| 4785 | + es_format_Copy(&filter->fmt_out, &filter->fmt_in); |
| 4786 | + if (!sys->half_rate) |
| 4787 | + filter->fmt_out.video.i_frame_rate *= 2; |
| 4788 | |
| 4789 | - if (buffers_to_send > buffers_available) |
| 4790 | - buffers_to_send = buffers_available; |
| 4791 | + status = mmal_port_format_commit(sys->input); |
| 4792 | + if (status != MMAL_SUCCESS) { |
| 4793 | + msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)", |
| 4794 | + sys->input->name, status, mmal_status_to_string(status)); |
| 4795 | + goto fail; |
| 4796 | + } |
| 4797 | + sys->input->buffer_size = sys->input->buffer_size_recommended; |
| 4798 | + sys->input->buffer_num = 30; |
| 4799 | +// sys->input->buffer_num = sys->input->buffer_num_recommended; |
| 4800 | |
| 4801 | -#ifndef NDEBUG |
| 4802 | - msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)", |
| 4803 | - buffers_to_send, buffers_available, sys->output_in_transit, |
| 4804 | - sys->output->buffer_num); |
| 4805 | -#endif |
| 4806 | - for (i = 0; i < buffers_to_send; ++i) { |
| 4807 | - if (send_output_buffer(filter) < 0) |
| 4808 | - break; |
| 4809 | + if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) |
| 4810 | + { |
| 4811 | + msg_Err(filter, "Failed to create input pool"); |
| 4812 | + goto fail; |
| 4813 | } |
| 4814 | -} |
| 4815 | |
| 4816 | -static picture_t *deinterlace(filter_t *filter, picture_t *picture) |
| 4817 | -{ |
| 4818 | - filter_sys_t *sys = filter->p_sys; |
| 4819 | - MMAL_BUFFER_HEADER_T *buffer; |
| 4820 | - picture_t *out_picture = NULL; |
| 4821 | - picture_t *ret = NULL; |
| 4822 | - MMAL_STATUS_T status; |
| 4823 | - unsigned i = 0; |
| 4824 | + status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true); |
| 4825 | + if (status != MMAL_SUCCESS) { |
| 4826 | + msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", |
| 4827 | + sys->input->name, status, mmal_status_to_string(status)); |
| 4828 | + goto fail; |
| 4829 | + } |
| 4830 | |
| 4831 | - fill_output_port(filter); |
| 4832 | + status = mmal_port_enable(sys->input, di_input_port_cb); |
| 4833 | + if (status != MMAL_SUCCESS) { |
| 4834 | + msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)", |
| 4835 | + sys->input->name, status, mmal_status_to_string(status)); |
| 4836 | + goto fail; |
| 4837 | + } |
| 4838 | |
| 4839 | - buffer = picture->p_sys->buffer; |
| 4840 | - buffer->user_data = picture; |
| 4841 | - buffer->pts = picture->date; |
| 4842 | - buffer->cmd = 0; |
| 4843 | |
| 4844 | - if (!picture->p_sys->displayed) { |
| 4845 | - status = mmal_port_send_buffer(sys->input, buffer); |
| 4846 | - if (status != MMAL_SUCCESS) { |
| 4847 | - msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)", |
| 4848 | - status, mmal_status_to_string(status)); |
| 4849 | - picture_Release(picture); |
| 4850 | - } else { |
| 4851 | - picture->p_sys->displayed = true; |
| 4852 | - atomic_fetch_add(&sys->input_in_transit, 1); |
| 4853 | - vlc_sem_post(&sys->sem); |
| 4854 | - } |
| 4855 | - } else { |
| 4856 | - picture_Release(picture); |
| 4857 | - } |
| 4858 | - |
| 4859 | - /* |
| 4860 | - * Send output buffers |
| 4861 | - */ |
| 4862 | - while(atomic_load(&sys->started) && i < 2) { |
| 4863 | - if (buffer = mmal_queue_timedwait(sys->filtered_pictures, 2000)) { |
| 4864 | - i++; |
| 4865 | - if (!out_picture) { |
| 4866 | - out_picture = (picture_t *)buffer->user_data; |
| 4867 | - ret = out_picture; |
| 4868 | - } else { |
| 4869 | - out_picture->p_next = (picture_t *)buffer->user_data; |
| 4870 | - out_picture = out_picture->p_next; |
| 4871 | - } |
| 4872 | - out_picture->date = buffer->pts; |
| 4873 | - } else { |
| 4874 | - msg_Dbg(filter, "Failed waiting for filtered picture"); |
| 4875 | - break; |
| 4876 | - } |
| 4877 | + if ((sys->out_q = mmal_queue_create()) == NULL) |
| 4878 | + { |
| 4879 | + msg_Err(filter, "Failed to create out Q"); |
| 4880 | + goto fail; |
| 4881 | } |
| 4882 | - if (out_picture) |
| 4883 | - out_picture->p_next = NULL; |
| 4884 | |
| 4885 | - return ret; |
| 4886 | -} |
| 4887 | - |
| 4888 | -static void flush(filter_t *filter) |
| 4889 | -{ |
| 4890 | - filter_sys_t *sys = filter->p_sys; |
| 4891 | - MMAL_BUFFER_HEADER_T *buffer; |
| 4892 | + sys->output = sys->component->output[0]; |
| 4893 | + mmal_format_full_copy(sys->output->format, sys->input->format); |
| 4894 | |
| 4895 | - msg_Dbg(filter, "flush deinterlace filter"); |
| 4896 | + if (!sys->is_cma) |
| 4897 | + { |
| 4898 | + if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS) |
| 4899 | + goto fail; |
| 4900 | + } |
| 4901 | + else |
| 4902 | + { |
| 4903 | + // CMA stuff |
| 4904 | + sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter; |
| 4905 | + |
| 4906 | + if ((sys->cma_out_pool = cma_buf_pool_new(8, 8, true, "deinterlace")) == NULL) |
| 4907 | + { |
| 4908 | + msg_Err(filter, "Failed to alloc cma buf pool"); |
| 4909 | + goto fail; |
| 4910 | + } |
| 4911 | |
| 4912 | - msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)", |
| 4913 | - sys->input_in_transit, sys->output_in_transit); |
| 4914 | - mmal_port_flush(sys->output); |
| 4915 | - mmal_port_flush(sys->input); |
| 4916 | - |
| 4917 | - msg_Dbg(filter, "flush: wait for all buffers to be returned"); |
| 4918 | - while (atomic_load(&sys->input_in_transit) || |
| 4919 | - atomic_load(&sys->output_in_transit)) |
| 4920 | - vlc_sem_wait(&sys->sem); |
| 4921 | - |
| 4922 | - while ((buffer = mmal_queue_get(sys->filtered_pictures))) { |
| 4923 | - picture_t *pic = (picture_t *)buffer->user_data; |
| 4924 | - msg_Dbg(filter, "flush: release already filtered pic %p", |
| 4925 | - (void *)pic); |
| 4926 | - picture_Release(pic); |
| 4927 | - } |
| 4928 | - atomic_store(&sys->started, false); |
| 4929 | - msg_Dbg(filter, "flush: done"); |
| 4930 | -} |
| 4931 | + // Rate control done by CMA in flight logic, so have "inexhaustable" pool here |
| 4932 | + if ((sys->out_pool = mmal_pool_create(30, 0)) == NULL) |
| 4933 | + { |
| 4934 | + msg_Err(filter, "Failed to alloc out pool"); |
| 4935 | + goto fail; |
| 4936 | + } |
| 4937 | |
| 4938 | -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 4939 | -{ |
| 4940 | - filter_t *filter = (filter_t *)port->userdata; |
| 4941 | - MMAL_STATUS_T status; |
| 4942 | + port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, true); |
| 4943 | |
| 4944 | - if (buffer->cmd == MMAL_EVENT_ERROR) { |
| 4945 | - status = *(uint32_t *)buffer->data; |
| 4946 | - msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status, |
| 4947 | - mmal_status_to_string(status)); |
| 4948 | - } |
| 4949 | + if ((status = mmal_port_format_commit(sys->output)) != MMAL_SUCCESS) |
| 4950 | + { |
| 4951 | + msg_Err(filter, "Output port format commit failed"); |
| 4952 | + goto fail; |
| 4953 | + } |
| 4954 | |
| 4955 | - mmal_buffer_header_release(buffer); |
| 4956 | -} |
| 4957 | + sys->output->buffer_num = 30; |
| 4958 | + sys->output->buffer_size = sys->output->buffer_size_recommended; |
| 4959 | |
| 4960 | -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 4961 | -{ |
| 4962 | - picture_t *picture = (picture_t *)buffer->user_data; |
| 4963 | - filter_t *filter = (filter_t *)port->userdata; |
| 4964 | - filter_sys_t *sys = filter->p_sys; |
| 4965 | + // CB just drops all bufs into out_q |
| 4966 | + if ((status = mmal_port_enable(sys->output, di_output_port_cb)) != MMAL_SUCCESS) |
| 4967 | + { |
| 4968 | + msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)", |
| 4969 | + sys->output->name, status, mmal_status_to_string(status)); |
| 4970 | + goto fail; |
| 4971 | + } |
| 4972 | + } |
| 4973 | |
| 4974 | - if (picture) { |
| 4975 | - picture_Release(picture); |
| 4976 | - } else { |
| 4977 | - msg_Warn(filter, "Got buffer without picture on input port - OOOPS"); |
| 4978 | - mmal_buffer_header_release(buffer); |
| 4979 | + status = mmal_component_enable(sys->component); |
| 4980 | + if (status != MMAL_SUCCESS) { |
| 4981 | + msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)", |
| 4982 | + sys->component->name, status, mmal_status_to_string(status)); |
| 4983 | + goto fail; |
| 4984 | } |
| 4985 | |
| 4986 | - atomic_fetch_sub(&sys->input_in_transit, 1); |
| 4987 | - vlc_sem_post(&sys->sem); |
| 4988 | + filter->pf_video_filter = deinterlace; |
| 4989 | + filter->pf_flush = di_flush; |
| 4990 | + return 0; |
| 4991 | + |
| 4992 | +fail: |
| 4993 | + CloseMmalDeinterlace(filter); |
| 4994 | + return ret; |
| 4995 | } |
| 4996 | |
| 4997 | -static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 4998 | -{ |
| 4999 | - filter_t *filter = (filter_t *)port->userdata; |
| 5000 | - filter_sys_t *sys = filter->p_sys; |
| 5001 | - picture_t *picture; |
| 5002 | +vlc_module_begin() |
| 5003 | + set_shortname(N_("MMAL deinterlace")) |
| 5004 | + set_description(N_("MMAL-based deinterlace filter plugin")) |
| 5005 | + set_capability("video filter", 900) |
| 5006 | + set_category(CAT_VIDEO) |
| 5007 | + set_subcategory(SUBCAT_VIDEO_VFILTER) |
| 5008 | + set_callbacks(OpenMmalDeinterlace, CloseMmalDeinterlace) |
| 5009 | + add_shortcut("deinterlace") |
| 5010 | + add_bool(MMAL_DEINTERLACE_NO_QPU, false, MMAL_DEINTERLACE_NO_QPU_TEXT, |
| 5011 | + MMAL_DEINTERLACE_NO_QPU_LONGTEXT, true); |
| 5012 | + add_bool(MMAL_DEINTERLACE_ADV, false, MMAL_DEINTERLACE_ADV_TEXT, |
| 5013 | + MMAL_DEINTERLACE_ADV_LONGTEXT, true); |
| 5014 | + add_bool(MMAL_DEINTERLACE_FAST, false, MMAL_DEINTERLACE_FAST_TEXT, |
| 5015 | + MMAL_DEINTERLACE_FAST_LONGTEXT, true); |
| 5016 | + add_bool(MMAL_DEINTERLACE_NONE, false, MMAL_DEINTERLACE_NONE_TEXT, |
| 5017 | + MMAL_DEINTERLACE_NONE_LONGTEXT, true); |
| 5018 | + add_bool(MMAL_DEINTERLACE_HALF_RATE, false, MMAL_DEINTERLACE_HALF_RATE_TEXT, |
| 5019 | + MMAL_DEINTERLACE_HALF_RATE_LONGTEXT, true); |
| 5020 | + add_bool(MMAL_DEINTERLACE_FULL_RATE, false, MMAL_DEINTERLACE_FULL_RATE_TEXT, |
| 5021 | + MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true); |
| 5022 | + |
| 5023 | +vlc_module_end() |
| 5024 | + |
| 5025 | |
| 5026 | - if (buffer->cmd == 0) { |
| 5027 | - if (buffer->length > 0) { |
| 5028 | - atomic_store(&sys->started, true); |
| 5029 | - mmal_queue_put(sys->filtered_pictures, buffer); |
| 5030 | - picture = (picture_t *)buffer->user_data; |
| 5031 | - } else { |
| 5032 | - picture = (picture_t *)buffer->user_data; |
| 5033 | - picture_Release(picture); |
| 5034 | - } |
| 5035 | - |
| 5036 | - atomic_fetch_sub(&sys->output_in_transit, 1); |
| 5037 | - vlc_sem_post(&sys->sem); |
| 5038 | - } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) { |
| 5039 | - msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled"); |
| 5040 | - mmal_buffer_header_release(buffer); |
| 5041 | - } else { |
| 5042 | - mmal_buffer_header_release(buffer); |
| 5043 | - } |
| 5044 | -} |
| 5045 | --- /dev/null |
| 5046 | +++ b/modules/hw/mmal/mmal_avcodec.c |
| 5047 | @@ -0,0 +1,2175 @@ |
| 5048 | +/***************************************************************************** |
| 5049 | + * video.c: video decoder using the libavcodec library |
| 5050 | + ***************************************************************************** |
| 5051 | + * Copyright (C) 1999-2001 VLC authors and VideoLAN |
| 5052 | + * $Id$ |
| 5053 | + * |
| 5054 | + * Authors: Laurent Aimar <fenrir@via.ecp.fr> |
| 5055 | + * Gildas Bazin <gbazin@videolan.org> |
| 5056 | + * |
| 5057 | + * This program is free software; you can redistribute it and/or modify it |
| 5058 | + * under the terms of the GNU Lesser General Public License as published by |
| 5059 | + * the Free Software Foundation; either version 2.1 of the License, or |
| 5060 | + * (at your option) any later version. |
| 5061 | + * |
| 5062 | + * This program is distributed in the hope that it will be useful, |
| 5063 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 5064 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 5065 | + * GNU Lesser General Public License for more details. |
| 5066 | + * |
| 5067 | + * You should have received a copy of the GNU Lesser General Public License |
| 5068 | + * along with this program; if not, write to the Free Software Foundation, |
| 5069 | + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
| 5070 | + *****************************************************************************/ |
| 5071 | + |
| 5072 | +/***************************************************************************** |
| 5073 | + * Preamble |
| 5074 | + *****************************************************************************/ |
| 5075 | +#include "config.h" |
| 5076 | + |
| 5077 | +#include <vlc_common.h> |
| 5078 | +#include <vlc_codec.h> |
| 5079 | +#include <vlc_avcodec.h> |
| 5080 | +#include <vlc_cpu.h> |
| 5081 | +#include <vlc_atomic.h> |
| 5082 | +#include <assert.h> |
| 5083 | + |
| 5084 | +#include <libavcodec/avcodec.h> |
| 5085 | +#include <libavutil/mem.h> |
| 5086 | +#include <libavutil/pixdesc.h> |
| 5087 | +#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) ) |
| 5088 | +#include <libavutil/mastering_display_metadata.h> |
| 5089 | +#endif |
| 5090 | + |
| 5091 | +//#include "avcodec.h" |
| 5092 | +//#include "va.h" |
| 5093 | + |
| 5094 | +#include <vlc_plugin.h> |
| 5095 | +#include <libavutil/rpi_sand_fns.h> |
| 5096 | +#include <libavcodec/rpi_zc.h> |
| 5097 | +#include "../../codec/cc.h" |
| 5098 | +#include "../../codec/avcodec/avcommon.h" // ??? Beware over inclusion |
| 5099 | +#include "mmal_cma.h" |
| 5100 | +#include "mmal_picture.h" |
| 5101 | + |
| 5102 | +#define TRACE_ALL 0 |
| 5103 | + |
| 5104 | +#define BUFFERS_IN_FLIGHT 5 // Default max value for in flight buffers |
| 5105 | +#define BUFFERS_IN_FLIGHT_UHD 3 // Fewer if very big |
| 5106 | + |
| 5107 | +#define MMAL_AVCODEC_BUFFERS "mmal-avcodec-buffers" |
| 5108 | +#define MMAL_AVCODEC_BUFFERS_TEXT N_("In flight buffer count before blocking.") |
| 5109 | +#define MMAL_AVCODEC_BUFFERS_LONGTEXT N_("In flight buffer count before blocking. " \ |
| 5110 | +"Beware that incautious changing of this can lead to lockup. " \ |
| 5111 | +"Zero will disable the module.") |
| 5112 | + |
| 5113 | + |
| 5114 | +// Fwd declarations required due to wanting to avoid reworking the original |
| 5115 | +// code too much |
| 5116 | +static void MmalAvcodecCloseDecoder( vlc_object_t *obj ); |
| 5117 | + |
| 5118 | + |
| 5119 | +/***************************************************************************** |
| 5120 | + * decoder_sys_t : decoder descriptor |
| 5121 | + *****************************************************************************/ |
| 5122 | +struct decoder_sys_t |
| 5123 | +{ |
| 5124 | + AVCodecContext *p_context; |
| 5125 | + const AVCodec *p_codec; |
| 5126 | + |
| 5127 | + /* Video decoder specific part */ |
| 5128 | + date_t pts; |
| 5129 | + |
| 5130 | + /* Closed captions for decoders */ |
| 5131 | + cc_data_t cc; |
| 5132 | + |
| 5133 | + /* for frame skipping algo */ |
| 5134 | + bool b_hurry_up; |
| 5135 | + bool b_show_corrupted; |
| 5136 | + bool b_from_preroll; |
| 5137 | + enum AVDiscard i_skip_frame; |
| 5138 | + |
| 5139 | + /* how many decoded frames are late */ |
| 5140 | + int i_late_frames; |
| 5141 | + mtime_t i_late_frames_start; |
| 5142 | + mtime_t i_last_late_delay; |
| 5143 | + |
| 5144 | + /* for direct rendering */ |
| 5145 | + bool b_direct_rendering; |
| 5146 | + atomic_bool b_dr_failure; |
| 5147 | + |
| 5148 | + /* Hack to force display of still pictures */ |
| 5149 | + bool b_first_frame; |
| 5150 | + |
| 5151 | + |
| 5152 | + /* */ |
| 5153 | + bool palette_sent; |
| 5154 | + |
| 5155 | + /* VA API */ |
| 5156 | +// vlc_va_t *p_va; |
| 5157 | + enum PixelFormat pix_fmt; |
| 5158 | + int profile; |
| 5159 | + int level; |
| 5160 | + |
| 5161 | + vlc_sem_t sem_mt; |
| 5162 | + |
| 5163 | + // Rpi vars |
| 5164 | + cma_buf_pool_t * cma_pool; |
| 5165 | + bool pool_alloc_1; |
| 5166 | + vcsm_init_type_t vcsm_init_type; |
| 5167 | + int cma_in_flight_max; |
| 5168 | + // Debug |
| 5169 | + decoder_t * p_dec; |
| 5170 | +}; |
| 5171 | + |
| 5172 | + |
| 5173 | +static vlc_fourcc_t |
| 5174 | +ZcFindVlcChroma(const int i_ffmpeg_chroma) |
| 5175 | +{ |
| 5176 | + switch (i_ffmpeg_chroma) |
| 5177 | + { |
| 5178 | + // This is all we claim to deal with |
| 5179 | + // In theory RGB should be doable within our current framework |
| 5180 | + case AV_PIX_FMT_YUV420P: |
| 5181 | + return VLC_CODEC_MMAL_ZC_I420; |
| 5182 | + case AV_PIX_FMT_SAND128: |
| 5183 | + case AV_PIX_FMT_RPI4_8: |
| 5184 | + return VLC_CODEC_MMAL_ZC_SAND8; |
| 5185 | + case AV_PIX_FMT_SAND64_10: |
| 5186 | + return VLC_CODEC_MMAL_ZC_SAND10; |
| 5187 | + case AV_PIX_FMT_RPI4_10: |
| 5188 | + return VLC_CODEC_MMAL_ZC_SAND30; |
| 5189 | + default: |
| 5190 | + break; |
| 5191 | + } |
| 5192 | + return 0; |
| 5193 | +} |
| 5194 | + |
| 5195 | +// Pix Fmt conv for MMal |
| 5196 | +// video_fromat from ffmpeg pic_fmt |
| 5197 | +static int |
| 5198 | +ZcGetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma ) |
| 5199 | +{ |
| 5200 | + fmt->i_rmask = 0; |
| 5201 | + fmt->i_gmask = 0; |
| 5202 | + fmt->i_bmask = 0; |
| 5203 | + fmt->i_chroma = ZcFindVlcChroma(i_ffmpeg_chroma); |
| 5204 | + |
| 5205 | + return fmt->i_chroma == 0 ? -1 : 0; |
| 5206 | +} |
| 5207 | + |
| 5208 | + |
| 5209 | +// Format chooser is way simpler than vlc |
| 5210 | +static enum PixelFormat |
| 5211 | +ZcGetFormat(AVCodecContext *p_context, const enum PixelFormat *pi_fmt) |
| 5212 | +{ |
| 5213 | + enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt); |
| 5214 | + for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++) |
| 5215 | + { |
| 5216 | + if (ZcFindVlcChroma(pi_fmt[i]) != 0) |
| 5217 | + return pi_fmt[i]; |
| 5218 | + } |
| 5219 | + return swfmt; |
| 5220 | +} |
| 5221 | + |
| 5222 | + |
| 5223 | +static void cma_avbuf_pool_free(void * v) |
| 5224 | +{ |
| 5225 | + cma_buf_unref(v); |
| 5226 | +} |
| 5227 | + |
| 5228 | +static unsigned int zc_buf_vcsm_handle(void * v) |
| 5229 | +{ |
| 5230 | + return cma_buf_vcsm_handle(v); |
| 5231 | +} |
| 5232 | + |
| 5233 | +static unsigned int zc_buf_vc_handle(void * v) |
| 5234 | +{ |
| 5235 | + return cma_buf_vc_handle(v); |
| 5236 | +} |
| 5237 | + |
| 5238 | +static void * zc_buf_map_arm(void * v) |
| 5239 | +{ |
| 5240 | + return cma_buf_addr(v); |
| 5241 | +} |
| 5242 | + |
| 5243 | +static unsigned int zc_buf_map_vc(void * v) |
| 5244 | +{ |
| 5245 | + return cma_buf_vc_addr(v); |
| 5246 | +} |
| 5247 | + |
| 5248 | + |
| 5249 | + |
| 5250 | +static const av_rpi_zc_buf_fn_tab_t zc_buf_fn_tab = { |
| 5251 | + .free = cma_avbuf_pool_free, |
| 5252 | + |
| 5253 | + .vcsm_handle = zc_buf_vcsm_handle, |
| 5254 | + .vc_handle = zc_buf_vc_handle, |
| 5255 | + .map_arm = zc_buf_map_arm, |
| 5256 | + .map_vc = zc_buf_map_vc |
| 5257 | +}; |
| 5258 | + |
| 5259 | + |
| 5260 | +static AVBufferRef * |
| 5261 | +zc_alloc_buf(void * v, size_t size, const AVRpiZcFrameGeometry * geo) |
| 5262 | +{ |
| 5263 | + decoder_t * const dec = v; |
| 5264 | + decoder_sys_t * const sys = dec->p_sys; |
| 5265 | + |
| 5266 | + VLC_UNUSED(geo); |
| 5267 | + |
| 5268 | + assert(sys != NULL); |
| 5269 | + |
| 5270 | + const unsigned int dec_pool_req = av_rpi_zc_get_decoder_pool_size(sys->p_context->opaque); |
| 5271 | + if (dec_pool_req != 0) |
| 5272 | + { |
| 5273 | + cma_buf_pool_resize(sys->cma_pool, dec_pool_req + sys->cma_in_flight_max, sys->cma_in_flight_max); |
| 5274 | + |
| 5275 | + if (!sys->pool_alloc_1) |
| 5276 | + { |
| 5277 | + sys->pool_alloc_1 = true; |
| 5278 | + msg_Dbg(dec, "Pool size: (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size); |
| 5279 | + if (cma_buf_pool_fill(sys->cma_pool, size) != 0) |
| 5280 | + msg_Warn(dec, "Failed to preallocate decoder pool (%d+%d) * %zd", dec_pool_req, sys->cma_in_flight_max, size); |
| 5281 | + } |
| 5282 | + } |
| 5283 | + |
| 5284 | + void * const cmabuf = cma_buf_pool_alloc_buf(sys->cma_pool, size); |
| 5285 | + |
| 5286 | + if (cmabuf == NULL) |
| 5287 | + { |
| 5288 | + msg_Err(dec, "CMA buf pool alloc buf failed"); |
| 5289 | + return NULL; |
| 5290 | + } |
| 5291 | + |
| 5292 | + AVBufferRef *const avbuf = av_rpi_zc_buf(cma_buf_size(cmabuf), 0, cmabuf, &zc_buf_fn_tab); |
| 5293 | + |
| 5294 | + if (avbuf == NULL) |
| 5295 | + { |
| 5296 | + msg_Err(dec, "av_rpi_zc_buf failed"); |
| 5297 | + cma_buf_unref(cmabuf); |
| 5298 | + return NULL; |
| 5299 | + } |
| 5300 | + |
| 5301 | + return avbuf; |
| 5302 | +} |
| 5303 | + |
| 5304 | +static void |
| 5305 | +zc_free_pool(void * v) |
| 5306 | +{ |
| 5307 | + decoder_t * const dec = v; |
| 5308 | + cma_buf_pool_delete(dec->p_sys->cma_pool); |
| 5309 | +} |
| 5310 | + |
| 5311 | + |
| 5312 | +static const uint8_t shift_01[] = {0,1,1,1}; |
| 5313 | +static const uint8_t pb_1[] = {1,1,1,1}; |
| 5314 | +static const uint8_t pb_12[] = {1,2,2,2}; |
| 5315 | +static const uint8_t pb_24[] = {2,4,4,4}; |
| 5316 | +static const uint8_t pb_4[] = {4,4,4,4}; |
| 5317 | + |
| 5318 | +static int set_pic_from_frame(picture_t * const pic, const AVFrame * const frame) |
| 5319 | +{ |
| 5320 | + const uint8_t * hs = shift_01; |
| 5321 | + const uint8_t * ws = shift_01; |
| 5322 | + const uint8_t * pb = pb_1; |
| 5323 | + |
| 5324 | + switch (pic->format.i_chroma) |
| 5325 | + { |
| 5326 | + case VLC_CODEC_MMAL_ZC_RGB32: |
| 5327 | + pic->i_planes = 1; |
| 5328 | + pb = pb_4; |
| 5329 | + break; |
| 5330 | + case VLC_CODEC_MMAL_ZC_I420: |
| 5331 | + pic->i_planes = 3; |
| 5332 | + break; |
| 5333 | + case VLC_CODEC_MMAL_ZC_SAND8: |
| 5334 | + pic->i_planes = 2; |
| 5335 | + pb = pb_12; |
| 5336 | + break; |
| 5337 | + case VLC_CODEC_MMAL_ZC_SAND10: |
| 5338 | + case VLC_CODEC_MMAL_ZC_SAND30: // Lies: SAND30 is "special" |
| 5339 | + pic->i_planes = 2; |
| 5340 | + pb = pb_24; |
| 5341 | + break; |
| 5342 | + default: |
| 5343 | + return VLC_EGENERIC; |
| 5344 | + } |
| 5345 | + |
| 5346 | + const cma_buf_t * const cb = cma_buf_pic_get(pic); |
| 5347 | + uint8_t * const data = cma_buf_addr(cb); |
| 5348 | + if (data == NULL) { |
| 5349 | + return VLC_ENOMEM; |
| 5350 | + } |
| 5351 | + |
| 5352 | + uint8_t * frame_end = frame->data[0] + cma_buf_size(cb); |
| 5353 | + for (int i = 0; i != pic->i_planes; ++i) { |
| 5354 | + // Calculate lines from gap between planes |
| 5355 | + // This will give us an accurate "height" for later use by MMAL |
| 5356 | + const int lines = ((i + 1 == pic->i_planes ? frame_end : frame->data[i + 1]) - |
| 5357 | + frame->data[i]) / frame->linesize[i]; |
| 5358 | + pic->p[i] = (plane_t){ |
| 5359 | + .p_pixels = data + (frame->data[i] - frame->data[0]), |
| 5360 | + .i_lines = lines, |
| 5361 | + .i_pitch = frame->linesize[i], |
| 5362 | + .i_pixel_pitch = pb[i], |
| 5363 | + .i_visible_lines = av_frame_cropped_height(frame) >> hs[i], |
| 5364 | + .i_visible_pitch = av_frame_cropped_width(frame) >> ws[i] |
| 5365 | + }; |
| 5366 | + } |
| 5367 | + return 0; |
| 5368 | +} |
| 5369 | + |
| 5370 | + |
| 5371 | +//============================================================================ |
| 5372 | +// |
| 5373 | +// Nicked from avcodec/fourcc.c |
| 5374 | +// |
| 5375 | +// * Really we should probably use that directly |
| 5376 | + |
| 5377 | +/* |
| 5378 | + * Video Codecs |
| 5379 | + */ |
| 5380 | + |
| 5381 | +struct vlc_avcodec_fourcc |
| 5382 | +{ |
| 5383 | + vlc_fourcc_t i_fourcc; |
| 5384 | + unsigned i_codec; |
| 5385 | +}; |
| 5386 | + |
| 5387 | + |
| 5388 | +static const struct vlc_avcodec_fourcc video_codecs[] = |
| 5389 | +{ |
| 5390 | + { VLC_CODEC_MP1V, AV_CODEC_ID_MPEG1VIDEO }, |
| 5391 | + { VLC_CODEC_MP2V, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */ |
| 5392 | + { VLC_CODEC_MPGV, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */ |
| 5393 | + /* AV_CODEC_ID_MPEG2VIDEO_XVMC */ |
| 5394 | + { VLC_CODEC_H261, AV_CODEC_ID_H261 }, |
| 5395 | + { VLC_CODEC_H263, AV_CODEC_ID_H263 }, |
| 5396 | + { VLC_CODEC_RV10, AV_CODEC_ID_RV10 }, |
| 5397 | + { VLC_CODEC_RV13, AV_CODEC_ID_RV10 }, |
| 5398 | + { VLC_CODEC_RV20, AV_CODEC_ID_RV20 }, |
| 5399 | + { VLC_CODEC_MJPG, AV_CODEC_ID_MJPEG }, |
| 5400 | + { VLC_CODEC_MJPGB, AV_CODEC_ID_MJPEGB }, |
| 5401 | + { VLC_CODEC_LJPG, AV_CODEC_ID_LJPEG }, |
| 5402 | + { VLC_CODEC_SP5X, AV_CODEC_ID_SP5X }, |
| 5403 | + { VLC_CODEC_JPEGLS, AV_CODEC_ID_JPEGLS }, |
| 5404 | + { VLC_CODEC_MP4V, AV_CODEC_ID_MPEG4 }, |
| 5405 | + /* AV_CODEC_ID_RAWVIDEO */ |
| 5406 | + { VLC_CODEC_DIV1, AV_CODEC_ID_MSMPEG4V1 }, |
| 5407 | + { VLC_CODEC_DIV2, AV_CODEC_ID_MSMPEG4V2 }, |
| 5408 | + { VLC_CODEC_DIV3, AV_CODEC_ID_MSMPEG4V3 }, |
| 5409 | + { VLC_CODEC_WMV1, AV_CODEC_ID_WMV1 }, |
| 5410 | + { VLC_CODEC_WMV2, AV_CODEC_ID_WMV2 }, |
| 5411 | + { VLC_CODEC_H263P, AV_CODEC_ID_H263P }, |
| 5412 | + { VLC_CODEC_H263I, AV_CODEC_ID_H263I }, |
| 5413 | + { VLC_CODEC_FLV1, AV_CODEC_ID_FLV1 }, |
| 5414 | + { VLC_CODEC_SVQ1, AV_CODEC_ID_SVQ1 }, |
| 5415 | + { VLC_CODEC_SVQ3, AV_CODEC_ID_SVQ3 }, |
| 5416 | + { VLC_CODEC_DV, AV_CODEC_ID_DVVIDEO }, |
| 5417 | + { VLC_CODEC_HUFFYUV, AV_CODEC_ID_HUFFYUV }, |
| 5418 | + { VLC_CODEC_CYUV, AV_CODEC_ID_CYUV }, |
| 5419 | + { VLC_CODEC_H264, AV_CODEC_ID_H264 }, |
| 5420 | + { VLC_CODEC_INDEO3, AV_CODEC_ID_INDEO3 }, |
| 5421 | + { VLC_CODEC_VP3, AV_CODEC_ID_VP3 }, |
| 5422 | + { VLC_CODEC_THEORA, AV_CODEC_ID_THEORA }, |
| 5423 | +#if ( !defined( WORDS_BIGENDIAN ) ) |
| 5424 | + /* Asus Video (Another thing that doesn't work on PPC) */ |
| 5425 | + { VLC_CODEC_ASV1, AV_CODEC_ID_ASV1 }, |
| 5426 | + { VLC_CODEC_ASV2, AV_CODEC_ID_ASV2 }, |
| 5427 | +#endif |
| 5428 | + { VLC_CODEC_FFV1, AV_CODEC_ID_FFV1 }, |
| 5429 | + { VLC_CODEC_4XM, AV_CODEC_ID_4XM }, |
| 5430 | + { VLC_CODEC_VCR1, AV_CODEC_ID_VCR1 }, |
| 5431 | + { VLC_CODEC_CLJR, AV_CODEC_ID_CLJR }, |
| 5432 | + { VLC_CODEC_MDEC, AV_CODEC_ID_MDEC }, |
| 5433 | + { VLC_CODEC_ROQ, AV_CODEC_ID_ROQ }, |
| 5434 | + { VLC_CODEC_INTERPLAY, AV_CODEC_ID_INTERPLAY_VIDEO }, |
| 5435 | + { VLC_CODEC_XAN_WC3, AV_CODEC_ID_XAN_WC3 }, |
| 5436 | + { VLC_CODEC_XAN_WC4, AV_CODEC_ID_XAN_WC4 }, |
| 5437 | + { VLC_CODEC_RPZA, AV_CODEC_ID_RPZA }, |
| 5438 | + { VLC_CODEC_CINEPAK, AV_CODEC_ID_CINEPAK }, |
| 5439 | + { VLC_CODEC_WS_VQA, AV_CODEC_ID_WS_VQA }, |
| 5440 | + { VLC_CODEC_MSRLE, AV_CODEC_ID_MSRLE }, |
| 5441 | + { VLC_CODEC_MSVIDEO1, AV_CODEC_ID_MSVIDEO1 }, |
| 5442 | + { VLC_CODEC_IDCIN, AV_CODEC_ID_IDCIN }, |
| 5443 | + { VLC_CODEC_8BPS, AV_CODEC_ID_8BPS }, |
| 5444 | + { VLC_CODEC_SMC, AV_CODEC_ID_SMC }, |
| 5445 | + { VLC_CODEC_FLIC, AV_CODEC_ID_FLIC }, |
| 5446 | + { VLC_CODEC_TRUEMOTION1, AV_CODEC_ID_TRUEMOTION1 }, |
| 5447 | + { VLC_CODEC_VMDVIDEO, AV_CODEC_ID_VMDVIDEO }, |
| 5448 | + { VLC_CODEC_LCL_MSZH, AV_CODEC_ID_MSZH }, |
| 5449 | + { VLC_CODEC_LCL_ZLIB, AV_CODEC_ID_ZLIB }, |
| 5450 | + { VLC_CODEC_QTRLE, AV_CODEC_ID_QTRLE }, |
| 5451 | + { VLC_CODEC_TSCC, AV_CODEC_ID_TSCC }, |
| 5452 | + { VLC_CODEC_ULTI, AV_CODEC_ID_ULTI }, |
| 5453 | + { VLC_CODEC_QDRAW, AV_CODEC_ID_QDRAW }, |
| 5454 | + { VLC_CODEC_VIXL, AV_CODEC_ID_VIXL }, |
| 5455 | + { VLC_CODEC_QPEG, AV_CODEC_ID_QPEG }, |
| 5456 | + { VLC_CODEC_PNG, AV_CODEC_ID_PNG }, |
| 5457 | + { VLC_CODEC_PPM, AV_CODEC_ID_PPM }, |
| 5458 | + /* AV_CODEC_ID_PBM */ |
| 5459 | + { VLC_CODEC_PGM, AV_CODEC_ID_PGM }, |
| 5460 | + { VLC_CODEC_PGMYUV, AV_CODEC_ID_PGMYUV }, |
| 5461 | + { VLC_CODEC_PAM, AV_CODEC_ID_PAM }, |
| 5462 | + { VLC_CODEC_FFVHUFF, AV_CODEC_ID_FFVHUFF }, |
| 5463 | + { VLC_CODEC_RV30, AV_CODEC_ID_RV30 }, |
| 5464 | + { VLC_CODEC_RV40, AV_CODEC_ID_RV40 }, |
| 5465 | + { VLC_CODEC_VC1, AV_CODEC_ID_VC1 }, |
| 5466 | + { VLC_CODEC_WMVA, AV_CODEC_ID_VC1 }, |
| 5467 | + { VLC_CODEC_WMV3, AV_CODEC_ID_WMV3 }, |
| 5468 | + { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3 }, |
| 5469 | + { VLC_CODEC_LOCO, AV_CODEC_ID_LOCO }, |
| 5470 | + { VLC_CODEC_WNV1, AV_CODEC_ID_WNV1 }, |
| 5471 | + { VLC_CODEC_AASC, AV_CODEC_ID_AASC }, |
| 5472 | + { VLC_CODEC_INDEO2, AV_CODEC_ID_INDEO2 }, |
| 5473 | + { VLC_CODEC_FRAPS, AV_CODEC_ID_FRAPS }, |
| 5474 | + { VLC_CODEC_TRUEMOTION2, AV_CODEC_ID_TRUEMOTION2 }, |
| 5475 | + { VLC_CODEC_BMP, AV_CODEC_ID_BMP }, |
| 5476 | + { VLC_CODEC_CSCD, AV_CODEC_ID_CSCD }, |
| 5477 | + { VLC_CODEC_MMVIDEO, AV_CODEC_ID_MMVIDEO }, |
| 5478 | + { VLC_CODEC_ZMBV, AV_CODEC_ID_ZMBV }, |
| 5479 | + { VLC_CODEC_AVS, AV_CODEC_ID_AVS }, |
| 5480 | + { VLC_CODEC_SMACKVIDEO, AV_CODEC_ID_SMACKVIDEO }, |
| 5481 | + { VLC_CODEC_NUV, AV_CODEC_ID_NUV }, |
| 5482 | + { VLC_CODEC_KMVC, AV_CODEC_ID_KMVC }, |
| 5483 | + { VLC_CODEC_FLASHSV, AV_CODEC_ID_FLASHSV }, |
| 5484 | + { VLC_CODEC_CAVS, AV_CODEC_ID_CAVS }, |
| 5485 | + { VLC_CODEC_JPEG2000, AV_CODEC_ID_JPEG2000 }, |
| 5486 | + { VLC_CODEC_VMNC, AV_CODEC_ID_VMNC }, |
| 5487 | + { VLC_CODEC_VP5, AV_CODEC_ID_VP5 }, |
| 5488 | + { VLC_CODEC_VP6, AV_CODEC_ID_VP6 }, |
| 5489 | + { VLC_CODEC_VP6F, AV_CODEC_ID_VP6F }, |
| 5490 | + { VLC_CODEC_TARGA, AV_CODEC_ID_TARGA }, |
| 5491 | + { VLC_CODEC_DSICINVIDEO, AV_CODEC_ID_DSICINVIDEO }, |
| 5492 | + { VLC_CODEC_TIERTEXSEQVIDEO, AV_CODEC_ID_TIERTEXSEQVIDEO }, |
| 5493 | + { VLC_CODEC_TIFF, AV_CODEC_ID_TIFF }, |
| 5494 | + { VLC_CODEC_GIF, AV_CODEC_ID_GIF }, |
| 5495 | + { VLC_CODEC_DXA, AV_CODEC_ID_DXA }, |
| 5496 | + { VLC_CODEC_DNXHD, AV_CODEC_ID_DNXHD }, |
| 5497 | + { VLC_CODEC_THP, AV_CODEC_ID_THP }, |
| 5498 | + { VLC_CODEC_SGI, AV_CODEC_ID_SGI }, |
| 5499 | + { VLC_CODEC_C93, AV_CODEC_ID_C93 }, |
| 5500 | + { VLC_CODEC_BETHSOFTVID, AV_CODEC_ID_BETHSOFTVID }, |
| 5501 | + /* AV_CODEC_ID_PTX */ |
| 5502 | + { VLC_CODEC_TXD, AV_CODEC_ID_TXD }, |
| 5503 | + { VLC_CODEC_VP6A, AV_CODEC_ID_VP6A }, |
| 5504 | + { VLC_CODEC_AMV, AV_CODEC_ID_AMV }, |
| 5505 | + { VLC_CODEC_VB, AV_CODEC_ID_VB }, |
| 5506 | + { VLC_CODEC_PCX, AV_CODEC_ID_PCX }, |
| 5507 | + /* AV_CODEC_ID_SUNRAST */ |
| 5508 | + { VLC_CODEC_INDEO4, AV_CODEC_ID_INDEO4 }, |
| 5509 | + { VLC_CODEC_INDEO5, AV_CODEC_ID_INDEO5 }, |
| 5510 | + { VLC_CODEC_MIMIC, AV_CODEC_ID_MIMIC }, |
| 5511 | + { VLC_CODEC_RL2, AV_CODEC_ID_RL2 }, |
| 5512 | + { VLC_CODEC_ESCAPE124, AV_CODEC_ID_ESCAPE124 }, |
| 5513 | + { VLC_CODEC_DIRAC, AV_CODEC_ID_DIRAC }, |
| 5514 | + { VLC_CODEC_BFI, AV_CODEC_ID_BFI }, |
| 5515 | + { VLC_CODEC_CMV, AV_CODEC_ID_CMV }, |
| 5516 | + { VLC_CODEC_MOTIONPIXELS, AV_CODEC_ID_MOTIONPIXELS }, |
| 5517 | + { VLC_CODEC_TGV, AV_CODEC_ID_TGV }, |
| 5518 | + { VLC_CODEC_TGQ, AV_CODEC_ID_TGQ }, |
| 5519 | + { VLC_CODEC_TQI, AV_CODEC_ID_TQI }, |
| 5520 | + { VLC_CODEC_AURA, AV_CODEC_ID_AURA }, |
| 5521 | + /* AV_CODEC_ID_AURA2 */ |
| 5522 | + /* AV_CODEC_ID_V210X */ |
| 5523 | + { VLC_CODEC_TMV, AV_CODEC_ID_TMV }, |
| 5524 | + { VLC_CODEC_V210, AV_CODEC_ID_V210 }, |
| 5525 | +#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( 54, 50, 100 ) && LIBAVCODEC_VERSION_MICRO >= 100 |
| 5526 | + { VLC_CODEC_VUYA, AV_CODEC_ID_AYUV }, |
| 5527 | +#endif |
| 5528 | + /* AV_CODEC_ID_DPX */ |
| 5529 | + { VLC_CODEC_MAD, AV_CODEC_ID_MAD }, |
| 5530 | + { VLC_CODEC_FRWU, AV_CODEC_ID_FRWU }, |
| 5531 | + { VLC_CODEC_FLASHSV2, AV_CODEC_ID_FLASHSV2 }, |
| 5532 | + /* AV_CODEC_ID_CDGRAPHICS */ |
| 5533 | + /* AV_CODEC_ID_R210 */ |
| 5534 | + { VLC_CODEC_ANM, AV_CODEC_ID_ANM }, |
| 5535 | + { VLC_CODEC_BINKVIDEO, AV_CODEC_ID_BINKVIDEO }, |
| 5536 | + /* AV_CODEC_ID_IFF_ILBM */ |
| 5537 | + /* AV_CODEC_ID_IFF_BYTERUN1 */ |
| 5538 | + { VLC_CODEC_KGV1, AV_CODEC_ID_KGV1 }, |
| 5539 | + { VLC_CODEC_YOP, AV_CODEC_ID_YOP }, |
| 5540 | + { VLC_CODEC_VP8, AV_CODEC_ID_VP8 }, |
| 5541 | + /* AV_CODEC_ID_PICTOR */ |
| 5542 | + /* AV_CODEC_ID_ANSI */ |
| 5543 | + /* AV_CODEC_ID_A64_MULTI */ |
| 5544 | + /* AV_CODEC_ID_A64_MULTI5 */ |
| 5545 | + /* AV_CODEC_ID_R10K */ |
| 5546 | + { VLC_CODEC_MXPEG, AV_CODEC_ID_MXPEG }, |
| 5547 | + { VLC_CODEC_LAGARITH, AV_CODEC_ID_LAGARITH }, |
| 5548 | + { VLC_CODEC_PRORES, AV_CODEC_ID_PRORES }, |
| 5549 | + { VLC_CODEC_JV, AV_CODEC_ID_JV }, |
| 5550 | + { VLC_CODEC_DFA, AV_CODEC_ID_DFA }, |
| 5551 | + { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3IMAGE }, |
| 5552 | + { VLC_CODEC_WMVP2, AV_CODEC_ID_VC1IMAGE }, |
| 5553 | + { VLC_CODEC_UTVIDEO, AV_CODEC_ID_UTVIDEO }, |
| 5554 | + { VLC_CODEC_BMVVIDEO, AV_CODEC_ID_BMV_VIDEO }, |
| 5555 | + { VLC_CODEC_VBLE, AV_CODEC_ID_VBLE }, |
| 5556 | + { VLC_CODEC_DXTORY, AV_CODEC_ID_DXTORY }, |
| 5557 | + /* AV_CODEC_ID_V410 */ |
| 5558 | + /* AV_CODEC_ID_XWD */ |
| 5559 | + { VLC_CODEC_CDXL, AV_CODEC_ID_CDXL }, |
| 5560 | + /* AV_CODEC_ID_XBM */ |
| 5561 | + /* AV_CODEC_ID_ZEROCODEC */ |
| 5562 | + { VLC_CODEC_MSS1, AV_CODEC_ID_MSS1 }, |
| 5563 | + { VLC_CODEC_MSA1, AV_CODEC_ID_MSA1 }, |
| 5564 | + { VLC_CODEC_TSC2, AV_CODEC_ID_TSCC2 }, |
| 5565 | + { VLC_CODEC_MTS2, AV_CODEC_ID_MTS2 }, |
| 5566 | + { VLC_CODEC_CLLC, AV_CODEC_ID_CLLC }, |
| 5567 | + { VLC_CODEC_MSS2, AV_CODEC_ID_MSS2 }, |
| 5568 | + { VLC_CODEC_VP9, AV_CODEC_ID_VP9 }, |
| 5569 | +#if LIBAVCODEC_VERSION_CHECK( 57, 26, 0, 83, 101 ) |
| 5570 | + { VLC_CODEC_AV1, AV_CODEC_ID_AV1 }, |
| 5571 | +#endif |
| 5572 | + { VLC_CODEC_ICOD, AV_CODEC_ID_AIC }, |
| 5573 | + /* AV_CODEC_ID_ESCAPE130 */ |
| 5574 | + { VLC_CODEC_G2M4, AV_CODEC_ID_G2M }, |
| 5575 | + { VLC_CODEC_G2M2, AV_CODEC_ID_G2M }, |
| 5576 | + { VLC_CODEC_G2M3, AV_CODEC_ID_G2M }, |
| 5577 | + /* AV_CODEC_ID_WEBP */ |
| 5578 | + { VLC_CODEC_HNM4_VIDEO, AV_CODEC_ID_HNM4_VIDEO }, |
| 5579 | + { VLC_CODEC_HEVC, AV_CODEC_ID_HEVC }, |
| 5580 | + |
| 5581 | + { VLC_CODEC_FIC , AV_CODEC_ID_FIC }, |
| 5582 | + /* AV_CODEC_ID_ALIAS_PIX */ |
| 5583 | + /* AV_CODEC_ID_BRENDER_PIX */ |
| 5584 | + /* AV_CODEC_ID_PAF_VIDEO */ |
| 5585 | + /* AV_CODEC_ID_EXR */ |
| 5586 | + |
| 5587 | + { VLC_CODEC_VP7 , AV_CODEC_ID_VP7 }, |
| 5588 | + /* AV_CODEC_ID_SANM */ |
| 5589 | + /* AV_CODEC_ID_SGIRLE */ |
| 5590 | + /* AV_CODEC_ID_MVC1 */ |
| 5591 | + /* AV_CODEC_ID_MVC2 */ |
| 5592 | + { VLC_CODEC_HQX, AV_CODEC_ID_HQX }, |
| 5593 | + |
| 5594 | + { VLC_CODEC_TDSC, AV_CODEC_ID_TDSC }, |
| 5595 | + |
| 5596 | + { VLC_CODEC_HQ_HQA, AV_CODEC_ID_HQ_HQA }, |
| 5597 | + |
| 5598 | + { VLC_CODEC_HAP, AV_CODEC_ID_HAP }, |
| 5599 | + /* AV_CODEC_ID_DDS */ |
| 5600 | + |
| 5601 | + { VLC_CODEC_DXV, AV_CODEC_ID_DXV }, |
| 5602 | + |
| 5603 | + /* ffmpeg only: AV_CODEC_ID_BRENDER_PIX */ |
| 5604 | + /* ffmpeg only: AV_CODEC_ID_Y41P */ |
| 5605 | + /* ffmpeg only: AV_CODEC_ID_EXR */ |
| 5606 | + /* ffmpeg only: AV_CODEC_ID_AVRP */ |
| 5607 | + /* ffmpeg only: AV_CODEC_ID_012V */ |
| 5608 | + /* ffmpeg only: AV_CODEC_ID_AVUI */ |
| 5609 | + /* ffmpeg only: AV_CODEC_ID_TARGA_Y216 */ |
| 5610 | + /* ffmpeg only: AV_CODEC_ID_V308 */ |
| 5611 | + /* ffmpeg only: AV_CODEC_ID_V408 */ |
| 5612 | + /* ffmpeg only: AV_CODEC_ID_YUV4 */ |
| 5613 | + /* ffmpeg only: AV_CODEC_ID_SANM */ |
| 5614 | + /* ffmpeg only: AV_CODEC_ID_PAF_VIDEO */ |
| 5615 | + /* ffmpeg only: AV_CODEC_ID_AVRN */ |
| 5616 | + /* ffmpeg only: AV_CODEC_ID_CPIA */ |
| 5617 | + /* ffmpeg only: AV_CODEC_ID_XFACE */ |
| 5618 | + /* ffmpeg only: AV_CODEC_ID_SGIRLE */ |
| 5619 | + /* ffmpeg only: AV_CODEC_ID_MVC1 */ |
| 5620 | + /* ffmpeg only: AV_CODEC_ID_MVC2 */ |
| 5621 | + /* ffmpeg only: AV_CODEC_ID_SNOW */ |
| 5622 | + /* ffmpeg only: AV_CODEC_ID_SMVJPEG */ |
| 5623 | + |
| 5624 | +#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 24, 102 ) |
| 5625 | + { VLC_CODEC_CINEFORM, AV_CODEC_ID_CFHD }, |
| 5626 | +#endif |
| 5627 | + |
| 5628 | +#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 70, 100 ) |
| 5629 | + { VLC_CODEC_PIXLET, AV_CODEC_ID_PIXLET }, |
| 5630 | +#endif |
| 5631 | + |
| 5632 | +#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 101 ) |
| 5633 | + { VLC_CODEC_SPEEDHQ, AV_CODEC_ID_SPEEDHQ }, |
| 5634 | +#endif |
| 5635 | + |
| 5636 | +#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 79, 100 ) |
| 5637 | + { VLC_CODEC_FMVC, AV_CODEC_ID_FMVC }, |
| 5638 | +#endif |
| 5639 | +}; |
| 5640 | + |
| 5641 | +// *** Really we should probably use GetFfmpegCodec with a pre-kludge for the bits we care about |
| 5642 | +static bool |
| 5643 | +ZcGetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc, |
| 5644 | + unsigned *pi_ffmpeg_codec, const char **ppsz_name ) |
| 5645 | +{ |
| 5646 | + const struct vlc_avcodec_fourcc *base; |
| 5647 | + size_t count; |
| 5648 | + |
| 5649 | + base = video_codecs; |
| 5650 | + count = ARRAY_SIZE(video_codecs); |
| 5651 | + i_fourcc = vlc_fourcc_GetCodec( cat, i_fourcc ); |
| 5652 | + |
| 5653 | + for( size_t i = 0; i < count; i++ ) |
| 5654 | + { |
| 5655 | + if( base[i].i_fourcc == i_fourcc ) |
| 5656 | + { |
| 5657 | + if( pi_ffmpeg_codec != NULL ) |
| 5658 | + *pi_ffmpeg_codec = base[i].i_codec; |
| 5659 | + if( ppsz_name ) |
| 5660 | + *ppsz_name = vlc_fourcc_GetDescription( cat, i_fourcc ); |
| 5661 | + return true; |
| 5662 | + } |
| 5663 | + } |
| 5664 | + return false; |
| 5665 | +} |
| 5666 | + |
| 5667 | + |
| 5668 | + |
| 5669 | +//============================================================================ |
| 5670 | +// Derived from codec/avcodec/avcodec.c |
| 5671 | + |
| 5672 | +static AVCodecContext * |
| 5673 | +ZcFfmpeg_AllocContext( decoder_t *p_dec, |
| 5674 | + const AVCodec **restrict codecp ) |
| 5675 | +{ |
| 5676 | + unsigned i_codec_id; |
| 5677 | + const char *psz_namecodec; |
| 5678 | + const AVCodec *p_codec = NULL; |
| 5679 | + |
| 5680 | + /* *** determine codec type *** */ |
| 5681 | + if( !ZcGetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec, |
| 5682 | + &i_codec_id, &psz_namecodec ) ) |
| 5683 | + return NULL; |
| 5684 | + |
| 5685 | + msg_Dbg( p_dec, "using %s %s", AVPROVIDER(LIBAVCODEC), LIBAVCODEC_IDENT ); |
| 5686 | + |
| 5687 | + /* Initialization must be done before avcodec_find_decoder() */ |
| 5688 | + vlc_init_avcodec(VLC_OBJECT(p_dec)); |
| 5689 | + |
| 5690 | + /* *** ask ffmpeg for a decoder *** */ |
| 5691 | + char *psz_decoder = var_InheritString( p_dec, "avcodec-codec" ); |
| 5692 | + if( psz_decoder != NULL ) |
| 5693 | + { |
| 5694 | + p_codec = avcodec_find_decoder_by_name( psz_decoder ); |
| 5695 | + if( !p_codec ) |
| 5696 | + msg_Err( p_dec, "Decoder `%s' not found", psz_decoder ); |
| 5697 | + else if( p_codec->id != i_codec_id ) |
| 5698 | + { |
| 5699 | + msg_Err( p_dec, "Decoder `%s' can't handle %4.4s", |
| 5700 | + psz_decoder, (char*)&p_dec->fmt_in.i_codec ); |
| 5701 | + p_codec = NULL; |
| 5702 | + } |
| 5703 | + free( psz_decoder ); |
| 5704 | + } |
| 5705 | + if( !p_codec ) |
| 5706 | +// p_codec = avcodec_find_decoder( i_codec_id ); |
| 5707 | + { |
| 5708 | + if( p_dec->fmt_in.i_codec != VLC_CODEC_HEVC ) |
| 5709 | + p_codec = avcodec_find_decoder(i_codec_id); |
| 5710 | + else |
| 5711 | + { |
| 5712 | + psz_namecodec = rpi_is_model_pi4() ? "hevc" : "hevc_rpi"; |
| 5713 | + msg_Info(p_dec, "Looking for HEVC decoder '%s'", psz_namecodec); |
| 5714 | + p_codec = avcodec_find_decoder_by_name(psz_namecodec); |
| 5715 | + } |
| 5716 | + } |
| 5717 | + |
| 5718 | + if( !p_codec ) |
| 5719 | + { |
| 5720 | + msg_Dbg( p_dec, "codec not found (%s)", psz_namecodec ); |
| 5721 | + return NULL; |
| 5722 | + } |
| 5723 | + |
| 5724 | + *codecp = p_codec; |
| 5725 | + |
| 5726 | + /* *** get a p_context *** */ |
| 5727 | + AVCodecContext *avctx = avcodec_alloc_context3(p_codec); |
| 5728 | + if( unlikely(avctx == NULL) ) |
| 5729 | + return NULL; |
| 5730 | + |
| 5731 | + avctx->debug = var_InheritInteger( p_dec, "avcodec-debug" ); |
| 5732 | + avctx->opaque = p_dec; |
| 5733 | + return avctx; |
| 5734 | +} |
| 5735 | + |
| 5736 | +/***************************************************************************** |
| 5737 | + * ffmpeg_OpenCodec: |
| 5738 | + *****************************************************************************/ |
| 5739 | + |
| 5740 | +static int |
| 5741 | +ZcFfmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx, |
| 5742 | + const AVCodec *codec ) |
| 5743 | +{ |
| 5744 | + char *psz_opts = var_InheritString( p_dec, "avcodec-options" ); |
| 5745 | + AVDictionary *options = NULL; |
| 5746 | + int ret; |
| 5747 | + |
| 5748 | + if (psz_opts) { |
| 5749 | + vlc_av_get_options(psz_opts, &options); |
| 5750 | + free(psz_opts); |
| 5751 | + } |
| 5752 | + |
| 5753 | + if (av_rpi_zc_init2(ctx, p_dec, zc_alloc_buf, zc_free_pool) != 0) |
| 5754 | + { |
| 5755 | + msg_Err(p_dec, "Failed to init AV ZC"); |
| 5756 | + return VLC_EGENERIC; |
| 5757 | + } |
| 5758 | + |
| 5759 | + vlc_avcodec_lock(); |
| 5760 | + ret = avcodec_open2( ctx, codec, options ? &options : NULL ); |
| 5761 | + vlc_avcodec_unlock(); |
| 5762 | + |
| 5763 | + AVDictionaryEntry *t = NULL; |
| 5764 | + while ((t = av_dict_get(options, "", t, AV_DICT_IGNORE_SUFFIX))) { |
| 5765 | + msg_Err( p_dec, "Unknown option \"%s\"", t->key ); |
| 5766 | + } |
| 5767 | + av_dict_free(&options); |
| 5768 | + |
| 5769 | + if( ret < 0 ) |
| 5770 | + { |
| 5771 | + msg_Err( p_dec, "cannot start codec (%s)", codec->name ); |
| 5772 | + return VLC_EGENERIC; |
| 5773 | + } |
| 5774 | + |
| 5775 | + msg_Dbg( p_dec, "codec (%s) started", codec->name ); |
| 5776 | + return VLC_SUCCESS; |
| 5777 | +} |
| 5778 | + |
| 5779 | +//============================================================================ |
| 5780 | +// Derived from 3.0.7.1 codec/avcodec/video.c |
| 5781 | + |
| 5782 | +static inline void wait_mt(decoder_sys_t *sys) |
| 5783 | +{ |
| 5784 | +#if 1 |
| 5785 | + // As we only ever update the output in our main thread this lock is |
| 5786 | + // redundant |
| 5787 | + VLC_UNUSED(sys); |
| 5788 | +#else |
| 5789 | + vlc_sem_wait(&sys->sem_mt); |
| 5790 | +#endif |
| 5791 | +} |
| 5792 | + |
| 5793 | +static inline void post_mt(decoder_sys_t *sys) |
| 5794 | +{ |
| 5795 | +#if 1 |
| 5796 | + // As we only ever update the output in our main thread this lock is |
| 5797 | + // redundant |
| 5798 | + VLC_UNUSED(sys); |
| 5799 | +#else |
| 5800 | + vlc_sem_post(&sys->sem_mt); |
| 5801 | +#endif |
| 5802 | +} |
| 5803 | + |
| 5804 | +/***************************************************************************** |
| 5805 | + * Local prototypes |
| 5806 | + *****************************************************************************/ |
| 5807 | +static void ffmpeg_InitCodec ( decoder_t * ); |
| 5808 | +static int DecodeVideo( decoder_t *, block_t * ); |
| 5809 | +static void Flush( decoder_t * ); |
| 5810 | + |
| 5811 | +static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc ) |
| 5812 | +{ |
| 5813 | + uint8_t *p = (uint8_t*)&fcc; |
| 5814 | + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); |
| 5815 | +} |
| 5816 | + |
| 5817 | +/***************************************************************************** |
| 5818 | + * Local Functions |
| 5819 | + *****************************************************************************/ |
| 5820 | + |
| 5821 | +/** |
| 5822 | + * Sets the decoder output format. |
| 5823 | + */ |
| 5824 | +static int lavc_GetVideoFormat(decoder_t *dec, video_format_t *restrict fmt, |
| 5825 | + AVCodecContext *ctx, enum AVPixelFormat pix_fmt, |
| 5826 | + enum AVPixelFormat sw_pix_fmt) |
| 5827 | +{ |
| 5828 | + int width = ctx->coded_width; |
| 5829 | + int height = ctx->coded_height; |
| 5830 | + |
| 5831 | + video_format_Init(fmt, 0); |
| 5832 | + |
| 5833 | +#if 1 |
| 5834 | + VLC_UNUSED(sw_pix_fmt); |
| 5835 | + if ((fmt->i_chroma = ZcFindVlcChroma(pix_fmt)) == 0) |
| 5836 | + return -1; |
| 5837 | +#else |
| 5838 | + if (pix_fmt == sw_pix_fmt) |
| 5839 | + { /* software decoding */ |
| 5840 | + int aligns[AV_NUM_DATA_POINTERS]; |
| 5841 | + |
| 5842 | + if (GetVlcChroma(fmt, pix_fmt)) |
| 5843 | + return -1; |
| 5844 | + |
| 5845 | + /* The libavcodec palette can only be fetched when the first output |
| 5846 | + * frame is decoded. Assume that the current chroma is RGB32 while we |
| 5847 | + * are waiting for a valid palette. Indeed, fmt_out.video.p_palette |
| 5848 | + * doesn't trigger a new vout request, but a new chroma yes. */ |
| 5849 | + if (pix_fmt == AV_PIX_FMT_PAL8 && !dec->fmt_out.video.p_palette) |
| 5850 | + fmt->i_chroma = VLC_CODEC_RGB32; |
| 5851 | + |
| 5852 | + avcodec_align_dimensions2(ctx, &width, &height, aligns); |
| 5853 | + } |
| 5854 | + else /* hardware decoding */ |
| 5855 | + fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt); |
| 5856 | +#endif |
| 5857 | + |
| 5858 | + if( width == 0 || height == 0 || width > 8192 || height > 8192 || |
| 5859 | + width < ctx->width || height < ctx->height ) |
| 5860 | + { |
| 5861 | + msg_Err(dec, "Invalid frame size %dx%d vsz %dx%d", |
| 5862 | + width, height, ctx->width, ctx->height ); |
| 5863 | + return -1; /* invalid display size */ |
| 5864 | + } |
| 5865 | + |
| 5866 | + fmt->i_width = width; |
| 5867 | + fmt->i_height = height; |
| 5868 | + fmt->i_visible_width = ctx->width; |
| 5869 | + fmt->i_visible_height = ctx->height; |
| 5870 | + |
| 5871 | + /* If an aspect-ratio was specified in the input format then force it */ |
| 5872 | + if (dec->fmt_in.video.i_sar_num > 0 && dec->fmt_in.video.i_sar_den > 0) |
| 5873 | + { |
| 5874 | + fmt->i_sar_num = dec->fmt_in.video.i_sar_num; |
| 5875 | + fmt->i_sar_den = dec->fmt_in.video.i_sar_den; |
| 5876 | + } |
| 5877 | + else |
| 5878 | + { |
| 5879 | + fmt->i_sar_num = ctx->sample_aspect_ratio.num; |
| 5880 | + fmt->i_sar_den = ctx->sample_aspect_ratio.den; |
| 5881 | + |
| 5882 | + if (fmt->i_sar_num == 0 || fmt->i_sar_den == 0) |
| 5883 | + fmt->i_sar_num = fmt->i_sar_den = 1; |
| 5884 | + } |
| 5885 | + |
| 5886 | + if (dec->fmt_in.video.i_frame_rate > 0 |
| 5887 | + && dec->fmt_in.video.i_frame_rate_base > 0) |
| 5888 | + { |
| 5889 | + fmt->i_frame_rate = dec->fmt_in.video.i_frame_rate; |
| 5890 | + fmt->i_frame_rate_base = dec->fmt_in.video.i_frame_rate_base; |
| 5891 | + } |
| 5892 | + else if (ctx->framerate.num > 0 && ctx->framerate.den > 0) |
| 5893 | + { |
| 5894 | + fmt->i_frame_rate = ctx->framerate.num; |
| 5895 | + fmt->i_frame_rate_base = ctx->framerate.den; |
| 5896 | +# if LIBAVCODEC_VERSION_MICRO < 100 |
| 5897 | + // for some reason libav don't thinkg framerate presents actually same thing as in ffmpeg |
| 5898 | + fmt->i_frame_rate_base *= __MAX(ctx->ticks_per_frame, 1); |
| 5899 | +# endif |
| 5900 | + } |
| 5901 | + else if (ctx->time_base.num > 0 && ctx->time_base.den > 0) |
| 5902 | + { |
| 5903 | + fmt->i_frame_rate = ctx->time_base.den; |
| 5904 | + fmt->i_frame_rate_base = ctx->time_base.num |
| 5905 | + * __MAX(ctx->ticks_per_frame, 1); |
| 5906 | + } |
| 5907 | + |
| 5908 | + /* FIXME we should only set the known values and let the core decide |
| 5909 | + * later of fallbacks, but we can't do that with a boolean */ |
| 5910 | + switch ( ctx->color_range ) |
| 5911 | + { |
| 5912 | + case AVCOL_RANGE_JPEG: |
| 5913 | + fmt->b_color_range_full = true; |
| 5914 | + break; |
| 5915 | + case AVCOL_RANGE_UNSPECIFIED: |
| 5916 | + fmt->b_color_range_full = !vlc_fourcc_IsYUV( fmt->i_chroma ); |
| 5917 | + break; |
| 5918 | + case AVCOL_RANGE_MPEG: |
| 5919 | + default: |
| 5920 | + fmt->b_color_range_full = false; |
| 5921 | + break; |
| 5922 | + } |
| 5923 | + |
| 5924 | + switch( ctx->colorspace ) |
| 5925 | + { |
| 5926 | + case AVCOL_SPC_BT709: |
| 5927 | + fmt->space = COLOR_SPACE_BT709; |
| 5928 | + break; |
| 5929 | + case AVCOL_SPC_SMPTE170M: |
| 5930 | + case AVCOL_SPC_BT470BG: |
| 5931 | + fmt->space = COLOR_SPACE_BT601; |
| 5932 | + break; |
| 5933 | + case AVCOL_SPC_BT2020_NCL: |
| 5934 | + case AVCOL_SPC_BT2020_CL: |
| 5935 | + fmt->space = COLOR_SPACE_BT2020; |
| 5936 | + break; |
| 5937 | + default: |
| 5938 | + break; |
| 5939 | + } |
| 5940 | + |
| 5941 | + switch( ctx->color_trc ) |
| 5942 | + { |
| 5943 | + case AVCOL_TRC_LINEAR: |
| 5944 | + fmt->transfer = TRANSFER_FUNC_LINEAR; |
| 5945 | + break; |
| 5946 | + case AVCOL_TRC_GAMMA22: |
| 5947 | + fmt->transfer = TRANSFER_FUNC_SRGB; |
| 5948 | + break; |
| 5949 | + case AVCOL_TRC_BT709: |
| 5950 | + fmt->transfer = TRANSFER_FUNC_BT709; |
| 5951 | + break; |
| 5952 | + case AVCOL_TRC_SMPTE170M: |
| 5953 | + case AVCOL_TRC_BT2020_10: |
| 5954 | + case AVCOL_TRC_BT2020_12: |
| 5955 | + fmt->transfer = TRANSFER_FUNC_BT2020; |
| 5956 | + break; |
| 5957 | +#if LIBAVUTIL_VERSION_CHECK( 55, 14, 0, 31, 100) |
| 5958 | + case AVCOL_TRC_ARIB_STD_B67: |
| 5959 | + fmt->transfer = TRANSFER_FUNC_ARIB_B67; |
| 5960 | + break; |
| 5961 | +#endif |
| 5962 | +#if LIBAVUTIL_VERSION_CHECK( 55, 17, 0, 37, 100) |
| 5963 | + case AVCOL_TRC_SMPTE2084: |
| 5964 | + fmt->transfer = TRANSFER_FUNC_SMPTE_ST2084; |
| 5965 | + break; |
| 5966 | + case AVCOL_TRC_SMPTE240M: |
| 5967 | + fmt->transfer = TRANSFER_FUNC_SMPTE_240; |
| 5968 | + break; |
| 5969 | + case AVCOL_TRC_GAMMA28: |
| 5970 | + fmt->transfer = TRANSFER_FUNC_BT470_BG; |
| 5971 | + break; |
| 5972 | +#endif |
| 5973 | + default: |
| 5974 | + break; |
| 5975 | + } |
| 5976 | + |
| 5977 | + switch( ctx->color_primaries ) |
| 5978 | + { |
| 5979 | + case AVCOL_PRI_BT709: |
| 5980 | + fmt->primaries = COLOR_PRIMARIES_BT709; |
| 5981 | + break; |
| 5982 | + case AVCOL_PRI_BT470BG: |
| 5983 | + fmt->primaries = COLOR_PRIMARIES_BT601_625; |
| 5984 | + break; |
| 5985 | + case AVCOL_PRI_SMPTE170M: |
| 5986 | + case AVCOL_PRI_SMPTE240M: |
| 5987 | + fmt->primaries = COLOR_PRIMARIES_BT601_525; |
| 5988 | + break; |
| 5989 | + case AVCOL_PRI_BT2020: |
| 5990 | + fmt->primaries = COLOR_PRIMARIES_BT2020; |
| 5991 | + break; |
| 5992 | + default: |
| 5993 | + break; |
| 5994 | + } |
| 5995 | + |
| 5996 | + switch( ctx->chroma_sample_location ) |
| 5997 | + { |
| 5998 | + case AVCHROMA_LOC_LEFT: |
| 5999 | + fmt->chroma_location = CHROMA_LOCATION_LEFT; |
| 6000 | + break; |
| 6001 | + case AVCHROMA_LOC_CENTER: |
| 6002 | + fmt->chroma_location = CHROMA_LOCATION_CENTER; |
| 6003 | + break; |
| 6004 | + case AVCHROMA_LOC_TOPLEFT: |
| 6005 | + fmt->chroma_location = CHROMA_LOCATION_TOP_LEFT; |
| 6006 | + break; |
| 6007 | + default: |
| 6008 | + break; |
| 6009 | + } |
| 6010 | + |
| 6011 | + return 0; |
| 6012 | +} |
| 6013 | + |
| 6014 | +static int lavc_UpdateVideoFormat(decoder_t *dec, AVCodecContext *ctx, |
| 6015 | + enum AVPixelFormat fmt, |
| 6016 | + enum AVPixelFormat swfmt) |
| 6017 | +{ |
| 6018 | + video_format_t fmt_out; |
| 6019 | + int val; |
| 6020 | +#if TRACE_ALL |
| 6021 | + msg_Dbg(dec, "<<< %s", __func__); |
| 6022 | +#endif |
| 6023 | + val = lavc_GetVideoFormat(dec, &fmt_out, ctx, fmt, swfmt); |
| 6024 | + if (val) |
| 6025 | + { |
| 6026 | + msg_Dbg(dec, "Failed to get format"); |
| 6027 | + return val; |
| 6028 | + } |
| 6029 | + |
| 6030 | + /* always have date in fields/ticks units */ |
| 6031 | + if(dec->p_sys->pts.i_divider_num) |
| 6032 | + date_Change(&dec->p_sys->pts, fmt_out.i_frame_rate * |
| 6033 | + __MAX(ctx->ticks_per_frame, 1), |
| 6034 | + fmt_out.i_frame_rate_base); |
| 6035 | + else |
| 6036 | + date_Init(&dec->p_sys->pts, fmt_out.i_frame_rate * |
| 6037 | + __MAX(ctx->ticks_per_frame, 1), |
| 6038 | + fmt_out.i_frame_rate_base); |
| 6039 | + |
| 6040 | + fmt_out.p_palette = dec-> fmt_out.video.p_palette; |
| 6041 | + dec->fmt_out.video.p_palette = NULL; |
| 6042 | + |
| 6043 | + es_format_Change(&dec->fmt_out, VIDEO_ES, fmt_out.i_chroma); |
| 6044 | + dec->fmt_out.video = fmt_out; |
| 6045 | + dec->fmt_out.video.orientation = dec->fmt_in.video.orientation; |
| 6046 | + dec->fmt_out.video.projection_mode = dec->fmt_in.video.projection_mode; |
| 6047 | + dec->fmt_out.video.multiview_mode = dec->fmt_in.video.multiview_mode; |
| 6048 | + dec->fmt_out.video.pose = dec->fmt_in.video.pose; |
| 6049 | + if ( dec->fmt_in.video.mastering.max_luminance ) |
| 6050 | + dec->fmt_out.video.mastering = dec->fmt_in.video.mastering; |
| 6051 | + dec->fmt_out.video.lighting = dec->fmt_in.video.lighting; |
| 6052 | + |
| 6053 | + val = decoder_UpdateVideoFormat(dec); |
| 6054 | +#if TRACE_ALL |
| 6055 | + msg_Dbg(dec, ">>> %s: rv=%d", __func__, val); |
| 6056 | +#endif |
| 6057 | + return val; |
| 6058 | +} |
| 6059 | + |
| 6060 | +static int OpenVideoCodec( decoder_t *p_dec ) |
| 6061 | +{ |
| 6062 | + decoder_sys_t *p_sys = p_dec->p_sys; |
| 6063 | + AVCodecContext *ctx = p_sys->p_context; |
| 6064 | + const AVCodec *codec = p_sys->p_codec; |
| 6065 | + int ret; |
| 6066 | + |
| 6067 | + if( ctx->extradata_size <= 0 ) |
| 6068 | + { |
| 6069 | + if( codec->id == AV_CODEC_ID_VC1 || |
| 6070 | + codec->id == AV_CODEC_ID_THEORA ) |
| 6071 | + { |
| 6072 | + msg_Warn( p_dec, "waiting for extra data for codec %s", |
| 6073 | + codec->name ); |
| 6074 | + return 1; |
| 6075 | + } |
| 6076 | + } |
| 6077 | + |
| 6078 | + ctx->width = p_dec->fmt_in.video.i_visible_width; |
| 6079 | + ctx->height = p_dec->fmt_in.video.i_visible_height; |
| 6080 | + |
| 6081 | + ctx->coded_width = p_dec->fmt_in.video.i_width; |
| 6082 | + ctx->coded_height = p_dec->fmt_in.video.i_height; |
| 6083 | + |
| 6084 | + ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel; |
| 6085 | + p_sys->pix_fmt = AV_PIX_FMT_NONE; |
| 6086 | + p_sys->profile = -1; |
| 6087 | + p_sys->level = -1; |
| 6088 | + cc_Init( &p_sys->cc ); |
| 6089 | + |
| 6090 | + set_video_color_settings( &p_dec->fmt_in.video, ctx ); |
| 6091 | + if( p_dec->fmt_in.video.i_frame_rate_base && |
| 6092 | + p_dec->fmt_in.video.i_frame_rate && |
| 6093 | + (double) p_dec->fmt_in.video.i_frame_rate / |
| 6094 | + p_dec->fmt_in.video.i_frame_rate_base < 6 ) |
| 6095 | + { |
| 6096 | + ctx->flags |= AV_CODEC_FLAG_LOW_DELAY; |
| 6097 | + } |
| 6098 | + |
| 6099 | + post_mt( p_sys ); |
| 6100 | + ret = ZcFfmpeg_OpenCodec( p_dec, ctx, codec ); |
| 6101 | + wait_mt( p_sys ); |
| 6102 | + if( ret < 0 ) |
| 6103 | + return ret; |
| 6104 | + |
| 6105 | + switch( ctx->active_thread_type ) |
| 6106 | + { |
| 6107 | + case FF_THREAD_FRAME: |
| 6108 | + msg_Dbg( p_dec, "using frame thread mode with %d threads", |
| 6109 | + ctx->thread_count ); |
| 6110 | + break; |
| 6111 | + case FF_THREAD_SLICE: |
| 6112 | + msg_Dbg( p_dec, "using slice thread mode with %d threads", |
| 6113 | + ctx->thread_count ); |
| 6114 | + break; |
| 6115 | + case 0: |
| 6116 | + if( ctx->thread_count > 1 ) |
| 6117 | + msg_Warn( p_dec, "failed to enable threaded decoding" ); |
| 6118 | + break; |
| 6119 | + default: |
| 6120 | + msg_Warn( p_dec, "using unknown thread mode with %d threads", |
| 6121 | + ctx->thread_count ); |
| 6122 | + break; |
| 6123 | + } |
| 6124 | + return 0; |
| 6125 | +} |
| 6126 | + |
| 6127 | +/***************************************************************************** |
| 6128 | + * InitVideo: initialize the video decoder |
| 6129 | + ***************************************************************************** |
| 6130 | + * the ffmpeg codec will be opened, some memory allocated. The vout is not yet |
| 6131 | + * opened (done after the first decoded frame). |
| 6132 | + *****************************************************************************/ |
| 6133 | +static int MmalAvcodecOpenDecoder( vlc_object_t *obj ) |
| 6134 | +{ |
| 6135 | + decoder_t *p_dec = (decoder_t *)obj; |
| 6136 | + const AVCodec *p_codec; |
| 6137 | + |
| 6138 | + int extra_buffers = var_InheritInteger(p_dec, MMAL_AVCODEC_BUFFERS); |
| 6139 | + |
| 6140 | + if (extra_buffers < 0) |
| 6141 | + { |
| 6142 | + extra_buffers = p_dec->fmt_in.video.i_height * p_dec->fmt_in.video.i_width >= 1920 * 1088 ? |
| 6143 | + BUFFERS_IN_FLIGHT_UHD : BUFFERS_IN_FLIGHT; |
| 6144 | + } |
| 6145 | + |
| 6146 | + if (extra_buffers <= 0) |
| 6147 | + { |
| 6148 | + msg_Dbg(p_dec, "%s: extra_buffers=%d - cannot use module", __func__, extra_buffers); |
| 6149 | + return VLC_EGENERIC; |
| 6150 | + } |
| 6151 | + |
| 6152 | + const vcsm_init_type_t vcsm_type = cma_vcsm_init(); |
| 6153 | + const int vcsm_size = |
| 6154 | + vcsm_type == VCSM_INIT_LEGACY ? hw_mmal_get_gpu_mem() : 512 << 20; |
| 6155 | + |
| 6156 | +#if 1 |
| 6157 | + { |
| 6158 | + char buf1[5], buf2[5], buf2a[5]; |
| 6159 | + char buf3[5], buf4[5]; |
| 6160 | + uint32_t in_fcc = 0; |
| 6161 | + msg_Dbg(p_dec, "%s: <<< (%s/%s)[%s] %dx%d -> (%s/%s) %dx%d [%s/%d] xb:%d", __func__, |
| 6162 | + str_fourcc(buf1, p_dec->fmt_in.i_codec), |
| 6163 | + str_fourcc(buf2, p_dec->fmt_in.video.i_chroma), |
| 6164 | + str_fourcc(buf2a, in_fcc), |
| 6165 | + p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height, |
| 6166 | + str_fourcc(buf3, p_dec->fmt_out.i_codec), |
| 6167 | + str_fourcc(buf4, p_dec->fmt_out.video.i_chroma), |
| 6168 | + p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height, |
| 6169 | + cma_vcsm_init_str(vcsm_type), vcsm_size, extra_buffers); |
| 6170 | + } |
| 6171 | +#endif |
| 6172 | + |
| 6173 | + if( vcsm_type == VCSM_INIT_NONE ) |
| 6174 | + return VLC_EGENERIC; |
| 6175 | +#if 1 |
| 6176 | + if( (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC && |
| 6177 | + (vcsm_type == VCSM_INIT_CMA || vcsm_size < (96 << 20))) || |
| 6178 | + (p_dec->fmt_in.i_codec == VLC_CODEC_HEVC && |
| 6179 | + vcsm_size < (128 << 20))) |
| 6180 | + { |
| 6181 | + cma_vcsm_exit(vcsm_type); |
| 6182 | + return VLC_EGENERIC; |
| 6183 | + } |
| 6184 | +#endif |
| 6185 | + |
| 6186 | + AVCodecContext *p_context = ZcFfmpeg_AllocContext( p_dec, &p_codec ); |
| 6187 | + if( p_context == NULL ) |
| 6188 | + { |
| 6189 | + cma_vcsm_exit(vcsm_type); |
| 6190 | + return VLC_EGENERIC; |
| 6191 | + } |
| 6192 | + |
| 6193 | + int i_val; |
| 6194 | + |
| 6195 | + /* Allocate the memory needed to store the decoder's structure */ |
| 6196 | + decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) ); |
| 6197 | + if( unlikely(p_sys == NULL) ) |
| 6198 | + { |
| 6199 | + avcodec_free_context( &p_context ); |
| 6200 | + cma_vcsm_exit(vcsm_type); |
| 6201 | + return VLC_ENOMEM; |
| 6202 | + } |
| 6203 | + |
| 6204 | + p_dec->p_sys = p_sys; |
| 6205 | + p_sys->p_context = p_context; |
| 6206 | + p_sys->p_codec = p_codec; |
| 6207 | + p_sys->p_dec = p_dec; |
| 6208 | +// p_sys->p_va = NULL; |
| 6209 | + p_sys->cma_in_flight_max = extra_buffers; |
| 6210 | + p_sys->vcsm_init_type = vcsm_type; |
| 6211 | + vlc_sem_init( &p_sys->sem_mt, 0 ); |
| 6212 | + |
| 6213 | + /* ***** Fill p_context with init values ***** */ |
| 6214 | + p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ? |
| 6215 | + p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec ); |
| 6216 | + |
| 6217 | + /* ***** Get configuration of ffmpeg plugin ***** */ |
| 6218 | + p_context->workaround_bugs = |
| 6219 | + var_InheritInteger( p_dec, "avcodec-workaround-bugs" ); |
| 6220 | + p_context->err_recognition = |
| 6221 | + var_InheritInteger( p_dec, "avcodec-error-resilience" ); |
| 6222 | + |
| 6223 | + if( var_CreateGetBool( p_dec, "grayscale" ) ) |
| 6224 | + p_context->flags |= AV_CODEC_FLAG_GRAY; |
| 6225 | + |
| 6226 | + /* ***** Output always the frames ***** */ |
| 6227 | + p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT; |
| 6228 | + |
| 6229 | + i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" ); |
| 6230 | + if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL; |
| 6231 | + else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY; |
| 6232 | + else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR; |
| 6233 | + else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF; |
| 6234 | + else p_context->skip_loop_filter = AVDISCARD_DEFAULT; |
| 6235 | + |
| 6236 | + if( var_CreateGetBool( p_dec, "avcodec-fast" ) ) |
| 6237 | + p_context->flags2 |= AV_CODEC_FLAG2_FAST; |
| 6238 | + |
| 6239 | + /* ***** libavcodec frame skipping ***** */ |
| 6240 | + p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" ); |
| 6241 | + p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" ); |
| 6242 | + |
| 6243 | + i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" ); |
| 6244 | + if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL; |
| 6245 | + else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY; |
| 6246 | + else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR; |
| 6247 | + else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF; |
| 6248 | + else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE; |
| 6249 | + else p_sys->i_skip_frame = AVDISCARD_DEFAULT; |
| 6250 | + p_context->skip_frame = p_sys->i_skip_frame; |
| 6251 | + |
| 6252 | + i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" ); |
| 6253 | + if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL; |
| 6254 | + else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY; |
| 6255 | + else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR; |
| 6256 | + else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF; |
| 6257 | + else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE; |
| 6258 | + else p_context->skip_idct = AVDISCARD_DEFAULT; |
| 6259 | + |
| 6260 | + /* ***** libavcodec direct rendering ***** */ |
| 6261 | + p_sys->b_direct_rendering = false; |
| 6262 | + atomic_init(&p_sys->b_dr_failure, false); |
| 6263 | + if( var_CreateGetBool( p_dec, "avcodec-dr" ) && |
| 6264 | + (p_codec->capabilities & AV_CODEC_CAP_DR1) && |
| 6265 | + /* No idea why ... but this fixes flickering on some TSCC streams */ |
| 6266 | + p_sys->p_codec->id != AV_CODEC_ID_TSCC && |
| 6267 | + p_sys->p_codec->id != AV_CODEC_ID_CSCD && |
| 6268 | + p_sys->p_codec->id != AV_CODEC_ID_CINEPAK ) |
| 6269 | + { |
| 6270 | + /* Some codecs set pix_fmt only after the 1st frame has been decoded, |
| 6271 | + * so we need to do another check in ffmpeg_GetFrameBuf() */ |
| 6272 | + p_sys->b_direct_rendering = true; |
| 6273 | + } |
| 6274 | + |
| 6275 | + p_context->get_format = ZcGetFormat; |
| 6276 | +#if 0 |
| 6277 | + p_context->get_format = ffmpeg_GetFormat; |
| 6278 | + /* Always use our get_buffer wrapper so we can calculate the |
| 6279 | + * PTS correctly */ |
| 6280 | + p_context->get_buffer2 = lavc_GetFrame; |
| 6281 | + p_context->opaque = p_dec; |
| 6282 | +#endif |
| 6283 | + |
| 6284 | + int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" ); |
| 6285 | + if( i_thread_count <= 0 ) |
| 6286 | +#if 1 |
| 6287 | + { |
| 6288 | + // Pick 5 threads for everything on Pi except for HEVC where the h/w |
| 6289 | + // really limits the useful size to 3 |
| 6290 | + i_thread_count = p_codec->id == AV_CODEC_ID_HEVC ? 3 : 5; |
| 6291 | + } |
| 6292 | +#else |
| 6293 | + { |
| 6294 | + i_thread_count = vlc_GetCPUCount(); |
| 6295 | + if( i_thread_count > 1 ) |
| 6296 | + i_thread_count++; |
| 6297 | + |
| 6298 | + //FIXME: take in count the decoding time |
| 6299 | +#if VLC_WINSTORE_APP |
| 6300 | + i_thread_count = __MIN( i_thread_count, 6 ); |
| 6301 | +#else |
| 6302 | + i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 ); |
| 6303 | +#endif |
| 6304 | + } |
| 6305 | + i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 ); |
| 6306 | +#endif |
| 6307 | + msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count ); |
| 6308 | + p_context->thread_count = i_thread_count; |
| 6309 | + p_context->thread_safe_callbacks = true; |
| 6310 | + |
| 6311 | + switch( p_codec->id ) |
| 6312 | + { |
| 6313 | + case AV_CODEC_ID_MPEG4: |
| 6314 | + case AV_CODEC_ID_H263: |
| 6315 | + p_context->thread_type = 0; |
| 6316 | + break; |
| 6317 | + case AV_CODEC_ID_MPEG1VIDEO: |
| 6318 | + case AV_CODEC_ID_MPEG2VIDEO: |
| 6319 | + p_context->thread_type &= ~FF_THREAD_SLICE; |
| 6320 | + /* fall through */ |
| 6321 | +# if (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(55, 1, 0)) |
| 6322 | + case AV_CODEC_ID_H264: |
| 6323 | + case AV_CODEC_ID_VC1: |
| 6324 | + case AV_CODEC_ID_WMV3: |
| 6325 | + p_context->thread_type &= ~FF_THREAD_FRAME; |
| 6326 | +# endif |
| 6327 | + default: |
| 6328 | + break; |
| 6329 | + } |
| 6330 | + |
| 6331 | + if( p_context->thread_type & FF_THREAD_FRAME ) |
| 6332 | + p_dec->i_extra_picture_buffers = 2 * p_context->thread_count; |
| 6333 | + |
| 6334 | + /* ***** misc init ***** */ |
| 6335 | + date_Init(&p_sys->pts, 1, 30001); |
| 6336 | + date_Set(&p_sys->pts, VLC_TS_INVALID); |
| 6337 | + p_sys->b_first_frame = true; |
| 6338 | + p_sys->i_late_frames = 0; |
| 6339 | + p_sys->b_from_preroll = false; |
| 6340 | + |
| 6341 | + /* Set output properties */ |
| 6342 | + if( ZcGetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS ) |
| 6343 | + { |
| 6344 | + /* we are doomed. but not really, because most codecs set their pix_fmt later on */ |
| 6345 | +// p_dec->fmt_out.i_codec = VLC_CODEC_I420; |
| 6346 | + p_dec->fmt_out.i_codec = VLC_CODEC_MMAL_ZC_I420; |
| 6347 | + } |
| 6348 | + p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma; |
| 6349 | + |
| 6350 | + p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation; |
| 6351 | + |
| 6352 | + if( p_dec->fmt_in.video.p_palette ) { |
| 6353 | + p_sys->palette_sent = false; |
| 6354 | + p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) ); |
| 6355 | + if( p_dec->fmt_out.video.p_palette ) |
| 6356 | + *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette; |
| 6357 | + } else |
| 6358 | + p_sys->palette_sent = true; |
| 6359 | + |
| 6360 | + 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) |
| 6361 | + { |
| 6362 | + msg_Err(p_dec, "CMA pool alloc failure"); |
| 6363 | + goto fail; |
| 6364 | + } |
| 6365 | + |
| 6366 | + /* ***** init this codec with special data ***** */ |
| 6367 | + ffmpeg_InitCodec( p_dec ); |
| 6368 | + |
| 6369 | + /* ***** Open the codec ***** */ |
| 6370 | + if( OpenVideoCodec( p_dec ) < 0 ) |
| 6371 | + { |
| 6372 | + vlc_sem_destroy( &p_sys->sem_mt ); |
| 6373 | + free( p_sys ); |
| 6374 | + avcodec_free_context( &p_context ); |
| 6375 | + return VLC_EGENERIC; |
| 6376 | + } |
| 6377 | + |
| 6378 | + p_dec->pf_decode = DecodeVideo; |
| 6379 | + p_dec->pf_flush = Flush; |
| 6380 | + |
| 6381 | + /* XXX: Writing input format makes little sense. */ |
| 6382 | + if( p_context->profile != FF_PROFILE_UNKNOWN ) |
| 6383 | + p_dec->fmt_in.i_profile = p_context->profile; |
| 6384 | + if( p_context->level != FF_LEVEL_UNKNOWN ) |
| 6385 | + p_dec->fmt_in.i_level = p_context->level; |
| 6386 | + |
| 6387 | +#if 1 |
| 6388 | + // Most of the time we have nothing useful by way of a format here |
| 6389 | + // wait till we've decoded something |
| 6390 | +#else |
| 6391 | + // Update output format |
| 6392 | + if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, |
| 6393 | + p_context->pix_fmt) != 0) |
| 6394 | + { |
| 6395 | + msg_Err(p_dec, "Unable to update format: pix_fmt=%d", p_context->pix_fmt); |
| 6396 | +// goto fail; |
| 6397 | + } |
| 6398 | +#endif |
| 6399 | + |
| 6400 | +#if TRACE_ALL |
| 6401 | + msg_Dbg(p_dec, "<<< %s: OK", __func__); |
| 6402 | +#endif |
| 6403 | + return VLC_SUCCESS; |
| 6404 | + |
| 6405 | +fail: |
| 6406 | + MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec)); |
| 6407 | + |
| 6408 | +#if TRACE_ALL |
| 6409 | + msg_Dbg(p_dec, "<<< %s: FAIL", __func__); |
| 6410 | +#endif |
| 6411 | + |
| 6412 | + return VLC_EGENERIC; |
| 6413 | +} |
| 6414 | + |
| 6415 | +/***************************************************************************** |
| 6416 | + * Flush: |
| 6417 | + *****************************************************************************/ |
| 6418 | +static void Flush( decoder_t *p_dec ) |
| 6419 | +{ |
| 6420 | + decoder_sys_t *p_sys = p_dec->p_sys; |
| 6421 | + AVCodecContext *p_context = p_sys->p_context; |
| 6422 | + |
| 6423 | +#if TRACE_ALL |
| 6424 | + msg_Dbg(p_dec, "<<< %s", __func__); |
| 6425 | +#endif |
| 6426 | + |
| 6427 | + date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */ |
| 6428 | + p_sys->i_late_frames = 0; |
| 6429 | + cc_Flush( &p_sys->cc ); |
| 6430 | + |
| 6431 | + /* Abort pictures in order to unblock all avcodec workers threads waiting |
| 6432 | + * for a picture. This will avoid a deadlock between avcodec_flush_buffers |
| 6433 | + * and workers threads */ |
| 6434 | +// It would probably be good to use AbortPicture but that often deadlocks on close |
| 6435 | +// and given that we wait for pics in the main thread it should be unneeded (whereas |
| 6436 | +// cma is alloced in the depths of ffmpeg on its own threads) |
| 6437 | +// decoder_AbortPictures( p_dec, true ); |
| 6438 | + cma_buf_pool_cancel(p_sys->cma_pool); |
| 6439 | + |
| 6440 | + post_mt( p_sys ); |
| 6441 | + /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ |
| 6442 | + if( avcodec_is_open( p_context ) ) |
| 6443 | + avcodec_flush_buffers( p_context ); |
| 6444 | + wait_mt( p_sys ); |
| 6445 | + |
| 6446 | + /* Reset cancel state to false */ |
| 6447 | + cma_buf_pool_uncancel(p_sys->cma_pool); |
| 6448 | +// decoder_AbortPictures( p_dec, false ); |
| 6449 | + |
| 6450 | +#if TRACE_ALL |
| 6451 | + msg_Dbg(p_dec, ">>> %s", __func__); |
| 6452 | +#endif |
| 6453 | + |
| 6454 | +} |
| 6455 | + |
| 6456 | +static bool check_block_validity( decoder_sys_t *p_sys, block_t *block ) |
| 6457 | +{ |
| 6458 | + if( !block) |
| 6459 | + return true; |
| 6460 | + |
| 6461 | + if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) ) |
| 6462 | + { |
| 6463 | + date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ |
| 6464 | + cc_Flush( &p_sys->cc ); |
| 6465 | + |
| 6466 | + p_sys->i_late_frames = 0; |
| 6467 | + if( block->i_flags & BLOCK_FLAG_CORRUPTED ) |
| 6468 | + { |
| 6469 | + block_Release( block ); |
| 6470 | + return false; |
| 6471 | + } |
| 6472 | + } |
| 6473 | + return true; |
| 6474 | +} |
| 6475 | + |
| 6476 | +static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time) |
| 6477 | +{ |
| 6478 | + if( !block ) |
| 6479 | + return false; |
| 6480 | + if( block->i_flags & BLOCK_FLAG_PREROLL ) |
| 6481 | + { |
| 6482 | + /* Do not care about late frames when prerolling |
| 6483 | + * TODO avoid decoding of non reference frame |
| 6484 | + * (ie all B except for H264 where it depends only on nal_ref_idc) */ |
| 6485 | + p_sys->i_late_frames = 0; |
| 6486 | + p_sys->b_from_preroll = true; |
| 6487 | + p_sys->i_last_late_delay = INT64_MAX; |
| 6488 | + } |
| 6489 | + |
| 6490 | + if( p_sys->i_late_frames <= 0 ) |
| 6491 | + return false; |
| 6492 | + |
| 6493 | + if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ)) |
| 6494 | + { |
| 6495 | + date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */ |
| 6496 | + block_Release( block ); |
| 6497 | + p_sys->i_late_frames--; |
| 6498 | + return true; |
| 6499 | + } |
| 6500 | + return false; |
| 6501 | +} |
| 6502 | + |
| 6503 | +static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture ) |
| 6504 | +{ |
| 6505 | + if( p_sys->i_late_frames <= 4) |
| 6506 | + return false; |
| 6507 | + |
| 6508 | + *b_need_output_picture = false; |
| 6509 | + if( p_sys->i_late_frames < 12 ) |
| 6510 | + { |
| 6511 | + p_context->skip_frame = |
| 6512 | + (p_sys->i_skip_frame <= AVDISCARD_NONREF) ? |
| 6513 | + AVDISCARD_NONREF : p_sys->i_skip_frame; |
| 6514 | + } |
| 6515 | + else |
| 6516 | + { |
| 6517 | + /* picture too late, won't decode |
| 6518 | + * but break picture until a new I, and for mpeg4 ...*/ |
| 6519 | + p_sys->i_late_frames--; /* needed else it will never be decrease */ |
| 6520 | + return true; |
| 6521 | + } |
| 6522 | + return false; |
| 6523 | +} |
| 6524 | + |
| 6525 | +static mtime_t interpolate_next_pts( decoder_t *p_dec, AVFrame *frame ) |
| 6526 | +{ |
| 6527 | + decoder_sys_t *p_sys = p_dec->p_sys; |
| 6528 | + AVCodecContext *p_context = p_sys->p_context; |
| 6529 | + |
| 6530 | + if( date_Get( &p_sys->pts ) == VLC_TS_INVALID || |
| 6531 | + p_sys->pts.i_divider_num == 0 ) |
| 6532 | + return VLC_TS_INVALID; |
| 6533 | + |
| 6534 | + int i_tick = p_context->ticks_per_frame; |
| 6535 | + if( i_tick <= 0 ) |
| 6536 | + i_tick = 1; |
| 6537 | + |
| 6538 | + /* interpolate the next PTS */ |
| 6539 | + return date_Increment( &p_sys->pts, i_tick + frame->repeat_pict ); |
| 6540 | +} |
| 6541 | + |
| 6542 | +static void update_late_frame_count( decoder_t *p_dec, block_t *p_block, |
| 6543 | + mtime_t current_time, mtime_t i_pts, |
| 6544 | + mtime_t i_next_pts ) |
| 6545 | +{ |
| 6546 | + decoder_sys_t *p_sys = p_dec->p_sys; |
| 6547 | + /* Update frame late count (except when doing preroll) */ |
| 6548 | + mtime_t i_display_date = VLC_TS_INVALID; |
| 6549 | + if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) |
| 6550 | + i_display_date = decoder_GetDisplayDate( p_dec, i_pts ); |
| 6551 | + |
| 6552 | + mtime_t i_threshold = i_next_pts != VLC_TS_INVALID ? (i_next_pts - i_pts) / 2 : 20000; |
| 6553 | + |
| 6554 | + if( i_display_date > VLC_TS_INVALID && i_display_date + i_threshold <= current_time ) |
| 6555 | + { |
| 6556 | + /* Out of preroll, consider only late frames on rising delay */ |
| 6557 | + if( p_sys->b_from_preroll ) |
| 6558 | + { |
| 6559 | + if( p_sys->i_last_late_delay > current_time - i_display_date ) |
| 6560 | + { |
| 6561 | + p_sys->i_last_late_delay = current_time - i_display_date; |
| 6562 | + return; |
| 6563 | + } |
| 6564 | + p_sys->b_from_preroll = false; |
| 6565 | + } |
| 6566 | + |
| 6567 | + p_sys->i_late_frames++; |
| 6568 | + if( p_sys->i_late_frames == 1 ) |
| 6569 | + p_sys->i_late_frames_start = current_time; |
| 6570 | + |
| 6571 | + } |
| 6572 | + else |
| 6573 | + { |
| 6574 | + p_sys->i_late_frames = 0; |
| 6575 | + } |
| 6576 | +} |
| 6577 | + |
| 6578 | + |
| 6579 | +static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic ) |
| 6580 | +{ |
| 6581 | + decoder_sys_t *p_sys = p_dec->p_sys; |
| 6582 | + bool format_changed = false; |
| 6583 | + |
| 6584 | +#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) ) |
| 6585 | +#define FROM_AVRAT(default_factor, avrat) \ |
| 6586 | +(uint64_t)(default_factor) * (avrat).num / (avrat).den |
| 6587 | + const AVFrameSideData *metadata = |
| 6588 | + av_frame_get_side_data( frame, |
| 6589 | + AV_FRAME_DATA_MASTERING_DISPLAY_METADATA ); |
| 6590 | + if ( metadata ) |
| 6591 | + { |
| 6592 | + const AVMasteringDisplayMetadata *hdr_meta = |
| 6593 | + (const AVMasteringDisplayMetadata *) metadata->data; |
| 6594 | + if ( hdr_meta->has_luminance ) |
| 6595 | + { |
| 6596 | +#define ST2086_LUMA_FACTOR 10000 |
| 6597 | + p_pic->format.mastering.max_luminance = |
| 6598 | + FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance); |
| 6599 | + p_pic->format.mastering.min_luminance = |
| 6600 | + FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance); |
| 6601 | + } |
| 6602 | + if ( hdr_meta->has_primaries ) |
| 6603 | + { |
| 6604 | +#define ST2086_RED 2 |
| 6605 | +#define ST2086_GREEN 0 |
| 6606 | +#define ST2086_BLUE 1 |
| 6607 | +#define LAV_RED 0 |
| 6608 | +#define LAV_GREEN 1 |
| 6609 | +#define LAV_BLUE 2 |
| 6610 | +#define ST2086_PRIM_FACTOR 50000 |
| 6611 | + p_pic->format.mastering.primaries[ST2086_RED*2 + 0] = |
| 6612 | + FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]); |
| 6613 | + p_pic->format.mastering.primaries[ST2086_RED*2 + 1] = |
| 6614 | + FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]); |
| 6615 | + p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] = |
| 6616 | + FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]); |
| 6617 | + p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] = |
| 6618 | + FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]); |
| 6619 | + p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] = |
| 6620 | + FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]); |
| 6621 | + p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] = |
| 6622 | + FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]); |
| 6623 | + p_pic->format.mastering.white_point[0] = |
| 6624 | + FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]); |
| 6625 | + p_pic->format.mastering.white_point[1] = |
| 6626 | + FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]); |
| 6627 | + } |
| 6628 | + |
| 6629 | + if ( memcmp( &p_dec->fmt_out.video.mastering, |
| 6630 | + &p_pic->format.mastering, |
| 6631 | + sizeof(p_pic->format.mastering) ) ) |
| 6632 | + { |
| 6633 | + p_dec->fmt_out.video.mastering = p_pic->format.mastering; |
| 6634 | + format_changed = true; |
| 6635 | + } |
| 6636 | +#undef FROM_AVRAT |
| 6637 | + } |
| 6638 | +#endif |
| 6639 | +#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) ) |
| 6640 | + const AVFrameSideData *metadata_lt = |
| 6641 | + av_frame_get_side_data( frame, |
| 6642 | + AV_FRAME_DATA_CONTENT_LIGHT_LEVEL ); |
| 6643 | + if ( metadata_lt ) |
| 6644 | + { |
| 6645 | + const AVContentLightMetadata *light_meta = |
| 6646 | + (const AVContentLightMetadata *) metadata_lt->data; |
| 6647 | + p_pic->format.lighting.MaxCLL = light_meta->MaxCLL; |
| 6648 | + p_pic->format.lighting.MaxFALL = light_meta->MaxFALL; |
| 6649 | + if ( memcmp( &p_dec->fmt_out.video.lighting, |
| 6650 | + &p_pic->format.lighting, |
| 6651 | + sizeof(p_pic->format.lighting) ) ) |
| 6652 | + { |
| 6653 | + p_dec->fmt_out.video.lighting = p_pic->format.lighting; |
| 6654 | + format_changed = true; |
| 6655 | + } |
| 6656 | + } |
| 6657 | +#endif |
| 6658 | + |
| 6659 | + if (format_changed && decoder_UpdateVideoFormat( p_dec )) |
| 6660 | + return -1; |
| 6661 | + |
| 6662 | + const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC ); |
| 6663 | + if( p_avcc ) |
| 6664 | + { |
| 6665 | + cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size ); |
| 6666 | + if( p_sys->cc.b_reorder || p_sys->cc.i_data ) |
| 6667 | + { |
| 6668 | + block_t *p_cc = block_Alloc( p_sys->cc.i_data ); |
| 6669 | + if( p_cc ) |
| 6670 | + { |
| 6671 | + memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data ); |
| 6672 | + if( p_sys->cc.b_reorder ) |
| 6673 | + p_cc->i_dts = p_cc->i_pts = p_pic->date; |
| 6674 | + else |
| 6675 | + p_cc->i_pts = p_cc->i_dts; |
| 6676 | + decoder_cc_desc_t desc; |
| 6677 | + desc.i_608_channels = p_sys->cc.i_608channels; |
| 6678 | + desc.i_708_channels = p_sys->cc.i_708channels; |
| 6679 | + desc.i_reorder_depth = 4; |
| 6680 | + decoder_QueueCc( p_dec, p_cc, &desc ); |
| 6681 | + } |
| 6682 | + cc_Flush( &p_sys->cc ); |
| 6683 | + } |
| 6684 | + } |
| 6685 | + return 0; |
| 6686 | +} |
| 6687 | + |
| 6688 | +/***************************************************************************** |
| 6689 | + * DecodeBlock: Called to decode one or more frames |
| 6690 | + *****************************************************************************/ |
| 6691 | + |
| 6692 | +static picture_t *DecodeBlock( decoder_t *p_dec, block_t **pp_block, bool *error ) |
| 6693 | +{ |
| 6694 | + decoder_sys_t *p_sys = p_dec->p_sys; |
| 6695 | + AVCodecContext *p_context = p_sys->p_context; |
| 6696 | + /* Boolean if we assume that we should get valid pic as result */ |
| 6697 | + bool b_need_output_picture = true; |
| 6698 | + |
| 6699 | + /* Boolean for END_OF_SEQUENCE */ |
| 6700 | + bool eos_spotted = false; |
| 6701 | + |
| 6702 | +#if TRACE_ALL |
| 6703 | + msg_Dbg(p_dec, "<<< %s: (buf_size=%d)", __func__, pp_block == NULL || *pp_block == NULL ? 0 : (*pp_block)->i_buffer); |
| 6704 | +#endif |
| 6705 | + |
| 6706 | + block_t *p_block; |
| 6707 | + mtime_t current_time; |
| 6708 | + picture_t *p_pic = NULL; |
| 6709 | + AVFrame *frame = NULL; |
| 6710 | + |
| 6711 | + // By default we are OK |
| 6712 | + *error = false; |
| 6713 | + |
| 6714 | + if( !p_context->extradata_size && p_dec->fmt_in.i_extra ) |
| 6715 | + { |
| 6716 | + ffmpeg_InitCodec( p_dec ); |
| 6717 | + if( !avcodec_is_open( p_context ) ) |
| 6718 | + OpenVideoCodec( p_dec ); |
| 6719 | + } |
| 6720 | + |
| 6721 | + p_block = pp_block ? *pp_block : NULL; |
| 6722 | + if(!p_block && !(p_sys->p_codec->capabilities & AV_CODEC_CAP_DELAY) ) |
| 6723 | + return NULL; |
| 6724 | + |
| 6725 | + if( !avcodec_is_open( p_context ) ) |
| 6726 | + { |
| 6727 | + if( p_block ) |
| 6728 | + block_Release( p_block ); |
| 6729 | + return NULL; |
| 6730 | + } |
| 6731 | + |
| 6732 | + if( !check_block_validity( p_sys, p_block ) ) |
| 6733 | + return NULL; |
| 6734 | + |
| 6735 | + current_time = mdate(); |
| 6736 | + if( p_dec->b_frame_drop_allowed && check_block_being_late( p_sys, p_block, current_time) ) |
| 6737 | + { |
| 6738 | + msg_Err( p_dec, "more than 5 seconds of late video -> " |
| 6739 | + "dropping frame (computer too slow ?)" ); |
| 6740 | + return NULL; |
| 6741 | + } |
| 6742 | + |
| 6743 | + |
| 6744 | + /* A good idea could be to decode all I pictures and see for the other */ |
| 6745 | + |
| 6746 | + /* Defaults that if we aren't in prerolling, we want output picture |
| 6747 | + same for if we are flushing (p_block==NULL) */ |
| 6748 | + if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) ) |
| 6749 | + b_need_output_picture = true; |
| 6750 | + else |
| 6751 | + b_need_output_picture = false; |
| 6752 | + |
| 6753 | + /* Change skip_frame config only if hurry_up is enabled */ |
| 6754 | + if( p_sys->b_hurry_up ) |
| 6755 | + { |
| 6756 | + p_context->skip_frame = p_sys->i_skip_frame; |
| 6757 | + |
| 6758 | + /* Check also if we should/can drop the block and move to next block |
| 6759 | + as trying to catchup the speed*/ |
| 6760 | + if( p_dec->b_frame_drop_allowed && |
| 6761 | + check_frame_should_be_dropped( p_sys, p_context, &b_need_output_picture ) ) |
| 6762 | + { |
| 6763 | + if( p_block ) |
| 6764 | + block_Release( p_block ); |
| 6765 | + msg_Warn( p_dec, "More than 11 late frames, dropping frame" ); |
| 6766 | + return NULL; |
| 6767 | + } |
| 6768 | + } |
| 6769 | + if( !b_need_output_picture ) |
| 6770 | + { |
| 6771 | + p_context->skip_frame = __MAX( p_context->skip_frame, |
| 6772 | + AVDISCARD_NONREF ); |
| 6773 | + } |
| 6774 | + |
| 6775 | + /* |
| 6776 | + * Do the actual decoding now */ |
| 6777 | + |
| 6778 | + /* Don't forget that libavcodec requires a little more bytes |
| 6779 | + * that the real frame size */ |
| 6780 | + if( p_block && p_block->i_buffer > 0 ) |
| 6781 | + { |
| 6782 | + eos_spotted = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0; |
| 6783 | + |
| 6784 | + p_block = block_Realloc( p_block, 0, |
| 6785 | + p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE ); |
| 6786 | + if( !p_block ) |
| 6787 | + return NULL; |
| 6788 | + p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE; |
| 6789 | + *pp_block = p_block; |
| 6790 | + memset( p_block->p_buffer + p_block->i_buffer, 0, |
| 6791 | + FF_INPUT_BUFFER_PADDING_SIZE ); |
| 6792 | + } |
| 6793 | + |
| 6794 | + while( !p_block || p_block->i_buffer > 0 || eos_spotted ) |
| 6795 | + { |
| 6796 | + int i_used; |
| 6797 | + AVPacket pkt; |
| 6798 | + |
| 6799 | + post_mt( p_sys ); |
| 6800 | + |
| 6801 | + av_init_packet( &pkt ); |
| 6802 | + if( p_block && p_block->i_buffer > 0 ) |
| 6803 | + { |
| 6804 | + pkt.data = p_block->p_buffer; |
| 6805 | + pkt.size = p_block->i_buffer; |
| 6806 | + pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE; |
| 6807 | + pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE; |
| 6808 | + } |
| 6809 | + else |
| 6810 | + { |
| 6811 | + /* Return delayed frames if codec has CODEC_CAP_DELAY */ |
| 6812 | + pkt.data = NULL; |
| 6813 | + pkt.size = 0; |
| 6814 | + } |
| 6815 | + |
| 6816 | + if( !p_sys->palette_sent ) |
| 6817 | + { |
| 6818 | + uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE); |
| 6819 | + if (pal) { |
| 6820 | + memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE); |
| 6821 | + p_sys->palette_sent = true; |
| 6822 | + } |
| 6823 | + } |
| 6824 | + |
| 6825 | + /* Make sure we don't reuse the same timestamps twice */ |
| 6826 | + if( p_block ) |
| 6827 | + { |
| 6828 | + p_block->i_pts = |
| 6829 | + p_block->i_dts = VLC_TS_INVALID; |
| 6830 | + } |
| 6831 | + |
| 6832 | + int ret = avcodec_send_packet(p_context, &pkt); |
| 6833 | + if( ret != 0 && ret != AVERROR(EAGAIN) ) |
| 6834 | + { |
| 6835 | + if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL)) |
| 6836 | + { |
| 6837 | + msg_Err(p_dec, "avcodec_send_packet critical error"); |
| 6838 | + *error = true; |
| 6839 | + } |
| 6840 | + av_packet_unref( &pkt ); |
| 6841 | + break; |
| 6842 | + } |
| 6843 | + i_used = ret != AVERROR(EAGAIN) ? pkt.size : 0; |
| 6844 | + av_packet_unref( &pkt ); |
| 6845 | + |
| 6846 | + frame = av_frame_alloc(); |
| 6847 | + if (unlikely(frame == NULL)) |
| 6848 | + { |
| 6849 | + *error = true; |
| 6850 | + break; |
| 6851 | + } |
| 6852 | + |
| 6853 | + ret = avcodec_receive_frame(p_context, frame); |
| 6854 | + if( ret != 0 && ret != AVERROR(EAGAIN) ) |
| 6855 | + { |
| 6856 | + msg_Dbg(p_dec, "No receive"); |
| 6857 | + if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL)) |
| 6858 | + { |
| 6859 | + msg_Err(p_dec, "avcodec_receive_frame critical error"); |
| 6860 | + *error = true; |
| 6861 | + } |
| 6862 | + av_frame_free(&frame); |
| 6863 | + /* After draining, we need to reset decoder with a flush */ |
| 6864 | + if( ret == AVERROR_EOF ) |
| 6865 | + avcodec_flush_buffers( p_sys->p_context ); |
| 6866 | + break; |
| 6867 | + } |
| 6868 | + bool not_received_frame = ret; |
| 6869 | + |
| 6870 | + wait_mt( p_sys ); |
| 6871 | + |
| 6872 | + if( eos_spotted ) |
| 6873 | + p_sys->b_first_frame = true; |
| 6874 | + |
| 6875 | + if( p_block ) |
| 6876 | + { |
| 6877 | + if( p_block->i_buffer <= 0 ) |
| 6878 | + eos_spotted = false; |
| 6879 | + |
| 6880 | + /* Consumed bytes */ |
| 6881 | + p_block->p_buffer += i_used; |
| 6882 | + p_block->i_buffer -= i_used; |
| 6883 | + } |
| 6884 | + |
| 6885 | + /* Nothing to display */ |
| 6886 | + if( not_received_frame ) |
| 6887 | + { |
| 6888 | +// msg_Dbg(p_dec, "No rx: used=%d", i_used); |
| 6889 | + av_frame_free(&frame); |
| 6890 | + if( i_used == 0 ) break; |
| 6891 | + continue; |
| 6892 | + } |
| 6893 | + |
| 6894 | + /* Compute the PTS */ |
| 6895 | +#ifdef FF_API_PKT_PTS |
| 6896 | + mtime_t i_pts = frame->pts; |
| 6897 | +#else |
| 6898 | + mtime_t i_pts = frame->pkt_pts; |
| 6899 | +#endif |
| 6900 | + if (i_pts == AV_NOPTS_VALUE ) |
| 6901 | + i_pts = frame->pkt_dts; |
| 6902 | + |
| 6903 | + if( i_pts == AV_NOPTS_VALUE ) |
| 6904 | + i_pts = date_Get( &p_sys->pts ); |
| 6905 | + |
| 6906 | + /* Interpolate the next PTS */ |
| 6907 | + if( i_pts > VLC_TS_INVALID ) |
| 6908 | + date_Set( &p_sys->pts, i_pts ); |
| 6909 | + |
| 6910 | + const mtime_t i_next_pts = interpolate_next_pts(p_dec, frame); |
| 6911 | + |
| 6912 | + update_late_frame_count( p_dec, p_block, current_time, i_pts, i_next_pts); |
| 6913 | + |
| 6914 | + if( !b_need_output_picture || |
| 6915 | +// ( !p_sys->p_va && !frame->linesize[0] ) || |
| 6916 | + ( !frame->linesize[0] ) || |
| 6917 | + ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) && |
| 6918 | + !p_sys->b_show_corrupted ) ) |
| 6919 | + { |
| 6920 | + av_frame_free(&frame); |
| 6921 | +// msg_Dbg(p_dec, "Bad frame"); |
| 6922 | + continue; |
| 6923 | + } |
| 6924 | + |
| 6925 | + if( p_context->pix_fmt == AV_PIX_FMT_PAL8 |
| 6926 | + && !p_dec->fmt_out.video.p_palette ) |
| 6927 | + { |
| 6928 | + /* See AV_PIX_FMT_PAL8 comment in avc_GetVideoFormat(): update the |
| 6929 | + * fmt_out palette and change the fmt_out chroma to request a new |
| 6930 | + * vout */ |
| 6931 | + assert( p_dec->fmt_out.video.i_chroma != VLC_CODEC_RGBP ); |
| 6932 | + |
| 6933 | + video_palette_t *p_palette; |
| 6934 | + p_palette = p_dec->fmt_out.video.p_palette |
| 6935 | + = malloc( sizeof(video_palette_t) ); |
| 6936 | + if( !p_palette ) |
| 6937 | + { |
| 6938 | + *error = true; |
| 6939 | + av_frame_free(&frame); |
| 6940 | + break; |
| 6941 | + } |
| 6942 | + static_assert( sizeof(p_palette->palette) == AVPALETTE_SIZE, |
| 6943 | + "Palette size mismatch between vlc and libavutil" ); |
| 6944 | + assert( frame->data[1] != NULL ); |
| 6945 | + memcpy( p_palette->palette, frame->data[1], AVPALETTE_SIZE ); |
| 6946 | + p_palette->i_entries = AVPALETTE_COUNT; |
| 6947 | + p_dec->fmt_out.video.i_chroma = VLC_CODEC_RGBP; |
| 6948 | + if( decoder_UpdateVideoFormat( p_dec ) ) |
| 6949 | + { |
| 6950 | + av_frame_free(&frame); |
| 6951 | + continue; |
| 6952 | + } |
| 6953 | + } |
| 6954 | + |
| 6955 | +#if 1 |
| 6956 | + { |
| 6957 | + cma_buf_t * const cb = av_rpi_zc_buf_v(frame->buf[0]); |
| 6958 | + |
| 6959 | + if (cb == NULL) |
| 6960 | + { |
| 6961 | + msg_Err(p_dec, "Frame has no attached CMA buffer"); |
| 6962 | + goto fail; |
| 6963 | + } |
| 6964 | + |
| 6965 | + if (lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, |
| 6966 | + p_context->pix_fmt) != 0) |
| 6967 | + { |
| 6968 | + msg_Err(p_dec, "Failed to update format"); |
| 6969 | + goto fail; |
| 6970 | + } |
| 6971 | + |
| 6972 | + if ((p_pic = decoder_NewPicture(p_dec)) == NULL) |
| 6973 | + { |
| 6974 | + msg_Err(p_dec, "Failed to allocate pic"); |
| 6975 | + goto fail; |
| 6976 | + } |
| 6977 | + |
| 6978 | + if (cma_buf_pic_attach(cma_buf_ref(cb), p_pic) != 0) |
| 6979 | + { |
| 6980 | + cma_buf_unref(cb); // Undo the in_flight |
| 6981 | + char dbuf0[5]; |
| 6982 | + msg_Err(p_dec, "Failed to attach bufs to pic: fmt=%s", str_fourcc(dbuf0, p_pic->format.i_chroma)); |
| 6983 | + goto fail; |
| 6984 | + } |
| 6985 | + |
| 6986 | + // ****** Set planes etc. |
| 6987 | + set_pic_from_frame(p_pic, frame); |
| 6988 | + } |
| 6989 | +#else |
| 6990 | + picture_t *p_pic = frame->opaque; |
| 6991 | + if( p_pic == NULL ) |
| 6992 | + { /* When direct rendering is not used, get_format() and get_buffer() |
| 6993 | + * might not be called. The output video format must be set here |
| 6994 | + * then picture buffer can be allocated. */ |
| 6995 | + if (p_sys->p_va == NULL |
| 6996 | + && lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt, |
| 6997 | + p_context->pix_fmt) == 0) |
| 6998 | + p_pic = decoder_NewPicture(p_dec); |
| 6999 | + |
| 7000 | + if( !p_pic ) |
| 7001 | + { |
| 7002 | + av_frame_free(&frame); |
| 7003 | + break; |
| 7004 | + } |
| 7005 | + |
| 7006 | + /* Fill picture_t from AVFrame */ |
| 7007 | + if( lavc_CopyPicture( p_dec, p_pic, frame ) != VLC_SUCCESS ) |
| 7008 | + { |
| 7009 | + av_frame_free(&frame); |
| 7010 | + picture_Release( p_pic ); |
| 7011 | + break; |
| 7012 | + } |
| 7013 | + } |
| 7014 | + else |
| 7015 | + { |
| 7016 | + /* Some codecs can return the same frame multiple times. By the |
| 7017 | + * time that the same frame is returned a second time, it will be |
| 7018 | + * too late to clone the underlying picture. So clone proactively. |
| 7019 | + * A single picture CANNOT be queued multiple times. |
| 7020 | + */ |
| 7021 | + p_pic = picture_Clone( p_pic ); |
| 7022 | + if( unlikely(p_pic == NULL) ) |
| 7023 | + { |
| 7024 | + av_frame_free(&frame); |
| 7025 | + break; |
| 7026 | + } |
| 7027 | + } |
| 7028 | +#endif |
| 7029 | + |
| 7030 | + if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den ) |
| 7031 | + { |
| 7032 | + /* Fetch again the aspect ratio in case it changed */ |
| 7033 | + p_dec->fmt_out.video.i_sar_num |
| 7034 | + = p_context->sample_aspect_ratio.num; |
| 7035 | + p_dec->fmt_out.video.i_sar_den |
| 7036 | + = p_context->sample_aspect_ratio.den; |
| 7037 | + |
| 7038 | + if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den ) |
| 7039 | + { |
| 7040 | + p_dec->fmt_out.video.i_sar_num = 1; |
| 7041 | + p_dec->fmt_out.video.i_sar_den = 1; |
| 7042 | + } |
| 7043 | + } |
| 7044 | + |
| 7045 | + p_pic->date = i_pts; |
| 7046 | + /* Hack to force display of still pictures */ |
| 7047 | + p_pic->b_force = p_sys->b_first_frame; |
| 7048 | + p_pic->i_nb_fields = 2 + frame->repeat_pict; |
| 7049 | + p_pic->b_progressive = !frame->interlaced_frame; |
| 7050 | + p_pic->b_top_field_first = frame->top_field_first; |
| 7051 | + |
| 7052 | + if (DecodeSidedata(p_dec, frame, p_pic)) |
| 7053 | + i_pts = VLC_TS_INVALID; |
| 7054 | + |
| 7055 | + av_frame_free(&frame); |
| 7056 | + |
| 7057 | + /* Send decoded frame to vout */ |
| 7058 | + if (i_pts > VLC_TS_INVALID) |
| 7059 | + { |
| 7060 | + p_sys->b_first_frame = false; |
| 7061 | +#if TRACE_ALL |
| 7062 | + msg_Dbg(p_dec, ">>> %s: Got pic", __func__); |
| 7063 | +#endif |
| 7064 | + return p_pic; |
| 7065 | + } |
| 7066 | + else |
| 7067 | + picture_Release( p_pic ); |
| 7068 | + } |
| 7069 | + |
| 7070 | + if( p_block ) |
| 7071 | + block_Release( p_block ); |
| 7072 | + |
| 7073 | +#if TRACE_ALL |
| 7074 | + msg_Dbg(p_dec, ">>> %s: NULL", __func__); |
| 7075 | +#endif |
| 7076 | + return NULL; |
| 7077 | + |
| 7078 | +fail: |
| 7079 | +#if TRACE_ALL |
| 7080 | + msg_Dbg(p_dec, ">>> %s: FAIL", __func__); |
| 7081 | +#endif |
| 7082 | + av_frame_free(&frame); |
| 7083 | + if (p_pic != NULL) |
| 7084 | + picture_Release(p_pic); |
| 7085 | + if (p_block != NULL) |
| 7086 | + block_Release(p_block); |
| 7087 | + *error = true; |
| 7088 | + return NULL; |
| 7089 | +} |
| 7090 | + |
| 7091 | +static int DecodeVideo( decoder_t *p_dec, block_t *p_block ) |
| 7092 | +{ |
| 7093 | + block_t **pp_block = p_block ? &p_block : NULL; |
| 7094 | + picture_t *p_pic; |
| 7095 | + bool error = false; |
| 7096 | + while( ( p_pic = DecodeBlock( p_dec, pp_block, &error ) ) != NULL ) |
| 7097 | + decoder_QueueVideo( p_dec, p_pic ); |
| 7098 | + return VLCDEC_SUCCESS; |
| 7099 | +// Easiest to just ignore all errors - returning a real error seems to |
| 7100 | +// kill output forever |
| 7101 | +// return error ? VLCDEC_ECRITICAL : VLCDEC_SUCCESS; |
| 7102 | +} |
| 7103 | + |
| 7104 | +/***************************************************************************** |
| 7105 | + * EndVideo: decoder destruction |
| 7106 | + ***************************************************************************** |
| 7107 | + * This function is called when the thread ends after a successful |
| 7108 | + * initialization. |
| 7109 | + *****************************************************************************/ |
| 7110 | +static void MmalAvcodecCloseDecoder( vlc_object_t *obj ) |
| 7111 | +{ |
| 7112 | + decoder_t *p_dec = (decoder_t *)obj; |
| 7113 | + decoder_sys_t *p_sys = p_dec->p_sys; |
| 7114 | + AVCodecContext *ctx = p_sys->p_context; |
| 7115 | +// void *hwaccel_context; |
| 7116 | + |
| 7117 | + msg_Dbg(obj, "<<< %s", __func__); |
| 7118 | + |
| 7119 | + post_mt( p_sys ); |
| 7120 | + |
| 7121 | + cma_buf_pool_cancel(p_sys->cma_pool); // Abort any pending frame allocs |
| 7122 | + |
| 7123 | + /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */ |
| 7124 | + if( avcodec_is_open( ctx ) ) |
| 7125 | + avcodec_flush_buffers( ctx ); |
| 7126 | + |
| 7127 | + av_rpi_zc_uninit2(ctx); |
| 7128 | + |
| 7129 | + wait_mt( p_sys ); |
| 7130 | + |
| 7131 | + cc_Flush( &p_sys->cc ); |
| 7132 | + |
| 7133 | +// hwaccel_context = ctx->hwaccel_context; |
| 7134 | + avcodec_free_context( &ctx ); |
| 7135 | + |
| 7136 | +// if( p_sys->p_va ) |
| 7137 | +// vlc_va_Delete( p_sys->p_va, &hwaccel_context ); |
| 7138 | + |
| 7139 | + cma_vcsm_exit(p_sys->vcsm_init_type); |
| 7140 | + |
| 7141 | + vlc_sem_destroy( &p_sys->sem_mt ); |
| 7142 | + free( p_sys ); |
| 7143 | +} |
| 7144 | + |
| 7145 | +/***************************************************************************** |
| 7146 | + * ffmpeg_InitCodec: setup codec extra initialization data for ffmpeg |
| 7147 | + *****************************************************************************/ |
| 7148 | +static void ffmpeg_InitCodec( decoder_t *p_dec ) |
| 7149 | +{ |
| 7150 | + decoder_sys_t *p_sys = p_dec->p_sys; |
| 7151 | + size_t i_size = p_dec->fmt_in.i_extra; |
| 7152 | + |
| 7153 | + if( !i_size ) return; |
| 7154 | + |
| 7155 | + if( p_sys->p_codec->id == AV_CODEC_ID_SVQ3 ) |
| 7156 | + { |
| 7157 | + uint8_t *p; |
| 7158 | + |
| 7159 | + p_sys->p_context->extradata_size = i_size + 12; |
| 7160 | + p = p_sys->p_context->extradata = |
| 7161 | + av_malloc( p_sys->p_context->extradata_size + |
| 7162 | + FF_INPUT_BUFFER_PADDING_SIZE ); |
| 7163 | + if( !p ) |
| 7164 | + return; |
| 7165 | + |
| 7166 | + memcpy( &p[0], "SVQ3", 4 ); |
| 7167 | + memset( &p[4], 0, 8 ); |
| 7168 | + memcpy( &p[12], p_dec->fmt_in.p_extra, i_size ); |
| 7169 | + |
| 7170 | + /* Now remove all atoms before the SMI one */ |
| 7171 | + if( p_sys->p_context->extradata_size > 0x5a && |
| 7172 | + strncmp( (char*)&p[0x56], "SMI ", 4 ) ) |
| 7173 | + { |
| 7174 | + uint8_t *psz = &p[0x52]; |
| 7175 | + |
| 7176 | + while( psz < &p[p_sys->p_context->extradata_size - 8] ) |
| 7177 | + { |
| 7178 | + uint_fast32_t atom_size = GetDWBE( psz ); |
| 7179 | + if( atom_size <= 1 ) |
| 7180 | + { |
| 7181 | + /* FIXME handle 1 as long size */ |
| 7182 | + break; |
| 7183 | + } |
| 7184 | + if( !strncmp( (char*)&psz[4], "SMI ", 4 ) ) |
| 7185 | + { |
| 7186 | + memmove( &p[0x52], psz, |
| 7187 | + &p[p_sys->p_context->extradata_size] - psz ); |
| 7188 | + break; |
| 7189 | + } |
| 7190 | + |
| 7191 | + psz += atom_size; |
| 7192 | + } |
| 7193 | + } |
| 7194 | + } |
| 7195 | + else |
| 7196 | + { |
| 7197 | + p_sys->p_context->extradata_size = i_size; |
| 7198 | + p_sys->p_context->extradata = |
| 7199 | + av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE ); |
| 7200 | + if( p_sys->p_context->extradata ) |
| 7201 | + { |
| 7202 | + memcpy( p_sys->p_context->extradata, |
| 7203 | + p_dec->fmt_in.p_extra, i_size ); |
| 7204 | + memset( p_sys->p_context->extradata + i_size, |
| 7205 | + 0, FF_INPUT_BUFFER_PADDING_SIZE ); |
| 7206 | + } |
| 7207 | + } |
| 7208 | +} |
| 7209 | + |
| 7210 | + |
| 7211 | +vlc_module_begin() |
| 7212 | + set_category( CAT_INPUT ) |
| 7213 | + set_subcategory( SUBCAT_INPUT_VCODEC ) |
| 7214 | + set_shortname(N_("MMAL avcodec")) |
| 7215 | + set_description(N_("MMAL buffered avcodec ")) |
| 7216 | + set_capability("video decoder", 80) |
| 7217 | + add_shortcut("mmal_avcodec") |
| 7218 | + add_integer(MMAL_AVCODEC_BUFFERS, -1, MMAL_AVCODEC_BUFFERS_TEXT, |
| 7219 | + MMAL_AVCODEC_BUFFERS_LONGTEXT, true) |
| 7220 | + set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder) |
| 7221 | +vlc_module_end() |
| 7222 | + |
| 7223 | --- /dev/null |
| 7224 | +++ b/modules/hw/mmal/mmal_cma.c |
| 7225 | @@ -0,0 +1,668 @@ |
| 7226 | +#ifdef HAVE_CONFIG_H |
| 7227 | +# include "config.h" |
| 7228 | +#endif |
| 7229 | + |
| 7230 | +#include <stdatomic.h> |
| 7231 | +#include <unistd.h> |
| 7232 | +#include <fcntl.h> |
| 7233 | +#include <sys/ioctl.h> |
| 7234 | +#include <sys/mman.h> |
| 7235 | + |
| 7236 | +#include <interface/vcsm/user-vcsm.h> |
| 7237 | + |
| 7238 | +#include <vlc_common.h> |
| 7239 | +#include <vlc_picture.h> |
| 7240 | + |
| 7241 | +#include "mmal_cma.h" |
| 7242 | +#include "mmal_picture.h" |
| 7243 | + |
| 7244 | +#include <assert.h> |
| 7245 | + |
| 7246 | +#define TRACE_ALL 0 |
| 7247 | + |
| 7248 | +//----------------------------------------------------------------------------- |
| 7249 | +// |
| 7250 | +// Generic pool functions |
| 7251 | +// Knows nothing about pool entries |
| 7252 | + |
| 7253 | +typedef void * cma_pool_alloc_fn(void * v, size_t size); |
| 7254 | +typedef void cma_pool_free_fn(void * v, void * el, size_t size); |
| 7255 | + |
| 7256 | +#if TRACE_ALL |
| 7257 | +static atomic_int pool_seq; |
| 7258 | +#endif |
| 7259 | + |
| 7260 | +// Pool structure |
| 7261 | +// Ref count is held by pool owner and pool els that have been got |
| 7262 | +// Els in the pool do not count towards its ref count |
| 7263 | +struct cma_pool_fixed_s |
| 7264 | +{ |
| 7265 | + atomic_int ref_count; |
| 7266 | + |
| 7267 | + vlc_mutex_t lock; |
| 7268 | + unsigned int n_in; |
| 7269 | + unsigned int n_out; |
| 7270 | + unsigned int pool_size; |
| 7271 | + int flight_size; |
| 7272 | + size_t el_size; |
| 7273 | + void ** pool; |
| 7274 | + |
| 7275 | + bool cancel; |
| 7276 | + int in_flight; |
| 7277 | + vlc_cond_t flight_cond; |
| 7278 | + |
| 7279 | + void * alloc_v; |
| 7280 | + cma_pool_alloc_fn * el_alloc_fn; |
| 7281 | + cma_pool_free_fn * el_free_fn; |
| 7282 | + cma_pool_on_delete_fn * on_delete_fn; |
| 7283 | + |
| 7284 | + const char * name; |
| 7285 | +#if TRACE_ALL |
| 7286 | + int seq; |
| 7287 | +#endif |
| 7288 | +}; |
| 7289 | + |
| 7290 | +static inline unsigned int inc_mod(const unsigned int n, const unsigned int m) |
| 7291 | +{ |
| 7292 | + return n + 1 >= m ? 0 : n + 1; |
| 7293 | +} |
| 7294 | + |
| 7295 | +static void free_pool(const cma_pool_fixed_t * const p, void ** const pool, |
| 7296 | + const unsigned int pool_size, const size_t el_size) |
| 7297 | +{ |
| 7298 | + if (pool == NULL) |
| 7299 | + return; |
| 7300 | + |
| 7301 | + for (unsigned int n = 0; n != pool_size; ++n) |
| 7302 | + if (pool[n] != NULL) |
| 7303 | + p->el_free_fn(p->alloc_v, pool[n], el_size); |
| 7304 | + free(pool); |
| 7305 | +} |
| 7306 | + |
| 7307 | +// Just kill this - no checks |
| 7308 | +static void cma_pool_fixed_delete(cma_pool_fixed_t * const p) |
| 7309 | +{ |
| 7310 | + cma_pool_on_delete_fn *const on_delete_fn = p->on_delete_fn; |
| 7311 | + void *const v = p->alloc_v; |
| 7312 | + |
| 7313 | + free_pool(p, p->pool, p->pool_size, p->el_size); |
| 7314 | + |
| 7315 | + if (p->name != NULL) |
| 7316 | + free((void *)p->name); // Discard const |
| 7317 | + |
| 7318 | + vlc_cond_destroy(&p->flight_cond); |
| 7319 | + vlc_mutex_destroy(&p->lock); |
| 7320 | + free(p); |
| 7321 | + |
| 7322 | + // Inform our container that we are dead (if it cares) |
| 7323 | + if (on_delete_fn) |
| 7324 | + on_delete_fn(v); |
| 7325 | +} |
| 7326 | + |
| 7327 | +static void cma_pool_fixed_unref(cma_pool_fixed_t * const p) |
| 7328 | +{ |
| 7329 | + if (atomic_fetch_sub(&p->ref_count, 1) <= 1) |
| 7330 | + cma_pool_fixed_delete(p); |
| 7331 | +} |
| 7332 | + |
| 7333 | +static void cma_pool_fixed_ref(cma_pool_fixed_t * const p) |
| 7334 | +{ |
| 7335 | + atomic_fetch_add(&p->ref_count, 1); |
| 7336 | +} |
| 7337 | + |
| 7338 | +static void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p) |
| 7339 | +{ |
| 7340 | + vlc_mutex_lock(&p->lock); |
| 7341 | + ++p->in_flight; |
| 7342 | + vlc_mutex_unlock(&p->lock); |
| 7343 | +} |
| 7344 | + |
| 7345 | +static void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p) |
| 7346 | +{ |
| 7347 | + vlc_mutex_lock(&p->lock); |
| 7348 | + if (--p->in_flight == 0) |
| 7349 | + vlc_cond_signal(&p->flight_cond); |
| 7350 | + vlc_mutex_unlock(&p->lock); |
| 7351 | +} |
| 7352 | + |
| 7353 | +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) |
| 7354 | +{ |
| 7355 | + void * v = NULL; |
| 7356 | + |
| 7357 | + vlc_mutex_lock(&p->lock); |
| 7358 | + |
| 7359 | + for (;;) |
| 7360 | + { |
| 7361 | + if (req_el_size != p->el_size) |
| 7362 | + { |
| 7363 | + void ** const deadpool = p->pool; |
| 7364 | + const size_t dead_size = p->el_size; |
| 7365 | + const unsigned int dead_n = p->pool_size; |
| 7366 | + |
| 7367 | + p->pool = NULL; |
| 7368 | + p->n_in = 0; |
| 7369 | + p->n_out = 0; |
| 7370 | + p->el_size = req_el_size; |
| 7371 | + |
| 7372 | + if (deadpool != NULL) |
| 7373 | + { |
| 7374 | + vlc_mutex_unlock(&p->lock); |
| 7375 | + // Do the free old op outside the mutex in case the free is slow |
| 7376 | + free_pool(p, deadpool, dead_n, dead_size); |
| 7377 | + vlc_mutex_lock(&p->lock); |
| 7378 | + continue; |
| 7379 | + } |
| 7380 | + } |
| 7381 | + |
| 7382 | + // Late abort if flush or cancel so we can still kill the pool |
| 7383 | + if (req_el_size == 0 || p->cancel) |
| 7384 | + { |
| 7385 | + vlc_mutex_unlock(&p->lock); |
| 7386 | + return NULL; |
| 7387 | + } |
| 7388 | + |
| 7389 | + if (p->pool != NULL && !no_pool) |
| 7390 | + { |
| 7391 | + v = p->pool[p->n_in]; |
| 7392 | + if (v != NULL) |
| 7393 | + { |
| 7394 | + p->pool[p->n_in] = NULL; |
| 7395 | + p->n_in = inc_mod(p->n_in, p->pool_size); |
| 7396 | + break; |
| 7397 | + } |
| 7398 | + } |
| 7399 | + |
| 7400 | + if (p->in_flight <= 0) |
| 7401 | + break; |
| 7402 | + |
| 7403 | + vlc_cond_wait(&p->flight_cond, &p->lock); |
| 7404 | + } |
| 7405 | + |
| 7406 | + if (inc_flight) |
| 7407 | + ++p->in_flight; |
| 7408 | + |
| 7409 | + vlc_mutex_unlock(&p->lock); |
| 7410 | + |
| 7411 | + if (v == NULL && req_el_size != 0) |
| 7412 | + v = p->el_alloc_fn(p->alloc_v, req_el_size); |
| 7413 | + |
| 7414 | + // Tag ref |
| 7415 | + if (v != NULL) |
| 7416 | + cma_pool_fixed_ref(p); |
| 7417 | + // Remove flight if we set it and error |
| 7418 | + else if (inc_flight) |
| 7419 | + cma_pool_fixed_dec_in_flight(p); |
| 7420 | + |
| 7421 | + return v; |
| 7422 | +} |
| 7423 | + |
| 7424 | +static void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight) |
| 7425 | +{ |
| 7426 | + vlc_mutex_lock(&p->lock); |
| 7427 | + |
| 7428 | + if (el_size == p->el_size && (p->pool == NULL || p->pool[p->n_out] == NULL)) |
| 7429 | + { |
| 7430 | + if (p->pool == NULL) |
| 7431 | + p->pool = calloc(p->pool_size, sizeof(void*)); |
| 7432 | + |
| 7433 | + p->pool[p->n_out] = v; |
| 7434 | + p->n_out = inc_mod(p->n_out, p->pool_size); |
| 7435 | + v = NULL; |
| 7436 | + } |
| 7437 | + |
| 7438 | + if (was_in_flight) |
| 7439 | + --p->in_flight; |
| 7440 | + |
| 7441 | + vlc_mutex_unlock(&p->lock); |
| 7442 | + |
| 7443 | + vlc_cond_signal(&p->flight_cond); |
| 7444 | + |
| 7445 | + if (v != NULL) |
| 7446 | + p->el_free_fn(p->alloc_v, v, el_size); |
| 7447 | + |
| 7448 | + cma_pool_fixed_unref(p); |
| 7449 | +} |
| 7450 | + |
| 7451 | +static int cma_pool_fixed_resize(cma_pool_fixed_t * const p, |
| 7452 | + const unsigned int new_pool_size, const int new_flight_size) |
| 7453 | +{ |
| 7454 | + void ** dead_pool = NULL; |
| 7455 | + size_t dead_size = 0; |
| 7456 | + unsigned int dead_n = 0; |
| 7457 | + |
| 7458 | + // This makes this non-reentrant but saves us a lot of time in the normal |
| 7459 | + // "nothing happens" case |
| 7460 | + if (p->pool_size == new_pool_size && p->flight_size == new_flight_size) |
| 7461 | + return 0; |
| 7462 | + |
| 7463 | + vlc_mutex_lock(&p->lock); |
| 7464 | + |
| 7465 | + if (p->pool != NULL && new_pool_size != p->pool_size) |
| 7466 | + { |
| 7467 | + void ** const new_pool = calloc(new_pool_size, sizeof(void*)); |
| 7468 | + unsigned int d, s; |
| 7469 | + dead_pool = p->pool; |
| 7470 | + dead_size = p->el_size; |
| 7471 | + dead_n = p->pool_size; |
| 7472 | + |
| 7473 | + if (new_pool == NULL) |
| 7474 | + { |
| 7475 | + vlc_mutex_unlock(&p->lock); |
| 7476 | + return -1; |
| 7477 | + } |
| 7478 | + |
| 7479 | + 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)) |
| 7480 | + dead_pool[s] = NULL; |
| 7481 | + |
| 7482 | + p->n_out = 0; |
| 7483 | + p->n_in = (d != new_pool_size) ? d : 0; |
| 7484 | + p->pool = new_pool; |
| 7485 | + } |
| 7486 | + |
| 7487 | + p->pool_size = new_pool_size; |
| 7488 | + if (new_flight_size > p->flight_size) |
| 7489 | + vlc_cond_broadcast(&p->flight_cond); // Lock still active so nothing happens till we release it |
| 7490 | + p->in_flight += p->flight_size - new_flight_size; |
| 7491 | + p->flight_size = new_flight_size; |
| 7492 | + |
| 7493 | + vlc_mutex_unlock(&p->lock); |
| 7494 | + |
| 7495 | + free_pool(p, dead_pool, dead_n, dead_size); |
| 7496 | + return 0; |
| 7497 | +} |
| 7498 | + |
| 7499 | +static int cma_pool_fixed_fill(cma_pool_fixed_t * const p, const size_t el_size) |
| 7500 | +{ |
| 7501 | + for (;;) |
| 7502 | + { |
| 7503 | + vlc_mutex_lock(&p->lock); |
| 7504 | + bool done = el_size == p->el_size && p->pool != NULL && p->pool[p->n_out] != NULL; |
| 7505 | + vlc_mutex_unlock(&p->lock); |
| 7506 | + if (done) |
| 7507 | + break; |
| 7508 | + void * buf = cma_pool_fixed_get(p, el_size, false, true); |
| 7509 | + if (buf == NULL) |
| 7510 | + return -ENOMEM; |
| 7511 | + cma_pool_fixed_put(p, buf, el_size, false); |
| 7512 | + } |
| 7513 | + return 0; |
| 7514 | +} |
| 7515 | + |
| 7516 | +static void cma_pool_fixed_cancel(cma_pool_fixed_t * const p) |
| 7517 | +{ |
| 7518 | + vlc_mutex_lock(&p->lock); |
| 7519 | + p->cancel = true; |
| 7520 | + vlc_cond_broadcast(&p->flight_cond); |
| 7521 | + vlc_mutex_unlock(&p->lock); |
| 7522 | +} |
| 7523 | + |
| 7524 | +static void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p) |
| 7525 | +{ |
| 7526 | + vlc_mutex_lock(&p->lock); |
| 7527 | + p->cancel = false; |
| 7528 | + vlc_mutex_unlock(&p->lock); |
| 7529 | +} |
| 7530 | + |
| 7531 | + |
| 7532 | +// Purge pool & unref |
| 7533 | +static void cma_pool_fixed_kill(cma_pool_fixed_t * const p) |
| 7534 | +{ |
| 7535 | + if (p == NULL) |
| 7536 | + return; |
| 7537 | + |
| 7538 | + // This flush is not strictly needed but it reclaims what memory we can reclaim asap |
| 7539 | + cma_pool_fixed_get(p, 0, false, false); |
| 7540 | + cma_pool_fixed_unref(p); |
| 7541 | +} |
| 7542 | + |
| 7543 | +// Create a new pool |
| 7544 | +static cma_pool_fixed_t* |
| 7545 | +cma_pool_fixed_new(const unsigned int pool_size, |
| 7546 | + const int flight_size, |
| 7547 | + void * const alloc_v, |
| 7548 | + cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn, |
| 7549 | + cma_pool_on_delete_fn * const on_delete_fn, |
| 7550 | + const char * const name) |
| 7551 | +{ |
| 7552 | + cma_pool_fixed_t* const p = calloc(1, sizeof(cma_pool_fixed_t)); |
| 7553 | + if (p == NULL) |
| 7554 | + return NULL; |
| 7555 | + |
| 7556 | + atomic_store(&p->ref_count, 1); |
| 7557 | + vlc_mutex_init(&p->lock); |
| 7558 | + vlc_cond_init(&p->flight_cond); |
| 7559 | + |
| 7560 | + p->pool_size = pool_size; |
| 7561 | + p->flight_size = flight_size; |
| 7562 | + p->in_flight = -flight_size; |
| 7563 | + |
| 7564 | + p->alloc_v = alloc_v; |
| 7565 | + p->el_alloc_fn = alloc_fn; |
| 7566 | + p->el_free_fn = free_fn; |
| 7567 | + p->on_delete_fn = on_delete_fn; |
| 7568 | + p->name = name == NULL ? NULL : strdup(name); |
| 7569 | +#if TRACE_ALL |
| 7570 | + p->seq = atomic_fetch_add(&pool_seq, 1); |
| 7571 | +#endif |
| 7572 | + |
| 7573 | + return p; |
| 7574 | +} |
| 7575 | + |
| 7576 | +// --------------------------------------------------------------------------- |
| 7577 | +// |
| 7578 | +// CMA buffer functions - uses cma_pool_fixed for pooling |
| 7579 | + |
| 7580 | +struct cma_buf_pool_s { |
| 7581 | + cma_pool_fixed_t * pool; |
| 7582 | + vcsm_init_type_t init_type; |
| 7583 | + |
| 7584 | + bool all_in_flight; |
| 7585 | +#if TRACE_ALL |
| 7586 | + size_t alloc_n; |
| 7587 | + size_t alloc_size; |
| 7588 | +#endif |
| 7589 | +}; |
| 7590 | + |
| 7591 | +typedef struct cma_buf_s { |
| 7592 | + atomic_int ref_count; |
| 7593 | + cma_buf_pool_t * cbp; |
| 7594 | + bool in_flight; |
| 7595 | + size_t size; |
| 7596 | + unsigned int vcsm_h; // VCSM handle from initial alloc |
| 7597 | + unsigned int vc_h; // VC handle for ZC mmal buffers |
| 7598 | + unsigned int vc_addr; // VC addr - unused by us but wanted by FFmpeg |
| 7599 | + int fd; // dmabuf handle for GL |
| 7600 | + void * mmap; // ARM mapped address |
| 7601 | + picture_context_t *ctx2; |
| 7602 | +} cma_buf_t; |
| 7603 | + |
| 7604 | +static void cma_pool_delete(cma_buf_t * const cb) |
| 7605 | +{ |
| 7606 | + assert(atomic_load(&cb->ref_count) == 0); |
| 7607 | +#if TRACE_ALL |
| 7608 | + cb->cbp->alloc_size -= cb->size; |
| 7609 | + --cb->cbp->alloc_n; |
| 7610 | + 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); |
| 7611 | +#endif |
| 7612 | + |
| 7613 | + if (cb->ctx2 != NULL) |
| 7614 | + cb->ctx2->destroy(cb->ctx2); |
| 7615 | + |
| 7616 | + if (cb->mmap != MAP_FAILED) |
| 7617 | + { |
| 7618 | + if (cb->cbp->init_type == VCSM_INIT_CMA) |
| 7619 | + munmap(cb->mmap, cb->size); |
| 7620 | + else |
| 7621 | + vcsm_unlock_hdl(cb->vcsm_h); |
| 7622 | + } |
| 7623 | + if (cb->fd != -1) |
| 7624 | + close(cb->fd); |
| 7625 | + if (cb->vcsm_h != 0) |
| 7626 | + vcsm_free(cb->vcsm_h); |
| 7627 | + free(cb); |
| 7628 | +} |
| 7629 | + |
| 7630 | +static void cma_pool_free_cb(void * v, void * el, size_t size) |
| 7631 | +{ |
| 7632 | + VLC_UNUSED(v); |
| 7633 | + VLC_UNUSED(size); |
| 7634 | + |
| 7635 | + cma_pool_delete(el); |
| 7636 | +} |
| 7637 | + |
| 7638 | +static void * cma_pool_alloc_cb(void * v, size_t size) |
| 7639 | +{ |
| 7640 | + cma_buf_pool_t * const cbp = v; |
| 7641 | + |
| 7642 | + cma_buf_t * const cb = malloc(sizeof(cma_buf_t)); |
| 7643 | + if (cb == NULL) |
| 7644 | + return NULL; |
| 7645 | + |
| 7646 | + *cb = (cma_buf_t){ |
| 7647 | + .ref_count = ATOMIC_VAR_INIT(0), |
| 7648 | + .cbp = cbp, |
| 7649 | + .in_flight = 0, |
| 7650 | + .size = size, |
| 7651 | + .vcsm_h = 0, |
| 7652 | + .vc_h = 0, |
| 7653 | + .fd = -1, |
| 7654 | + .mmap = MAP_FAILED, |
| 7655 | + .ctx2 = NULL |
| 7656 | + }; |
| 7657 | +#if TRACE_ALL |
| 7658 | + cb->cbp->alloc_size += cb->size; |
| 7659 | + ++cb->cbp->alloc_n; |
| 7660 | + fprintf(stderr, "%s[%d:%s]: N=%d, Total=%d\n", __func__, cbp->pool->seq, cbp->pool->name, cbp->alloc_n, cbp->alloc_size); |
| 7661 | +#endif |
| 7662 | + |
| 7663 | + // 0x80 is magic value to force full ARM-side mapping - otherwise |
| 7664 | + // cache requests can cause kernel crashes |
| 7665 | + if ((cb->vcsm_h = vcsm_malloc_cache(size, VCSM_CACHE_TYPE_HOST | 0x80, "VLC frame")) == 0) |
| 7666 | + { |
| 7667 | +#if TRACE_ALL |
| 7668 | + fprintf(stderr, "vcsm_malloc_cache fail\n"); |
| 7669 | +#endif |
| 7670 | + goto fail; |
| 7671 | + } |
| 7672 | + |
| 7673 | + if ((cb->vc_h = vcsm_vc_hdl_from_hdl(cb->vcsm_h)) == 0) |
| 7674 | + { |
| 7675 | +#if TRACE_ALL |
| 7676 | + fprintf(stderr, "vcsm_vc_hdl_from_hdl fail\n"); |
| 7677 | +#endif |
| 7678 | + goto fail; |
| 7679 | + } |
| 7680 | + |
| 7681 | + if (cbp->init_type == VCSM_INIT_CMA) |
| 7682 | + { |
| 7683 | + if ((cb->fd = vcsm_export_dmabuf(cb->vcsm_h)) == -1) |
| 7684 | + { |
| 7685 | +#if TRACE_ALL |
| 7686 | + fprintf(stderr, "vcsm_export_dmabuf fail\n"); |
| 7687 | +#endif |
| 7688 | + goto fail; |
| 7689 | + } |
| 7690 | + |
| 7691 | + if ((cb->mmap = mmap(NULL, cb->size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_LOCKED, cb->fd, 0)) == MAP_FAILED) |
| 7692 | + goto fail; |
| 7693 | + } |
| 7694 | + else |
| 7695 | + { |
| 7696 | + void * arm_addr; |
| 7697 | + if ((arm_addr = vcsm_lock(cb->vcsm_h)) == NULL) |
| 7698 | + { |
| 7699 | +#if TRACE_ALL |
| 7700 | + fprintf(stderr, "vcsm_lock fail\n"); |
| 7701 | +#endif |
| 7702 | + goto fail; |
| 7703 | + } |
| 7704 | + cb->mmap = arm_addr; |
| 7705 | + } |
| 7706 | + |
| 7707 | + cb->vc_addr = vcsm_vc_addr_from_hdl(cb->vcsm_h); |
| 7708 | + |
| 7709 | + return cb; |
| 7710 | + |
| 7711 | +fail: |
| 7712 | + cma_pool_delete(cb); |
| 7713 | + return NULL; |
| 7714 | +} |
| 7715 | + |
| 7716 | +// Pool has died - safe now to exit vcsm |
| 7717 | +static void cma_buf_pool_on_delete_cb(void * v) |
| 7718 | +{ |
| 7719 | + cma_buf_pool_t * const cbp = v; |
| 7720 | + |
| 7721 | + cma_vcsm_exit(cbp->init_type); |
| 7722 | + free(cbp); |
| 7723 | +} |
| 7724 | + |
| 7725 | +void cma_buf_pool_cancel(cma_buf_pool_t * const cbp) |
| 7726 | +{ |
| 7727 | + if (cbp == NULL || cbp->pool == NULL) |
| 7728 | + return; |
| 7729 | + |
| 7730 | + cma_pool_fixed_cancel(cbp->pool); |
| 7731 | +} |
| 7732 | + |
| 7733 | +void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp) |
| 7734 | +{ |
| 7735 | + if (cbp == NULL || cbp->pool == NULL) |
| 7736 | + return; |
| 7737 | + |
| 7738 | + cma_pool_fixed_uncancel(cbp->pool); |
| 7739 | +} |
| 7740 | + |
| 7741 | +// User finished with pool |
| 7742 | +void cma_buf_pool_delete(cma_buf_pool_t * const cbp) |
| 7743 | +{ |
| 7744 | + if (cbp == NULL) |
| 7745 | + return; |
| 7746 | + |
| 7747 | + if (cbp->pool != NULL) |
| 7748 | + { |
| 7749 | + // We will call cma_buf_pool_on_delete_cb when the pool finally dies |
| 7750 | + // (might be now) which will free up our env. |
| 7751 | + cma_pool_fixed_kill(cbp->pool); |
| 7752 | + } |
| 7753 | + else |
| 7754 | + { |
| 7755 | + // Had no pool for some reason (error) but must still finish cleanup |
| 7756 | + cma_buf_pool_on_delete_cb(cbp); |
| 7757 | + } |
| 7758 | +} |
| 7759 | + |
| 7760 | +int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size) |
| 7761 | +{ |
| 7762 | + return cma_pool_fixed_fill(cbp->pool, el_size); |
| 7763 | +} |
| 7764 | + |
| 7765 | +int cma_buf_pool_resize(cma_buf_pool_t * const cbp, |
| 7766 | + const unsigned int new_pool_size, const int new_flight_size) |
| 7767 | +{ |
| 7768 | + return cma_pool_fixed_resize(cbp->pool, new_pool_size, new_flight_size); |
| 7769 | +} |
| 7770 | + |
| 7771 | +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) |
| 7772 | +{ |
| 7773 | + vcsm_init_type_t const init_type = cma_vcsm_init(); |
| 7774 | + if (init_type == VCSM_INIT_NONE) |
| 7775 | + return NULL; |
| 7776 | + |
| 7777 | + cma_buf_pool_t * const cbp = calloc(1, sizeof(cma_buf_pool_t)); |
| 7778 | + if (cbp == NULL) |
| 7779 | + return NULL; |
| 7780 | + |
| 7781 | + cbp->init_type = init_type; |
| 7782 | + cbp->all_in_flight = all_in_flight; |
| 7783 | + |
| 7784 | + 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) |
| 7785 | + goto fail; |
| 7786 | + return cbp; |
| 7787 | + |
| 7788 | +fail: |
| 7789 | + cma_buf_pool_delete(cbp); |
| 7790 | + return NULL; |
| 7791 | +} |
| 7792 | + |
| 7793 | + |
| 7794 | +void cma_buf_in_flight(cma_buf_t * const cb) |
| 7795 | +{ |
| 7796 | + if (!cb->cbp->all_in_flight) |
| 7797 | + { |
| 7798 | + assert(!cb->in_flight); |
| 7799 | + cb->in_flight = true; |
| 7800 | + cma_pool_fixed_inc_in_flight(cb->cbp->pool); |
| 7801 | + } |
| 7802 | +} |
| 7803 | + |
| 7804 | +void cma_buf_end_flight(cma_buf_t * const cb) |
| 7805 | +{ |
| 7806 | + if (cb != NULL && !cb->cbp->all_in_flight && cb->in_flight) |
| 7807 | + { |
| 7808 | + cb->in_flight = false; |
| 7809 | + cma_pool_fixed_dec_in_flight(cb->cbp->pool); |
| 7810 | + } |
| 7811 | +} |
| 7812 | + |
| 7813 | + |
| 7814 | +// Return vcsm handle |
| 7815 | +unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb) |
| 7816 | +{ |
| 7817 | + return cb->vcsm_h; |
| 7818 | +} |
| 7819 | + |
| 7820 | +size_t cma_buf_size(const cma_buf_t * const cb) |
| 7821 | +{ |
| 7822 | + return cb->size; |
| 7823 | +} |
| 7824 | + |
| 7825 | +int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2) |
| 7826 | +{ |
| 7827 | + if (cb->ctx2 != NULL) |
| 7828 | + return VLC_EGENERIC; |
| 7829 | + |
| 7830 | + cb->ctx2 = ctx2; |
| 7831 | + return VLC_SUCCESS; |
| 7832 | +} |
| 7833 | + |
| 7834 | +unsigned int cma_buf_vc_handle(const cma_buf_t *const cb) |
| 7835 | +{ |
| 7836 | + return cb->vc_h; |
| 7837 | +} |
| 7838 | + |
| 7839 | +int cma_buf_fd(const cma_buf_t *const cb) |
| 7840 | +{ |
| 7841 | + return cb->fd; |
| 7842 | +} |
| 7843 | + |
| 7844 | +void * cma_buf_addr(const cma_buf_t *const cb) |
| 7845 | +{ |
| 7846 | + return cb->mmap; |
| 7847 | +} |
| 7848 | + |
| 7849 | +unsigned int cma_buf_vc_addr(const cma_buf_t *const cb) |
| 7850 | +{ |
| 7851 | + return cb->vc_addr; |
| 7852 | +} |
| 7853 | + |
| 7854 | + |
| 7855 | +picture_context_t * cma_buf_context2(const cma_buf_t *const cb) |
| 7856 | +{ |
| 7857 | + return cb->ctx2; |
| 7858 | +} |
| 7859 | + |
| 7860 | + |
| 7861 | +void cma_buf_unref(cma_buf_t * const cb) |
| 7862 | +{ |
| 7863 | + if (cb == NULL) |
| 7864 | + return; |
| 7865 | + if (atomic_fetch_sub(&cb->ref_count, 1) <= 1) |
| 7866 | + { |
| 7867 | + const bool was_in_flight = cb->in_flight; |
| 7868 | + cb->in_flight = false; |
| 7869 | + cma_pool_fixed_put(cb->cbp->pool, cb, cb->size, was_in_flight); |
| 7870 | + } |
| 7871 | +} |
| 7872 | + |
| 7873 | +cma_buf_t * cma_buf_ref(cma_buf_t * const cb) |
| 7874 | +{ |
| 7875 | + if (cb == NULL) |
| 7876 | + return NULL; |
| 7877 | + atomic_fetch_add(&cb->ref_count, 1); |
| 7878 | + return cb; |
| 7879 | +} |
| 7880 | + |
| 7881 | +cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const cbp, const size_t size) |
| 7882 | +{ |
| 7883 | + cma_buf_t *const cb = cma_pool_fixed_get(cbp->pool, size, cbp->all_in_flight, false); |
| 7884 | + |
| 7885 | + if (cb == NULL) |
| 7886 | + return NULL; |
| 7887 | + |
| 7888 | + cb->in_flight = cbp->all_in_flight; |
| 7889 | + // When 1st allocated or retrieved from the pool the block will have a |
| 7890 | + // ref count of 0 so ref here |
| 7891 | + return cma_buf_ref(cb); |
| 7892 | +} |
| 7893 | + |
| 7894 | --- /dev/null |
| 7895 | +++ b/modules/hw/mmal/mmal_cma.h |
| 7896 | @@ -0,0 +1,71 @@ |
| 7897 | +#ifndef VLC_MMAL_MMAL_CMA_H_ |
| 7898 | +#define VLC_MMAL_MMAL_CMA_H_ |
| 7899 | + |
| 7900 | + |
| 7901 | +struct cma_pool_fixed_s; |
| 7902 | +typedef struct cma_pool_fixed_s cma_pool_fixed_t; |
| 7903 | + |
| 7904 | +typedef void * cma_pool_alloc_fn(void * v, size_t size); |
| 7905 | +typedef void cma_pool_free_fn(void * v, void * el, size_t size); |
| 7906 | +typedef void cma_pool_on_delete_fn(void * v); |
| 7907 | + |
| 7908 | +#if 0 |
| 7909 | +void cma_pool_fixed_unref(cma_pool_fixed_t * const p); |
| 7910 | +void cma_pool_fixed_ref(cma_pool_fixed_t * const p); |
| 7911 | +void * cma_pool_fixed_get(cma_pool_fixed_t * const p, const size_t req_el_size, const bool in_flight); |
| 7912 | +void cma_pool_fixed_put(cma_pool_fixed_t * const p, void * v, const size_t el_size, const bool was_in_flight); |
| 7913 | +void cma_pool_fixed_inc_in_flight(cma_pool_fixed_t * const p); |
| 7914 | +void cma_pool_fixed_dec_in_flight(cma_pool_fixed_t * const p); |
| 7915 | +void cma_pool_fixed_cancel(cma_pool_fixed_t * const p); |
| 7916 | +void cma_pool_fixed_uncancel(cma_pool_fixed_t * const p); |
| 7917 | +void cma_pool_fixed_kill(cma_pool_fixed_t * const p); |
| 7918 | +int cma_pool_fixed_resize(cma_pool_fixed_t * const p, |
| 7919 | + const unsigned int new_pool_size, const int new_flight_size); |
| 7920 | +cma_pool_fixed_t * cma_pool_fixed_new(const unsigned int pool_size, |
| 7921 | + const int flight_size, |
| 7922 | + void * const alloc_v, |
| 7923 | + cma_pool_alloc_fn * const alloc_fn, cma_pool_free_fn * const free_fn, |
| 7924 | + cma_pool_on_delete_fn * const on_delete_fn, |
| 7925 | + const char * const name); |
| 7926 | +#endif |
| 7927 | + |
| 7928 | +struct cma_buf_s; |
| 7929 | +typedef struct cma_buf_s cma_buf_t; |
| 7930 | + |
| 7931 | +void cma_buf_in_flight(cma_buf_t * const cb); |
| 7932 | +void cma_buf_end_flight(cma_buf_t * const cb); |
| 7933 | +unsigned int cma_buf_vcsm_handle(const cma_buf_t * const cb); |
| 7934 | +size_t cma_buf_size(const cma_buf_t * const cb); |
| 7935 | +int cma_buf_add_context2(cma_buf_t *const cb, picture_context_t * const ctx2); |
| 7936 | +unsigned int cma_buf_vc_handle(const cma_buf_t *const cb); |
| 7937 | +int cma_buf_fd(const cma_buf_t *const cb); |
| 7938 | +void * cma_buf_addr(const cma_buf_t *const cb); |
| 7939 | +unsigned int cma_buf_vc_addr(const cma_buf_t *const cb); |
| 7940 | +picture_context_t * cma_buf_context2(const cma_buf_t *const cb); |
| 7941 | + |
| 7942 | +void cma_buf_unref(cma_buf_t * const cb); |
| 7943 | +cma_buf_t * cma_buf_ref(cma_buf_t * const cb); |
| 7944 | + |
| 7945 | +struct cma_buf_pool_s; |
| 7946 | +typedef struct cma_buf_pool_s cma_buf_pool_t; |
| 7947 | + |
| 7948 | +cma_buf_t * cma_buf_pool_alloc_buf(cma_buf_pool_t * const p, const size_t size); |
| 7949 | +void cma_buf_pool_cancel(cma_buf_pool_t * const cbp); |
| 7950 | +void cma_buf_pool_uncancel(cma_buf_pool_t * const cbp); |
| 7951 | +void cma_buf_pool_delete(cma_buf_pool_t * const p); |
| 7952 | +int cma_buf_pool_fill(cma_buf_pool_t * const cbp, const size_t el_size); |
| 7953 | +int cma_buf_pool_resize(cma_buf_pool_t * const cbp, |
| 7954 | + const unsigned int new_pool_size, const int new_flight_size); |
| 7955 | +cma_buf_pool_t * cma_buf_pool_new(const unsigned int pool_size, const unsigned int flight_size, |
| 7956 | + const bool all_in_flight, const char * const name); |
| 7957 | + |
| 7958 | +static inline void cma_buf_pool_deletez(cma_buf_pool_t ** const pp) |
| 7959 | +{ |
| 7960 | + cma_buf_pool_t * const p = *pp; |
| 7961 | + if (p != NULL) { |
| 7962 | + *pp = NULL; |
| 7963 | + cma_buf_pool_delete(p); |
| 7964 | + } |
| 7965 | +} |
| 7966 | + |
| 7967 | +#endif // VLC_MMAL_MMAL_CMA_H_ |
| 7968 | --- /dev/null |
| 7969 | +++ b/modules/hw/mmal/mmal_gl.h |
| 7970 | @@ -0,0 +1,45 @@ |
| 7971 | +// Trim this include list! |
| 7972 | + |
| 7973 | +#include <libdrm/drm.h> |
| 7974 | +#include <libdrm/drm_mode.h> |
| 7975 | +#include <libdrm/drm_fourcc.h> |
| 7976 | +//#include <xf86drm.h> |
| 7977 | +//#include <xf86drmMode.h> |
| 7978 | +#include <X11/Xlib.h> |
| 7979 | +#include <X11/Xutil.h> |
| 7980 | +#include <X11/Xlib-xcb.h> |
| 7981 | +#include <epoxy/gl.h> |
| 7982 | +#include <epoxy/egl.h> |
| 7983 | +#include <xcb/xcb.h> |
| 7984 | +#include <xcb/dri3.h> |
| 7985 | + |
| 7986 | +struct mmal_gl_converter_s; |
| 7987 | + |
| 7988 | +typedef struct cma_buf_s { |
| 7989 | + struct mmal_gl_converter_s * sys; |
| 7990 | + |
| 7991 | + size_t size; |
| 7992 | + __u32 h_dumb; |
| 7993 | + int fd; |
| 7994 | + unsigned int h_vcsm; |
| 7995 | + void * mapped_addr; |
| 7996 | + GLuint texture; |
| 7997 | +} cma_buf_t; |
| 7998 | + |
| 7999 | +typedef struct cma_pic_sys_s { |
| 8000 | + cma_buf_t * cmabuf; |
| 8001 | +} cma_pic_sys_t; |
| 8002 | + |
| 8003 | +static inline unsigned int |
| 8004 | +hw_mmal_h_vcsm(const picture_t * const pic) |
| 8005 | +{ |
| 8006 | + const cma_pic_sys_t *const pic_sys = (cma_pic_sys_t *)pic->p_sys; |
| 8007 | + |
| 8008 | + if (pic->format.i_chroma != VLC_CODEC_MMAL_GL_RGB32 || |
| 8009 | + pic_sys == NULL || pic_sys->cmabuf == NULL) { |
| 8010 | + return 0; |
| 8011 | + } |
| 8012 | + |
| 8013 | + return pic_sys->cmabuf->h_vcsm; |
| 8014 | +} |
| 8015 | + |
| 8016 | --- /dev/null |
| 8017 | +++ b/modules/hw/mmal/mmal_piccpy_neon.S |
| 8018 | @@ -0,0 +1,105 @@ |
| 8019 | +// Copy pix |
| 8020 | + |
| 8021 | + .syntax unified |
| 8022 | + .arm |
| 8023 | +// .thumb |
| 8024 | + .text |
| 8025 | + .align 16 |
| 8026 | + .arch armv7-a |
| 8027 | + .fpu neon-vfpv4 |
| 8028 | + |
| 8029 | + |
| 8030 | +.macro function name |
| 8031 | + .global \name |
| 8032 | +#ifdef __ELF__ |
| 8033 | + .type \name, %function |
| 8034 | +#endif |
| 8035 | +\name: |
| 8036 | +.endm |
| 8037 | + |
| 8038 | + |
| 8039 | +.macro piccpy_to_8, bit_depth |
| 8040 | + subs r2, #128 |
| 8041 | + vpush {q4-q7} |
| 8042 | + blt 2f |
| 8043 | +1: |
| 8044 | + vldm r1!, {q0-q7} |
| 8045 | + subs r2, #128 |
| 8046 | + vqrshrn.u16 d0, q0, #\bit_depth - 8 |
| 8047 | + vqrshrn.u16 d1, q1, #\bit_depth - 8 |
| 8048 | + vqrshrn.u16 d2, q2, #\bit_depth - 8 |
| 8049 | + vqrshrn.u16 d3, q3, #\bit_depth - 8 |
| 8050 | + vldm r1!, {q8-q15} |
| 8051 | + vqrshrn.u16 d4, q4, #\bit_depth - 8 |
| 8052 | + vqrshrn.u16 d5, q5, #\bit_depth - 8 |
| 8053 | + vqrshrn.u16 d6, q6, #\bit_depth - 8 |
| 8054 | + vqrshrn.u16 d7, q7, #\bit_depth - 8 |
| 8055 | + vqrshrn.u16 d8, q8, #\bit_depth - 8 |
| 8056 | + vqrshrn.u16 d9, q9, #\bit_depth - 8 |
| 8057 | + vqrshrn.u16 d10, q10, #\bit_depth - 8 |
| 8058 | + vqrshrn.u16 d11, q11, #\bit_depth - 8 |
| 8059 | + vqrshrn.u16 d12, q12, #\bit_depth - 8 |
| 8060 | + vqrshrn.u16 d13, q13, #\bit_depth - 8 |
| 8061 | + vqrshrn.u16 d14, q14, #\bit_depth - 8 |
| 8062 | + vqrshrn.u16 d15, q15, #\bit_depth - 8 |
| 8063 | + vstm r0!, {q0-q7} |
| 8064 | + bge 1b |
| 8065 | +2: |
| 8066 | + adds r2, #64 |
| 8067 | + blt 1f |
| 8068 | + |
| 8069 | + vldm r1!, {q0-q7} |
| 8070 | + vqrshrn.u16 d0, q0, #\bit_depth - 8 |
| 8071 | + vqrshrn.u16 d1, q1, #\bit_depth - 8 |
| 8072 | + vqrshrn.u16 d2, q2, #\bit_depth - 8 |
| 8073 | + vqrshrn.u16 d3, q3, #\bit_depth - 8 |
| 8074 | + vqrshrn.u16 d4, q4, #\bit_depth - 8 |
| 8075 | + vqrshrn.u16 d5, q5, #\bit_depth - 8 |
| 8076 | + vqrshrn.u16 d6, q6, #\bit_depth - 8 |
| 8077 | + vqrshrn.u16 d7, q7, #\bit_depth - 8 |
| 8078 | + vstm r0!, {q0-q3} |
| 8079 | +1: |
| 8080 | + adds r2, #32 |
| 8081 | + blt 1f |
| 8082 | + |
| 8083 | + vldm r1!, {q0-q3} |
| 8084 | + vqrshrn.u16 d0, q0, #\bit_depth - 8 |
| 8085 | + vqrshrn.u16 d1, q1, #\bit_depth - 8 |
| 8086 | + vqrshrn.u16 d2, q2, #\bit_depth - 8 |
| 8087 | + vqrshrn.u16 d3, q3, #\bit_depth - 8 |
| 8088 | + vstm r0!, {q0-q1} |
| 8089 | +1: |
| 8090 | + adds r2, #16 |
| 8091 | + blt 1f |
| 8092 | + |
| 8093 | + vldm r1!, {q0-q1} |
| 8094 | + vqrshrn.u16 d0, q0, #\bit_depth - 8 |
| 8095 | + vqrshrn.u16 d1, q1, #\bit_depth - 8 |
| 8096 | + vstm r0!, {q0} |
| 8097 | +1: |
| 8098 | + adds r2, #8 |
| 8099 | + blt 1f |
| 8100 | + |
| 8101 | + vldm r1!, {q0} |
| 8102 | + vqrshrn.u16 d0, q0, #\bit_depth - 8 |
| 8103 | + vstr d0, [r0] |
| 8104 | + add r0, #8 |
| 8105 | +1: |
| 8106 | + adds r2, #4 |
| 8107 | + blt 1f |
| 8108 | + |
| 8109 | + vldr d0, [r1] |
| 8110 | + vqrshrn.u16 d0, q0, #\bit_depth - 8 |
| 8111 | + vstr s0, [r0] |
| 8112 | +1: |
| 8113 | + vpop {q4-q7} |
| 8114 | + bx lr |
| 8115 | +.endm |
| 8116 | + |
| 8117 | + |
| 8118 | +@ [r0] Dest |
| 8119 | +@ [r1] Src |
| 8120 | +@ r2 Pels |
| 8121 | +function mmal_piccpy_10_to_8_neon |
| 8122 | + piccpy_to_8 10 |
| 8123 | + |
| 8124 | --- a/modules/hw/mmal/mmal_picture.c |
| 8125 | +++ b/modules/hw/mmal/mmal_picture.c |
| 8126 | @@ -21,25 +21,1542 @@ |
| 8127 | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
| 8128 | *****************************************************************************/ |
| 8129 | |
| 8130 | +// We would really like to use vlc_thread.h but the detach thread stuff can't be |
| 8131 | +// used here :-( |
| 8132 | +#include <pthread.h> |
| 8133 | + |
| 8134 | +#include <stdatomic.h> |
| 8135 | +#include <unistd.h> |
| 8136 | +#include <fcntl.h> |
| 8137 | + |
| 8138 | #include <vlc_common.h> |
| 8139 | +#include <vlc_cpu.h> |
| 8140 | #include <vlc_picture.h> |
| 8141 | + |
| 8142 | +#pragma GCC diagnostic push |
| 8143 | +#pragma GCC diagnostic ignored "-Wbad-function-cast" |
| 8144 | +#include <bcm_host.h> |
| 8145 | +#pragma GCC diagnostic pop |
| 8146 | #include <interface/mmal/mmal.h> |
| 8147 | +#include <interface/mmal/util/mmal_util.h> |
| 8148 | +#include <interface/mmal/util/mmal_default_components.h> |
| 8149 | +#include <interface/vmcs_host/vcgencmd.h> |
| 8150 | +#include <interface/vcsm/user-vcsm.h> |
| 8151 | |
| 8152 | +#include "mmal_cma.h" |
| 8153 | #include "mmal_picture.h" |
| 8154 | +#include "transform_ops.h" |
| 8155 | + |
| 8156 | +#define TRACE_TRANSFORMS 0 |
| 8157 | + |
| 8158 | +#define UINT64_SIZE(s) (((s) + sizeof(uint64_t) - 1)/sizeof(uint64_t)) |
| 8159 | + |
| 8160 | +static inline char safe_char(const unsigned int c0) |
| 8161 | +{ |
| 8162 | + const unsigned int c = c0 & 0xff; |
| 8163 | + return c > ' ' && c < 0x7f ? c : '.'; |
| 8164 | +} |
| 8165 | + |
| 8166 | +const char * str_fourcc(char * const buf, const unsigned int fcc) |
| 8167 | +{ |
| 8168 | + if (fcc == 0) |
| 8169 | + return "----"; |
| 8170 | + buf[0] = safe_char(fcc >> 0); |
| 8171 | + buf[1] = safe_char(fcc >> 8); |
| 8172 | + buf[2] = safe_char(fcc >> 16); |
| 8173 | + buf[3] = safe_char(fcc >> 24); |
| 8174 | + buf[4] = 0; |
| 8175 | + return buf; |
| 8176 | +} |
| 8177 | + |
| 8178 | +// WB + Inv |
| 8179 | +static inline void flush_range(void * const start, const size_t len) |
| 8180 | +{ |
| 8181 | + uint64_t buf[UINT64_SIZE(sizeof(struct vcsm_user_clean_invalid2_s) + sizeof(struct vcsm_user_clean_invalid2_block_s))]; |
| 8182 | + struct vcsm_user_clean_invalid2_s * const b = (struct vcsm_user_clean_invalid2_s *)buf; |
| 8183 | + |
| 8184 | + *b = (struct vcsm_user_clean_invalid2_s){ |
| 8185 | + .op_count = 1 |
| 8186 | + }; |
| 8187 | + |
| 8188 | + b->s[0] = (struct vcsm_user_clean_invalid2_block_s){ |
| 8189 | + .invalidate_mode = 3, // wb + invalidate |
| 8190 | + .block_count = 1, |
| 8191 | + .start_address = start, // Rely on clean inv to fix up align & size boundries |
| 8192 | + .block_size = len, |
| 8193 | + .inter_block_stride = 0 |
| 8194 | + }; |
| 8195 | + |
| 8196 | + vcsm_clean_invalid2(b); |
| 8197 | +} |
| 8198 | + |
| 8199 | +MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs) |
| 8200 | +{ |
| 8201 | + switch (vlc_cs) |
| 8202 | + { |
| 8203 | + case COLOR_SPACE_BT601: |
| 8204 | + return MMAL_COLOR_SPACE_ITUR_BT601; |
| 8205 | + case COLOR_SPACE_BT709: |
| 8206 | + return MMAL_COLOR_SPACE_ITUR_BT709; |
| 8207 | + default: |
| 8208 | + break; |
| 8209 | + } |
| 8210 | + return MMAL_COLOR_SPACE_UNKNOWN; |
| 8211 | +} |
| 8212 | + |
| 8213 | +MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc) |
| 8214 | +{ |
| 8215 | + switch (vf_vlc->i_chroma) { |
| 8216 | + case VLC_CODEC_MMAL_ZC_RGB32: |
| 8217 | + case VLC_CODEC_RGB32: |
| 8218 | + { |
| 8219 | + // VLC RGB32 aka RV32 means we have to look at the mask values |
| 8220 | + const uint32_t r = vf_vlc->i_rmask; |
| 8221 | + const uint32_t g = vf_vlc->i_gmask; |
| 8222 | + const uint32_t b = vf_vlc->i_bmask; |
| 8223 | + if (r == 0xff0000 && g == 0xff00 && b == 0xff) |
| 8224 | + return MMAL_ENCODING_BGRA; |
| 8225 | + if (r == 0xff && g == 0xff00 && b == 0xff0000) |
| 8226 | + return MMAL_ENCODING_RGBA; |
| 8227 | + if (r == 0xff000000 && g == 0xff0000 && b == 0xff00) |
| 8228 | + return MMAL_ENCODING_ABGR; |
| 8229 | + if (r == 0xff00 && g == 0xff0000 && b == 0xff000000) |
| 8230 | + return MMAL_ENCODING_ARGB; |
| 8231 | + break; |
| 8232 | + } |
| 8233 | + case VLC_CODEC_RGB16: |
| 8234 | + { |
| 8235 | + // VLC RGB16 aka RV16 means we have to look at the mask values |
| 8236 | + const uint32_t r = vf_vlc->i_rmask; |
| 8237 | + const uint32_t g = vf_vlc->i_gmask; |
| 8238 | + const uint32_t b = vf_vlc->i_bmask; |
| 8239 | + if (r == 0xf800 && g == 0x7e0 && b == 0x1f) |
| 8240 | + return MMAL_ENCODING_RGB16; |
| 8241 | + break; |
| 8242 | + } |
| 8243 | + case VLC_CODEC_I420: |
| 8244 | + case VLC_CODEC_MMAL_ZC_I420: |
| 8245 | + return MMAL_ENCODING_I420; |
| 8246 | + case VLC_CODEC_RGBA: |
| 8247 | + return MMAL_ENCODING_RGBA; |
| 8248 | + case VLC_CODEC_BGRA: |
| 8249 | + return MMAL_ENCODING_BGRA; |
| 8250 | + case VLC_CODEC_ARGB: |
| 8251 | + return MMAL_ENCODING_ARGB; |
| 8252 | + // VLC_CODEC_ABGR does not exist in VLC |
| 8253 | + case VLC_CODEC_MMAL_OPAQUE: |
| 8254 | + return MMAL_ENCODING_OPAQUE; |
| 8255 | + case VLC_CODEC_MMAL_ZC_SAND8: |
| 8256 | + return MMAL_ENCODING_YUVUV128; |
| 8257 | + case VLC_CODEC_MMAL_ZC_SAND10: |
| 8258 | + return MMAL_ENCODING_YUVUV64_10; |
| 8259 | + case VLC_CODEC_MMAL_ZC_SAND30: |
| 8260 | + return MMAL_ENCODING_YUV10_COL; |
| 8261 | + default: |
| 8262 | + break; |
| 8263 | + } |
| 8264 | + return 0; |
| 8265 | +} |
| 8266 | + |
| 8267 | +static void vlc_fmt_to_video_format(MMAL_VIDEO_FORMAT_T *const vf_mmal, const video_frame_format_t * const vf_vlc) |
| 8268 | +{ |
| 8269 | + const unsigned int wmask = (vf_vlc->i_chroma == VLC_CODEC_MMAL_ZC_I420 || |
| 8270 | + vf_vlc->i_chroma == VLC_CODEC_I420) ? 31 : 15; |
| 8271 | + |
| 8272 | + vf_mmal->width = (vf_vlc->i_width + wmask) & ~wmask; |
| 8273 | + vf_mmal->height = (vf_vlc->i_height + 15) & ~15; |
| 8274 | + vf_mmal->crop.x = vf_vlc->i_x_offset; |
| 8275 | + vf_mmal->crop.y = vf_vlc->i_y_offset; |
| 8276 | + vf_mmal->crop.width = vf_vlc->i_visible_width; |
| 8277 | + vf_mmal->crop.height = vf_vlc->i_visible_height; |
| 8278 | + if (vf_vlc->i_sar_num == 0 || vf_vlc->i_sar_den == 0) { |
| 8279 | + vf_mmal->par.num = 1; |
| 8280 | + vf_mmal->par.den = 1; |
| 8281 | + } else { |
| 8282 | + vf_mmal->par.num = vf_vlc->i_sar_num; |
| 8283 | + vf_mmal->par.den = vf_vlc->i_sar_den; |
| 8284 | + } |
| 8285 | + vf_mmal->frame_rate.num = vf_vlc->i_frame_rate; |
| 8286 | + vf_mmal->frame_rate.den = vf_vlc->i_frame_rate_base; |
| 8287 | + vf_mmal->color_space = vlc_to_mmal_color_space(vf_vlc->space); |
| 8288 | +} |
| 8289 | + |
| 8290 | + |
| 8291 | +void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc) |
| 8292 | +{ |
| 8293 | + vlc_fmt_to_video_format(&es_fmt->es->video, vf_vlc); |
| 8294 | +} |
| 8295 | + |
| 8296 | +bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic) |
| 8297 | +{ |
| 8298 | + MMAL_VIDEO_FORMAT_T vf_new_ss; |
| 8299 | + MMAL_VIDEO_FORMAT_T *const vf_old = &es_fmt->es->video; |
| 8300 | + MMAL_VIDEO_FORMAT_T *const vf_new = &vf_new_ss; |
| 8301 | + |
| 8302 | + vlc_fmt_to_video_format(vf_new, &pic->format); |
| 8303 | + |
| 8304 | + // If we have a format that might have come from ffmpeg then rework for |
| 8305 | + // a better guess as to layout. All sand stuff is "special" with regards to |
| 8306 | + // width/height vs real layout so leave as is if that |
| 8307 | + if ((pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420 || |
| 8308 | + pic->format.i_chroma == VLC_CODEC_MMAL_ZC_RGB32) && |
| 8309 | + pic->p[0].i_pixel_pitch != 0) |
| 8310 | + { |
| 8311 | + // Now overwrite width/height with a better guess as to actual layout info |
| 8312 | + vf_new->height = pic->p[0].i_lines; |
| 8313 | + vf_new->width = pic->p[0].i_pitch / pic->p[0].i_pixel_pitch; |
| 8314 | + } |
| 8315 | + |
| 8316 | + if ( |
| 8317 | + vf_new->width != vf_old->width || |
| 8318 | + vf_new->height != vf_old->height || |
| 8319 | + vf_new->crop.x != vf_old->crop.x || |
| 8320 | + vf_new->crop.y != vf_old->crop.y || |
| 8321 | + vf_new->crop.width != vf_old->crop.width || |
| 8322 | + vf_new->crop.height != vf_old->crop.height || |
| 8323 | + vf_new->par.num != vf_old->par.num || |
| 8324 | + vf_new->par.den != vf_old->par.den || |
| 8325 | + // Frame rate ignored |
| 8326 | + vf_new->color_space != vf_old->color_space) |
| 8327 | + { |
| 8328 | +#if 0 |
| 8329 | + char dbuf0[5], dbuf1[5]; |
| 8330 | + printf("%dx%d (%d,%d %dx%d) par:%d/%d %s -> %dx%d (%d,%d %dx%d) par:%d/%d %s\n", |
| 8331 | + vf_old->width , |
| 8332 | + vf_old->height , |
| 8333 | + vf_old->crop.x , |
| 8334 | + vf_old->crop.y , |
| 8335 | + vf_old->crop.width , |
| 8336 | + vf_old->crop.height , |
| 8337 | + vf_old->par.num , |
| 8338 | + vf_old->par.den , |
| 8339 | + str_fourcc(dbuf0, vf_old->color_space) , |
| 8340 | + vf_new->width , |
| 8341 | + vf_new->height , |
| 8342 | + vf_new->crop.x , |
| 8343 | + vf_new->crop.y , |
| 8344 | + vf_new->crop.width , |
| 8345 | + vf_new->crop.height , |
| 8346 | + vf_new->par.num , |
| 8347 | + vf_new->par.den , |
| 8348 | + str_fourcc(dbuf1, vf_new->color_space) ); |
| 8349 | +#endif |
| 8350 | + *vf_old = *vf_new; |
| 8351 | + return true; |
| 8352 | + } |
| 8353 | + return false; |
| 8354 | +} |
| 8355 | + |
| 8356 | + |
| 8357 | +hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port, |
| 8358 | + const unsigned int headers, const uint32_t payload_size) |
| 8359 | +{ |
| 8360 | + hw_mmal_port_pool_ref_t * ppr = calloc(1, sizeof(hw_mmal_port_pool_ref_t)); |
| 8361 | + if (ppr == NULL) |
| 8362 | + return NULL; |
| 8363 | + |
| 8364 | + if ((ppr->pool = mmal_port_pool_create(port, headers, payload_size)) == NULL) |
| 8365 | + goto fail; |
| 8366 | + |
| 8367 | + ppr->port = port; |
| 8368 | + atomic_store(&ppr->refs, 1); |
| 8369 | + return ppr; |
| 8370 | + |
| 8371 | +fail: |
| 8372 | + free(ppr); |
| 8373 | + return NULL; |
| 8374 | +} |
| 8375 | + |
| 8376 | +static void do_detached(void *(*fn)(void *), void * v) |
| 8377 | +{ |
| 8378 | + pthread_t dothread; |
| 8379 | + pthread_create(&dothread, NULL, fn, v); |
| 8380 | + pthread_detach(dothread); |
| 8381 | +} |
| 8382 | + |
| 8383 | +// Destroy a ppr - aranged s.t. it has the correct prototype for a pthread |
| 8384 | +static void * kill_ppr(void * v) |
| 8385 | +{ |
| 8386 | + hw_mmal_port_pool_ref_t * const ppr = v; |
| 8387 | + if (ppr->port->is_enabled) |
| 8388 | + mmal_port_disable(ppr->port); // Avoid annoyed messages from MMAL when we kill the pool |
| 8389 | + mmal_port_pool_destroy(ppr->port, ppr->pool); |
| 8390 | + free(ppr); |
| 8391 | + return NULL; |
| 8392 | +} |
| 8393 | + |
| 8394 | +void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb) |
| 8395 | +{ |
| 8396 | + if (ppr == NULL) |
| 8397 | + return; |
| 8398 | + if (atomic_fetch_sub(&ppr->refs, 1) != 1) |
| 8399 | + return; |
| 8400 | + if (in_cb) |
| 8401 | + do_detached(kill_ppr, ppr); |
| 8402 | + else |
| 8403 | + kill_ppr(ppr); |
| 8404 | +} |
| 8405 | + |
| 8406 | +// Put buffer in port if possible - if not then release to pool |
| 8407 | +// Returns true if sent, false if recycled |
| 8408 | +bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf) |
| 8409 | +{ |
| 8410 | + mmal_buffer_header_reset(buf); |
| 8411 | + buf->user_data = NULL; |
| 8412 | + |
| 8413 | + if (mmal_port_send_buffer(ppr->port, buf) == MMAL_SUCCESS) |
| 8414 | + return true; |
| 8415 | + mmal_buffer_header_release(buf); |
| 8416 | + return false; |
| 8417 | +} |
| 8418 | + |
| 8419 | +MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr) |
| 8420 | +{ |
| 8421 | + MMAL_BUFFER_HEADER_T * buf; |
| 8422 | + MMAL_STATUS_T err = MMAL_SUCCESS; |
| 8423 | + |
| 8424 | + while ((buf = mmal_queue_get(ppr->pool->queue)) != NULL) { |
| 8425 | + if ((err = mmal_port_send_buffer(ppr->port, buf)) != MMAL_SUCCESS) |
| 8426 | + { |
| 8427 | + mmal_queue_put_back(ppr->pool->queue, buf); |
| 8428 | + break; |
| 8429 | + } |
| 8430 | + } |
| 8431 | + return err; |
| 8432 | +} |
| 8433 | + |
| 8434 | + |
| 8435 | +MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj, |
| 8436 | + hw_mmal_port_pool_ref_t ** pppr, |
| 8437 | + MMAL_PORT_T * const port, |
| 8438 | + const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback) |
| 8439 | +{ |
| 8440 | + MMAL_STATUS_T status; |
| 8441 | + |
| 8442 | + port->userdata = (struct MMAL_PORT_USERDATA_T *)obj; |
| 8443 | + |
| 8444 | + status = port_parameter_set_uint32(port, MMAL_PARAMETER_EXTRA_BUFFERS, extra_buffers); |
| 8445 | + if (status != MMAL_SUCCESS) { |
| 8446 | + msg_Err(obj, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)", |
| 8447 | + status, mmal_status_to_string(status)); |
| 8448 | + return status; |
| 8449 | + } |
| 8450 | + |
| 8451 | + status = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, 1); |
| 8452 | + if (status != MMAL_SUCCESS) { |
| 8453 | + msg_Err(obj, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", |
| 8454 | + port->name, status, mmal_status_to_string(status)); |
| 8455 | + return status; |
| 8456 | + } |
| 8457 | + |
| 8458 | + port->format->encoding = MMAL_ENCODING_OPAQUE; |
| 8459 | + port->format->encoding_variant = 0; |
| 8460 | + if ((status = mmal_port_format_commit(port)) != MMAL_SUCCESS) |
| 8461 | + { |
| 8462 | + msg_Err(obj, "Failed to commit format on port %s (status=%"PRIx32" %s)", |
| 8463 | + port->name, status, mmal_status_to_string(status)); |
| 8464 | + return status; |
| 8465 | + } |
| 8466 | + |
| 8467 | + port->buffer_num = 30; |
| 8468 | + port->buffer_size = port->buffer_size_recommended; |
| 8469 | + |
| 8470 | + if ((*pppr = hw_mmal_port_pool_ref_create(port, port->buffer_num, port->buffer_size)) == NULL) { |
| 8471 | + msg_Err(obj, "Failed to create output pool"); |
| 8472 | + return status; |
| 8473 | + } |
| 8474 | + |
| 8475 | + status = mmal_port_enable(port, callback); |
| 8476 | + if (status != MMAL_SUCCESS) { |
| 8477 | + hw_mmal_port_pool_ref_release(*pppr, false); |
| 8478 | + *pppr = NULL; |
| 8479 | + msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)", |
| 8480 | + port->name, status, mmal_status_to_string(status)); |
| 8481 | + return status; |
| 8482 | + } |
| 8483 | + |
| 8484 | + return MMAL_SUCCESS; |
| 8485 | +} |
| 8486 | + |
| 8487 | + |
| 8488 | +void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn) |
| 8489 | +{ |
| 8490 | + pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic_ctx_cmn; |
| 8491 | + unsigned int i; |
| 8492 | + |
| 8493 | + for (i = 0; i != ctx->buf_count; ++i) { |
| 8494 | + if (ctx->bufs[i] != NULL) |
| 8495 | + mmal_buffer_header_release(ctx->bufs[i]); |
| 8496 | + } |
| 8497 | + |
| 8498 | + cma_buf_end_flight(ctx->cb); |
| 8499 | + cma_buf_unref(ctx->cb); |
| 8500 | + |
| 8501 | + free(ctx); |
| 8502 | +} |
| 8503 | + |
| 8504 | +picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn) |
| 8505 | +{ |
| 8506 | + const pic_ctx_mmal_t * const src_ctx = (pic_ctx_mmal_t *)pic_ctx_cmn; |
| 8507 | + pic_ctx_mmal_t * const dst_ctx = calloc(1, sizeof(*dst_ctx)); |
| 8508 | + unsigned int i; |
| 8509 | + |
| 8510 | + if (dst_ctx == NULL) |
| 8511 | + return NULL; |
| 8512 | + |
| 8513 | + // Copy |
| 8514 | + dst_ctx->cmn = src_ctx->cmn; |
| 8515 | + |
| 8516 | + dst_ctx->cb = cma_buf_ref(src_ctx->cb); |
| 8517 | + |
| 8518 | + dst_ctx->buf_count = src_ctx->buf_count; |
| 8519 | + for (i = 0; i != src_ctx->buf_count; ++i) { |
| 8520 | + dst_ctx->bufs[i] = src_ctx->bufs[i]; |
| 8521 | + if (dst_ctx->bufs[i] != NULL) |
| 8522 | + mmal_buffer_header_acquire(dst_ctx->bufs[i]); |
| 8523 | + } |
| 8524 | + |
| 8525 | + return &dst_ctx->cmn; |
| 8526 | +} |
| 8527 | + |
| 8528 | +static MMAL_BOOL_T |
| 8529 | +buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata) |
| 8530 | +{ |
| 8531 | + hw_mmal_port_pool_ref_t * const ppr = userdata; |
| 8532 | + |
| 8533 | + // Kill the callback - otherwise we will go in circles! |
| 8534 | + mmal_buffer_header_pre_release_cb_set(buf, (MMAL_BH_PRE_RELEASE_CB_T)0, NULL); |
| 8535 | + mmal_buffer_header_acquire(buf); // Ref it again |
| 8536 | + |
| 8537 | + // As we have re-acquired the buffer we need a full release |
| 8538 | + // (not continue) to zap the ref count back to zero |
| 8539 | + // This is "safe" 'cos we have already reset the cb |
| 8540 | + hw_mmal_port_pool_ref_recycle(ppr, buf); |
| 8541 | + hw_mmal_port_pool_ref_release(ppr, true); // Assume in callback |
| 8542 | + |
| 8543 | + return MMAL_TRUE; |
| 8544 | +} |
| 8545 | + |
| 8546 | +// Buffer belongs to context on successful return from this fn |
| 8547 | +// is still valid on failure |
| 8548 | +picture_context_t * |
| 8549 | +hw_mmal_gen_context(MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr) |
| 8550 | +{ |
| 8551 | + pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t)); |
| 8552 | + |
| 8553 | + if (ctx == NULL) |
| 8554 | + return NULL; |
| 8555 | + |
| 8556 | + // If we have an associated ppr then ref & set appropriate callbacks |
| 8557 | + if (ppr != NULL) { |
| 8558 | + hw_mmal_port_pool_ref_acquire(ppr); |
| 8559 | + mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr); |
| 8560 | + buf->user_data = NULL; |
| 8561 | + } |
| 8562 | + |
| 8563 | + ctx->cmn.copy = hw_mmal_pic_ctx_copy; |
| 8564 | + ctx->cmn.destroy = hw_mmal_pic_ctx_destroy; |
| 8565 | + |
| 8566 | + ctx->buf_count = 1; |
| 8567 | + ctx->bufs[0] = buf; |
| 8568 | + |
| 8569 | + return &ctx->cmn; |
| 8570 | +} |
| 8571 | + |
| 8572 | +// n is els |
| 8573 | +// * Make NEON! |
| 8574 | +typedef void piccpy_fn(void * dest, const void * src, size_t n); |
| 8575 | + |
| 8576 | +extern piccpy_fn mmal_piccpy_10_to_8_neon; |
| 8577 | + |
| 8578 | +static void piccpy_10_to_8_c(void * dest, const void * src, size_t n) |
| 8579 | +{ |
| 8580 | + uint8_t * d = dest; |
| 8581 | + const uint16_t * s = src; |
| 8582 | + while (n-- != 0) |
| 8583 | + *d++ = *s++ >> 2; |
| 8584 | +} |
| 8585 | + |
| 8586 | +// Do a stride converting copy - if the strides are the same and line_len is |
| 8587 | +// close then do a single block copy - we don't expect to have to preserve |
| 8588 | +// pixels in the output frame |
| 8589 | +static void mem_copy_2d(uint8_t * d_ptr, const size_t d_stride, |
| 8590 | + const uint8_t * s_ptr, const size_t s_stride, |
| 8591 | + size_t lines, const size_t line_len) |
| 8592 | +{ |
| 8593 | + if (s_stride == d_stride && d_stride < line_len + 32) |
| 8594 | + { |
| 8595 | + memcpy(d_ptr, s_ptr, d_stride * lines); |
| 8596 | + } |
| 8597 | + else |
| 8598 | + { |
| 8599 | + while (lines-- != 0) { |
| 8600 | + memcpy(d_ptr, s_ptr, line_len); |
| 8601 | + d_ptr += d_stride; |
| 8602 | + s_ptr += s_stride; |
| 8603 | + } |
| 8604 | + } |
| 8605 | +} |
| 8606 | + |
| 8607 | +// line_len in D units |
| 8608 | +static void mem_copy_2d_10_to_8(uint8_t * d_ptr, const size_t d_stride, |
| 8609 | + const uint8_t * s_ptr, const size_t s_stride, |
| 8610 | + size_t lines, const size_t line_len) |
| 8611 | +{ |
| 8612 | + piccpy_fn * const docpy = vlc_CPU_ARM_NEON() ? mmal_piccpy_10_to_8_neon : piccpy_10_to_8_c; |
| 8613 | + if (s_stride == d_stride * 2 && d_stride < line_len + 32) |
| 8614 | + { |
| 8615 | + docpy(d_ptr, s_ptr, d_stride * lines); |
| 8616 | + } |
| 8617 | + else |
| 8618 | + { |
| 8619 | + while (lines-- != 0) { |
| 8620 | + docpy(d_ptr, s_ptr, line_len); |
| 8621 | + d_ptr += d_stride; |
| 8622 | + s_ptr += s_stride; |
| 8623 | + } |
| 8624 | + } |
| 8625 | +} |
| 8626 | + |
| 8627 | + |
| 8628 | +int hw_mmal_copy_pic_to_buf(void * const buf_data, |
| 8629 | + uint32_t * const pLength, |
| 8630 | + const MMAL_ES_FORMAT_T * const fmt, |
| 8631 | + const picture_t * const pic) |
| 8632 | +{ |
| 8633 | + const MMAL_VIDEO_FORMAT_T *const video = &fmt->es->video; |
| 8634 | + uint8_t * const dest = buf_data; |
| 8635 | + size_t length = 0; |
| 8636 | + |
| 8637 | + //**** Worry about x/y_offsets |
| 8638 | + |
| 8639 | + assert(fmt->encoding == MMAL_ENCODING_I420); |
| 8640 | + |
| 8641 | + switch (pic->format.i_chroma) { |
| 8642 | + case VLC_CODEC_I420: |
| 8643 | + { |
| 8644 | + const size_t y_size = video->width * video->height; |
| 8645 | + mem_copy_2d(dest, video->width, |
| 8646 | + pic->p[0].p_pixels, pic->p[0].i_pitch, |
| 8647 | + video->crop.height, |
| 8648 | + video->crop.width); |
| 8649 | + |
| 8650 | + mem_copy_2d(dest + y_size, video->width / 2, |
| 8651 | + pic->p[1].p_pixels, pic->p[1].i_pitch, |
| 8652 | + video->crop.height / 2, |
| 8653 | + video->crop.width / 2); |
| 8654 | + |
| 8655 | + mem_copy_2d(dest + y_size + y_size / 4, video->width / 2, |
| 8656 | + pic->p[2].p_pixels, pic->p[2].i_pitch, |
| 8657 | + video->crop.height / 2, |
| 8658 | + video->crop.width / 2); |
| 8659 | + |
| 8660 | + // And make sure it is actually in memory |
| 8661 | + length = y_size + y_size / 2; |
| 8662 | + break; |
| 8663 | + } |
| 8664 | + |
| 8665 | + case VLC_CODEC_I420_10L: |
| 8666 | + { |
| 8667 | + const size_t y_size = video->width * video->height; |
| 8668 | + mem_copy_2d_10_to_8(dest, video->width, |
| 8669 | + pic->p[0].p_pixels, pic->p[0].i_pitch, |
| 8670 | + video->crop.height, |
| 8671 | + video->crop.width); |
| 8672 | + |
| 8673 | + mem_copy_2d_10_to_8(dest + y_size, video->width / 2, |
| 8674 | + pic->p[1].p_pixels, pic->p[1].i_pitch, |
| 8675 | + video->crop.height / 2, |
| 8676 | + video->crop.width / 2); |
| 8677 | + |
| 8678 | + mem_copy_2d_10_to_8(dest + y_size + y_size / 4, video->width / 2, |
| 8679 | + pic->p[2].p_pixels, pic->p[2].i_pitch, |
| 8680 | + video->crop.height / 2, |
| 8681 | + video->crop.width / 2); |
| 8682 | + |
| 8683 | + // And make sure it is actually in memory |
| 8684 | + length = y_size + y_size / 2; |
| 8685 | + break; |
| 8686 | + } |
| 8687 | + |
| 8688 | + default: |
| 8689 | + if (pLength != NULL) |
| 8690 | + *pLength = 0; |
| 8691 | + return VLC_EBADVAR; |
| 8692 | + } |
| 8693 | + |
| 8694 | + if (cma_vcsm_type() == VCSM_INIT_LEGACY) { // ** CMA is currently always uncached |
| 8695 | + flush_range(dest, length); |
| 8696 | + } |
| 8697 | + |
| 8698 | + if (pLength != NULL) |
| 8699 | + *pLength = (uint32_t)length; |
| 8700 | + |
| 8701 | + return VLC_SUCCESS; |
| 8702 | +} |
| 8703 | + |
| 8704 | + |
| 8705 | +static MMAL_BOOL_T rep_buf_free_cb(MMAL_BUFFER_HEADER_T *header, void *userdata) |
| 8706 | +{ |
| 8707 | + cma_buf_t * const cb = userdata; |
| 8708 | + VLC_UNUSED(header); |
| 8709 | + |
| 8710 | + cma_buf_unref(cb); |
| 8711 | + return MMAL_FALSE; |
| 8712 | +} |
| 8713 | + |
| 8714 | +static int cma_buf_buf_attach(MMAL_BUFFER_HEADER_T * const buf, cma_buf_t * const cb) |
| 8715 | +{ |
| 8716 | + // Just a CMA buffer - fill in new buffer |
| 8717 | + const uintptr_t vc_h = cma_buf_vc_handle(cb); |
| 8718 | + if (vc_h == 0) |
| 8719 | + return VLC_EGENERIC; |
| 8720 | + |
| 8721 | + mmal_buffer_header_reset(buf); |
| 8722 | + buf->data = (uint8_t *)vc_h; |
| 8723 | + buf->alloc_size = cma_buf_size(cb); |
| 8724 | + buf->length = buf->alloc_size; |
| 8725 | + // Ensure cb remains valid for the duration of this buffer |
| 8726 | + mmal_buffer_header_pre_release_cb_set(buf, rep_buf_free_cb, cma_buf_ref(cb)); |
| 8727 | + return VLC_SUCCESS; |
| 8728 | +} |
| 8729 | + |
| 8730 | +MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic, |
| 8731 | + MMAL_POOL_T * const rep_pool, |
| 8732 | + MMAL_PORT_T * const port, |
| 8733 | + cma_buf_pool_t * const cbp) |
| 8734 | +{ |
| 8735 | + MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(rep_pool->queue); |
| 8736 | + if (buf == NULL) |
| 8737 | + goto fail0; |
| 8738 | + |
| 8739 | + cma_buf_t * const cb = cma_buf_pool_alloc_buf(cbp, port->buffer_size); |
| 8740 | + if (cb == NULL) |
| 8741 | + goto fail1; |
| 8742 | + |
| 8743 | + if (cma_buf_buf_attach(buf, cb) != VLC_SUCCESS) |
| 8744 | + goto fail2; |
| 8745 | + |
| 8746 | + pic_to_buf_copy_props(buf, pic); |
| 8747 | + |
| 8748 | + if (hw_mmal_copy_pic_to_buf(cma_buf_addr(cb), &buf->length, port->format, pic) != VLC_SUCCESS) |
| 8749 | + goto fail2; |
| 8750 | + buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; |
| 8751 | + |
| 8752 | + cma_buf_unref(cb); |
| 8753 | + return buf; |
| 8754 | + |
| 8755 | +fail2: |
| 8756 | + cma_buf_unref(cb); |
| 8757 | +fail1: |
| 8758 | + mmal_buffer_header_release(buf); |
| 8759 | +fail0: |
| 8760 | + return NULL; |
| 8761 | +} |
| 8762 | + |
| 8763 | +MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool) |
| 8764 | +{ |
| 8765 | + pic_ctx_mmal_t *const ctx = (pic_ctx_mmal_t *)pic->context; |
| 8766 | + MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue); |
| 8767 | + |
| 8768 | + if (rep_buf == NULL) |
| 8769 | + return NULL; |
| 8770 | + |
| 8771 | + if (ctx->bufs[0] != NULL) |
| 8772 | + { |
| 8773 | + // Existing buffer - replicate it |
| 8774 | + if (mmal_buffer_header_replicate(rep_buf, ctx->bufs[0]) != MMAL_SUCCESS) |
| 8775 | + goto fail; |
| 8776 | + } |
| 8777 | + else if (ctx->cb != NULL) |
| 8778 | + { |
| 8779 | + // Just a CMA buffer - fill in new buffer |
| 8780 | + if (cma_buf_buf_attach(rep_buf, ctx->cb) != 0) |
| 8781 | + goto fail; |
| 8782 | + } |
| 8783 | + else |
| 8784 | + goto fail; |
| 8785 | + |
| 8786 | + pic_to_buf_copy_props(rep_buf, pic); |
| 8787 | + return rep_buf; |
| 8788 | + |
| 8789 | +fail: |
| 8790 | + mmal_buffer_header_release(rep_buf); |
| 8791 | + return NULL; |
| 8792 | +} |
| 8793 | + |
| 8794 | + |
| 8795 | + |
| 8796 | + |
| 8797 | +int hw_mmal_get_gpu_mem(void) { |
| 8798 | + static int stashed_val = -2; |
| 8799 | + VCHI_INSTANCE_T vchi_instance; |
| 8800 | + VCHI_CONNECTION_T *vchi_connection = NULL; |
| 8801 | + char rbuf[1024] = { 0 }; |
| 8802 | + |
| 8803 | + if (stashed_val >= -1) |
| 8804 | + return stashed_val; |
| 8805 | + |
| 8806 | + if (vchi_initialise(&vchi_instance) != 0) |
| 8807 | + goto fail0; |
| 8808 | + |
| 8809 | + //create a vchi connection |
| 8810 | + if (vchi_connect(NULL, 0, vchi_instance) != 0) |
| 8811 | + goto fail0; |
| 8812 | + |
| 8813 | + vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1); |
| 8814 | + |
| 8815 | + //send the gencmd for the argument |
| 8816 | + if (vc_gencmd_send("get_mem gpu") != 0) |
| 8817 | + goto fail; |
| 8818 | + |
| 8819 | + if (vc_gencmd_read_response(rbuf, sizeof(rbuf) - 1) != 0) |
| 8820 | + goto fail; |
| 8821 | + |
| 8822 | + if (strncmp(rbuf, "gpu=", 4) != 0) |
| 8823 | + goto fail; |
| 8824 | + |
| 8825 | + char *p; |
| 8826 | + unsigned long m = strtoul(rbuf + 4, &p, 10); |
| 8827 | + |
| 8828 | + if (p[0] != 'M' || p[1] != '\0') |
| 8829 | + stashed_val = -1; |
| 8830 | + else |
| 8831 | + stashed_val = (int)m << 20; |
| 8832 | + |
| 8833 | + vc_gencmd_stop(); |
| 8834 | + |
| 8835 | + //close the vchi connection |
| 8836 | + vchi_disconnect(vchi_instance); |
| 8837 | + |
| 8838 | + return stashed_val; |
| 8839 | + |
| 8840 | +fail: |
| 8841 | + vc_gencmd_stop(); |
| 8842 | + vchi_disconnect(vchi_instance); |
| 8843 | +fail0: |
| 8844 | + stashed_val = -1; |
| 8845 | + return -1; |
| 8846 | +}; |
| 8847 | + |
| 8848 | +// =========================================================================== |
| 8849 | + |
| 8850 | +typedef struct pool_ent_s |
| 8851 | +{ |
| 8852 | + struct pool_ent_s * next; |
| 8853 | + struct pool_ent_s * prev; |
| 8854 | + |
| 8855 | + atomic_int ref_count; |
| 8856 | + unsigned int seq; |
| 8857 | + |
| 8858 | + size_t size; |
| 8859 | + |
| 8860 | + int vcsm_hdl; |
| 8861 | + int vc_hdl; |
| 8862 | + void * buf; |
| 8863 | + |
| 8864 | + unsigned int width; |
| 8865 | + unsigned int height; |
| 8866 | + MMAL_FOURCC_T enc_type; |
| 8867 | + |
| 8868 | + picture_t * pic; |
| 8869 | +} pool_ent_t; |
| 8870 | + |
| 8871 | + |
| 8872 | +typedef struct ent_list_hdr_s |
| 8873 | +{ |
| 8874 | + pool_ent_t * ents; |
| 8875 | + pool_ent_t * tail; |
| 8876 | + unsigned int n; |
| 8877 | +} ent_list_hdr_t; |
| 8878 | + |
| 8879 | +#define ENT_LIST_HDR_INIT (ent_list_hdr_t){ \ |
| 8880 | + .ents = NULL, \ |
| 8881 | + .tail = NULL, \ |
| 8882 | + .n = 0 \ |
| 8883 | +} |
| 8884 | + |
| 8885 | +struct vzc_pool_ctl_s |
| 8886 | +{ |
| 8887 | + atomic_int ref_count; |
| 8888 | + |
| 8889 | + ent_list_hdr_t ent_pool; |
| 8890 | + ent_list_hdr_t ents_cur; |
| 8891 | + ent_list_hdr_t ents_prev; |
| 8892 | + |
| 8893 | + unsigned int max_n; |
| 8894 | + unsigned int seq; |
| 8895 | + |
| 8896 | + vlc_mutex_t lock; |
| 8897 | + |
| 8898 | + MMAL_POOL_T * buf_pool; |
| 8899 | + |
| 8900 | + vcsm_init_type_t vcsm_init_type; |
| 8901 | +}; |
| 8902 | + |
| 8903 | +typedef struct vzc_subbuf_ent_s |
| 8904 | +{ |
| 8905 | + pool_ent_t * ent; |
| 8906 | + MMAL_RECT_T pic_rect; |
| 8907 | + MMAL_RECT_T orig_dest_rect; |
| 8908 | + MMAL_DISPLAYREGION_T dreg; |
| 8909 | +} vzc_subbuf_ent_t; |
| 8910 | + |
| 8911 | + |
| 8912 | +static pool_ent_t * ent_extract(ent_list_hdr_t * const elh, pool_ent_t * const ent) |
| 8913 | +{ |
| 8914 | +// printf("List %p [%d]: Ext %p\n", elh, elh->n, ent); |
| 8915 | + |
| 8916 | + if (ent == NULL) |
| 8917 | + return NULL; |
| 8918 | + |
| 8919 | + if (ent->next == NULL) |
| 8920 | + elh->tail = ent->prev; |
| 8921 | + else |
| 8922 | + ent->next->prev = ent->prev; |
| 8923 | + |
| 8924 | + if (ent->prev == NULL) |
| 8925 | + elh->ents = ent->next; |
| 8926 | + else |
| 8927 | + ent->prev->next = ent->next; |
| 8928 | + |
| 8929 | + ent->prev = ent->next = NULL; |
| 8930 | + |
| 8931 | + --elh->n; |
| 8932 | + |
| 8933 | + return ent; // For convienience |
| 8934 | +} |
| 8935 | + |
| 8936 | +static inline pool_ent_t * ent_extract_tail(ent_list_hdr_t * const elh) |
| 8937 | +{ |
| 8938 | + return ent_extract(elh, elh->tail); |
| 8939 | +} |
| 8940 | + |
| 8941 | +static void ent_add_head(ent_list_hdr_t * const elh, pool_ent_t * const ent) |
| 8942 | +{ |
| 8943 | +// printf("List %p [%d]: Add %p\n", elh, elh->n, ent); |
| 8944 | + |
| 8945 | + if ((ent->next = elh->ents) == NULL) |
| 8946 | + elh->tail = ent; |
| 8947 | + else |
| 8948 | + ent->next->prev = ent; |
| 8949 | + |
| 8950 | + ent->prev = NULL; |
| 8951 | + elh->ents = ent; |
| 8952 | + ++elh->n; |
| 8953 | +} |
| 8954 | + |
| 8955 | +static void ent_free(pool_ent_t * const ent) |
| 8956 | +{ |
| 8957 | +// printf("Free ent: %p\n", ent); |
| 8958 | + if (ent != NULL) { |
| 8959 | + // If we still have a ref to a pic - kill it now |
| 8960 | + if (ent->pic != NULL) |
| 8961 | + picture_Release(ent->pic); |
| 8962 | + |
| 8963 | + // Free contents |
| 8964 | + vcsm_unlock_hdl(ent->vcsm_hdl); |
| 8965 | + |
| 8966 | + vcsm_free(ent->vcsm_hdl); |
| 8967 | + |
| 8968 | + free(ent); |
| 8969 | + } |
| 8970 | +} |
| 8971 | + |
| 8972 | +static void ent_free_list(ent_list_hdr_t * const elh) |
| 8973 | +{ |
| 8974 | + pool_ent_t * ent = elh->ents; |
| 8975 | + |
| 8976 | +// printf("Free list: %p [%d]\n", elh, elh->n); |
| 8977 | + |
| 8978 | + *elh = ENT_LIST_HDR_INIT; |
| 8979 | + |
| 8980 | + while (ent != NULL) { |
| 8981 | + pool_ent_t * const t = ent; |
| 8982 | + ent = t->next; |
| 8983 | + ent_free(t); |
| 8984 | + } |
| 8985 | +} |
| 8986 | + |
| 8987 | +static void ent_list_move(ent_list_hdr_t * const dst, ent_list_hdr_t * const src) |
| 8988 | +{ |
| 8989 | +// printf("Move %p->%p\n", src, dst); |
| 8990 | + |
| 8991 | + *dst = *src; |
| 8992 | + *src = ENT_LIST_HDR_INIT; |
| 8993 | +} |
| 8994 | + |
| 8995 | +// Scans "backwards" as that should give us the fastest match if we are |
| 8996 | +// presented with pics in the same order each time |
| 8997 | +static pool_ent_t * ent_list_extract_pic_ent(ent_list_hdr_t * const elh, picture_t * const pic) |
| 8998 | +{ |
| 8999 | + pool_ent_t *ent = elh->tail; |
| 9000 | + |
| 9001 | +// printf("Find list: %p [%d]; pic:%p\n", elh, elh->n, pic); |
| 9002 | + |
| 9003 | + while (ent != NULL) { |
| 9004 | +// printf("Check ent: %p, pic:%p\n", ent, ent->pic); |
| 9005 | + |
| 9006 | + if (ent->pic == pic) |
| 9007 | + return ent_extract(elh, ent); |
| 9008 | + ent = ent->prev; |
| 9009 | + } |
| 9010 | + return NULL; |
| 9011 | +} |
| 9012 | + |
| 9013 | +#define POOL_ENT_ALLOC_BLOCK 0x10000 |
| 9014 | + |
| 9015 | +static pool_ent_t * pool_ent_alloc_new(size_t req_size) |
| 9016 | +{ |
| 9017 | + pool_ent_t * ent = calloc(1, sizeof(*ent)); |
| 9018 | + const size_t alloc_size = (req_size + POOL_ENT_ALLOC_BLOCK - 1) & ~(POOL_ENT_ALLOC_BLOCK - 1); |
| 9019 | + |
| 9020 | + if (ent == NULL) |
| 9021 | + return NULL; |
| 9022 | + |
| 9023 | + ent->next = ent->prev = NULL; |
| 9024 | + |
| 9025 | + // Alloc from vcsm |
| 9026 | + if ((ent->vcsm_hdl = vcsm_malloc_cache(alloc_size, VCSM_CACHE_TYPE_HOST, (char *)"vlc-subpic")) == -1) |
| 9027 | + goto fail1; |
| 9028 | + if ((ent->vc_hdl = vcsm_vc_hdl_from_hdl(ent->vcsm_hdl)) == 0) |
| 9029 | + goto fail2; |
| 9030 | + if ((ent->buf = vcsm_lock(ent->vcsm_hdl)) == NULL) |
| 9031 | + goto fail2; |
| 9032 | + |
| 9033 | + ent->size = alloc_size; |
| 9034 | + return ent; |
| 9035 | + |
| 9036 | +fail2: |
| 9037 | + vcsm_free(ent->vcsm_hdl); |
| 9038 | +fail1: |
| 9039 | + free(ent); |
| 9040 | + return NULL; |
| 9041 | +} |
| 9042 | + |
| 9043 | +static inline pool_ent_t * pool_ent_ref(pool_ent_t * const ent) |
| 9044 | +{ |
| 9045 | +// int n = atomic_fetch_add(&ent->ref_count, 1) + 1; |
| 9046 | +// printf("Ref: %p: %d\n", ent, n); |
| 9047 | + atomic_fetch_add(&ent->ref_count, 1); |
| 9048 | + return ent; |
| 9049 | +} |
| 9050 | + |
| 9051 | +static void pool_recycle(vzc_pool_ctl_t * const pc, pool_ent_t * const ent) |
| 9052 | +{ |
| 9053 | + pool_ent_t * xs = NULL; |
| 9054 | + int n; |
| 9055 | + |
| 9056 | + if (ent == NULL) |
| 9057 | + return; |
| 9058 | + |
| 9059 | + n = atomic_fetch_sub(&ent->ref_count, 1) - 1; |
| 9060 | + |
| 9061 | +// printf("%s: Pool: %p: Ent: %p: %d\n", __func__, &pc->ent_pool, ent, n); |
| 9062 | + |
| 9063 | + if (n != 0) |
| 9064 | + return; |
| 9065 | + |
| 9066 | + if (ent->pic != NULL) { |
| 9067 | + picture_Release(ent->pic); |
| 9068 | + ent->pic = NULL; |
| 9069 | + } |
| 9070 | + |
| 9071 | + vlc_mutex_lock(&pc->lock); |
| 9072 | + |
| 9073 | + // If we have a full pool then extract the LRU and free it |
| 9074 | + // Free done outside mutex |
| 9075 | + if (pc->ent_pool.n >= pc->max_n) |
| 9076 | + xs = ent_extract_tail(&pc->ent_pool); |
| 9077 | + |
| 9078 | + ent_add_head(&pc->ent_pool, ent); |
| 9079 | + |
| 9080 | + vlc_mutex_unlock(&pc->lock); |
| 9081 | + |
| 9082 | + ent_free(xs); |
| 9083 | +} |
| 9084 | + |
| 9085 | +// * This could be made more efficient, but this is easy |
| 9086 | +static void pool_recycle_list(vzc_pool_ctl_t * const pc, ent_list_hdr_t * const elh) |
| 9087 | +{ |
| 9088 | + pool_ent_t * ent; |
| 9089 | + while ((ent = ent_extract_tail(elh)) != NULL) { |
| 9090 | + pool_recycle(pc, ent); |
| 9091 | + } |
| 9092 | +} |
| 9093 | + |
| 9094 | +static pool_ent_t * pool_best_fit(vzc_pool_ctl_t * const pc, size_t req_size) |
| 9095 | +{ |
| 9096 | + pool_ent_t * best = NULL; |
| 9097 | + |
| 9098 | + vlc_mutex_lock(&pc->lock); |
| 9099 | + |
| 9100 | + { |
| 9101 | + pool_ent_t * ent = pc->ent_pool.ents; |
| 9102 | + |
| 9103 | + // Simple scan |
| 9104 | + while (ent != NULL) { |
| 9105 | + if (ent->size >= req_size && ent->size <= req_size * 2 + POOL_ENT_ALLOC_BLOCK && |
| 9106 | + (best == NULL || best->size > ent->size)) |
| 9107 | + best = ent; |
| 9108 | + ent = ent->next; |
| 9109 | + } |
| 9110 | + |
| 9111 | + // extract best from chain if we've found it |
| 9112 | + ent_extract(&pc->ent_pool, best); |
| 9113 | + } |
| 9114 | + |
| 9115 | + vlc_mutex_unlock(&pc->lock); |
| 9116 | + |
| 9117 | + if (best == NULL) |
| 9118 | + best = pool_ent_alloc_new(req_size); |
| 9119 | + |
| 9120 | + if ((best->seq = ++pc->seq) == 0) |
| 9121 | + best->seq = ++pc->seq; // Never allow to be zero |
| 9122 | + |
| 9123 | + atomic_store(&best->ref_count, 1); |
| 9124 | + return best; |
| 9125 | +} |
| 9126 | + |
| 9127 | + |
| 9128 | +const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[] = { VLC_CODEC_RGBA, VLC_CODEC_BGRA, VLC_CODEC_ARGB, 0 }; |
| 9129 | + |
| 9130 | +void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH) |
| 9131 | +{ |
| 9132 | + const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent; |
| 9133 | + *pW = ent->width; |
| 9134 | + *pH = ent->height; |
| 9135 | +} |
| 9136 | + |
| 9137 | +bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt) |
| 9138 | +{ |
| 9139 | + const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent; |
| 9140 | + MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video; |
| 9141 | + |
| 9142 | + es_fmt->type = MMAL_ES_TYPE_VIDEO; |
| 9143 | + es_fmt->encoding = ent->enc_type; |
| 9144 | + es_fmt->encoding_variant = 0; |
| 9145 | + |
| 9146 | + v_fmt->width = ent->width; |
| 9147 | + v_fmt->height = ent->height; |
| 9148 | + v_fmt->crop.x = 0; |
| 9149 | + v_fmt->crop.y = 0; |
| 9150 | + v_fmt->crop.width = ent->width; |
| 9151 | + v_fmt->crop.height = ent->height; |
| 9152 | + |
| 9153 | + return true; |
| 9154 | +} |
| 9155 | + |
| 9156 | +void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf, |
| 9157 | + uint32_t * const pWidth, uint32_t * const pHeight) |
| 9158 | +{ |
| 9159 | + const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent; |
| 9160 | + *pWidth = ent->width; |
| 9161 | + *pHeight = ent->height; |
| 9162 | +} |
| 9163 | + |
| 9164 | + |
| 9165 | +MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf) |
| 9166 | +{ |
| 9167 | + vzc_subbuf_ent_t * sb = buf->user_data; |
| 9168 | + return &sb->dreg; |
| 9169 | +} |
| 9170 | + |
| 9171 | +static inline int rescale_x(int x, int mul, int div) |
| 9172 | +{ |
| 9173 | + return div == 0 ? x * mul : (x * mul + div/2) / div; |
| 9174 | +} |
| 9175 | + |
| 9176 | +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) |
| 9177 | +{ |
| 9178 | + d->x = rescale_x(s->x - div_rect->x, mul_rect->width, div_rect->width) + mul_rect->x; |
| 9179 | + d->y = rescale_x(s->y - div_rect->y, mul_rect->height, div_rect->height) + mul_rect->y; |
| 9180 | + d->width = rescale_x(s->width, mul_rect->width, div_rect->width); |
| 9181 | + d->height = rescale_x(s->height, mul_rect->height, div_rect->height); |
| 9182 | +#if TRACE_TRANSFORMS |
| 9183 | + fprintf(stderr, "(%d,%d %dx%d) * (%d,%d %dx%d) / (%d,%d %dx%d) -> (%d,%d %dx%d)\n", |
| 9184 | + s->x, s->y, s->width, s->height, |
| 9185 | + mul_rect->x, mul_rect->y, mul_rect->width, mul_rect->height, |
| 9186 | + div_rect->x, div_rect->y, div_rect->width, div_rect->height, |
| 9187 | + d->x, d->y, d->width, d->height); |
| 9188 | +#endif |
| 9189 | +} |
| 9190 | + |
| 9191 | +static MMAL_RECT_T |
| 9192 | +rect_untransform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t) |
| 9193 | +{ |
| 9194 | +#if TRACE_TRANSFORMS |
| 9195 | + fprintf(stderr, "t=%d, s=%d,%d:%dx%d, c=%d,%d:%dx%d -> ", (int)t, |
| 9196 | + s.x,s.y,s.width,s.height, |
| 9197 | + c.x,c.y,c.width,c.height); |
| 9198 | +#endif |
| 9199 | + if (is_transform_hflip(t)) |
| 9200 | + s = rect_hflip(s, c); |
| 9201 | + if (is_transform_vflip(t) != 0) |
| 9202 | + s = rect_vflip(s, c); |
| 9203 | + if (is_transform_transpose(t) != 0) |
| 9204 | + s = rect_transpose(s); |
| 9205 | +#if TRACE_TRANSFORMS |
| 9206 | + fprintf(stderr, "s=%d,%d:%dx%d\n", |
| 9207 | + s.x,s.y,s.width,s.height); |
| 9208 | +#endif |
| 9209 | + return s; |
| 9210 | +} |
| 9211 | + |
| 9212 | +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) |
| 9213 | +{ |
| 9214 | + vzc_subbuf_ent_t * sb = buf->user_data; |
| 9215 | + if (scale_rect == NULL) { |
| 9216 | + sb->dreg.dest_rect = sb->orig_dest_rect; |
| 9217 | + sb->dreg.transform = MMAL_DISPLAY_ROT0; |
| 9218 | + } |
| 9219 | + else |
| 9220 | + { |
| 9221 | + // The scale rect has been transposed if we have a transposing |
| 9222 | + // transform - untranspose so we are the same way up as the source |
| 9223 | + const MMAL_RECT_T c = (scale_transform & 4) == 0 ? *scale_rect : rect_transpose(*scale_rect); |
| 9224 | + rescale_rect(&sb->dreg.dest_rect, &sb->orig_dest_rect, |
| 9225 | + &c, &sb->pic_rect); |
| 9226 | + sb->dreg.dest_rect = rect_untransform(sb->dreg.dest_rect, c, scale_transform); |
| 9227 | + sb->dreg.transform = scale_transform; |
| 9228 | + } |
| 9229 | +} |
| 9230 | + |
| 9231 | +unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf) |
| 9232 | +{ |
| 9233 | + vzc_subbuf_ent_t * sb = buf->user_data; |
| 9234 | + return sb->ent->seq; |
| 9235 | +} |
| 9236 | + |
| 9237 | + |
| 9238 | +// The intent with the ents_cur & ents_last stuff is to remember the buffers |
| 9239 | +// we used on the last frame and reuse them on the current one if they are the |
| 9240 | +// same. Unfortunately detection of "is_first" is only a heuristic (there are |
| 9241 | +// no rules governing the order in which things are blended) so we must deal |
| 9242 | +// (fairly) gracefully with it never (or always) being set. |
| 9243 | + |
| 9244 | +// dst_fmt gives the number space in which the destination pixels are specified |
| 9245 | + |
| 9246 | +MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, |
| 9247 | + picture_t * const pic, |
| 9248 | + const MMAL_RECT_T dst_pic_rect, |
| 9249 | + const int x_offset, const int y_offset, |
| 9250 | + const unsigned int alpha, |
| 9251 | + const bool is_first) |
| 9252 | +{ |
| 9253 | + MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(pc->buf_pool->queue); |
| 9254 | + vzc_subbuf_ent_t * sb; |
| 9255 | + |
| 9256 | + if (buf == NULL) |
| 9257 | + return NULL; |
| 9258 | + |
| 9259 | + if ((sb = calloc(1, sizeof(*sb))) == NULL) |
| 9260 | + goto fail1; |
| 9261 | + |
| 9262 | + // If first or we've had a lot of stuff move everything to the last list |
| 9263 | + // (we could deal more gracefully with the "too many" case but it shouldn't |
| 9264 | + // really happen) |
| 9265 | + if (is_first || pc->ents_cur.n >= CTX_BUFS_MAX) { |
| 9266 | + pool_recycle_list(pc, &pc->ents_prev); |
| 9267 | + ent_list_move(&pc->ents_prev, &pc->ents_cur); |
| 9268 | + } |
| 9269 | + |
| 9270 | + sb->dreg.hdr.id = MMAL_PARAMETER_DISPLAYREGION; |
| 9271 | + sb->dreg.hdr.size = sizeof(sb->dreg); |
| 9272 | + buf->user_data = sb; |
| 9273 | + |
| 9274 | + { |
| 9275 | + // ?? Round start offset as well as length |
| 9276 | + const video_format_t *const fmt = &pic->format; |
| 9277 | + |
| 9278 | + const unsigned int bpp = (fmt->i_bits_per_pixel + 7) >> 3; |
| 9279 | + const unsigned int xl = (fmt->i_x_offset & ~15); |
| 9280 | + const unsigned int xr = (fmt->i_x_offset + fmt->i_visible_width + 15) & ~15; |
| 9281 | + const size_t dst_stride = (xr - xl) * bpp; |
| 9282 | + const size_t dst_lines = ((fmt->i_visible_height + 15) & ~15); |
| 9283 | + const size_t dst_size = dst_stride * dst_lines; |
| 9284 | + |
| 9285 | + pool_ent_t * ent = ent_list_extract_pic_ent(&pc->ents_prev, pic); |
| 9286 | + bool needs_copy = false; |
| 9287 | + |
| 9288 | + // If we didn't find ent in last then look in cur in case is_first |
| 9289 | + // isn't working |
| 9290 | + if (ent == NULL) |
| 9291 | + ent = ent_list_extract_pic_ent(&pc->ents_cur, pic); |
| 9292 | + |
| 9293 | +// printf("ent_found: %p\n", ent); |
| 9294 | |
| 9295 | -int mmal_picture_lock(picture_t *picture) |
| 9296 | + if (ent == NULL) |
| 9297 | + { |
| 9298 | + // Need a new ent |
| 9299 | + needs_copy = true; |
| 9300 | + |
| 9301 | + if ((ent = pool_best_fit(pc, dst_size)) == NULL) |
| 9302 | + goto fail2; |
| 9303 | + if ((ent->enc_type = vlc_to_mmal_video_fourcc(&pic->format)) == 0) |
| 9304 | + goto fail2; |
| 9305 | + |
| 9306 | + ent->pic = picture_Hold(pic); |
| 9307 | + } |
| 9308 | + |
| 9309 | + ent_add_head(&pc->ents_cur, ent); |
| 9310 | + |
| 9311 | + sb->ent = pool_ent_ref(ent); |
| 9312 | + hw_mmal_vzc_pool_ref(pc); |
| 9313 | + |
| 9314 | + // Copy data |
| 9315 | + buf->next = NULL; |
| 9316 | + buf->cmd = 0; |
| 9317 | + buf->data = (uint8_t *)(ent->vc_hdl); |
| 9318 | + buf->alloc_size = buf->length = dst_size; |
| 9319 | + buf->offset = 0; |
| 9320 | + buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; |
| 9321 | + buf->pts = buf->dts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN; |
| 9322 | + buf->type->video = (MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T){ |
| 9323 | + .planes = 1, |
| 9324 | + .pitch = { dst_stride } |
| 9325 | + }; |
| 9326 | + |
| 9327 | + // Remember offsets |
| 9328 | + sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT | |
| 9329 | + MMAL_DISPLAY_SET_DEST_RECT | |
| 9330 | + MMAL_DISPLAY_SET_FULLSCREEN | |
| 9331 | + MMAL_DISPLAY_SET_TRANSFORM | |
| 9332 | + MMAL_DISPLAY_SET_ALPHA; |
| 9333 | + |
| 9334 | + sb->dreg.fullscreen = 0; |
| 9335 | + |
| 9336 | + // Will be set later - zero now to avoid any confusion |
| 9337 | + sb->dreg.transform = MMAL_DISPLAY_ROT0; |
| 9338 | + sb->dreg.dest_rect = (MMAL_RECT_T){0, 0, 0, 0}; |
| 9339 | + |
| 9340 | + sb->dreg.alpha = (uint32_t)(alpha & 0xff) | MMAL_DISPLAY_ALPHA_FLAGS_MIX; |
| 9341 | + |
| 9342 | +// 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); |
| 9343 | + |
| 9344 | + sb->dreg.src_rect = (MMAL_RECT_T){ |
| 9345 | + .x = (fmt->i_x_offset - xl), |
| 9346 | + .y = 0, |
| 9347 | + .width = fmt->i_visible_width, |
| 9348 | + .height = fmt->i_visible_height |
| 9349 | + }; |
| 9350 | + |
| 9351 | + sb->pic_rect = dst_pic_rect; |
| 9352 | + |
| 9353 | + sb->orig_dest_rect = (MMAL_RECT_T){ |
| 9354 | + .x = x_offset, |
| 9355 | + .y = y_offset, |
| 9356 | + .width = fmt->i_visible_width, |
| 9357 | + .height = fmt->i_visible_height |
| 9358 | + }; |
| 9359 | + |
| 9360 | + if (needs_copy) |
| 9361 | + { |
| 9362 | + ent->width = dst_stride / bpp; |
| 9363 | + ent->height = dst_lines; |
| 9364 | + |
| 9365 | + // 2D copy |
| 9366 | + { |
| 9367 | + uint8_t *d = ent->buf; |
| 9368 | + const uint8_t *s = pic->p[0].p_pixels + xl * bpp + fmt->i_y_offset * pic->p[0].i_pitch; |
| 9369 | + |
| 9370 | + mem_copy_2d(d, dst_stride, s, pic->p[0].i_pitch, fmt->i_visible_height, dst_stride); |
| 9371 | + |
| 9372 | + // And make sure it is actually in memory |
| 9373 | + if (pc->vcsm_init_type != VCSM_INIT_CMA) { // ** CMA is currently always uncached |
| 9374 | + flush_range(ent->buf, dst_stride * fmt->i_visible_height); |
| 9375 | + } |
| 9376 | + } |
| 9377 | + } |
| 9378 | + } |
| 9379 | + |
| 9380 | + return buf; |
| 9381 | + |
| 9382 | +fail2: |
| 9383 | + free(sb); |
| 9384 | +fail1: |
| 9385 | + mmal_buffer_header_release(buf); |
| 9386 | + return NULL; |
| 9387 | +} |
| 9388 | + |
| 9389 | +void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc) |
| 9390 | +{ |
| 9391 | + pool_recycle_list(pc, &pc->ents_prev); |
| 9392 | + pool_recycle_list(pc, &pc->ents_cur); |
| 9393 | +} |
| 9394 | + |
| 9395 | +static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc) |
| 9396 | +{ |
| 9397 | + |
| 9398 | +// printf("<<< %s\n", __func__); |
| 9399 | + |
| 9400 | + hw_mmal_vzc_pool_flush(pc); |
| 9401 | + |
| 9402 | + ent_free_list(&pc->ent_pool); |
| 9403 | + |
| 9404 | + if (pc->buf_pool != NULL) |
| 9405 | + mmal_pool_destroy(pc->buf_pool); |
| 9406 | + |
| 9407 | + vlc_mutex_destroy(&pc->lock); |
| 9408 | + |
| 9409 | + cma_vcsm_exit(pc->vcsm_init_type); |
| 9410 | + |
| 9411 | +// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash |
| 9412 | + free (pc); |
| 9413 | + |
| 9414 | + // printf(">>> %s\n", __func__); |
| 9415 | +} |
| 9416 | + |
| 9417 | +void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc) |
| 9418 | +{ |
| 9419 | + int n; |
| 9420 | + |
| 9421 | + if (pc == NULL) |
| 9422 | + return; |
| 9423 | + |
| 9424 | + n = atomic_fetch_sub(&pc->ref_count, 1) - 1; |
| 9425 | + |
| 9426 | + if (n != 0) |
| 9427 | + return; |
| 9428 | + |
| 9429 | + hw_mmal_vzc_pool_delete(pc); |
| 9430 | +} |
| 9431 | + |
| 9432 | +void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc) |
| 9433 | +{ |
| 9434 | + atomic_fetch_add(&pc->ref_count, 1); |
| 9435 | +} |
| 9436 | + |
| 9437 | +static MMAL_BOOL_T vcz_pool_release_cb(MMAL_POOL_T * buf_pool, MMAL_BUFFER_HEADER_T *buf, void *userdata) |
| 9438 | +{ |
| 9439 | + vzc_pool_ctl_t * const pc = userdata; |
| 9440 | + vzc_subbuf_ent_t * const sb = buf->user_data; |
| 9441 | + |
| 9442 | + VLC_UNUSED(buf_pool); |
| 9443 | + |
| 9444 | +// printf("<<< %s\n", __func__); |
| 9445 | + |
| 9446 | + if (sb != NULL) { |
| 9447 | + buf->user_data = NULL; |
| 9448 | + pool_recycle(pc, sb->ent); |
| 9449 | + hw_mmal_vzc_pool_release(pc); |
| 9450 | + free(sb); |
| 9451 | + } |
| 9452 | + |
| 9453 | +// printf(">>> %s\n", __func__); |
| 9454 | + |
| 9455 | + return MMAL_TRUE; |
| 9456 | +} |
| 9457 | + |
| 9458 | +vzc_pool_ctl_t * hw_mmal_vzc_pool_new() |
| 9459 | +{ |
| 9460 | + vzc_pool_ctl_t * const pc = calloc(1, sizeof(*pc)); |
| 9461 | + |
| 9462 | + if (pc == NULL) |
| 9463 | + return NULL; |
| 9464 | + |
| 9465 | + if ((pc->vcsm_init_type = cma_vcsm_init()) == VCSM_INIT_NONE) |
| 9466 | + { |
| 9467 | + free(pc); |
| 9468 | + return NULL; |
| 9469 | + } |
| 9470 | + |
| 9471 | + pc->max_n = 8; |
| 9472 | + vlc_mutex_init(&pc->lock); // Must init before potential destruction |
| 9473 | + |
| 9474 | + if ((pc->buf_pool = mmal_pool_create(64, 0)) == NULL) |
| 9475 | + { |
| 9476 | + hw_mmal_vzc_pool_delete(pc); |
| 9477 | + return NULL; |
| 9478 | + } |
| 9479 | + |
| 9480 | + atomic_store(&pc->ref_count, 1); |
| 9481 | + |
| 9482 | + mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc); |
| 9483 | + |
| 9484 | + return pc; |
| 9485 | +} |
| 9486 | + |
| 9487 | +//---------------------------------------------------------------------------- |
| 9488 | + |
| 9489 | + |
| 9490 | +static const uint8_t shift_00[] = {0,0,0,0}; |
| 9491 | +static const uint8_t shift_01[] = {0,1,1,1}; |
| 9492 | + |
| 9493 | +int cma_pic_set_data(picture_t * const pic, |
| 9494 | + const MMAL_ES_FORMAT_T * const mm_esfmt, |
| 9495 | + const MMAL_BUFFER_HEADER_T * const buf) |
| 9496 | { |
| 9497 | - picture_sys_t *pic_sys = picture->p_sys; |
| 9498 | - MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; |
| 9499 | + const MMAL_VIDEO_FORMAT_T * const mm_fmt = &mm_esfmt->es->video; |
| 9500 | + const MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T *const buf_vid = (buf == NULL) ? NULL : &buf->type->video; |
| 9501 | + cma_buf_t *const cb = cma_buf_pic_get(pic); |
| 9502 | + unsigned int planes = 1; |
| 9503 | + |
| 9504 | + uint8_t * const data = cma_buf_addr(cb); |
| 9505 | + if (data == NULL) { |
| 9506 | + return VLC_ENOMEM; |
| 9507 | + } |
| 9508 | + |
| 9509 | + const uint8_t * ws = shift_00; |
| 9510 | + const uint8_t * hs = shift_00; |
| 9511 | + int pb = 1; |
| 9512 | + |
| 9513 | + switch (mm_esfmt->encoding) |
| 9514 | + { |
| 9515 | + case MMAL_ENCODING_ARGB: |
| 9516 | + case MMAL_ENCODING_ABGR: |
| 9517 | + case MMAL_ENCODING_RGBA: |
| 9518 | + case MMAL_ENCODING_BGRA: |
| 9519 | + case MMAL_ENCODING_RGB32: |
| 9520 | + case MMAL_ENCODING_BGR32: |
| 9521 | + pb = 4; |
| 9522 | + break; |
| 9523 | + case MMAL_ENCODING_RGB16: |
| 9524 | + pb = 2; |
| 9525 | + break; |
| 9526 | |
| 9527 | - int offset = 0; |
| 9528 | - picture->p[0].p_pixels = buffer->data; |
| 9529 | - for (int i = 1; i < picture->i_planes; i++) { |
| 9530 | - offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines; |
| 9531 | - picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset; |
| 9532 | + case MMAL_ENCODING_I420: |
| 9533 | + ws = shift_01; |
| 9534 | + hs = shift_01; |
| 9535 | + planes = 3; |
| 9536 | + break; |
| 9537 | + |
| 9538 | + case MMAL_ENCODING_YUVUV128: |
| 9539 | + hs = shift_01; |
| 9540 | + planes = 2; |
| 9541 | + break; |
| 9542 | + |
| 9543 | + default: |
| 9544 | +// msg_Err(p_filter, "%s: Unexpected format", __func__); |
| 9545 | + return VLC_EGENERIC; |
| 9546 | } |
| 9547 | |
| 9548 | - pic_sys->displayed = false; |
| 9549 | + // Fix up SAR if unset |
| 9550 | + if (pic->format.i_sar_den == 0 || pic->format.i_sar_num == 0) { |
| 9551 | + pic->format.i_sar_den = mm_fmt->par.den; |
| 9552 | + pic->format.i_sar_num = mm_fmt->par.num; |
| 9553 | + } |
| 9554 | |
| 9555 | + pic->i_planes = planes; |
| 9556 | + unsigned int offset = 0; |
| 9557 | + for (unsigned int i = 0; i != planes; ++i) { |
| 9558 | + pic->p[i] = (plane_t){ |
| 9559 | + .p_pixels = data + (buf_vid != NULL ? buf_vid->offset[i] : offset), |
| 9560 | + .i_lines = mm_fmt->height >> hs[i], |
| 9561 | + .i_pitch = buf_vid != NULL ? buf_vid->pitch[i] : mm_fmt->width * pb, |
| 9562 | + .i_pixel_pitch = pb, |
| 9563 | + .i_visible_lines = mm_fmt->crop.height >> hs[i], |
| 9564 | + .i_visible_pitch = mm_fmt->crop.width >> ws[i] |
| 9565 | + }; |
| 9566 | + offset += pic->p[i].i_pitch * pic->p[i].i_lines; |
| 9567 | + } |
| 9568 | return VLC_SUCCESS; |
| 9569 | } |
| 9570 | + |
| 9571 | +int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic) |
| 9572 | +{ |
| 9573 | + if (!is_cma_buf_pic_chroma(pic->format.i_chroma)) |
| 9574 | + return VLC_EGENERIC; |
| 9575 | + if (pic->context != NULL) |
| 9576 | + return VLC_EBADVAR; |
| 9577 | + |
| 9578 | + pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t)); |
| 9579 | + |
| 9580 | + if (ctx == NULL) |
| 9581 | + return VLC_ENOMEM; |
| 9582 | + |
| 9583 | + ctx->cmn.copy = hw_mmal_pic_ctx_copy; |
| 9584 | + ctx->cmn.destroy = hw_mmal_pic_ctx_destroy; |
| 9585 | + ctx->buf_count = 1; // cb takes the place of the 1st buf |
| 9586 | + ctx->cb = cb; |
| 9587 | + |
| 9588 | + cma_buf_in_flight(cb); |
| 9589 | + |
| 9590 | + pic->context = &ctx->cmn; |
| 9591 | + return VLC_SUCCESS; |
| 9592 | +} |
| 9593 | + |
| 9594 | +cma_buf_t * cma_buf_pic_get(picture_t * const pic) |
| 9595 | +{ |
| 9596 | + pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; |
| 9597 | + return !is_cma_buf_pic_chroma(pic->format.i_chroma) || ctx == NULL ? 0 : ctx->cb; |
| 9598 | +} |
| 9599 | + |
| 9600 | + |
| 9601 | +//---------------------------------------------------------------------------- |
| 9602 | + |
| 9603 | +/* Returns the type of the Pi being used |
| 9604 | +*/ |
| 9605 | +bool rpi_is_model_pi4(void) { |
| 9606 | + return bcm_host_is_model_pi4(); |
| 9607 | +} |
| 9608 | + |
| 9609 | +// Preferred mode - none->cma on Pi4 otherwise legacy |
| 9610 | +static volatile vcsm_init_type_t last_vcsm_type = VCSM_INIT_NONE; |
| 9611 | + |
| 9612 | +vcsm_init_type_t cma_vcsm_type(void) |
| 9613 | +{ |
| 9614 | + return last_vcsm_type; |
| 9615 | +} |
| 9616 | + |
| 9617 | +vcsm_init_type_t cma_vcsm_init(void) |
| 9618 | +{ |
| 9619 | + vcsm_init_type_t rv = VCSM_INIT_NONE; |
| 9620 | + // We don't bother locking - taking a copy here should be good enough |
| 9621 | + vcsm_init_type_t try_type = last_vcsm_type; |
| 9622 | + |
| 9623 | + if (try_type == VCSM_INIT_NONE) { |
| 9624 | + if (bcm_host_is_fkms_active()) |
| 9625 | + try_type = VCSM_INIT_CMA; |
| 9626 | + else |
| 9627 | + try_type = VCSM_INIT_LEGACY; |
| 9628 | + } |
| 9629 | + |
| 9630 | + if (try_type == VCSM_INIT_CMA) { |
| 9631 | + if (vcsm_init_ex(1, -1) == 0) |
| 9632 | + rv = VCSM_INIT_CMA; |
| 9633 | + else if (vcsm_init_ex(0, -1) == 0) |
| 9634 | + rv = VCSM_INIT_LEGACY; |
| 9635 | + } |
| 9636 | + else |
| 9637 | + { |
| 9638 | + if (vcsm_init_ex(0, -1) == 0) |
| 9639 | + rv = VCSM_INIT_LEGACY; |
| 9640 | + else if (vcsm_init_ex(1, -1) == 0) |
| 9641 | + rv = VCSM_INIT_CMA; |
| 9642 | + } |
| 9643 | + |
| 9644 | + // Just in case this affects vcsm init do after that |
| 9645 | + if (rv != VCSM_INIT_NONE) |
| 9646 | + bcm_host_init(); |
| 9647 | + |
| 9648 | + last_vcsm_type = rv; |
| 9649 | + return rv; |
| 9650 | +} |
| 9651 | + |
| 9652 | +void cma_vcsm_exit(const vcsm_init_type_t init_mode) |
| 9653 | +{ |
| 9654 | + if (init_mode != VCSM_INIT_NONE) |
| 9655 | + { |
| 9656 | + vcsm_exit(); |
| 9657 | + bcm_host_deinit(); // Does nothing but add in case it ever does |
| 9658 | + } |
| 9659 | +} |
| 9660 | + |
| 9661 | +const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode) |
| 9662 | +{ |
| 9663 | + switch (init_mode) |
| 9664 | + { |
| 9665 | + case VCSM_INIT_CMA: |
| 9666 | + return "CMA"; |
| 9667 | + case VCSM_INIT_LEGACY: |
| 9668 | + return "Legacy"; |
| 9669 | + case VCSM_INIT_NONE: |
| 9670 | + return "none"; |
| 9671 | + default: |
| 9672 | + break; |
| 9673 | + } |
| 9674 | + return "???"; |
| 9675 | +} |
| 9676 | + |
| 9677 | + |
| 9678 | --- a/modules/hw/mmal/mmal_picture.h |
| 9679 | +++ b/modules/hw/mmal/mmal_picture.h |
| 9680 | @@ -24,19 +24,298 @@ |
| 9681 | #ifndef VLC_MMAL_MMAL_PICTURE_H_ |
| 9682 | #define VLC_MMAL_MMAL_PICTURE_H_ |
| 9683 | |
| 9684 | +#include <stdatomic.h> |
| 9685 | + |
| 9686 | #include <vlc_common.h> |
| 9687 | #include <interface/mmal/mmal.h> |
| 9688 | |
| 9689 | +#include "mmal_cma.h" |
| 9690 | + |
| 9691 | /* Think twice before changing this. Incorrect values cause havoc. */ |
| 9692 | #define NUM_ACTUAL_OPAQUE_BUFFERS 30 |
| 9693 | |
| 9694 | -struct picture_sys_t { |
| 9695 | - vlc_object_t *owner; |
| 9696 | +#ifndef VLC_TICK_INVALID |
| 9697 | +#define VLC_TICK_INVALID VLC_TS_INVALID |
| 9698 | +#define VLC_VER_3 1 |
| 9699 | +#else |
| 9700 | +#define VLC_VER_3 0 |
| 9701 | +#endif |
| 9702 | + |
| 9703 | +typedef struct mmal_port_pool_ref_s |
| 9704 | +{ |
| 9705 | + atomic_uint refs; |
| 9706 | + MMAL_POOL_T * pool; |
| 9707 | + MMAL_PORT_T * port; |
| 9708 | +} hw_mmal_port_pool_ref_t; |
| 9709 | + |
| 9710 | +typedef struct pic_ctx_subpic_s { |
| 9711 | + picture_t * subpic; |
| 9712 | + int x, y; |
| 9713 | + int alpha; |
| 9714 | +} pic_ctx_subpic_t; |
| 9715 | + |
| 9716 | + |
| 9717 | +#define CTX_BUFS_MAX 4 |
| 9718 | +typedef struct pic_ctx_mmal_s { |
| 9719 | + picture_context_t cmn; // PARENT: Common els at start |
| 9720 | + |
| 9721 | + cma_buf_t * cb; |
| 9722 | + |
| 9723 | + unsigned int buf_count; |
| 9724 | + MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX]; |
| 9725 | + |
| 9726 | +} pic_ctx_mmal_t; |
| 9727 | + |
| 9728 | +const char * str_fourcc(char * const buf, const unsigned int fcc); |
| 9729 | + |
| 9730 | +MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc); |
| 9731 | +MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs); |
| 9732 | +void hw_mmal_vlc_fmt_to_mmal_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc); |
| 9733 | +// Returns true if fmt_changed |
| 9734 | +// frame_rate ignored for compare, but is set if something else is updated |
| 9735 | +bool hw_mmal_vlc_pic_to_mmal_fmt_update(MMAL_ES_FORMAT_T *const es_fmt, const picture_t * const pic); |
| 9736 | + |
| 9737 | +// Copy pic contents into an existing buffer |
| 9738 | +int hw_mmal_copy_pic_to_buf(void * const buf_data, uint32_t * const pLength, |
| 9739 | + const MMAL_ES_FORMAT_T * const fmt, const picture_t * const pic); |
| 9740 | + |
| 9741 | +hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port, |
| 9742 | + const unsigned int headers, const uint32_t payload_size); |
| 9743 | +void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb); |
| 9744 | +bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf); |
| 9745 | +MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr); |
| 9746 | +static inline void hw_mmal_port_pool_ref_acquire(hw_mmal_port_pool_ref_t * const ppr) |
| 9747 | +{ |
| 9748 | + atomic_fetch_add(&ppr->refs, 1); |
| 9749 | +} |
| 9750 | +MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj, |
| 9751 | + hw_mmal_port_pool_ref_t ** pppr, |
| 9752 | + MMAL_PORT_T * const port, |
| 9753 | + const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback); |
| 9754 | + |
| 9755 | +static inline int hw_mmal_pic_has_sub_bufs(picture_t * const pic) |
| 9756 | +{ |
| 9757 | + pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; |
| 9758 | + return ctx->buf_count > 1; |
| 9759 | +} |
| 9760 | + |
| 9761 | +static inline void hw_mmal_pic_sub_buf_add(picture_t * const pic, MMAL_BUFFER_HEADER_T * const sub) |
| 9762 | +{ |
| 9763 | + pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; |
| 9764 | + |
| 9765 | + if (ctx->buf_count >= CTX_BUFS_MAX) { |
| 9766 | + mmal_buffer_header_release(sub); |
| 9767 | + return; |
| 9768 | + } |
| 9769 | + |
| 9770 | + ctx->bufs[ctx->buf_count++] = sub; |
| 9771 | +} |
| 9772 | + |
| 9773 | +static inline MMAL_BUFFER_HEADER_T * hw_mmal_pic_sub_buf_get(picture_t * const pic, const unsigned int n) |
| 9774 | +{ |
| 9775 | + pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context; |
| 9776 | + |
| 9777 | + return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1]; |
| 9778 | +} |
| 9779 | + |
| 9780 | +static inline bool hw_mmal_chroma_is_mmal(const vlc_fourcc_t chroma) |
| 9781 | +{ |
| 9782 | + return |
| 9783 | + chroma == VLC_CODEC_MMAL_OPAQUE || |
| 9784 | + chroma == VLC_CODEC_MMAL_ZC_SAND8 || |
| 9785 | + chroma == VLC_CODEC_MMAL_ZC_SAND10 || |
| 9786 | + chroma == VLC_CODEC_MMAL_ZC_SAND30 || |
| 9787 | + chroma == VLC_CODEC_MMAL_ZC_I420 || |
| 9788 | + chroma == VLC_CODEC_MMAL_ZC_RGB32; |
| 9789 | +} |
| 9790 | + |
| 9791 | +static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic) |
| 9792 | +{ |
| 9793 | + return hw_mmal_chroma_is_mmal(pic->format.i_chroma); |
| 9794 | +} |
| 9795 | + |
| 9796 | +picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn); |
| 9797 | +void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn); |
| 9798 | +picture_context_t * hw_mmal_gen_context( |
| 9799 | + MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr); |
| 9800 | + |
| 9801 | +int hw_mmal_get_gpu_mem(void); |
| 9802 | + |
| 9803 | + |
| 9804 | +static inline MMAL_STATUS_T port_parameter_set_uint32(MMAL_PORT_T * port, uint32_t id, uint32_t val) |
| 9805 | +{ |
| 9806 | + const MMAL_PARAMETER_UINT32_T param = { |
| 9807 | + .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_UINT32_T)}, |
| 9808 | + .value = val |
| 9809 | + }; |
| 9810 | + return mmal_port_parameter_set(port, ¶m.hdr); |
| 9811 | +} |
| 9812 | + |
| 9813 | +static inline MMAL_STATUS_T port_parameter_set_bool(MMAL_PORT_T * const port, const uint32_t id, const bool val) |
| 9814 | +{ |
| 9815 | + const MMAL_PARAMETER_BOOLEAN_T param = { |
| 9816 | + .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_BOOLEAN_T)}, |
| 9817 | + .enable = val |
| 9818 | + }; |
| 9819 | + return mmal_port_parameter_set(port, ¶m.hdr); |
| 9820 | +} |
| 9821 | + |
| 9822 | +static inline MMAL_STATUS_T port_send_replicated(MMAL_PORT_T * const port, MMAL_POOL_T * const rep_pool, |
| 9823 | + MMAL_BUFFER_HEADER_T * const src_buf, |
| 9824 | + const uint64_t seq) |
| 9825 | +{ |
| 9826 | + MMAL_STATUS_T err; |
| 9827 | + MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue); |
| 9828 | + |
| 9829 | + if (rep_buf == NULL) |
| 9830 | + return MMAL_ENOSPC; |
| 9831 | + |
| 9832 | + if ((err = mmal_buffer_header_replicate(rep_buf, src_buf)) != MMAL_SUCCESS) |
| 9833 | + return err; |
| 9834 | + |
| 9835 | + rep_buf->pts = seq; |
| 9836 | + |
| 9837 | + if ((err = mmal_port_send_buffer(port, rep_buf)) != MMAL_SUCCESS) |
| 9838 | + { |
| 9839 | + mmal_buffer_header_release(rep_buf); |
| 9840 | + return err; |
| 9841 | + } |
| 9842 | + |
| 9843 | + return MMAL_SUCCESS; |
| 9844 | +} |
| 9845 | + |
| 9846 | + |
| 9847 | +static inline void pic_to_buf_copy_props(MMAL_BUFFER_HEADER_T * const buf, const picture_t * const pic) |
| 9848 | +{ |
| 9849 | + if (!pic->b_progressive) |
| 9850 | + { |
| 9851 | + buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED; |
| 9852 | + buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED; |
| 9853 | + } |
| 9854 | + else |
| 9855 | + { |
| 9856 | + buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED; |
| 9857 | + buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED; |
| 9858 | + } |
| 9859 | + if (pic->b_top_field_first) |
| 9860 | + { |
| 9861 | + buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST; |
| 9862 | + buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST; |
| 9863 | + } |
| 9864 | + else |
| 9865 | + { |
| 9866 | + buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST; |
| 9867 | + buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST; |
| 9868 | + } |
| 9869 | + buf->pts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN; |
| 9870 | + buf->dts = buf->pts; |
| 9871 | +} |
| 9872 | + |
| 9873 | +static inline void buf_to_pic_copy_props(picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf) |
| 9874 | +{ |
| 9875 | + // Contrary to docn the interlace & tff flags turn up in the header flags rather than the |
| 9876 | + // video specific flags (which appear to be currently unused). |
| 9877 | + pic->b_progressive = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED) == 0; |
| 9878 | + pic->b_top_field_first = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST) != 0; |
| 9879 | + |
| 9880 | + pic->date = buf->pts != MMAL_TIME_UNKNOWN ? buf->pts : |
| 9881 | + buf->dts != MMAL_TIME_UNKNOWN ? buf->dts : |
| 9882 | + VLC_TICK_INVALID; |
| 9883 | +} |
| 9884 | + |
| 9885 | +MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_copied(const picture_t *const pic, |
| 9886 | + MMAL_POOL_T * const rep_pool, |
| 9887 | + MMAL_PORT_T * const port, |
| 9888 | + cma_buf_pool_t * const cbp); |
| 9889 | + |
| 9890 | +MMAL_BUFFER_HEADER_T * hw_mmal_pic_buf_replicated(const picture_t *const pic, MMAL_POOL_T * const rep_pool); |
| 9891 | + |
| 9892 | +struct vzc_pool_ctl_s; |
| 9893 | +typedef struct vzc_pool_ctl_s vzc_pool_ctl_t; |
| 9894 | + |
| 9895 | +// At the moment we cope with any mono-planar RGBA thing |
| 9896 | +// We could cope with many other things but they currently don't occur |
| 9897 | +extern const vlc_fourcc_t hw_mmal_vzc_subpicture_chromas[]; |
| 9898 | +static inline bool hw_mmal_vzc_subpic_fmt_valid(const video_frame_format_t * const vf_vlc) |
| 9899 | +{ |
| 9900 | + const vlc_fourcc_t vfcc_src = vf_vlc->i_chroma; |
| 9901 | + for (const vlc_fourcc_t * p = hw_mmal_vzc_subpicture_chromas; *p != 0; ++p) |
| 9902 | + if (*p == vfcc_src) |
| 9903 | + return true; |
| 9904 | + |
| 9905 | + return false; |
| 9906 | +} |
| 9907 | + |
| 9908 | +bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt); |
| 9909 | +MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf); |
| 9910 | +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); |
| 9911 | +void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH); |
| 9912 | +unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf); |
| 9913 | +MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic, |
| 9914 | + const MMAL_RECT_T dst_pic_rect, |
| 9915 | + const int x_offset, const int y_offset, |
| 9916 | + const unsigned int alpha, const bool is_first); |
| 9917 | +void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf, |
| 9918 | + uint32_t * const pWidth, uint32_t * const pHeight); |
| 9919 | + |
| 9920 | +void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc); |
| 9921 | +void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc); |
| 9922 | +void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc); |
| 9923 | +vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void); |
| 9924 | + |
| 9925 | + |
| 9926 | +static inline MMAL_RECT_T vis_mmal_rect(const video_format_t * const fmt) |
| 9927 | +{ |
| 9928 | + return (MMAL_RECT_T){ |
| 9929 | + .x = fmt->i_x_offset, |
| 9930 | + .y = fmt->i_y_offset, |
| 9931 | + .width = fmt->i_visible_width, |
| 9932 | + .height = fmt->i_visible_height |
| 9933 | + }; |
| 9934 | +} |
| 9935 | + |
| 9936 | +int cma_pic_set_data(picture_t * const pic, |
| 9937 | + const MMAL_ES_FORMAT_T * const mm_esfmt, |
| 9938 | + const MMAL_BUFFER_HEADER_T * const buf); |
| 9939 | + |
| 9940 | +// Attaches cma buf to pic |
| 9941 | +// Marks in_flight if not all_in_flight anyway |
| 9942 | +int cma_buf_pic_attach(cma_buf_t * const cb, picture_t * const pic); |
| 9943 | +// Returns a pointer to the cma_buf attached to the pic |
| 9944 | +// Just a pointer - doesn't add a ref |
| 9945 | +cma_buf_t * cma_buf_pic_get(picture_t * const pic); |
| 9946 | + |
| 9947 | +static inline bool is_cma_buf_pic_chroma(const uint32_t chroma) |
| 9948 | +{ |
| 9949 | + return chroma == VLC_CODEC_MMAL_ZC_RGB32 || |
| 9950 | + chroma == VLC_CODEC_MMAL_ZC_SAND8 || |
| 9951 | + chroma == VLC_CODEC_MMAL_ZC_SAND10 || |
| 9952 | + chroma == VLC_CODEC_MMAL_ZC_SAND30 || |
| 9953 | + chroma == VLC_CODEC_MMAL_ZC_I420; |
| 9954 | +} |
| 9955 | + |
| 9956 | + |
| 9957 | +int rpi_get_model_type(void); |
| 9958 | +bool rpi_is_model_pi4(void); |
| 9959 | +bool rpi_is_fkms_active(void); |
| 9960 | + |
| 9961 | +typedef enum vcsm_init_type_e { |
| 9962 | + VCSM_INIT_NONE = 0, |
| 9963 | + VCSM_INIT_LEGACY, |
| 9964 | + VCSM_INIT_CMA |
| 9965 | +} vcsm_init_type_t; |
| 9966 | + |
| 9967 | +vcsm_init_type_t cma_vcsm_init(void); |
| 9968 | +void cma_vcsm_exit(const vcsm_init_type_t init_mode); |
| 9969 | +vcsm_init_type_t cma_vcsm_type(void); |
| 9970 | +const char * cma_vcsm_init_str(const vcsm_init_type_t init_mode); |
| 9971 | + |
| 9972 | |
| 9973 | - MMAL_BUFFER_HEADER_T *buffer; |
| 9974 | - bool displayed; |
| 9975 | -}; |
| 9976 | +#define VOUT_DISPLAY_CHANGE_MMAL_BASE 1024 |
| 9977 | +#define VOUT_DISPLAY_CHANGE_MMAL_HIDE (VOUT_DISPLAY_CHANGE_MMAL_BASE + 0) |
| 9978 | |
| 9979 | -int mmal_picture_lock(picture_t *picture); |
| 9980 | +#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize" |
| 9981 | +#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp" |
| 9982 | +#define MMAL_COMPONENT_HVS "vc.ril.hvs" |
| 9983 | |
| 9984 | #endif |
| 9985 | --- /dev/null |
| 9986 | +++ b/modules/hw/mmal/rpi_prof.h |
| 9987 | @@ -0,0 +1,110 @@ |
| 9988 | +#ifndef RPI_PROFILE_H |
| 9989 | +#define RPI_PROFILE_H |
| 9990 | + |
| 9991 | +#include <stdint.h> |
| 9992 | +#include <inttypes.h> |
| 9993 | + |
| 9994 | +#ifndef RPI_PROFILE |
| 9995 | +#define RPI_PROFILE 0 |
| 9996 | +#endif |
| 9997 | + |
| 9998 | +#if RPI_PROFILE |
| 9999 | + |
| 10000 | +#include "v7_pmu.h" |
| 10001 | + |
| 10002 | +#ifdef RPI_PROC_ALLOC |
| 10003 | +#define X volatile |
| 10004 | +#define Z =0 |
| 10005 | +#else |
| 10006 | +#define X extern volatile |
| 10007 | +#define Z |
| 10008 | +#endif |
| 10009 | + |
| 10010 | +X uint64_t av_rpi_prof0_cycles Z; |
| 10011 | +X unsigned int av_rpi_prof0_cnt Z; |
| 10012 | +#define RPI_prof0_MAX_DURATION 100000 |
| 10013 | + |
| 10014 | +X uint64_t av_rpi_prof1_cycles Z; |
| 10015 | +X unsigned int av_rpi_prof1_cnt Z; |
| 10016 | +#define RPI_prof1_MAX_DURATION 100000 |
| 10017 | + |
| 10018 | +X uint64_t av_rpi_prof2_cycles Z; |
| 10019 | +X unsigned int av_rpi_prof2_cnt Z; |
| 10020 | +#define RPI_prof2_MAX_DURATION 10000 |
| 10021 | + |
| 10022 | +X uint64_t av_rpi_prof_n_cycles[128]; |
| 10023 | +X unsigned int av_rpi_prof_n_cnt[128]; |
| 10024 | +#define RPI_prof_n_MAX_DURATION 10000 |
| 10025 | + |
| 10026 | + |
| 10027 | +#undef X |
| 10028 | +#undef Z |
| 10029 | + |
| 10030 | +#define PROFILE_INIT()\ |
| 10031 | +do {\ |
| 10032 | + enable_pmu();\ |
| 10033 | + enable_ccnt();\ |
| 10034 | +} while (0) |
| 10035 | + |
| 10036 | +#define PROFILE_START()\ |
| 10037 | +do {\ |
| 10038 | + volatile uint32_t perf_1 = read_ccnt();\ |
| 10039 | + volatile uint32_t perf_2 |
| 10040 | + |
| 10041 | + |
| 10042 | +#define PROFILE_ACC(x)\ |
| 10043 | + perf_2 = read_ccnt();\ |
| 10044 | + {\ |
| 10045 | + const uint32_t duration = perf_2 - perf_1;\ |
| 10046 | + if (duration < RPI_##x##_MAX_DURATION)\ |
| 10047 | + {\ |
| 10048 | + av_rpi_##x##_cycles += duration;\ |
| 10049 | + av_rpi_##x##_cnt += 1;\ |
| 10050 | + }\ |
| 10051 | + }\ |
| 10052 | +} while(0) |
| 10053 | + |
| 10054 | + |
| 10055 | +#define PROFILE_ACC_N(n)\ |
| 10056 | + if ((n) >= 0) {\ |
| 10057 | + perf_2 = read_ccnt();\ |
| 10058 | + {\ |
| 10059 | + const uint32_t duration = perf_2 - perf_1;\ |
| 10060 | + if (duration < RPI_prof_n_MAX_DURATION)\ |
| 10061 | + {\ |
| 10062 | + av_rpi_prof_n_cycles[n] += duration;\ |
| 10063 | + av_rpi_prof_n_cnt[n] += 1;\ |
| 10064 | + }\ |
| 10065 | + }\ |
| 10066 | + }\ |
| 10067 | +} while(0) |
| 10068 | + |
| 10069 | +#define PROFILE_PRINTF(x)\ |
| 10070 | + printf("%-20s cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", #x, av_rpi_##x##_cycles, av_rpi_##x##_cnt,\ |
| 10071 | + av_rpi_##x##_cnt == 0 ? (uint64_t)0 : av_rpi_##x##_cycles / (uint64_t)av_rpi_##x##_cnt) |
| 10072 | + |
| 10073 | +#define PROFILE_PRINTF_N(n)\ |
| 10074 | + 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],\ |
| 10075 | + av_rpi_prof_n_cnt[n] == 0 ? (uint64_t)0 : av_rpi_prof_n_cycles[n] / (uint64_t)av_rpi_prof_n_cnt[n]) |
| 10076 | + |
| 10077 | +#define PROFILE_CLEAR_N(n) \ |
| 10078 | +do {\ |
| 10079 | + av_rpi_prof_n_cycles[n] = 0;\ |
| 10080 | + av_rpi_prof_n_cnt[n] = 0;\ |
| 10081 | +} while(0) |
| 10082 | + |
| 10083 | +#else |
| 10084 | + |
| 10085 | +// No profile |
| 10086 | +#define PROFILE_INIT() |
| 10087 | +#define PROFILE_START() |
| 10088 | +#define PROFILE_ACC(x) |
| 10089 | +#define PROFILE_ACC_N(x) |
| 10090 | +#define PROFILE_PRINTF(x) |
| 10091 | +#define PROFILE_PRINTF_N(x) |
| 10092 | +#define PROFILE_CLEAR_N(n) |
| 10093 | + |
| 10094 | +#endif |
| 10095 | + |
| 10096 | +#endif |
| 10097 | + |
| 10098 | --- /dev/null |
| 10099 | +++ b/modules/hw/mmal/subpic.c |
| 10100 | @@ -0,0 +1,257 @@ |
| 10101 | +/***************************************************************************** |
| 10102 | + * mmal.c: MMAL-based decoder plugin for Raspberry Pi |
| 10103 | + ***************************************************************************** |
| 10104 | + * Authors: jc@kynesim.co.uk |
| 10105 | + * |
| 10106 | + * This program is free software; you can redistribute it and/or modify it |
| 10107 | + * under the terms of the GNU Lesser General Public License as published by |
| 10108 | + * the Free Software Foundation; either version 2.1 of the License, or |
| 10109 | + * (at your option) any later version. |
| 10110 | + * |
| 10111 | + * This program is distributed in the hope that it will be useful, |
| 10112 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10113 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10114 | + * GNU Lesser General Public License for more details. |
| 10115 | + * |
| 10116 | + * You should have received a copy of the GNU Lesser General Public License |
| 10117 | + * along with this program; if not, write to the Free Software Foundation, |
| 10118 | + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
| 10119 | + *****************************************************************************/ |
| 10120 | + |
| 10121 | +#ifdef HAVE_CONFIG_H |
| 10122 | +#include "config.h" |
| 10123 | +#endif |
| 10124 | + |
| 10125 | +#include <stdatomic.h> |
| 10126 | + |
| 10127 | +#include <vlc_common.h> |
| 10128 | +#include <vlc_plugin.h> |
| 10129 | +#include <vlc_codec.h> |
| 10130 | +#include <vlc_filter.h> |
| 10131 | +#include <vlc_threads.h> |
| 10132 | + |
| 10133 | +#include <bcm_host.h> |
| 10134 | +#include <interface/mmal/mmal.h> |
| 10135 | +#include <interface/mmal/util/mmal_util.h> |
| 10136 | +#include <interface/mmal/util/mmal_default_components.h> |
| 10137 | + |
| 10138 | +#include "mmal_picture.h" |
| 10139 | +#include "subpic.h" |
| 10140 | + |
| 10141 | + |
| 10142 | +#define TRACE_ALL 0 |
| 10143 | + |
| 10144 | +static inline bool cmp_rect(const MMAL_RECT_T * const a, const MMAL_RECT_T * const b) |
| 10145 | +{ |
| 10146 | + return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height; |
| 10147 | +} |
| 10148 | + |
| 10149 | +void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const sub) |
| 10150 | +{ |
| 10151 | + VLC_UNUSED(p_filter); |
| 10152 | + if (sub->port != NULL && sub->port->is_enabled) |
| 10153 | + mmal_port_disable(sub->port); |
| 10154 | + sub->seq = 0; |
| 10155 | +} |
| 10156 | + |
| 10157 | +void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe) |
| 10158 | +{ |
| 10159 | + hw_mmal_subpic_flush(p_filter, spe); |
| 10160 | + |
| 10161 | + if (spe->pool != NULL) |
| 10162 | + mmal_pool_destroy(spe->pool); |
| 10163 | + |
| 10164 | + // Zap to avoid any accidental reuse |
| 10165 | + *spe = (subpic_reg_stash_t){NULL}; |
| 10166 | +} |
| 10167 | + |
| 10168 | +MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, |
| 10169 | + const int display_id, const unsigned int layer) |
| 10170 | +{ |
| 10171 | + MMAL_STATUS_T err; |
| 10172 | + |
| 10173 | + // Start by zapping all to zero |
| 10174 | + *spe = (subpic_reg_stash_t){NULL}; |
| 10175 | + |
| 10176 | + if ((err = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) |
| 10177 | + { |
| 10178 | + msg_Err(p_filter, "Failed to set sub port zero copy"); |
| 10179 | + return err; |
| 10180 | + } |
| 10181 | + |
| 10182 | + if ((spe->pool = mmal_pool_create(30, 0)) == NULL) |
| 10183 | + { |
| 10184 | + msg_Err(p_filter, "Failed to create sub pool"); |
| 10185 | + return MMAL_ENOMEM; |
| 10186 | + } |
| 10187 | + |
| 10188 | + port->userdata = (void *)p_filter; |
| 10189 | + spe->port = port; |
| 10190 | + spe->display_id = display_id; |
| 10191 | + spe->layer = layer; |
| 10192 | + |
| 10193 | + return MMAL_SUCCESS; |
| 10194 | +} |
| 10195 | + |
| 10196 | +static void conv_subpic_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) |
| 10197 | +{ |
| 10198 | +#if TRACE_ALL |
| 10199 | + msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, user=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld", |
| 10200 | + __func__, buf->cmd, buf->user_data, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts); |
| 10201 | +#else |
| 10202 | + VLC_UNUSED(port); |
| 10203 | +#endif |
| 10204 | + |
| 10205 | + mmal_buffer_header_release(buf); // Will extract & release pic in pool callback |
| 10206 | +} |
| 10207 | + |
| 10208 | +static int |
| 10209 | +subpic_send_empty(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, const uint64_t pts) |
| 10210 | +{ |
| 10211 | + MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(spe->pool->queue); |
| 10212 | + MMAL_STATUS_T err; |
| 10213 | + |
| 10214 | + if (buf == NULL) { |
| 10215 | + msg_Err(p_filter, "Buffer get for subpic failed"); |
| 10216 | + return -1; |
| 10217 | + } |
| 10218 | +#if TRACE_ALL |
| 10219 | + msg_Dbg(p_filter, "Remove pic for sub %d", spe->seq); |
| 10220 | +#endif |
| 10221 | + buf->cmd = 0; |
| 10222 | + buf->data = NULL; |
| 10223 | + buf->alloc_size = 0; |
| 10224 | + buf->offset = 0; |
| 10225 | + buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; |
| 10226 | + buf->pts = pts; |
| 10227 | + buf->dts = MMAL_TIME_UNKNOWN; |
| 10228 | + buf->user_data = NULL; |
| 10229 | + |
| 10230 | + if ((err = mmal_port_send_buffer(spe->port, buf)) != MMAL_SUCCESS) |
| 10231 | + { |
| 10232 | + msg_Err(p_filter, "Send buffer to subput failed"); |
| 10233 | + mmal_buffer_header_release(buf); |
| 10234 | + return -1; |
| 10235 | + } |
| 10236 | + return 0; |
| 10237 | +} |
| 10238 | + |
| 10239 | +// < 0 Error |
| 10240 | +// 0 Done & stop |
| 10241 | +// 1 Done & continue |
| 10242 | + |
| 10243 | +int hw_mmal_subpic_update(vlc_object_t * const p_filter, |
| 10244 | + MMAL_BUFFER_HEADER_T * const sub_buf, |
| 10245 | + subpic_reg_stash_t * const spe, |
| 10246 | + const video_format_t * const fmt, |
| 10247 | + const MMAL_RECT_T * const scale_out, |
| 10248 | + const MMAL_DISPLAYTRANSFORM_T transform_out, |
| 10249 | + const uint64_t pts) |
| 10250 | +{ |
| 10251 | + MMAL_STATUS_T err; |
| 10252 | + |
| 10253 | + if (sub_buf == NULL) |
| 10254 | + { |
| 10255 | + if (spe->port->is_enabled && spe->seq != 0) |
| 10256 | + { |
| 10257 | + subpic_send_empty(p_filter, spe, pts); |
| 10258 | + spe->seq = 0; |
| 10259 | + } |
| 10260 | + } |
| 10261 | + else |
| 10262 | + { |
| 10263 | + const unsigned int seq = hw_mmal_vzc_buf_seq(sub_buf); |
| 10264 | + bool needs_update = (spe->seq != seq); |
| 10265 | + |
| 10266 | + hw_mmal_vzc_buf_scale_dest_rect(sub_buf, scale_out, transform_out); |
| 10267 | + |
| 10268 | + if (hw_mmal_vzc_buf_set_format(sub_buf, spe->port->format)) |
| 10269 | + { |
| 10270 | + MMAL_DISPLAYREGION_T * const dreg = hw_mmal_vzc_buf_region(sub_buf); |
| 10271 | + MMAL_VIDEO_FORMAT_T *const v_fmt = &spe->port->format->es->video; |
| 10272 | + |
| 10273 | + v_fmt->frame_rate.den = fmt->i_frame_rate_base; |
| 10274 | + v_fmt->frame_rate.num = fmt->i_frame_rate; |
| 10275 | + v_fmt->par.den = fmt->i_sar_den; |
| 10276 | + v_fmt->par.num = fmt->i_sar_num; |
| 10277 | + v_fmt->color_space = MMAL_COLOR_SPACE_UNKNOWN; |
| 10278 | + |
| 10279 | + if (needs_update || dreg->alpha != spe->alpha || !cmp_rect(&dreg->dest_rect, &spe->dest_rect)) { |
| 10280 | + |
| 10281 | + spe->alpha = dreg->alpha; |
| 10282 | + spe->dest_rect = dreg->dest_rect; |
| 10283 | + needs_update = true; |
| 10284 | + |
| 10285 | + if (spe->display_id >= 0) |
| 10286 | + { |
| 10287 | + dreg->display_num = spe->display_id; |
| 10288 | + dreg->set |= MMAL_DISPLAY_SET_NUM; |
| 10289 | + } |
| 10290 | + dreg->layer = spe->layer; |
| 10291 | + dreg->set |= MMAL_DISPLAY_SET_LAYER; |
| 10292 | + |
| 10293 | +#if TRACE_ALL |
| 10294 | + msg_Dbg(p_filter, "%s: Update region: Set=%x, dest=%dx%d @ (%d,%d), src=%dx%d @ (%d,%d), layer=%d, alpha=%#x", |
| 10295 | + __func__, dreg->set, |
| 10296 | + dreg->dest_rect.width, dreg->dest_rect.height, dreg->dest_rect.x, dreg->dest_rect.y, |
| 10297 | + dreg->src_rect.width, dreg->src_rect.height, dreg->src_rect.x, dreg->src_rect.y, |
| 10298 | + dreg->layer, dreg->alpha); |
| 10299 | +#endif |
| 10300 | + |
| 10301 | + // If now completely offscreen just flush this & return |
| 10302 | + // We only do -ve as (a) that is easy and (b) it seems to be |
| 10303 | + // something that can confuse mmal |
| 10304 | + if (dreg->dest_rect.y + dreg->dest_rect.height <= 0 || |
| 10305 | + dreg->dest_rect.x + dreg->dest_rect.width <= 0) |
| 10306 | + { |
| 10307 | + if (spe->port->is_enabled) |
| 10308 | + subpic_send_empty(p_filter, spe, pts); |
| 10309 | + spe->seq = seq; |
| 10310 | + return 1; |
| 10311 | + } |
| 10312 | + |
| 10313 | + if ((err = mmal_port_parameter_set(spe->port, &dreg->hdr)) != MMAL_SUCCESS) |
| 10314 | + { |
| 10315 | + msg_Err(p_filter, "Set display region on subput failed"); |
| 10316 | + return -1; |
| 10317 | + } |
| 10318 | + |
| 10319 | + if ((err = mmal_port_format_commit(spe->port)) != MMAL_SUCCESS) |
| 10320 | + { |
| 10321 | + msg_Dbg(p_filter, "%s: Subpic commit fail: %d", __func__, err); |
| 10322 | + return -1; |
| 10323 | + } |
| 10324 | + } |
| 10325 | + } |
| 10326 | + |
| 10327 | + if (!spe->port->is_enabled) |
| 10328 | + { |
| 10329 | + spe->port->buffer_num = 30; |
| 10330 | + spe->port->buffer_size = spe->port->buffer_size_recommended; // Not used but shuts up the error checking |
| 10331 | + |
| 10332 | + if ((err = mmal_port_enable(spe->port, conv_subpic_cb)) != MMAL_SUCCESS) |
| 10333 | + { |
| 10334 | + msg_Dbg(p_filter, "%s: Subpic enable fail: %d", __func__, err); |
| 10335 | + return -1; |
| 10336 | + } |
| 10337 | + } |
| 10338 | + |
| 10339 | + if (needs_update) |
| 10340 | + { |
| 10341 | +#if TRACE_ALL |
| 10342 | + msg_Dbg(p_filter, "Update pic for sub %d", spe->seq); |
| 10343 | +#endif |
| 10344 | + if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS) |
| 10345 | + { |
| 10346 | + msg_Err(p_filter, "Send buffer to subput failed"); |
| 10347 | + return -1; |
| 10348 | + } |
| 10349 | + |
| 10350 | + spe->seq = seq; |
| 10351 | + } |
| 10352 | + } |
| 10353 | + return 1; |
| 10354 | +} |
| 10355 | + |
| 10356 | + |
| 10357 | + |
| 10358 | --- /dev/null |
| 10359 | +++ b/modules/hw/mmal/subpic.h |
| 10360 | @@ -0,0 +1,33 @@ |
| 10361 | +#ifndef VLC_HW_MMAL_SUBPIC_H_ |
| 10362 | +#define VLC_HW_MMAL_SUBPIC_H_ |
| 10363 | + |
| 10364 | +typedef struct subpic_reg_stash_s |
| 10365 | +{ |
| 10366 | + MMAL_PORT_T * port; |
| 10367 | + MMAL_POOL_T * pool; |
| 10368 | + int display_id; // -1 => do not set |
| 10369 | + unsigned int layer; |
| 10370 | + // Shadow vars so we can tell if stuff has changed |
| 10371 | + MMAL_RECT_T dest_rect; |
| 10372 | + unsigned int alpha; |
| 10373 | + unsigned int seq; |
| 10374 | +} subpic_reg_stash_t; |
| 10375 | + |
| 10376 | +int hw_mmal_subpic_update(vlc_object_t * const p_filter, |
| 10377 | + MMAL_BUFFER_HEADER_T * const sub_buf, |
| 10378 | + subpic_reg_stash_t * const spe, |
| 10379 | + const video_format_t * const fmt, |
| 10380 | + const MMAL_RECT_T * const scale_out, |
| 10381 | + const MMAL_DISPLAYTRANSFORM_T transform_out, |
| 10382 | + const uint64_t pts); |
| 10383 | + |
| 10384 | +void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe); |
| 10385 | + |
| 10386 | +void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe); |
| 10387 | + |
| 10388 | +// If display id is -1 it will be unset |
| 10389 | +MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, |
| 10390 | + const int display_id, const unsigned int layer); |
| 10391 | + |
| 10392 | +#endif |
| 10393 | + |
| 10394 | --- /dev/null |
| 10395 | +++ b/modules/hw/mmal/transform_ops.h |
| 10396 | @@ -0,0 +1,99 @@ |
| 10397 | +#ifndef VLC_MMAL_TRANSFORM_OPS_H |
| 10398 | +#define VLC_MMAL_TRANSFORM_OPS_H |
| 10399 | + |
| 10400 | +#include <vlc_common.h> |
| 10401 | +#include <vlc_picture.h> |
| 10402 | +#include <interface/mmal/mmal.h> |
| 10403 | + |
| 10404 | + |
| 10405 | +// These are enums with the same order so simply coerce |
| 10406 | +static inline MMAL_DISPLAYTRANSFORM_T vlc_to_mmal_transform(const video_orientation_t orientation){ |
| 10407 | + return (MMAL_DISPLAYTRANSFORM_T)orientation; |
| 10408 | +} |
| 10409 | + |
| 10410 | +// MMAL headers comment these (getting 2 a bit wrong) but do not give |
| 10411 | +// defines |
| 10412 | +#define XFORM_H_SHIFT 0 // Hflip |
| 10413 | +#define XFORM_V_SHIFT 1 // Vflip |
| 10414 | +#define XFORM_T_SHIFT 2 // Transpose |
| 10415 | +#define XFORM_H_BIT (1 << XFORM_H_SHIFT) |
| 10416 | +#define XFORM_V_BIT (1 << XFORM_V_SHIFT) |
| 10417 | +#define XFORM_T_BIT (1 << XFORM_T_SHIFT) |
| 10418 | + |
| 10419 | +static inline bool |
| 10420 | +is_transform_transpose(const MMAL_DISPLAYTRANSFORM_T t) |
| 10421 | +{ |
| 10422 | + return ((unsigned int)t & XFORM_T_BIT) != 0; |
| 10423 | +} |
| 10424 | + |
| 10425 | +static inline bool |
| 10426 | +is_transform_hflip(const MMAL_DISPLAYTRANSFORM_T t) |
| 10427 | +{ |
| 10428 | + return ((unsigned int)t & XFORM_H_BIT) != 0; |
| 10429 | +} |
| 10430 | + |
| 10431 | +static inline bool |
| 10432 | +is_transform_vflip(const MMAL_DISPLAYTRANSFORM_T t) |
| 10433 | +{ |
| 10434 | + return ((unsigned int)t & XFORM_V_BIT) != 0; |
| 10435 | +} |
| 10436 | + |
| 10437 | +static inline MMAL_DISPLAYTRANSFORM_T |
| 10438 | +swap_transform_hv(const MMAL_DISPLAYTRANSFORM_T x) |
| 10439 | +{ |
| 10440 | + return (((x >> XFORM_H_SHIFT) & 1) << XFORM_V_SHIFT) | |
| 10441 | + (((x >> XFORM_V_SHIFT) & 1) << XFORM_H_SHIFT) | |
| 10442 | + (x & XFORM_T_BIT); |
| 10443 | +} |
| 10444 | + |
| 10445 | +static inline MMAL_DISPLAYTRANSFORM_T |
| 10446 | +transform_inverse(const MMAL_DISPLAYTRANSFORM_T x) |
| 10447 | +{ |
| 10448 | + return is_transform_transpose(x) ? swap_transform_hv(x) : x; |
| 10449 | +} |
| 10450 | + |
| 10451 | +// Transform generated by A then B |
| 10452 | +// All ops are self inverse so can simply be XORed on their own |
| 10453 | +// H & V flips after a transpose need to be swapped |
| 10454 | +static inline MMAL_DISPLAYTRANSFORM_T |
| 10455 | +combine_transform(const MMAL_DISPLAYTRANSFORM_T a, const MMAL_DISPLAYTRANSFORM_T b) |
| 10456 | +{ |
| 10457 | + return a ^ (is_transform_transpose(a) ? swap_transform_hv(b) : b); |
| 10458 | +} |
| 10459 | + |
| 10460 | +static inline MMAL_RECT_T |
| 10461 | +rect_transpose(const MMAL_RECT_T s) |
| 10462 | +{ |
| 10463 | + return (MMAL_RECT_T){ |
| 10464 | + .x = s.y, |
| 10465 | + .y = s.x, |
| 10466 | + .width = s.height, |
| 10467 | + .height = s.width |
| 10468 | + }; |
| 10469 | +} |
| 10470 | + |
| 10471 | +// hflip s in c |
| 10472 | +static inline MMAL_RECT_T rect_hflip(const MMAL_RECT_T s, const MMAL_RECT_T c) |
| 10473 | +{ |
| 10474 | + return (MMAL_RECT_T){ |
| 10475 | + .x = c.x + (c.x + c.width) - (s.x + s.width), |
| 10476 | + .y = s.y, |
| 10477 | + .width = s.width, |
| 10478 | + .height = s.height |
| 10479 | + }; |
| 10480 | +} |
| 10481 | + |
| 10482 | +// vflip s in c |
| 10483 | +static inline MMAL_RECT_T rect_vflip(const MMAL_RECT_T s, const MMAL_RECT_T c) |
| 10484 | +{ |
| 10485 | + return (MMAL_RECT_T){ |
| 10486 | + .x = s.x, |
| 10487 | + .y = (c.y + c.height) - (s.y - c.y) - s.height, |
| 10488 | + .width = s.width, |
| 10489 | + .height = s.height |
| 10490 | + }; |
| 10491 | +} |
| 10492 | + |
| 10493 | + |
| 10494 | +#endif |
| 10495 | + |
| 10496 | --- /dev/null |
| 10497 | +++ b/modules/hw/mmal/v7_pmu.S |
| 10498 | @@ -0,0 +1,263 @@ |
| 10499 | +/*------------------------------------------------------------ |
| 10500 | +Performance Monitor Block |
| 10501 | +------------------------------------------------------------*/ |
| 10502 | + .arm @ Make sure we are in ARM mode. |
| 10503 | + .text |
| 10504 | + .align 2 |
| 10505 | + .global getPMN @ export this function for the linker |
| 10506 | + |
| 10507 | +/* Returns the number of progammable counters uint32_t getPMN(void) */ |
| 10508 | + |
| 10509 | +getPMN: |
| 10510 | + MRC p15, 0, r0, c9, c12, 0 /* Read PMNC Register */ |
| 10511 | + MOV r0, r0, LSR #11 /* Shift N field down to bit 0 */ |
| 10512 | + AND r0, r0, #0x1F /* Mask to leave just the 5 N bits */ |
| 10513 | + BX lr |
| 10514 | + |
| 10515 | + |
| 10516 | + |
| 10517 | + .global pmn_config @ export this function for the linker |
| 10518 | + /* Sets the event for a programmable counter to record */ |
| 10519 | + /* void pmn_config(unsigned counter, uint32_t event) */ |
| 10520 | + /* counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1 */ |
| 10521 | + /* event = r1 = The event code */ |
| 10522 | +pmn_config: |
| 10523 | + AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ |
| 10524 | + MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ |
| 10525 | + MCR p15, 0, r1, c9, c13, 1 /* Write EVTSELx Register */ |
| 10526 | + BX lr |
| 10527 | + |
| 10528 | + |
| 10529 | + |
| 10530 | + .global ccnt_divider @ export this function for the linker |
| 10531 | + /* Enables/disables the divider (1/64) on CCNT */ |
| 10532 | + /* void ccnt_divider(int divider) */ |
| 10533 | + /* divider = r0 = If 0 disable divider, else enable dvider */ |
| 10534 | +ccnt_divider: |
| 10535 | + MRC p15, 0, r1, c9, c12, 0 /* Read PMNC */ |
| 10536 | + |
| 10537 | + CMP r0, #0x0 /* IF (r0 == 0) */ |
| 10538 | + BICEQ r1, r1, #0x08 /* THEN: Clear the D bit (disables the */ |
| 10539 | + ORRNE r1, r1, #0x08 /* ELSE: Set the D bit (enables the di */ |
| 10540 | + |
| 10541 | + MCR p15, 0, r1, c9, c12, 0 /* Write PMNC */ |
| 10542 | + BX lr |
| 10543 | + |
| 10544 | + |
| 10545 | + /* --------------------------------------------------------------- */ |
| 10546 | + /* Enable/Disable */ |
| 10547 | + /* --------------------------------------------------------------- */ |
| 10548 | + |
| 10549 | + .global enable_pmu @ export this function for the linker |
| 10550 | + /* Global PMU enable */ |
| 10551 | + /* void enable_pmu(void) */ |
| 10552 | +enable_pmu: |
| 10553 | + MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ |
| 10554 | + ORR r0, r0, #0x01 /* Set E bit */ |
| 10555 | + MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ |
| 10556 | + BX lr |
| 10557 | + |
| 10558 | + |
| 10559 | + |
| 10560 | + .global disable_pmu @ export this function for the linker |
| 10561 | + /* Global PMU disable */ |
| 10562 | + /* void disable_pmu(void) */ |
| 10563 | +disable_pmu: |
| 10564 | + MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ |
| 10565 | + BIC r0, r0, #0x01 /* Clear E bit */ |
| 10566 | + MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ |
| 10567 | + BX lr |
| 10568 | + |
| 10569 | + |
| 10570 | + |
| 10571 | + .global enable_ccnt @ export this function for the linker |
| 10572 | + /* Enable the CCNT */ |
| 10573 | + /* void enable_ccnt(void) */ |
| 10574 | +enable_ccnt: |
| 10575 | + MOV r0, #0x80000000 /* Set C bit */ |
| 10576 | + MCR p15, 0, r0, c9, c12, 1 /* Write CNTENS Register */ |
| 10577 | + BX lr |
| 10578 | + |
| 10579 | + |
| 10580 | + |
| 10581 | + .global disable_ccnt @ export this function for the linker |
| 10582 | + /* Disable the CCNT */ |
| 10583 | + /* void disable_ccnt(void) */ |
| 10584 | +disable_ccnt: |
| 10585 | + MOV r0, #0x80000000 /* Clear C bit */ |
| 10586 | + MCR p15, 0, r0, c9, c12, 2 /* Write CNTENC Register */ |
| 10587 | + BX lr |
| 10588 | + |
| 10589 | + |
| 10590 | + |
| 10591 | + .global enable_pmn @ export this function for the linker |
| 10592 | + /* Enable PMN{n} */ |
| 10593 | + /* void enable_pmn(uint32_t counter) */ |
| 10594 | + /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) |
| 10595 | +enable_pmn: */ |
| 10596 | + MOV r1, #0x1 /* Use arg (r0) to set which counter t */ |
| 10597 | + MOV r1, r1, LSL r0 |
| 10598 | + |
| 10599 | + MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ |
| 10600 | + BX lr |
| 10601 | + |
| 10602 | + |
| 10603 | + |
| 10604 | + .global disable_pmn @ export this function for the linker |
| 10605 | + /* Enable PMN{n} */ |
| 10606 | + /* void disable_pmn(uint32_t counter) */ |
| 10607 | + /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) |
| 10608 | +disable_pmn: */ |
| 10609 | + MOV r1, #0x1 /* Use arg (r0) to set which counter t */ |
| 10610 | + MOV r1, r1, LSL r0 |
| 10611 | + |
| 10612 | + MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */ |
| 10613 | + BX lr |
| 10614 | + |
| 10615 | + |
| 10616 | + |
| 10617 | + .global enable_pmu_user_access @ export this function for the linker |
| 10618 | + /* Enables User mode access to the PMU (must be called in a priviledge */ |
| 10619 | + /* void enable_pmu_user_access(void) */ |
| 10620 | +enable_pmu_user_access: |
| 10621 | + MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ |
| 10622 | + ORR r0, r0, #0x01 /* Set EN bit (bit 0) */ |
| 10623 | + MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ |
| 10624 | + BX lr |
| 10625 | + |
| 10626 | + |
| 10627 | + |
| 10628 | + .global disable_pmu_user_access @ export this function for the linke |
| 10629 | + /* Disables User mode access to the PMU (must be called in a priviledg */ |
| 10630 | + /* void disable_pmu_user_access(void) */ |
| 10631 | +disable_pmu_user_access: |
| 10632 | + MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */ |
| 10633 | + BIC r0, r0, #0x01 /* Clear EN bit (bit 0) */ |
| 10634 | + MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */ |
| 10635 | + BX lr |
| 10636 | + |
| 10637 | + |
| 10638 | + /* --------------------------------------------------------------- */ |
| 10639 | + /* Counter read registers */ |
| 10640 | + /* --------------------------------------------------------------- */ |
| 10641 | + |
| 10642 | + .global read_ccnt @ export this function for the linker |
| 10643 | + /* Returns the value of CCNT */ |
| 10644 | + /* uint32_t read_ccnt(void) */ |
| 10645 | +read_ccnt: |
| 10646 | + MRC p15, 0, r0, c9, c13, 0 /* Read CCNT Register */ |
| 10647 | + BX lr |
| 10648 | + |
| 10649 | + |
| 10650 | + .global read_pmn @ export this function for the linker |
| 10651 | + /* Returns the value of PMN{n} */ |
| 10652 | + /* uint32_t read_pmn(uint32_t counter) */ |
| 10653 | + /* counter = r0 = The counter to read (e.g. 0 for PMN0, 1 for PMN1) * |
| 10654 | +read_pmn: */ |
| 10655 | + AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */ |
| 10656 | + MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */ |
| 10657 | + MRC p15, 0, r0, c9, c13, 2 /* Read current PMNx Register */ |
| 10658 | + BX lr |
| 10659 | + |
| 10660 | + |
| 10661 | + /* --------------------------------------------------------------- */ |
| 10662 | + /* Software Increment */ |
| 10663 | + /* --------------------------------------------------------------- */ |
| 10664 | + |
| 10665 | + .global pmu_software_increment @ export this function for the linker |
| 10666 | + /* Writes to software increment register */ |
| 10667 | + /* void pmu_software_increment(uint32_t counter) */ |
| 10668 | + /* counter = r0 = The counter to increment (e.g. 0 for PMN0, 1 for PMN |
| 10669 | +pmu_software_increment: */ |
| 10670 | + MOV r1, #0x01 |
| 10671 | + MOV r1, r1, LSL r0 |
| 10672 | + MCR p15, 0, r1, c9, c12, 4 /* Write SWINCR Register */ |
| 10673 | + BX lr |
| 10674 | + |
| 10675 | + /* --------------------------------------------------------------- */ |
| 10676 | + /* Overflow & Interrupt Generation */ |
| 10677 | + /* --------------------------------------------------------------- */ |
| 10678 | + |
| 10679 | + .global read_flags @ export this function for the linker |
| 10680 | + /* Returns the value of the overflow flags */ |
| 10681 | + /* uint32_t read_flags(void) */ |
| 10682 | +read_flags: |
| 10683 | + MRC p15, 0, r0, c9, c12, 3 /* Read FLAG Register */ |
| 10684 | + BX lr |
| 10685 | + |
| 10686 | + |
| 10687 | + .global write_flags @ export this function for the linker |
| 10688 | + /* Writes the overflow flags */ |
| 10689 | + /* void write_flags(uint32_t flags) */ |
| 10690 | +write_flags: |
| 10691 | + MCR p15, 0, r0, c9, c12, 3 /* Write FLAG Register */ |
| 10692 | + BX lr |
| 10693 | + |
| 10694 | + |
| 10695 | + .global enable_ccnt_irq @ export this function for the linker |
| 10696 | + /* Enables interrupt generation on overflow of the CCNT */ |
| 10697 | + /* void enable_ccnt_irq(void) */ |
| 10698 | +enable_ccnt_irq: |
| 10699 | + MOV r0, #0x80000000 |
| 10700 | + MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ |
| 10701 | + BX lr |
| 10702 | + |
| 10703 | + .global disable_ccnt_irq @ export this function for the linker |
| 10704 | + /* Disables interrupt generation on overflow of the CCNT */ |
| 10705 | + /* void disable_ccnt_irq(void) */ |
| 10706 | +disable_ccnt_irq: |
| 10707 | + MOV r0, #0x80000000 |
| 10708 | + MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ |
| 10709 | + BX lr |
| 10710 | + |
| 10711 | + |
| 10712 | + .global enable_pmn_irq @ export this function for the linker |
| 10713 | + /* Enables interrupt generation on overflow of PMN{x} */ |
| 10714 | + /* void enable_pmn_irq(uint32_t counter) */ |
| 10715 | + /* counter = r0 = The counter to enable the interrupt for (e.g. 0 for |
| 10716 | +enable_pmn_irq: */ |
| 10717 | + MOV r1, #0x1 /* Use arg (r0) to set which counter */ |
| 10718 | + MOV r0, r1, LSL r0 |
| 10719 | + MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */ |
| 10720 | + BX lr |
| 10721 | + |
| 10722 | + .global disable_pmn_irq @ export this function for the linker |
| 10723 | + /* Disables interrupt generation on overflow of PMN{x} */ |
| 10724 | + /* void disable_pmn_irq(uint32_t counter) */ |
| 10725 | + /* counter = r0 = The counter to disable the interrupt for (e.g. 0 fo |
| 10726 | +disable_pmn_irq: */ |
| 10727 | + MOV r1, #0x1 /* Use arg (r0) to set which counter t */ |
| 10728 | + MOV r0, r1, LSL r0 |
| 10729 | + MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */ |
| 10730 | + BX lr |
| 10731 | + |
| 10732 | + /* --------------------------------------------------------------- */ |
| 10733 | + /* Reset Functions */ |
| 10734 | + /* --------------------------------------------------------------- */ |
| 10735 | + |
| 10736 | + .global reset_pmn @ export this function for the linker |
| 10737 | + /* Resets the programmable counters */ |
| 10738 | + /* void reset_pmn(void) */ |
| 10739 | +reset_pmn: |
| 10740 | + MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ |
| 10741 | + ORR r0, r0, #0x02 /* Set P bit (Event Counter Reset) */ |
| 10742 | + MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ |
| 10743 | + BX lr |
| 10744 | + |
| 10745 | + |
| 10746 | + .global reset_ccnt @ export this function for the linker |
| 10747 | + /* Resets the CCNT */ |
| 10748 | + /* void reset_ccnt(void) */ |
| 10749 | +reset_ccnt: |
| 10750 | + MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */ |
| 10751 | + ORR r0, r0, #0x04 /* Set C bit (Event Counter Reset) */ |
| 10752 | + MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */ |
| 10753 | + BX lr |
| 10754 | + |
| 10755 | + |
| 10756 | + .end @end of code, this line is optional. |
| 10757 | +/* ------------------------------------------------------------ */ |
| 10758 | +/* End of v7_pmu.s */ |
| 10759 | +/* ------------------------------------------------------------ */ |
| 10760 | + |
| 10761 | + |
| 10762 | --- /dev/null |
| 10763 | +++ b/modules/hw/mmal/v7_pmu.h |
| 10764 | @@ -0,0 +1,113 @@ |
| 10765 | +// ------------------------------------------------------------ |
| 10766 | +// PMU for Cortex-A/R (v7-A/R) |
| 10767 | +// ------------------------------------------------------------ |
| 10768 | + |
| 10769 | +#ifndef _V7_PMU_H |
| 10770 | +#define _V7_PMU_H |
| 10771 | + |
| 10772 | +// Returns the number of progammable counters |
| 10773 | +unsigned int getPMN(void); |
| 10774 | + |
| 10775 | +// Sets the event for a programmable counter to record |
| 10776 | +// counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1) |
| 10777 | +// event = r1 = The event code (from appropiate TRM or ARM Architecture Reference Manual) |
| 10778 | +void pmn_config(unsigned int counter, unsigned int event); |
| 10779 | + |
| 10780 | +// Enables/disables the divider (1/64) on CCNT |
| 10781 | +// divider = r0 = If 0 disable divider, else enable dvider |
| 10782 | +void ccnt_divider(int divider); |
| 10783 | + |
| 10784 | +// |
| 10785 | +// Enables and disables |
| 10786 | +// |
| 10787 | + |
| 10788 | +// Global PMU enable |
| 10789 | +// On ARM11 this enables the PMU, and the counters start immediately |
| 10790 | +// On Cortex this enables the PMU, there are individual enables for the counters |
| 10791 | +void enable_pmu(void); |
| 10792 | + |
| 10793 | +// Global PMU disable |
| 10794 | +// On Cortex, this overrides the enable state of the individual counters |
| 10795 | +void disable_pmu(void); |
| 10796 | + |
| 10797 | +// Enable the CCNT |
| 10798 | +void enable_ccnt(void); |
| 10799 | + |
| 10800 | +// Disable the CCNT |
| 10801 | +void disable_ccnt(void); |
| 10802 | + |
| 10803 | +// Enable PMN{n} |
| 10804 | +// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) |
| 10805 | +void enable_pmn(unsigned int counter); |
| 10806 | + |
| 10807 | +// Enable PMN{n} |
| 10808 | +// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1) |
| 10809 | +void disable_pmn(unsigned int counter); |
| 10810 | + |
| 10811 | +// |
| 10812 | +// Read counter values |
| 10813 | +// |
| 10814 | + |
| 10815 | +// Returns the value of CCNT |
| 10816 | +unsigned int read_ccnt(void); |
| 10817 | + |
| 10818 | +// Returns the value of PMN{n} |
| 10819 | +// counter = The counter to read (e.g. 0 for PMN0, 1 for PMN1) |
| 10820 | +unsigned int read_pmn(unsigned int counter); |
| 10821 | + |
| 10822 | +// |
| 10823 | +// Overflow and interrupts |
| 10824 | +// |
| 10825 | + |
| 10826 | +// Returns the value of the overflow flags |
| 10827 | +unsigned int read_flags(void); |
| 10828 | + |
| 10829 | +// Writes the overflow flags |
| 10830 | +void write_flags(unsigned int flags); |
| 10831 | + |
| 10832 | +// Enables interrupt generation on overflow of the CCNT |
| 10833 | +void enable_ccnt_irq(void); |
| 10834 | + |
| 10835 | +// Disables interrupt generation on overflow of the CCNT |
| 10836 | +void disable_ccnt_irq(void); |
| 10837 | + |
| 10838 | +// Enables interrupt generation on overflow of PMN{x} |
| 10839 | +// counter = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) |
| 10840 | +void enable_pmn_irq(unsigned int counter); |
| 10841 | + |
| 10842 | +// Disables interrupt generation on overflow of PMN{x} |
| 10843 | +// counter = r0 = The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1) |
| 10844 | +void disable_pmn_irq(unsigned int counter); |
| 10845 | + |
| 10846 | +// |
| 10847 | +// Counter reset functions |
| 10848 | +// |
| 10849 | + |
| 10850 | +// Resets the programmable counters |
| 10851 | +void reset_pmn(void); |
| 10852 | + |
| 10853 | +// Resets the CCNT |
| 10854 | +void reset_ccnt(void); |
| 10855 | + |
| 10856 | +// |
| 10857 | +// Software Increment |
| 10858 | + |
| 10859 | +// Writes to software increment register |
| 10860 | +// counter = The counter to increment (e.g. 0 for PMN0, 1 for PMN1) |
| 10861 | +void pmu_software_increment(unsigned int counter); |
| 10862 | + |
| 10863 | +// |
| 10864 | +// User mode access |
| 10865 | +// |
| 10866 | + |
| 10867 | +// Enables User mode access to the PMU (must be called in a priviledged mode) |
| 10868 | +void enable_pmu_user_access(void); |
| 10869 | + |
| 10870 | +// Disables User mode access to the PMU (must be called in a priviledged mode) |
| 10871 | +void disable_pmu_user_access(void); |
| 10872 | + |
| 10873 | +#endif |
| 10874 | +// ------------------------------------------------------------ |
| 10875 | +// End of v7_pmu.h |
| 10876 | +// ------------------------------------------------------------ |
| 10877 | + |
| 10878 | --- a/modules/hw/mmal/vout.c |
| 10879 | +++ b/modules/hw/mmal/vout.c |
| 10880 | @@ -27,21 +27,28 @@ |
| 10881 | #endif |
| 10882 | |
| 10883 | #include <math.h> |
| 10884 | +#include <stdatomic.h> |
| 10885 | |
| 10886 | #include <vlc_common.h> |
| 10887 | -#include <vlc_atomic.h> |
| 10888 | #include <vlc_plugin.h> |
| 10889 | #include <vlc_threads.h> |
| 10890 | #include <vlc_vout_display.h> |
| 10891 | +#include <vlc_modules.h> |
| 10892 | |
| 10893 | -#include "mmal_picture.h" |
| 10894 | - |
| 10895 | +#pragma GCC diagnostic push |
| 10896 | +#pragma GCC diagnostic ignored "-Wbad-function-cast" |
| 10897 | #include <bcm_host.h> |
| 10898 | +#pragma GCC diagnostic pop |
| 10899 | #include <interface/mmal/mmal.h> |
| 10900 | #include <interface/mmal/util/mmal_util.h> |
| 10901 | #include <interface/mmal/util/mmal_default_components.h> |
| 10902 | #include <interface/vmcs_host/vc_tvservice.h> |
| 10903 | -#include <interface/vmcs_host/vc_dispmanx.h> |
| 10904 | + |
| 10905 | +#include "mmal_picture.h" |
| 10906 | +#include "subpic.h" |
| 10907 | +#include "transform_ops.h" |
| 10908 | + |
| 10909 | +#define TRACE_ALL 0 |
| 10910 | |
| 10911 | #define MAX_BUFFERS_IN_TRANSIT 1 |
| 10912 | #define VC_TV_MAX_MODE_IDS 127 |
| 10913 | @@ -50,10 +57,28 @@ |
| 10914 | #define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.") |
| 10915 | #define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.") |
| 10916 | |
| 10917 | -#define MMAL_BLANK_BACKGROUND_NAME "mmal-blank-background" |
| 10918 | -#define MMAL_BLANK_BACKGROUND_TEXT N_("Blank screen below video.") |
| 10919 | -#define MMAL_BLANK_BACKGROUND_LONGTEXT N_("Render blank screen below video. " \ |
| 10920 | - "Increases VideoCore load.") |
| 10921 | +#define MMAL_DISPLAY_NAME "mmal-display" |
| 10922 | +#define MMAL_DISPLAY_TEXT N_("Output device for Rpi fullscreen.") |
| 10923 | +#define MMAL_DISPLAY_LONGTEXT N_("Output device for Rpi fullscreen. " \ |
| 10924 | +"Valid values are HDMI-1,HDMI-2. By default if qt-fullscreen-screennumber " \ |
| 10925 | +"is specified (or set by Fullscreen Output Device in Preferences) " \ |
| 10926 | +"HDMI-<qt-fullscreen-screennumber+1> will be used, otherwise HDMI-1.") |
| 10927 | + |
| 10928 | +#define MMAL_VOUT_TRANSFORM_NAME "mmal-vout-transform" |
| 10929 | +#define MMAL_VOUT_TRANSFORM_TEXT N_("Video transform for Rpi fullscreen.") |
| 10930 | +#define MMAL_VOUT_TRANSFORM_LONGTEXT N_("Video transform for Rpi fullscreen."\ |
| 10931 | +"Transforms availible: auto, 0, 90, 180, 270, hflip, vflip, transpose, antitranspose") |
| 10932 | + |
| 10933 | +#define MMAL_VOUT_WINDOW_NAME "mmal-vout-window" |
| 10934 | +#define MMAL_VOUT_WINDOW_TEXT N_("Display window for Rpi fullscreen") |
| 10935 | +#define MMAL_VOUT_WINDOW_LONGTEXT N_("Display window for Rpi fullscreen."\ |
| 10936 | +"fullscreen|<width>x<height>+<x>+<y>") |
| 10937 | + |
| 10938 | +#define MMAL_VOUT_TRANSPARENT_NAME "mmal-vout-transparent" |
| 10939 | +#define MMAL_VOUT_TRANSPARENT_TEXT N_("Enable layers beneeth the vodeo layer.") |
| 10940 | +#define MMAL_VOUT_TRANSPARENT_LONGTEXT N_("Enable layers beneath the video layer."\ |
| 10941 | +" By default these are disabled."\ |
| 10942 | +" Having the lower layers enabled can impact video performance") |
| 10943 | |
| 10944 | #define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate" |
| 10945 | #define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.") |
| 10946 | @@ -68,332 +93,628 @@ |
| 10947 | #define PHASE_OFFSET_TARGET ((double)0.25) |
| 10948 | #define PHASE_CHECK_INTERVAL 100 |
| 10949 | |
| 10950 | -static int Open(vlc_object_t *); |
| 10951 | -static void Close(vlc_object_t *); |
| 10952 | - |
| 10953 | -vlc_module_begin() |
| 10954 | - set_shortname(N_("MMAL vout")) |
| 10955 | - set_description(N_("MMAL-based vout plugin for Raspberry Pi")) |
| 10956 | - set_capability("vout display", 90) |
| 10957 | - add_shortcut("mmal_vout") |
| 10958 | - add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false) |
| 10959 | - add_bool(MMAL_BLANK_BACKGROUND_NAME, true, MMAL_BLANK_BACKGROUND_TEXT, |
| 10960 | - MMAL_BLANK_BACKGROUND_LONGTEXT, true); |
| 10961 | - add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT, |
| 10962 | - MMAL_ADJUST_REFRESHRATE_LONGTEXT, false) |
| 10963 | - add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT, |
| 10964 | - MMAL_NATIVE_INTERLACE_LONGTEXT, false) |
| 10965 | - set_callbacks(Open, Close) |
| 10966 | -vlc_module_end() |
| 10967 | +#define SUBS_MAX 4 |
| 10968 | |
| 10969 | -struct dmx_region_t { |
| 10970 | - struct dmx_region_t *next; |
| 10971 | - picture_t *picture; |
| 10972 | - VC_RECT_T bmp_rect; |
| 10973 | - VC_RECT_T src_rect; |
| 10974 | - VC_RECT_T dst_rect; |
| 10975 | - VC_DISPMANX_ALPHA_T alpha; |
| 10976 | - DISPMANX_ELEMENT_HANDLE_T element; |
| 10977 | - DISPMANX_RESOURCE_HANDLE_T resource; |
| 10978 | - int32_t pos_x; |
| 10979 | - int32_t pos_y; |
| 10980 | -}; |
| 10981 | +typedef struct vout_subpic_s { |
| 10982 | + MMAL_COMPONENT_T *component; |
| 10983 | + subpic_reg_stash_t sub; |
| 10984 | +} vout_subpic_t; |
| 10985 | |
| 10986 | struct vout_display_sys_t { |
| 10987 | - vlc_cond_t buffer_cond; |
| 10988 | - vlc_mutex_t buffer_mutex; |
| 10989 | vlc_mutex_t manage_mutex; |
| 10990 | |
| 10991 | - plane_t planes[3]; /* Depending on video format up to 3 planes are used */ |
| 10992 | - picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */ |
| 10993 | - picture_pool_t *picture_pool; |
| 10994 | - |
| 10995 | + vcsm_init_type_t init_type; |
| 10996 | MMAL_COMPONENT_T *component; |
| 10997 | MMAL_PORT_T *input; |
| 10998 | MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/ |
| 10999 | - struct dmx_region_t *dmx_region; |
| 11000 | int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */ |
| 11001 | |
| 11002 | - uint32_t buffer_size; /* size of actual mmal buffers */ |
| 11003 | int buffers_in_transit; /* number of buffers currently pushed to mmal component */ |
| 11004 | unsigned num_buffers; /* number of buffers allocated at mmal port */ |
| 11005 | |
| 11006 | - DISPMANX_DISPLAY_HANDLE_T dmx_handle; |
| 11007 | - DISPMANX_ELEMENT_HANDLE_T bkg_element; |
| 11008 | - DISPMANX_RESOURCE_HANDLE_T bkg_resource; |
| 11009 | - unsigned display_width; |
| 11010 | - unsigned display_height; |
| 11011 | + int display_id; |
| 11012 | + MMAL_RECT_T win_rect; // Window rect after transform(s) |
| 11013 | + MMAL_RECT_T display_rect; // Actual shape of display (x, y always 0) |
| 11014 | + MMAL_RECT_T req_win; // User requested window (w=0 => fullscreen) |
| 11015 | + |
| 11016 | + MMAL_RECT_T spu_rect; // Output rectangle in cfg coords (for subpic placement) |
| 11017 | + MMAL_RECT_T dest_rect; // Output rectangle in display coords |
| 11018 | + MMAL_DISPLAYTRANSFORM_T dest_transform; // Dest window coord transform |
| 11019 | + MMAL_DISPLAYTRANSFORM_T display_transform; // "Native" display transform |
| 11020 | + MMAL_DISPLAYTRANSFORM_T video_transform; // Combined config+native transform |
| 11021 | |
| 11022 | - int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */ |
| 11023 | - int i_frame_rate; |
| 11024 | + unsigned int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */ |
| 11025 | + unsigned int i_frame_rate; |
| 11026 | |
| 11027 | int next_phase_check; /* lowpass for phase check frequency */ |
| 11028 | int phase_offset; /* currently applied offset to presentation time in ns */ |
| 11029 | int layer; /* the dispman layer (z-index) used for video rendering */ |
| 11030 | + bool transparent; // Do not disable layers beneath ours |
| 11031 | |
| 11032 | bool need_configure_display; /* indicates a required display reconfigure to main thread */ |
| 11033 | bool adjust_refresh_rate; |
| 11034 | bool native_interlaced; |
| 11035 | bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */ |
| 11036 | bool b_progressive; |
| 11037 | - bool opaque; /* indicated use of opaque picture format (zerocopy) */ |
| 11038 | -}; |
| 11039 | + bool force_config; |
| 11040 | |
| 11041 | -static const vlc_fourcc_t subpicture_chromas[] = { |
| 11042 | - VLC_CODEC_RGBA, |
| 11043 | - 0 |
| 11044 | -}; |
| 11045 | + vout_subpic_t subs[SUBS_MAX]; |
| 11046 | + // Stash for subpics derived from the passed subpicture rather than |
| 11047 | + // included with the main pic |
| 11048 | + MMAL_BUFFER_HEADER_T * subpic_bufs[SUBS_MAX]; |
| 11049 | + |
| 11050 | + picture_pool_t * pic_pool; |
| 11051 | + |
| 11052 | + struct vout_isp_conf_s { |
| 11053 | + MMAL_COMPONENT_T *component; |
| 11054 | + MMAL_PORT_T * input; |
| 11055 | + MMAL_PORT_T * output; |
| 11056 | + MMAL_QUEUE_T * out_q; |
| 11057 | + MMAL_POOL_T * in_pool; |
| 11058 | + MMAL_POOL_T * out_pool; |
| 11059 | + bool pending; |
| 11060 | + } isp; |
| 11061 | |
| 11062 | -/* Utility functions */ |
| 11063 | -static inline uint32_t align(uint32_t x, uint32_t y); |
| 11064 | -static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, |
| 11065 | - const video_format_t *fmt); |
| 11066 | + MMAL_POOL_T * copy_pool; |
| 11067 | + MMAL_BUFFER_HEADER_T * copy_buf; |
| 11068 | |
| 11069 | -/* VLC vout display callbacks */ |
| 11070 | -static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count); |
| 11071 | -static void vd_prepare(vout_display_t *vd, picture_t *picture, |
| 11072 | - subpicture_t *subpicture); |
| 11073 | -static void vd_display(vout_display_t *vd, picture_t *picture, |
| 11074 | - subpicture_t *subpicture); |
| 11075 | -static int vd_control(vout_display_t *vd, int query, va_list args); |
| 11076 | -static void vd_manage(vout_display_t *vd); |
| 11077 | - |
| 11078 | -/* MMAL callbacks */ |
| 11079 | -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
| 11080 | -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer); |
| 11081 | + // Subpic blend if we have to do it here |
| 11082 | + vzc_pool_ctl_t * vzc; |
| 11083 | +}; |
| 11084 | |
| 11085 | -/* TV service */ |
| 11086 | -static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height); |
| 11087 | -static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, |
| 11088 | - uint32_t param2); |
| 11089 | -static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt); |
| 11090 | -static int set_latency_target(vout_display_t *vd, bool enable); |
| 11091 | |
| 11092 | -/* DispManX */ |
| 11093 | -static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture); |
| 11094 | -static void close_dmx(vout_display_t *vd); |
| 11095 | -static struct dmx_region_t *dmx_region_new(vout_display_t *vd, |
| 11096 | - DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region); |
| 11097 | -static void dmx_region_update(struct dmx_region_t *dmx_region, |
| 11098 | - DISPMANX_UPDATE_HANDLE_T update, picture_t *picture); |
| 11099 | -static void dmx_region_delete(struct dmx_region_t *dmx_region, |
| 11100 | - DISPMANX_UPDATE_HANDLE_T update); |
| 11101 | -static void show_background(vout_display_t *vd, bool enable); |
| 11102 | -static void maintain_phase_sync(vout_display_t *vd); |
| 11103 | +// ISP setup |
| 11104 | |
| 11105 | -static int Open(vlc_object_t *object) |
| 11106 | +static inline bool want_isp(const vout_display_t * const vd) |
| 11107 | { |
| 11108 | - vout_display_t *vd = (vout_display_t *)object; |
| 11109 | - vout_display_sys_t *sys; |
| 11110 | - uint32_t buffer_pitch, buffer_height; |
| 11111 | - vout_display_place_t place; |
| 11112 | - MMAL_DISPLAYREGION_T display_region; |
| 11113 | - MMAL_STATUS_T status; |
| 11114 | - int ret = VLC_SUCCESS; |
| 11115 | - unsigned i; |
| 11116 | + return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10); |
| 11117 | +} |
| 11118 | |
| 11119 | - if (vout_display_IsWindowed(vd)) |
| 11120 | - return VLC_EGENERIC; |
| 11121 | +static inline bool want_copy(const vout_display_t * const vd) |
| 11122 | +{ |
| 11123 | + return (vd->fmt.i_chroma == VLC_CODEC_I420 || vd->fmt.i_chroma == VLC_CODEC_I420_10L); |
| 11124 | +} |
| 11125 | |
| 11126 | - sys = calloc(1, sizeof(struct vout_display_sys_t)); |
| 11127 | - if (!sys) |
| 11128 | - return VLC_ENOMEM; |
| 11129 | - vd->sys = sys; |
| 11130 | +static inline vlc_fourcc_t req_chroma(const vout_display_t * const vd) |
| 11131 | +{ |
| 11132 | + return !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma) && !want_copy(vd) ? |
| 11133 | + VLC_CODEC_I420 : |
| 11134 | + vd->fmt.i_chroma; |
| 11135 | +} |
| 11136 | |
| 11137 | - sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); |
| 11138 | - bcm_host_init(); |
| 11139 | +static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc) |
| 11140 | +{ |
| 11141 | + switch (fcc){ |
| 11142 | + case VLC_CODEC_MMAL_OPAQUE: |
| 11143 | + return MMAL_ENCODING_OPAQUE; |
| 11144 | + case VLC_CODEC_MMAL_ZC_SAND8: |
| 11145 | + return MMAL_ENCODING_YUVUV128; |
| 11146 | + case VLC_CODEC_MMAL_ZC_SAND10: |
| 11147 | + return MMAL_ENCODING_YUVUV64_10; |
| 11148 | + case VLC_CODEC_MMAL_ZC_SAND30: |
| 11149 | + return MMAL_ENCODING_YUV10_COL; |
| 11150 | + case VLC_CODEC_MMAL_ZC_I420: |
| 11151 | + case VLC_CODEC_I420: |
| 11152 | + return MMAL_ENCODING_I420; |
| 11153 | + default: |
| 11154 | + break; |
| 11155 | + } |
| 11156 | + return MMAL_ENCODING_I420; |
| 11157 | +} |
| 11158 | |
| 11159 | - sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE; |
| 11160 | +static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate) |
| 11161 | +{ |
| 11162 | + const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ; |
| 11163 | + const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height; |
| 11164 | + MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video; |
| 11165 | |
| 11166 | - status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component); |
| 11167 | - if (status != MMAL_SUCCESS) { |
| 11168 | - msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)", |
| 11169 | - MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status)); |
| 11170 | - ret = VLC_EGENERIC; |
| 11171 | - goto out; |
| 11172 | + es_fmt->type = MMAL_ES_TYPE_VIDEO; |
| 11173 | + es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma); |
| 11174 | + es_fmt->encoding_variant = 0; |
| 11175 | + |
| 11176 | + v_fmt->width = (w + 31) & ~31; |
| 11177 | + v_fmt->height = (h + 15) & ~15; |
| 11178 | + v_fmt->crop.x = 0; |
| 11179 | + v_fmt->crop.y = 0; |
| 11180 | + v_fmt->crop.width = w; |
| 11181 | + v_fmt->crop.height = h; |
| 11182 | + if (vd->fmt.i_sar_num == 0 || vd->fmt.i_sar_den == 0) { |
| 11183 | + v_fmt->par.num = 1; |
| 11184 | + v_fmt->par.den = 1; |
| 11185 | + } else { |
| 11186 | + v_fmt->par.num = vd->fmt.i_sar_num; |
| 11187 | + v_fmt->par.den = vd->fmt.i_sar_den; |
| 11188 | } |
| 11189 | + v_fmt->frame_rate.num = vd->fmt.i_frame_rate; |
| 11190 | + v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base; |
| 11191 | + v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space); |
| 11192 | |
| 11193 | - sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; |
| 11194 | - status = mmal_port_enable(sys->component->control, control_port_cb); |
| 11195 | - if (status != MMAL_SUCCESS) { |
| 11196 | - msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)", |
| 11197 | - sys->component->control->name, status, mmal_status_to_string(status)); |
| 11198 | - ret = VLC_EGENERIC; |
| 11199 | - goto out; |
| 11200 | + msg_Dbg(vd, "WxH: %dx%d, Crop: %dx%d", v_fmt->width, v_fmt->height, v_fmt->crop.width, v_fmt->crop.height); |
| 11201 | +} |
| 11202 | + |
| 11203 | +static MMAL_RECT_T |
| 11204 | +display_src_rect(const vout_display_t * const vd, const video_format_t * const src) |
| 11205 | +{ |
| 11206 | + const bool wants_isp = want_isp(vd); |
| 11207 | + |
| 11208 | + // Scale source derived cropping to actual picture shape |
| 11209 | + return (MMAL_RECT_T){ |
| 11210 | + .x = wants_isp ? 0 : src->i_x_offset * vd->fmt.i_width / src->i_width, |
| 11211 | + .y = wants_isp ? 0 : src->i_y_offset * vd->fmt.i_height / src->i_height, |
| 11212 | + .width = src->i_visible_width * vd->fmt.i_width / src->i_width, |
| 11213 | + .height = src->i_visible_height * vd->fmt.i_height / src->i_height |
| 11214 | + }; |
| 11215 | +} |
| 11216 | + |
| 11217 | +static void isp_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) |
| 11218 | +{ |
| 11219 | +#if TRACE_ALL |
| 11220 | + vout_display_t * const vd = (vout_display_t *)port->userdata; |
| 11221 | + pic_ctx_mmal_t * ctx = buf->user_data; |
| 11222 | + msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf, |
| 11223 | + buf->flags, (long long)buf->pts); |
| 11224 | +#else |
| 11225 | + VLC_UNUSED(port); |
| 11226 | +#endif |
| 11227 | + |
| 11228 | + mmal_buffer_header_release(buf); |
| 11229 | + |
| 11230 | +#if TRACE_ALL |
| 11231 | + msg_Dbg(vd, ">>> %s", __func__); |
| 11232 | +#endif |
| 11233 | +} |
| 11234 | + |
| 11235 | +static void isp_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 11236 | +{ |
| 11237 | + vout_display_t *vd = (vout_display_t *)port->userdata; |
| 11238 | + MMAL_STATUS_T status; |
| 11239 | + |
| 11240 | + if (buffer->cmd == MMAL_EVENT_ERROR) { |
| 11241 | + status = *(uint32_t *)buffer->data; |
| 11242 | + msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); |
| 11243 | } |
| 11244 | |
| 11245 | - sys->input = sys->component->input[0]; |
| 11246 | - sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd; |
| 11247 | + mmal_buffer_header_release(buffer); |
| 11248 | +} |
| 11249 | |
| 11250 | - if (sys->opaque) { |
| 11251 | - sys->input->format->encoding = MMAL_ENCODING_OPAQUE; |
| 11252 | - sys->i_planes = 1; |
| 11253 | - sys->buffer_size = sys->input->buffer_size_recommended; |
| 11254 | - } else { |
| 11255 | - sys->input->format->encoding = MMAL_ENCODING_I420; |
| 11256 | - vd->fmt.i_chroma = VLC_CODEC_I420; |
| 11257 | - buffer_pitch = align(vd->fmt.i_width, 32); |
| 11258 | - buffer_height = align(vd->fmt.i_height, 16); |
| 11259 | - sys->i_planes = 3; |
| 11260 | - sys->buffer_size = 3 * buffer_pitch * buffer_height / 2; |
| 11261 | - } |
| 11262 | - |
| 11263 | - sys->input->format->es->video.width = vd->fmt.i_width; |
| 11264 | - sys->input->format->es->video.height = vd->fmt.i_height; |
| 11265 | - sys->input->format->es->video.crop.x = 0; |
| 11266 | - sys->input->format->es->video.crop.y = 0; |
| 11267 | - sys->input->format->es->video.crop.width = vd->fmt.i_width; |
| 11268 | - sys->input->format->es->video.crop.height = vd->fmt.i_height; |
| 11269 | - sys->input->format->es->video.par.num = vd->source.i_sar_num; |
| 11270 | - sys->input->format->es->video.par.den = vd->source.i_sar_den; |
| 11271 | +static void isp_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) |
| 11272 | +{ |
| 11273 | + if (buf->cmd == 0 && buf->length != 0) |
| 11274 | + { |
| 11275 | + // The filter structure etc. should always exist if we have contents |
| 11276 | + // but might not on later flushes as we shut down |
| 11277 | + vout_display_t * const vd = (vout_display_t *)port->userdata; |
| 11278 | + struct vout_isp_conf_s *const isp = &vd->sys->isp; |
| 11279 | |
| 11280 | - status = mmal_port_format_commit(sys->input); |
| 11281 | - if (status != MMAL_SUCCESS) { |
| 11282 | - msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)", |
| 11283 | - sys->input->name, status, mmal_status_to_string(status)); |
| 11284 | - ret = VLC_EGENERIC; |
| 11285 | - goto out; |
| 11286 | +#if TRACE_ALL |
| 11287 | + msg_Dbg(vd, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts); |
| 11288 | +#endif |
| 11289 | + mmal_queue_put(isp->out_q, buf); |
| 11290 | +#if TRACE_ALL |
| 11291 | + msg_Dbg(vd, ">>> %s: out Q len=%d", __func__, mmal_queue_length(isp->out_q)); |
| 11292 | +#endif |
| 11293 | } |
| 11294 | - sys->input->buffer_size = sys->input->buffer_size_recommended; |
| 11295 | + else |
| 11296 | + { |
| 11297 | + mmal_buffer_header_reset(buf); |
| 11298 | + mmal_buffer_header_release(buf); |
| 11299 | + } |
| 11300 | +} |
| 11301 | |
| 11302 | - vout_display_PlacePicture(&place, &vd->source, vd->cfg, false); |
| 11303 | - display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; |
| 11304 | - display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T); |
| 11305 | - display_region.fullscreen = MMAL_FALSE; |
| 11306 | - display_region.src_rect.x = vd->fmt.i_x_offset; |
| 11307 | - display_region.src_rect.y = vd->fmt.i_y_offset; |
| 11308 | - display_region.src_rect.width = vd->fmt.i_visible_width; |
| 11309 | - display_region.src_rect.height = vd->fmt.i_visible_height; |
| 11310 | - display_region.dest_rect.x = place.x; |
| 11311 | - display_region.dest_rect.y = place.y; |
| 11312 | - display_region.dest_rect.width = place.width; |
| 11313 | - display_region.dest_rect.height = place.height; |
| 11314 | - display_region.layer = sys->layer; |
| 11315 | - display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT | |
| 11316 | - MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER; |
| 11317 | - status = mmal_port_parameter_set(sys->input, &display_region.hdr); |
| 11318 | - if (status != MMAL_SUCCESS) { |
| 11319 | - msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", |
| 11320 | - status, mmal_status_to_string(status)); |
| 11321 | - ret = VLC_EGENERIC; |
| 11322 | - goto out; |
| 11323 | +static void isp_empty_out_q(struct vout_isp_conf_s * const isp) |
| 11324 | +{ |
| 11325 | + MMAL_BUFFER_HEADER_T * buf; |
| 11326 | + // We can be called as part of error recovery so allow for missing Q |
| 11327 | + if (isp->out_q == NULL) |
| 11328 | + return; |
| 11329 | + |
| 11330 | + while ((buf = mmal_queue_get(isp->out_q)) != NULL) |
| 11331 | + mmal_buffer_header_release(buf); |
| 11332 | +} |
| 11333 | + |
| 11334 | +static void isp_flush(struct vout_isp_conf_s * const isp) |
| 11335 | +{ |
| 11336 | + if (!isp->input->is_enabled) |
| 11337 | + mmal_port_disable(isp->input); |
| 11338 | + |
| 11339 | + if (isp->output->is_enabled) |
| 11340 | + mmal_port_disable(isp->output); |
| 11341 | + |
| 11342 | + isp_empty_out_q(isp); |
| 11343 | + isp->pending = false; |
| 11344 | +} |
| 11345 | + |
| 11346 | +static MMAL_STATUS_T isp_prepare(vout_display_t * const vd, struct vout_isp_conf_s * const isp) |
| 11347 | +{ |
| 11348 | + MMAL_STATUS_T err; |
| 11349 | + MMAL_BUFFER_HEADER_T * buf; |
| 11350 | + |
| 11351 | + if (!isp->output->is_enabled) { |
| 11352 | + if ((err = mmal_port_enable(isp->output, isp_output_cb)) != MMAL_SUCCESS) |
| 11353 | + { |
| 11354 | + msg_Err(vd, "ISP output port enable failed"); |
| 11355 | + return err; |
| 11356 | + } |
| 11357 | } |
| 11358 | |
| 11359 | - for (i = 0; i < sys->i_planes; ++i) { |
| 11360 | - sys->planes[i].i_lines = buffer_height; |
| 11361 | - sys->planes[i].i_pitch = buffer_pitch; |
| 11362 | - sys->planes[i].i_visible_lines = vd->fmt.i_visible_height; |
| 11363 | - sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width; |
| 11364 | + while ((buf = mmal_queue_get(isp->out_pool->queue)) != NULL) { |
| 11365 | + if ((err = mmal_port_send_buffer(isp->output, buf)) != MMAL_SUCCESS) |
| 11366 | + { |
| 11367 | + msg_Err(vd, "ISP output port stuff failed"); |
| 11368 | + return err; |
| 11369 | + } |
| 11370 | + } |
| 11371 | |
| 11372 | - if (i > 0) { |
| 11373 | - sys->planes[i].i_lines /= 2; |
| 11374 | - sys->planes[i].i_pitch /= 2; |
| 11375 | - sys->planes[i].i_visible_lines /= 2; |
| 11376 | - sys->planes[i].i_visible_pitch /= 2; |
| 11377 | + if (!isp->input->is_enabled) { |
| 11378 | + if ((err = mmal_port_enable(isp->input, isp_input_cb)) != MMAL_SUCCESS) |
| 11379 | + { |
| 11380 | + msg_Err(vd, "ISP input port enable failed"); |
| 11381 | + return err; |
| 11382 | } |
| 11383 | } |
| 11384 | + return MMAL_SUCCESS; |
| 11385 | +} |
| 11386 | |
| 11387 | - vlc_mutex_init(&sys->buffer_mutex); |
| 11388 | - vlc_cond_init(&sys->buffer_cond); |
| 11389 | - vlc_mutex_init(&sys->manage_mutex); |
| 11390 | +static void isp_close(vout_display_t * const vd, vout_display_sys_t * const vd_sys) |
| 11391 | +{ |
| 11392 | + struct vout_isp_conf_s * const isp = &vd_sys->isp; |
| 11393 | + VLC_UNUSED(vd); |
| 11394 | |
| 11395 | - vd->pool = vd_pool; |
| 11396 | - vd->prepare = vd_prepare; |
| 11397 | - vd->display = vd_display; |
| 11398 | - vd->control = vd_control; |
| 11399 | - vd->manage = vd_manage; |
| 11400 | + if (isp->component == NULL) |
| 11401 | + return; |
| 11402 | |
| 11403 | - vc_tv_register_callback(tvservice_cb, vd); |
| 11404 | + isp_flush(isp); |
| 11405 | |
| 11406 | - if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) { |
| 11407 | - vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height); |
| 11408 | - } else { |
| 11409 | - sys->display_width = vd->cfg->display.width; |
| 11410 | - sys->display_height = vd->cfg->display.height; |
| 11411 | + if (isp->component->control->is_enabled) |
| 11412 | + mmal_port_disable(isp->component->control); |
| 11413 | + |
| 11414 | + if (isp->out_q != NULL) { |
| 11415 | + // 1st junk anything lying around |
| 11416 | + isp_empty_out_q(isp); |
| 11417 | + |
| 11418 | + mmal_queue_destroy(isp->out_q); |
| 11419 | + isp->out_q = NULL; |
| 11420 | } |
| 11421 | |
| 11422 | - sys->dmx_handle = vc_dispmanx_display_open(0); |
| 11423 | - vd->info.subpicture_chromas = subpicture_chromas; |
| 11424 | + if (isp->out_pool != NULL) { |
| 11425 | + mmal_port_pool_destroy(isp->output, isp->out_pool); |
| 11426 | + isp->out_pool = NULL; |
| 11427 | + } |
| 11428 | |
| 11429 | - vout_display_DeleteWindow(vd, NULL); |
| 11430 | + isp->input = NULL; |
| 11431 | + isp->output = NULL; |
| 11432 | |
| 11433 | -out: |
| 11434 | - if (ret != VLC_SUCCESS) |
| 11435 | - Close(object); |
| 11436 | + mmal_component_release(isp->component); |
| 11437 | + isp->component = NULL; |
| 11438 | |
| 11439 | - return ret; |
| 11440 | + return; |
| 11441 | } |
| 11442 | |
| 11443 | -static void Close(vlc_object_t *object) |
| 11444 | +// Restuff into output rather than return to pool is we can |
| 11445 | +static MMAL_BOOL_T isp_out_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata) |
| 11446 | { |
| 11447 | - vout_display_t *vd = (vout_display_t *)object; |
| 11448 | - vout_display_sys_t *sys = vd->sys; |
| 11449 | - char response[20]; /* answer is hvs_update_fields=%1d */ |
| 11450 | - unsigned i; |
| 11451 | + struct vout_isp_conf_s * const isp = userdata; |
| 11452 | + VLC_UNUSED(pool); |
| 11453 | + if (isp->output->is_enabled) { |
| 11454 | + mmal_buffer_header_reset(buffer); |
| 11455 | + if (mmal_port_send_buffer(isp->output, buffer) == MMAL_SUCCESS) |
| 11456 | + return MMAL_FALSE; |
| 11457 | + } |
| 11458 | + return MMAL_TRUE; |
| 11459 | +} |
| 11460 | |
| 11461 | - vc_tv_unregister_callback_full(tvservice_cb, vd); |
| 11462 | +static MMAL_STATUS_T isp_setup(vout_display_t * const vd, vout_display_sys_t * const vd_sys) |
| 11463 | +{ |
| 11464 | + struct vout_isp_conf_s * const isp = &vd_sys->isp; |
| 11465 | + MMAL_STATUS_T err; |
| 11466 | |
| 11467 | - if (sys->dmx_handle) |
| 11468 | - close_dmx(vd); |
| 11469 | + if ((err = mmal_component_create(MMAL_COMPONENT_ISP_RESIZER, &isp->component)) != MMAL_SUCCESS) { |
| 11470 | + msg_Err(vd, "Cannot create ISP component"); |
| 11471 | + return err; |
| 11472 | + } |
| 11473 | + isp->input = isp->component->input[0]; |
| 11474 | + isp->output = isp->component->output[0]; |
| 11475 | |
| 11476 | - if (sys->component && sys->component->control->is_enabled) |
| 11477 | - mmal_port_disable(sys->component->control); |
| 11478 | + isp->component->control->userdata = (void *)vd; |
| 11479 | + if ((err = mmal_port_enable(isp->component->control, isp_control_port_cb)) != MMAL_SUCCESS) { |
| 11480 | + msg_Err(vd, "Failed to enable ISP control port"); |
| 11481 | + goto fail; |
| 11482 | + } |
| 11483 | |
| 11484 | - if (sys->input && sys->input->is_enabled) |
| 11485 | - mmal_port_disable(sys->input); |
| 11486 | + isp->input->userdata = (void *)vd; |
| 11487 | + display_set_format(vd, isp->input->format, false); |
| 11488 | |
| 11489 | - if (sys->component && sys->component->is_enabled) |
| 11490 | - mmal_component_disable(sys->component); |
| 11491 | + if ((err = port_parameter_set_bool(isp->input, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) |
| 11492 | + goto fail; |
| 11493 | |
| 11494 | - if (sys->pool) |
| 11495 | - mmal_port_pool_destroy(sys->input, sys->pool); |
| 11496 | + if ((err = mmal_port_format_commit(isp->input)) != MMAL_SUCCESS) { |
| 11497 | + msg_Err(vd, "Failed to set ISP input format"); |
| 11498 | + goto fail; |
| 11499 | + } |
| 11500 | |
| 11501 | - if (sys->component) |
| 11502 | - mmal_component_release(sys->component); |
| 11503 | + isp->input->buffer_size = isp->input->buffer_size_recommended; |
| 11504 | + isp->input->buffer_num = 30; |
| 11505 | |
| 11506 | - if (sys->picture_pool) |
| 11507 | - picture_pool_Release(sys->picture_pool); |
| 11508 | - else |
| 11509 | - for (i = 0; i < sys->num_buffers; ++i) |
| 11510 | - if (sys->pictures[i]) { |
| 11511 | - mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer); |
| 11512 | - picture_Release(sys->pictures[i]); |
| 11513 | - } |
| 11514 | + if ((isp->in_pool = mmal_pool_create(isp->input->buffer_num, 0)) == NULL) |
| 11515 | + { |
| 11516 | + msg_Err(vd, "Failed to create input pool"); |
| 11517 | + goto fail; |
| 11518 | + } |
| 11519 | |
| 11520 | - vlc_mutex_destroy(&sys->buffer_mutex); |
| 11521 | - vlc_cond_destroy(&sys->buffer_cond); |
| 11522 | - vlc_mutex_destroy(&sys->manage_mutex); |
| 11523 | + if ((isp->out_q = mmal_queue_create()) == NULL) |
| 11524 | + { |
| 11525 | + err = MMAL_ENOMEM; |
| 11526 | + goto fail; |
| 11527 | + } |
| 11528 | |
| 11529 | - if (sys->native_interlaced) { |
| 11530 | - if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 || |
| 11531 | - response[18] != '0') |
| 11532 | - msg_Warn(vd, "Could not reset hvs field mode"); |
| 11533 | + display_set_format(vd, isp->output->format, true); |
| 11534 | + |
| 11535 | + if ((err = port_parameter_set_bool(isp->output, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS) |
| 11536 | + goto fail; |
| 11537 | + |
| 11538 | + if ((err = mmal_port_format_commit(isp->output)) != MMAL_SUCCESS) { |
| 11539 | + msg_Err(vd, "Failed to set ISP input format"); |
| 11540 | + goto fail; |
| 11541 | } |
| 11542 | |
| 11543 | - free(sys->pictures); |
| 11544 | - free(sys); |
| 11545 | + isp->output->buffer_size = isp->output->buffer_size_recommended; |
| 11546 | + isp->output->buffer_num = 2; |
| 11547 | + isp->output->userdata = (void *)vd; |
| 11548 | + |
| 11549 | + if ((isp->out_pool = mmal_port_pool_create(isp->output, isp->output->buffer_num, isp->output->buffer_size)) == NULL) |
| 11550 | + { |
| 11551 | + msg_Err(vd, "Failed to make ISP port pool"); |
| 11552 | + goto fail; |
| 11553 | + } |
| 11554 | + |
| 11555 | + mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp); |
| 11556 | + |
| 11557 | + if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS) |
| 11558 | + goto fail; |
| 11559 | + |
| 11560 | + return MMAL_SUCCESS; |
| 11561 | |
| 11562 | - bcm_host_deinit(); |
| 11563 | +fail: |
| 11564 | + isp_close(vd, vd_sys); |
| 11565 | + return err; |
| 11566 | } |
| 11567 | |
| 11568 | -static inline uint32_t align(uint32_t x, uint32_t y) { |
| 11569 | - uint32_t mod = x % y; |
| 11570 | - if (mod == 0) |
| 11571 | - return x; |
| 11572 | +static MMAL_STATUS_T isp_check(vout_display_t * const vd, vout_display_sys_t * const vd_sys) |
| 11573 | +{ |
| 11574 | + struct vout_isp_conf_s *const isp = &vd_sys->isp; |
| 11575 | + const bool has_isp = (isp->component != NULL); |
| 11576 | + const bool wants_isp = want_isp(vd); |
| 11577 | + |
| 11578 | + if (has_isp == wants_isp) |
| 11579 | + { |
| 11580 | + // All OK - do nothing |
| 11581 | + } |
| 11582 | + else if (has_isp) |
| 11583 | + { |
| 11584 | + // ISP active but we don't want it |
| 11585 | + isp_flush(isp); |
| 11586 | + |
| 11587 | + // Check we have everything back and then kill it |
| 11588 | + if (mmal_queue_length(isp->out_pool->queue) == isp->output->buffer_num) |
| 11589 | + isp_close(vd, vd_sys); |
| 11590 | + } |
| 11591 | else |
| 11592 | - return x + y - mod; |
| 11593 | + { |
| 11594 | + // ISP closed but we want it |
| 11595 | + return isp_setup(vd, vd_sys); |
| 11596 | + } |
| 11597 | + |
| 11598 | + return MMAL_SUCCESS; |
| 11599 | +} |
| 11600 | + |
| 11601 | +/* TV service */ |
| 11602 | +static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, |
| 11603 | + uint32_t param2); |
| 11604 | +static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt); |
| 11605 | +static int set_latency_target(vout_display_t *vd, bool enable); |
| 11606 | + |
| 11607 | +// Mmal |
| 11608 | +static void maintain_phase_sync(vout_display_t *vd); |
| 11609 | + |
| 11610 | + |
| 11611 | + |
| 11612 | +static void vd_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) |
| 11613 | +{ |
| 11614 | +#if TRACE_ALL |
| 11615 | + vout_display_t * const vd = (vout_display_t *)port->userdata; |
| 11616 | + pic_ctx_mmal_t * ctx = buf->user_data; |
| 11617 | + msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf, |
| 11618 | + buf->flags, (long long)buf->pts); |
| 11619 | +#else |
| 11620 | + VLC_UNUSED(port); |
| 11621 | +#endif |
| 11622 | + |
| 11623 | + mmal_buffer_header_release(buf); |
| 11624 | + |
| 11625 | +#if TRACE_ALL |
| 11626 | + msg_Dbg(vd, ">>> %s", __func__); |
| 11627 | +#endif |
| 11628 | +} |
| 11629 | + |
| 11630 | +static int query_resolution(vout_display_t *vd, const int display_id, unsigned *width, unsigned *height) |
| 11631 | +{ |
| 11632 | + TV_DISPLAY_STATE_T display_state = {0}; |
| 11633 | + int ret = 0; |
| 11634 | + |
| 11635 | + if (vc_tv_get_display_state_id(display_id, &display_state) == 0) { |
| 11636 | + msg_Dbg(vd, "State=%#x", display_state.state); |
| 11637 | + if (display_state.state & 0xFF) { |
| 11638 | + msg_Dbg(vd, "HDMI: %dx%d", display_state.display.hdmi.width, display_state.display.hdmi.height); |
| 11639 | + *width = display_state.display.hdmi.width; |
| 11640 | + *height = display_state.display.hdmi.height; |
| 11641 | + } else if (display_state.state & 0xFF00) { |
| 11642 | + msg_Dbg(vd, "SDTV: %dx%d", display_state.display.sdtv.width, display_state.display.sdtv.height); |
| 11643 | + *width = display_state.display.sdtv.width; |
| 11644 | + *height = display_state.display.sdtv.height; |
| 11645 | + } else { |
| 11646 | + msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state); |
| 11647 | + ret = -1; |
| 11648 | + } |
| 11649 | + } else { |
| 11650 | + msg_Warn(vd, "Failed to query display resolution"); |
| 11651 | + ret = -1; |
| 11652 | + } |
| 11653 | + |
| 11654 | + return ret; |
| 11655 | +} |
| 11656 | + |
| 11657 | +static inline MMAL_RECT_T |
| 11658 | +place_to_mmal_rect(const vout_display_place_t place) |
| 11659 | +{ |
| 11660 | + return (MMAL_RECT_T){ |
| 11661 | + .x = place.x, |
| 11662 | + .y = place.y, |
| 11663 | + .width = place.width, |
| 11664 | + .height = place.height |
| 11665 | + }; |
| 11666 | +} |
| 11667 | + |
| 11668 | +static MMAL_RECT_T |
| 11669 | +place_out(const vout_display_cfg_t * cfg, |
| 11670 | + const video_format_t * fmt, |
| 11671 | + const MMAL_RECT_T r) |
| 11672 | +{ |
| 11673 | + video_format_t tfmt; |
| 11674 | + vout_display_cfg_t tcfg; |
| 11675 | + vout_display_place_t place; |
| 11676 | + |
| 11677 | + // Fix SAR if unknown |
| 11678 | + if (fmt->i_sar_den == 0 || fmt->i_sar_num == 0) { |
| 11679 | + tfmt = *fmt; |
| 11680 | + tfmt.i_sar_den = 1; |
| 11681 | + tfmt.i_sar_num = 1; |
| 11682 | + fmt = &tfmt; |
| 11683 | + } |
| 11684 | + |
| 11685 | + // Override what VLC thinks might be going on with display size |
| 11686 | + // if we know better |
| 11687 | + if (r.width != 0 && r.height != 0) |
| 11688 | + { |
| 11689 | + tcfg = *cfg; |
| 11690 | + tcfg.display.width = r.width; |
| 11691 | + tcfg.display.height = r.height; |
| 11692 | + cfg = &tcfg; |
| 11693 | + } |
| 11694 | + |
| 11695 | + vout_display_PlacePicture(&place, fmt, cfg, false); |
| 11696 | + |
| 11697 | + place.x += r.x; |
| 11698 | + place.y += r.y; |
| 11699 | + |
| 11700 | + return place_to_mmal_rect(place); |
| 11701 | +} |
| 11702 | + |
| 11703 | +static MMAL_RECT_T |
| 11704 | +rect_transform(MMAL_RECT_T s, const MMAL_RECT_T c, const MMAL_DISPLAYTRANSFORM_T t) |
| 11705 | +{ |
| 11706 | + if (is_transform_transpose(t)) |
| 11707 | + s = rect_transpose(s); |
| 11708 | + if (is_transform_hflip(t)) |
| 11709 | + s = rect_hflip(s, c); |
| 11710 | + if (is_transform_vflip(t) != 0) |
| 11711 | + s = rect_vflip(s, c); |
| 11712 | + return s; |
| 11713 | +} |
| 11714 | + |
| 11715 | +static void |
| 11716 | +place_dest_rect(vout_display_t * const vd, |
| 11717 | + const vout_display_cfg_t * const cfg, |
| 11718 | + const video_format_t * fmt) |
| 11719 | +{ |
| 11720 | + vout_display_sys_t * const sys = vd->sys; |
| 11721 | + sys->dest_rect = rect_transform(place_out(cfg, fmt, sys->win_rect), |
| 11722 | + sys->display_rect, sys->dest_transform); |
| 11723 | +} |
| 11724 | + |
| 11725 | +static void |
| 11726 | +place_spu_rect(vout_display_t * const vd, |
| 11727 | + const vout_display_cfg_t * const cfg, |
| 11728 | + const video_format_t * fmt) |
| 11729 | +{ |
| 11730 | + vout_display_sys_t * const sys = vd->sys; |
| 11731 | + static const MMAL_RECT_T r0 = {0}; |
| 11732 | + |
| 11733 | + sys->spu_rect = place_out(cfg, fmt, r0); |
| 11734 | + sys->spu_rect.x = 0; |
| 11735 | + sys->spu_rect.y = 0; |
| 11736 | + |
| 11737 | + // Copy place override logic for spu pos from video_output.c |
| 11738 | + // This info doesn't appear to reside anywhere natively |
| 11739 | + |
| 11740 | + if (fmt->i_width * fmt->i_height >= (unsigned int)(sys->spu_rect.width * sys->spu_rect.height)) { |
| 11741 | + sys->spu_rect.width = fmt->i_visible_width; |
| 11742 | + sys->spu_rect.height = fmt->i_visible_height; |
| 11743 | + } |
| 11744 | + |
| 11745 | + if (ORIENT_IS_SWAP(fmt->orientation)) |
| 11746 | + sys->spu_rect = rect_transpose(sys->spu_rect); |
| 11747 | +} |
| 11748 | + |
| 11749 | +static void |
| 11750 | +place_rects(vout_display_t * const vd, |
| 11751 | + const vout_display_cfg_t * const cfg, |
| 11752 | + const video_format_t * fmt) |
| 11753 | +{ |
| 11754 | + place_dest_rect(vd, cfg, fmt); |
| 11755 | + place_spu_rect(vd, cfg, fmt); |
| 11756 | +} |
| 11757 | + |
| 11758 | +static int |
| 11759 | +set_input_region(vout_display_t * const vd, const video_format_t * const fmt) |
| 11760 | +{ |
| 11761 | + const vout_display_sys_t * const sys = vd->sys; |
| 11762 | + MMAL_DISPLAYREGION_T display_region = { |
| 11763 | + .hdr = { |
| 11764 | + .id = MMAL_PARAMETER_DISPLAYREGION, |
| 11765 | + .size = sizeof(MMAL_DISPLAYREGION_T) |
| 11766 | + }, |
| 11767 | + .display_num = sys->display_id, |
| 11768 | + .fullscreen = MMAL_FALSE, |
| 11769 | + .transform = sys->video_transform, |
| 11770 | + .dest_rect = sys->dest_rect, |
| 11771 | + .src_rect = display_src_rect(vd, fmt), |
| 11772 | + .noaspect = MMAL_TRUE, |
| 11773 | + .mode = MMAL_DISPLAY_MODE_FILL, |
| 11774 | + .layer = sys->layer, |
| 11775 | + .alpha = 0xff | (sys->transparent ? 0 : (1 << 29)), |
| 11776 | + .set = |
| 11777 | + MMAL_DISPLAY_SET_NUM | |
| 11778 | + MMAL_DISPLAY_SET_FULLSCREEN | |
| 11779 | + MMAL_DISPLAY_SET_TRANSFORM | |
| 11780 | + MMAL_DISPLAY_SET_DEST_RECT | |
| 11781 | + MMAL_DISPLAY_SET_SRC_RECT | |
| 11782 | + MMAL_DISPLAY_SET_NOASPECT | |
| 11783 | + MMAL_DISPLAY_SET_MODE | |
| 11784 | + MMAL_DISPLAY_SET_LAYER | |
| 11785 | + MMAL_DISPLAY_SET_ALPHA |
| 11786 | + }; |
| 11787 | + MMAL_STATUS_T status = mmal_port_parameter_set(sys->input, &display_region.hdr); |
| 11788 | + if (status != MMAL_SUCCESS) { |
| 11789 | + msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", |
| 11790 | + status, mmal_status_to_string(status)); |
| 11791 | + return -EINVAL; |
| 11792 | + } |
| 11793 | + return 0; |
| 11794 | } |
| 11795 | |
| 11796 | static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg, |
| 11797 | const video_format_t *fmt) |
| 11798 | { |
| 11799 | - vout_display_sys_t *sys = vd->sys; |
| 11800 | - vout_display_place_t place; |
| 11801 | - MMAL_DISPLAYREGION_T display_region; |
| 11802 | + vout_display_sys_t * const sys = vd->sys; |
| 11803 | MMAL_STATUS_T status; |
| 11804 | |
| 11805 | if (!cfg && !fmt) |
| 11806 | + { |
| 11807 | + msg_Err(vd, "%s: Missing cfg & fmt", __func__); |
| 11808 | return -EINVAL; |
| 11809 | + } |
| 11810 | + |
| 11811 | + isp_check(vd, sys); |
| 11812 | |
| 11813 | if (fmt) { |
| 11814 | sys->input->format->es->video.par.num = fmt->i_sar_num; |
| 11815 | @@ -412,30 +733,14 @@ static int configure_display(vout_displa |
| 11816 | if (!cfg) |
| 11817 | cfg = vd->cfg; |
| 11818 | |
| 11819 | - vout_display_PlacePicture(&place, fmt, cfg, false); |
| 11820 | + sys->video_transform = combine_transform( |
| 11821 | + vlc_to_mmal_transform(fmt->orientation), sys->display_transform); |
| 11822 | |
| 11823 | - display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION; |
| 11824 | - display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T); |
| 11825 | - display_region.fullscreen = MMAL_FALSE; |
| 11826 | - display_region.src_rect.x = fmt->i_x_offset; |
| 11827 | - display_region.src_rect.y = fmt->i_y_offset; |
| 11828 | - display_region.src_rect.width = fmt->i_visible_width; |
| 11829 | - display_region.src_rect.height = fmt->i_visible_height; |
| 11830 | - display_region.dest_rect.x = place.x; |
| 11831 | - display_region.dest_rect.y = place.y; |
| 11832 | - display_region.dest_rect.width = place.width; |
| 11833 | - display_region.dest_rect.height = place.height; |
| 11834 | - display_region.layer = sys->layer; |
| 11835 | - display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT | |
| 11836 | - MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER; |
| 11837 | - status = mmal_port_parameter_set(sys->input, &display_region.hdr); |
| 11838 | - if (status != MMAL_SUCCESS) { |
| 11839 | - msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)", |
| 11840 | - status, mmal_status_to_string(status)); |
| 11841 | + place_rects(vd, cfg, fmt); |
| 11842 | + |
| 11843 | + if (set_input_region(vd, fmt) != 0) |
| 11844 | return -EINVAL; |
| 11845 | - } |
| 11846 | |
| 11847 | - show_background(vd, var_InheritBool(vd, MMAL_BLANK_BACKGROUND_NAME)); |
| 11848 | sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME); |
| 11849 | sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED); |
| 11850 | if (sys->adjust_refresh_rate) { |
| 11851 | @@ -446,204 +751,217 @@ static int configure_display(vout_displa |
| 11852 | return 0; |
| 11853 | } |
| 11854 | |
| 11855 | +static void kill_pool(vout_display_sys_t * const sys) |
| 11856 | +{ |
| 11857 | + if (sys->pic_pool != NULL) { |
| 11858 | + picture_pool_Release(sys->pic_pool); |
| 11859 | + sys->pic_pool = NULL; |
| 11860 | + } |
| 11861 | +} |
| 11862 | + |
| 11863 | +// Actual picture pool for MMAL opaques is just a set of trivial containers |
| 11864 | static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count) |
| 11865 | { |
| 11866 | - vout_display_sys_t *sys = vd->sys; |
| 11867 | - picture_resource_t picture_res; |
| 11868 | - picture_pool_configuration_t picture_pool_cfg; |
| 11869 | - video_format_t fmt = vd->fmt; |
| 11870 | - MMAL_STATUS_T status; |
| 11871 | - unsigned i; |
| 11872 | + vout_display_sys_t * const sys = vd->sys; |
| 11873 | |
| 11874 | - if (sys->picture_pool) { |
| 11875 | - if (sys->num_buffers < count) |
| 11876 | - msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures", |
| 11877 | - count, sys->num_buffers); |
| 11878 | + msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__, |
| 11879 | + 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); |
| 11880 | |
| 11881 | - goto out; |
| 11882 | + if (sys->pic_pool == NULL) { |
| 11883 | + sys->pic_pool = picture_pool_NewFromFormat(&vd->fmt, count); |
| 11884 | } |
| 11885 | + return sys->pic_pool; |
| 11886 | +} |
| 11887 | |
| 11888 | - if (sys->opaque) { |
| 11889 | - if (count <= NUM_ACTUAL_OPAQUE_BUFFERS) |
| 11890 | - count = NUM_ACTUAL_OPAQUE_BUFFERS; |
| 11891 | +static inline bool |
| 11892 | +check_shape(vout_display_t * const vd, const picture_t * const p_pic) |
| 11893 | +{ |
| 11894 | + if (vd->fmt.i_width == p_pic->format.i_width && |
| 11895 | + vd->fmt.i_height == p_pic->format.i_height) |
| 11896 | + return true; |
| 11897 | + return false; |
| 11898 | +} |
| 11899 | |
| 11900 | - MMAL_PARAMETER_BOOLEAN_T zero_copy = { |
| 11901 | - { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) }, |
| 11902 | - 1 |
| 11903 | - }; |
| 11904 | +static void vd_display(vout_display_t *vd, picture_t *p_pic, |
| 11905 | + subpicture_t *subpicture) |
| 11906 | +{ |
| 11907 | + vout_display_sys_t * const sys = vd->sys; |
| 11908 | + MMAL_STATUS_T err; |
| 11909 | |
| 11910 | - status = mmal_port_parameter_set(sys->input, &zero_copy.hdr); |
| 11911 | - if (status != MMAL_SUCCESS) { |
| 11912 | - msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", |
| 11913 | - sys->input->name, status, mmal_status_to_string(status)); |
| 11914 | - goto out; |
| 11915 | - } |
| 11916 | +#if TRACE_ALL |
| 11917 | + { |
| 11918 | + char dbuf0[5]; |
| 11919 | + msg_Dbg(vd, "<<< %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d -> %dx%d@%d,%d", __func__, |
| 11920 | + str_fourcc(dbuf0, p_pic->format.i_chroma), p_pic->format.i_width, p_pic->format.i_height, |
| 11921 | + p_pic->format.i_x_offset, p_pic->format.i_y_offset, |
| 11922 | + p_pic->format.i_visible_width, p_pic->format.i_visible_height, |
| 11923 | + p_pic->format.i_sar_num, p_pic->format.i_sar_den, |
| 11924 | + sys->dest_rect.width, sys->dest_rect.height, sys->dest_rect.x, sys->dest_rect.y); |
| 11925 | } |
| 11926 | - |
| 11927 | - if (count < sys->input->buffer_num_recommended) |
| 11928 | - count = sys->input->buffer_num_recommended; |
| 11929 | - |
| 11930 | -#ifndef NDEBUG |
| 11931 | - msg_Dbg(vd, "Creating picture pool with %u pictures", count); |
| 11932 | #endif |
| 11933 | |
| 11934 | - sys->input->buffer_num = count; |
| 11935 | - status = mmal_port_enable(sys->input, input_port_cb); |
| 11936 | - if (status != MMAL_SUCCESS) { |
| 11937 | - msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)", |
| 11938 | - sys->input->name, status, mmal_status_to_string(status)); |
| 11939 | - goto out; |
| 11940 | + // If we had subpics then we have attached them to the main pic in prepare |
| 11941 | + // so all we have to do here is delete the refs |
| 11942 | + if (subpicture != NULL) { |
| 11943 | + subpicture_Delete(subpicture); |
| 11944 | } |
| 11945 | |
| 11946 | - status = mmal_component_enable(sys->component); |
| 11947 | - if (status != MMAL_SUCCESS) { |
| 11948 | - msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)", |
| 11949 | - sys->component->name, status, mmal_status_to_string(status)); |
| 11950 | - goto out; |
| 11951 | + if (!check_shape(vd, p_pic)) |
| 11952 | + { |
| 11953 | + msg_Err(vd, "Pic/fmt shape mismatch"); |
| 11954 | + goto fail; |
| 11955 | + } |
| 11956 | + |
| 11957 | + if (!sys->input->is_enabled && |
| 11958 | + (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS) |
| 11959 | + { |
| 11960 | + msg_Err(vd, "Input port enable failed"); |
| 11961 | + goto fail; |
| 11962 | + } |
| 11963 | + // Stuff into input |
| 11964 | + // We assume the BH is already set up with values reflecting pic date etc. |
| 11965 | + if (sys->copy_buf != NULL) { |
| 11966 | + MMAL_BUFFER_HEADER_T *const buf = sys->copy_buf; |
| 11967 | + sys->copy_buf = NULL; |
| 11968 | +#if TRACE_ALL |
| 11969 | + msg_Dbg(vd, "--- %s: Copy stuff", __func__); |
| 11970 | +#endif |
| 11971 | + if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS) |
| 11972 | + { |
| 11973 | + mmal_buffer_header_release(buf); |
| 11974 | + msg_Err(vd, "Send copy buffer to render input failed"); |
| 11975 | + goto fail; |
| 11976 | + } |
| 11977 | } |
| 11978 | - |
| 11979 | - sys->num_buffers = count; |
| 11980 | - sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers, |
| 11981 | - sys->input->buffer_size); |
| 11982 | - if (!sys->pool) { |
| 11983 | - msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32, |
| 11984 | - count, sys->input->buffer_size); |
| 11985 | - goto out; |
| 11986 | + else if (sys->isp.pending) { |
| 11987 | + MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q); |
| 11988 | + sys->isp.pending = false; |
| 11989 | +#if TRACE_ALL |
| 11990 | + msg_Dbg(vd, "--- %s: ISP stuff", __func__); |
| 11991 | +#endif |
| 11992 | + if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS) |
| 11993 | + { |
| 11994 | + mmal_buffer_header_release(buf); |
| 11995 | + msg_Err(vd, "Send ISP buffer to render input failed"); |
| 11996 | + goto fail; |
| 11997 | + } |
| 11998 | } |
| 11999 | - |
| 12000 | - memset(&picture_res, 0, sizeof(picture_resource_t)); |
| 12001 | - sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *)); |
| 12002 | - for (i = 0; i < sys->num_buffers; ++i) { |
| 12003 | - picture_res.p_sys = calloc(1, sizeof(picture_sys_t)); |
| 12004 | - picture_res.p_sys->owner = (vlc_object_t *)vd; |
| 12005 | - picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue); |
| 12006 | - |
| 12007 | - sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res); |
| 12008 | - if (!sys->pictures[i]) { |
| 12009 | - msg_Err(vd, "Failed to create picture"); |
| 12010 | - free(picture_res.p_sys); |
| 12011 | - goto out; |
| 12012 | + else |
| 12013 | + { |
| 12014 | + MMAL_BUFFER_HEADER_T *const pic_buf = hw_mmal_pic_buf_replicated(p_pic, sys->pool); |
| 12015 | + if (pic_buf == NULL) |
| 12016 | + { |
| 12017 | + msg_Err(vd, "Replicated buffer get fail"); |
| 12018 | + goto fail; |
| 12019 | } |
| 12020 | |
| 12021 | - sys->pictures[i]->i_planes = sys->i_planes; |
| 12022 | - memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t)); |
| 12023 | - } |
| 12024 | |
| 12025 | - memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t)); |
| 12026 | - picture_pool_cfg.picture_count = sys->num_buffers; |
| 12027 | - picture_pool_cfg.picture = sys->pictures; |
| 12028 | - picture_pool_cfg.lock = mmal_picture_lock; |
| 12029 | + // If dimensions have chnaged then fix that |
| 12030 | + if (hw_mmal_vlc_pic_to_mmal_fmt_update(sys->input->format, p_pic)) |
| 12031 | + { |
| 12032 | + msg_Dbg(vd, "Reset port format"); |
| 12033 | + |
| 12034 | + // HVS can deal with on-line dimension changes |
| 12035 | + if (mmal_port_format_commit(sys->input) != MMAL_SUCCESS) |
| 12036 | + msg_Warn(vd, "Input format commit failed"); |
| 12037 | + } |
| 12038 | |
| 12039 | - sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg); |
| 12040 | - if (!sys->picture_pool) { |
| 12041 | - msg_Err(vd, "Failed to create picture pool"); |
| 12042 | - goto out; |
| 12043 | + if ((err = mmal_port_send_buffer(sys->input, pic_buf)) != MMAL_SUCCESS) |
| 12044 | + { |
| 12045 | + mmal_buffer_header_release(pic_buf); |
| 12046 | + msg_Err(vd, "Send buffer to input failed"); |
| 12047 | + goto fail; |
| 12048 | + } |
| 12049 | } |
| 12050 | |
| 12051 | -out: |
| 12052 | - return sys->picture_pool; |
| 12053 | -} |
| 12054 | - |
| 12055 | -static void vd_prepare(vout_display_t *vd, picture_t *picture, |
| 12056 | - subpicture_t *subpicture) |
| 12057 | -{ |
| 12058 | - vout_display_sys_t *sys = vd->sys; |
| 12059 | - picture_sys_t *pic_sys = picture->p_sys; |
| 12060 | - |
| 12061 | - if (!sys->adjust_refresh_rate || pic_sys->displayed) |
| 12062 | - return; |
| 12063 | - |
| 12064 | - /* Apply the required phase_offset to the picture, so that vd_display() |
| 12065 | - * will be called at the corrected time from the core */ |
| 12066 | - picture->date += sys->phase_offset; |
| 12067 | -} |
| 12068 | - |
| 12069 | -static void vd_display(vout_display_t *vd, picture_t *picture, |
| 12070 | - subpicture_t *subpicture) |
| 12071 | -{ |
| 12072 | - vout_display_sys_t *sys = vd->sys; |
| 12073 | - picture_sys_t *pic_sys = picture->p_sys; |
| 12074 | - MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer; |
| 12075 | - MMAL_STATUS_T status; |
| 12076 | - |
| 12077 | - if (picture->format.i_frame_rate != sys->i_frame_rate || |
| 12078 | - picture->format.i_frame_rate_base != sys->i_frame_rate_base || |
| 12079 | - picture->b_progressive != sys->b_progressive || |
| 12080 | - picture->b_top_field_first != sys->b_top_field_first) { |
| 12081 | - sys->b_top_field_first = picture->b_top_field_first; |
| 12082 | - sys->b_progressive = picture->b_progressive; |
| 12083 | - sys->i_frame_rate = picture->format.i_frame_rate; |
| 12084 | - sys->i_frame_rate_base = picture->format.i_frame_rate_base; |
| 12085 | - configure_display(vd, NULL, &picture->format); |
| 12086 | - } |
| 12087 | - |
| 12088 | - if (!pic_sys->displayed || !sys->opaque) { |
| 12089 | - buffer->cmd = 0; |
| 12090 | - buffer->length = sys->input->buffer_size; |
| 12091 | - buffer->user_data = picture; |
| 12092 | - |
| 12093 | - status = mmal_port_send_buffer(sys->input, buffer); |
| 12094 | - if (status == MMAL_SUCCESS) |
| 12095 | - atomic_fetch_add(&sys->buffers_in_transit, 1); |
| 12096 | - |
| 12097 | - if (status != MMAL_SUCCESS) { |
| 12098 | - msg_Err(vd, "Failed to send buffer to input port. Frame dropped"); |
| 12099 | - picture_Release(picture); |
| 12100 | + { |
| 12101 | + unsigned int sub_no = 0; |
| 12102 | + MMAL_BUFFER_HEADER_T **psub_bufs2 = sys->subpic_bufs; |
| 12103 | + const bool is_mmal_pic = hw_mmal_pic_is_mmal(p_pic); |
| 12104 | + |
| 12105 | + for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) { |
| 12106 | + int rv; |
| 12107 | + MMAL_BUFFER_HEADER_T * const sub_buf = !is_mmal_pic ? NULL : |
| 12108 | + hw_mmal_pic_sub_buf_get(p_pic, sub_no); |
| 12109 | + |
| 12110 | + if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd), |
| 12111 | + sub_buf != NULL ? sub_buf : *psub_bufs2++, |
| 12112 | + &sys->subs[sub_no].sub, |
| 12113 | + &p_pic->format, |
| 12114 | + &sys->dest_rect, |
| 12115 | + sys->display_transform, |
| 12116 | + p_pic->date)) == 0) |
| 12117 | + break; |
| 12118 | + else if (rv < 0) |
| 12119 | + goto fail; |
| 12120 | } |
| 12121 | - |
| 12122 | - pic_sys->displayed = true; |
| 12123 | - } else { |
| 12124 | - picture_Release(picture); |
| 12125 | } |
| 12126 | |
| 12127 | - display_subpicture(vd, subpicture); |
| 12128 | +fail: |
| 12129 | + for (unsigned int i = 0; i != SUBS_MAX && sys->subpic_bufs[i] != NULL; ++i) { |
| 12130 | + mmal_buffer_header_release(sys->subpic_bufs[i]); |
| 12131 | + sys->subpic_bufs[i] = NULL; |
| 12132 | + } |
| 12133 | |
| 12134 | - if (subpicture) |
| 12135 | - subpicture_Delete(subpicture); |
| 12136 | + picture_Release(p_pic); |
| 12137 | |
| 12138 | if (sys->next_phase_check == 0 && sys->adjust_refresh_rate) |
| 12139 | maintain_phase_sync(vd); |
| 12140 | sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL; |
| 12141 | - |
| 12142 | - if (sys->opaque) { |
| 12143 | - vlc_mutex_lock(&sys->buffer_mutex); |
| 12144 | - while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT) |
| 12145 | - vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex); |
| 12146 | - vlc_mutex_unlock(&sys->buffer_mutex); |
| 12147 | - } |
| 12148 | } |
| 12149 | |
| 12150 | static int vd_control(vout_display_t *vd, int query, va_list args) |
| 12151 | { |
| 12152 | - vout_display_sys_t *sys = vd->sys; |
| 12153 | - vout_display_cfg_t cfg; |
| 12154 | - const vout_display_cfg_t *tmp_cfg; |
| 12155 | + vout_display_sys_t * const sys = vd->sys; |
| 12156 | int ret = VLC_EGENERIC; |
| 12157 | + VLC_UNUSED(args); |
| 12158 | |
| 12159 | switch (query) { |
| 12160 | - case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: |
| 12161 | - tmp_cfg = va_arg(args, const vout_display_cfg_t *); |
| 12162 | - if (tmp_cfg->display.width == sys->display_width && |
| 12163 | - tmp_cfg->display.height == sys->display_height) { |
| 12164 | - cfg = *vd->cfg; |
| 12165 | - cfg.display.width = sys->display_width; |
| 12166 | - cfg.display.height = sys->display_height; |
| 12167 | - if (configure_display(vd, &cfg, NULL) >= 0) |
| 12168 | - ret = VLC_SUCCESS; |
| 12169 | - } |
| 12170 | - break; |
| 12171 | - |
| 12172 | case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: |
| 12173 | case VOUT_DISPLAY_CHANGE_SOURCE_CROP: |
| 12174 | - if (configure_display(vd, NULL, &vd->source) >= 0) |
| 12175 | + if (configure_display(vd, vd->cfg, &vd->source) >= 0) |
| 12176 | ret = VLC_SUCCESS; |
| 12177 | break; |
| 12178 | |
| 12179 | - case VOUT_DISPLAY_RESET_PICTURES: |
| 12180 | - vlc_assert_unreachable(); |
| 12181 | case VOUT_DISPLAY_CHANGE_ZOOM: |
| 12182 | - msg_Warn(vd, "Unsupported control query %d", query); |
| 12183 | + case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: |
| 12184 | + case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED: |
| 12185 | + { |
| 12186 | + const vout_display_cfg_t * const cfg = va_arg(args, const vout_display_cfg_t *); |
| 12187 | + |
| 12188 | + if (configure_display(vd, cfg, &vd->source) >= 0) |
| 12189 | + ret = VLC_SUCCESS; |
| 12190 | + break; |
| 12191 | + } |
| 12192 | + |
| 12193 | + case VOUT_DISPLAY_RESET_PICTURES: |
| 12194 | + msg_Warn(vd, "Reset Pictures"); |
| 12195 | + kill_pool(sys); |
| 12196 | + vd->fmt = vd->source; // Take (nearly) whatever source wants to give us |
| 12197 | + vd->fmt.i_chroma = req_chroma(vd); // Adjust chroma to something we can actaully deal with |
| 12198 | + ret = VLC_SUCCESS; |
| 12199 | + break; |
| 12200 | + |
| 12201 | + case VOUT_DISPLAY_CHANGE_MMAL_HIDE: |
| 12202 | + { |
| 12203 | + MMAL_STATUS_T err; |
| 12204 | + unsigned int i; |
| 12205 | + |
| 12206 | + msg_Dbg(vd, "Hide display"); |
| 12207 | + |
| 12208 | + for (i = 0; i != SUBS_MAX; ++i) |
| 12209 | + hw_mmal_subpic_flush(VLC_OBJECT(vd), &sys->subs[i].sub); |
| 12210 | + |
| 12211 | + if (sys->input->is_enabled && |
| 12212 | + (err = mmal_port_disable(sys->input)) != MMAL_SUCCESS) |
| 12213 | + { |
| 12214 | + msg_Err(vd, "Unable to disable port: err=%d", err); |
| 12215 | + break; |
| 12216 | + } |
| 12217 | + sys->force_config = true; |
| 12218 | + ret = VLC_SUCCESS; |
| 12219 | break; |
| 12220 | + } |
| 12221 | |
| 12222 | default: |
| 12223 | msg_Warn(vd, "Unknown control query %d", query); |
| 12224 | @@ -653,79 +971,207 @@ static int vd_control(vout_display_t *vd |
| 12225 | return ret; |
| 12226 | } |
| 12227 | |
| 12228 | +static void set_display_windows(vout_display_t *const vd, vout_display_sys_t *const sys) |
| 12229 | +{ |
| 12230 | + unsigned int width, height; |
| 12231 | + if (query_resolution(vd, sys->display_id, &width, &height) < 0) { |
| 12232 | + width = vd->cfg->display.width; |
| 12233 | + height = vd->cfg->display.height; |
| 12234 | + } |
| 12235 | + sys->display_rect = (MMAL_RECT_T){0, 0, width, height}; |
| 12236 | + |
| 12237 | + sys->win_rect = (sys->req_win.width != 0) ? |
| 12238 | + sys->req_win : |
| 12239 | + is_transform_transpose(sys->display_transform) ? |
| 12240 | + rect_transpose(sys->display_rect) : sys->display_rect; |
| 12241 | +} |
| 12242 | + |
| 12243 | static void vd_manage(vout_display_t *vd) |
| 12244 | { |
| 12245 | - vout_display_sys_t *sys = vd->sys; |
| 12246 | - unsigned width, height; |
| 12247 | + vout_display_sys_t *const sys = vd->sys; |
| 12248 | |
| 12249 | vlc_mutex_lock(&sys->manage_mutex); |
| 12250 | |
| 12251 | if (sys->need_configure_display) { |
| 12252 | - close_dmx(vd); |
| 12253 | - sys->dmx_handle = vc_dispmanx_display_open(0); |
| 12254 | - |
| 12255 | - if (query_resolution(vd, &width, &height) >= 0) { |
| 12256 | - sys->display_width = width; |
| 12257 | - sys->display_height = height; |
| 12258 | - vout_display_SendEventDisplaySize(vd, width, height); |
| 12259 | - } |
| 12260 | - |
| 12261 | sys->need_configure_display = false; |
| 12262 | + set_display_windows(vd, sys); |
| 12263 | } |
| 12264 | |
| 12265 | vlc_mutex_unlock(&sys->manage_mutex); |
| 12266 | } |
| 12267 | |
| 12268 | -static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 12269 | + |
| 12270 | +static int attach_subpics(vout_display_t * const vd, vout_display_sys_t * const sys, |
| 12271 | + subpicture_t * const subpicture) |
| 12272 | { |
| 12273 | - vout_display_t *vd = (vout_display_t *)port->userdata; |
| 12274 | - MMAL_STATUS_T status; |
| 12275 | + unsigned int n = 0; |
| 12276 | |
| 12277 | - if (buffer->cmd == MMAL_EVENT_ERROR) { |
| 12278 | - status = *(uint32_t *)buffer->data; |
| 12279 | - msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); |
| 12280 | + if (sys->vzc == NULL) { |
| 12281 | + if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL) |
| 12282 | + { |
| 12283 | + msg_Err(vd, "Failed to allocate VZC"); |
| 12284 | + return VLC_ENOMEM; |
| 12285 | + } |
| 12286 | } |
| 12287 | |
| 12288 | - mmal_buffer_header_release(buffer); |
| 12289 | + // Attempt to import the subpics |
| 12290 | + for (subpicture_t * spic = subpicture; spic != NULL; spic = spic->p_next) |
| 12291 | + { |
| 12292 | + for (subpicture_region_t *sreg = spic->p_region; sreg != NULL; sreg = sreg->p_next) { |
| 12293 | + picture_t *const src = sreg->p_picture; |
| 12294 | + |
| 12295 | +#if TRACE_ALL |
| 12296 | + char dbuf0[5]; |
| 12297 | + 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, |
| 12298 | + sreg->i_x, sreg->i_y, |
| 12299 | + sreg->i_max_width, sreg->i_max_height, |
| 12300 | + src->format.i_visible_width, src->format.i_visible_height, |
| 12301 | + src->format.i_width, src->format.i_height, |
| 12302 | + src->format.orientation, |
| 12303 | + sys->spu_rect.x, sys->spu_rect.y, sys->spu_rect.width, sys->spu_rect.height, |
| 12304 | + vd->fmt.i_visible_width, vd->fmt.i_visible_height, |
| 12305 | + vd->fmt.i_width, vd->fmt.i_height, |
| 12306 | + vd->source.i_visible_width, vd->source.i_visible_height, |
| 12307 | + vd->source.i_width, vd->source.i_height, |
| 12308 | + vd->cfg->display.width, vd->cfg->display.height, |
| 12309 | + vd->cfg->zoom.num, vd->cfg->zoom.den, |
| 12310 | + sreg->i_alpha, |
| 12311 | + str_fourcc(dbuf0, src->format.i_chroma)); |
| 12312 | +#endif |
| 12313 | + |
| 12314 | + // At this point I think the subtitles are being placed in the |
| 12315 | + // coord space of the placed rectangle in the cfg display space |
| 12316 | + if ((sys->subpic_bufs[n] = hw_mmal_vzc_buf_from_pic(sys->vzc, |
| 12317 | + src, |
| 12318 | + (MMAL_RECT_T){.width = sys->spu_rect.width, .height=sys->spu_rect.height}, |
| 12319 | + sreg->i_x, sreg->i_y, |
| 12320 | + sreg->i_alpha, |
| 12321 | + n == 0)) == NULL) |
| 12322 | + { |
| 12323 | + msg_Err(vd, "Failed to allocate vzc buffer for subpic"); |
| 12324 | + return VLC_ENOMEM; |
| 12325 | + } |
| 12326 | + |
| 12327 | + if (++n == SUBS_MAX) |
| 12328 | + return VLC_SUCCESS; |
| 12329 | + } |
| 12330 | + } |
| 12331 | + return VLC_SUCCESS; |
| 12332 | } |
| 12333 | |
| 12334 | -static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 12335 | + |
| 12336 | +static void vd_prepare(vout_display_t *vd, picture_t *p_pic, |
| 12337 | +#if VLC_VER_3 |
| 12338 | + subpicture_t *subpicture |
| 12339 | +#else |
| 12340 | + subpicture_t *subpicture, vlc_tick_t date |
| 12341 | +#endif |
| 12342 | + ) |
| 12343 | { |
| 12344 | - vout_display_t *vd = (vout_display_t *)port->userdata; |
| 12345 | + MMAL_STATUS_T err; |
| 12346 | + vout_display_sys_t * const sys = vd->sys; |
| 12347 | + |
| 12348 | + vd_manage(vd); |
| 12349 | + |
| 12350 | + if (!check_shape(vd, p_pic)) |
| 12351 | + return; |
| 12352 | + |
| 12353 | + if (sys->force_config || |
| 12354 | + p_pic->format.i_frame_rate != sys->i_frame_rate || |
| 12355 | + p_pic->format.i_frame_rate_base != sys->i_frame_rate_base || |
| 12356 | + p_pic->b_progressive != sys->b_progressive || |
| 12357 | + p_pic->b_top_field_first != sys->b_top_field_first) |
| 12358 | + { |
| 12359 | + sys->force_config = false; |
| 12360 | + sys->b_top_field_first = p_pic->b_top_field_first; |
| 12361 | + sys->b_progressive = p_pic->b_progressive; |
| 12362 | + sys->i_frame_rate = p_pic->format.i_frame_rate; |
| 12363 | + sys->i_frame_rate_base = p_pic->format.i_frame_rate_base; |
| 12364 | + configure_display(vd, NULL, &vd->source); |
| 12365 | + } |
| 12366 | + |
| 12367 | + // Subpics can either turn up attached to the main pic or in the |
| 12368 | + // subpic list here - if they turn up here then process into temp |
| 12369 | + // buffers |
| 12370 | + if (subpicture != NULL) { |
| 12371 | + attach_subpics(vd, sys, subpicture); |
| 12372 | + } |
| 12373 | + |
| 12374 | + // ***** |
| 12375 | + if (want_copy(vd)) { |
| 12376 | + if (sys->copy_buf != NULL) { |
| 12377 | + msg_Err(vd, "Copy buf not NULL"); |
| 12378 | + mmal_buffer_header_release(sys->copy_buf); |
| 12379 | + sys->copy_buf = NULL; |
| 12380 | + } |
| 12381 | + |
| 12382 | + MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->copy_pool->queue); |
| 12383 | + // Copy 2d |
| 12384 | + hw_mmal_copy_pic_to_buf(buf->data, &buf->length, sys->input->format, p_pic); |
| 12385 | + buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END; |
| 12386 | + |
| 12387 | + sys->copy_buf = buf; |
| 12388 | + } |
| 12389 | + |
| 12390 | + if (isp_check(vd, sys) != MMAL_SUCCESS) { |
| 12391 | + return; |
| 12392 | + } |
| 12393 | + |
| 12394 | + if (want_isp(vd)) |
| 12395 | + { |
| 12396 | + struct vout_isp_conf_s * const isp = &sys->isp; |
| 12397 | + MMAL_BUFFER_HEADER_T * buf; |
| 12398 | + |
| 12399 | + // This should be empty - make it so if it isn't |
| 12400 | + isp_empty_out_q(isp); |
| 12401 | + isp->pending = false; |
| 12402 | + |
| 12403 | + // Stuff output |
| 12404 | + if (isp_prepare(vd, isp) != MMAL_SUCCESS) |
| 12405 | + return; |
| 12406 | + |
| 12407 | + if ((buf = hw_mmal_pic_buf_replicated(p_pic, isp->in_pool)) == NULL) |
| 12408 | + { |
| 12409 | + msg_Err(vd, "Pic has no attached buffer"); |
| 12410 | + return; |
| 12411 | + } |
| 12412 | + |
| 12413 | + if ((err = mmal_port_send_buffer(isp->input, buf)) != MMAL_SUCCESS) |
| 12414 | + { |
| 12415 | + msg_Err(vd, "Send buffer to input failed"); |
| 12416 | + mmal_buffer_header_release(buf); |
| 12417 | + return; |
| 12418 | + } |
| 12419 | + |
| 12420 | + isp->pending = true; |
| 12421 | + } |
| 12422 | + |
| 12423 | +#if 0 |
| 12424 | + VLC_UNUSED(date); |
| 12425 | vout_display_sys_t *sys = vd->sys; |
| 12426 | - picture_t *picture = (picture_t *)buffer->user_data; |
| 12427 | + picture_sys_t *pic_sys = picture->p_sys; |
| 12428 | |
| 12429 | - if (picture) |
| 12430 | - picture_Release(picture); |
| 12431 | + if (!sys->adjust_refresh_rate || pic_sys->displayed) |
| 12432 | + return; |
| 12433 | |
| 12434 | - vlc_mutex_lock(&sys->buffer_mutex); |
| 12435 | - atomic_fetch_sub(&sys->buffers_in_transit, 1); |
| 12436 | - vlc_cond_signal(&sys->buffer_cond); |
| 12437 | - vlc_mutex_unlock(&sys->buffer_mutex); |
| 12438 | + /* Apply the required phase_offset to the picture, so that vd_display() |
| 12439 | + * will be called at the corrected time from the core */ |
| 12440 | + picture->date += sys->phase_offset; |
| 12441 | +#endif |
| 12442 | } |
| 12443 | |
| 12444 | -static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height) |
| 12445 | + |
| 12446 | +static void vd_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer) |
| 12447 | { |
| 12448 | - TV_DISPLAY_STATE_T display_state; |
| 12449 | - int ret = 0; |
| 12450 | + vout_display_t *vd = (vout_display_t *)port->userdata; |
| 12451 | + MMAL_STATUS_T status; |
| 12452 | |
| 12453 | - if (vc_tv_get_display_state(&display_state) == 0) { |
| 12454 | - if (display_state.state & 0xFF) { |
| 12455 | - *width = display_state.display.hdmi.width; |
| 12456 | - *height = display_state.display.hdmi.height; |
| 12457 | - } else if (display_state.state & 0xFF00) { |
| 12458 | - *width = display_state.display.sdtv.width; |
| 12459 | - *height = display_state.display.sdtv.height; |
| 12460 | - } else { |
| 12461 | - msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state); |
| 12462 | - ret = -1; |
| 12463 | - } |
| 12464 | - } else { |
| 12465 | - msg_Warn(vd, "Failed to query display resolution"); |
| 12466 | - ret = -1; |
| 12467 | + if (buffer->cmd == MMAL_EVENT_ERROR) { |
| 12468 | + status = *(uint32_t *)buffer->data; |
| 12469 | + msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status)); |
| 12470 | } |
| 12471 | |
| 12472 | - return ret; |
| 12473 | + mmal_buffer_header_release(buffer); |
| 12474 | } |
| 12475 | |
| 12476 | static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2) |
| 12477 | @@ -780,9 +1226,9 @@ static void adjust_refresh_rate(vout_dis |
| 12478 | double best_score, score; |
| 12479 | int i; |
| 12480 | |
| 12481 | - vc_tv_get_display_state(&display_state); |
| 12482 | + vc_tv_get_display_state_id(sys->display_id, &display_state); |
| 12483 | if(display_state.display.hdmi.mode != HDMI_MODE_OFF) { |
| 12484 | - num_modes = vc_tv_hdmi_get_supported_modes_new(display_state.display.hdmi.group, |
| 12485 | + num_modes = vc_tv_hdmi_get_supported_modes_new_id(sys->display_id, display_state.display.hdmi.group, |
| 12486 | supported_modes, VC_TV_MAX_MODE_IDS, NULL, NULL); |
| 12487 | |
| 12488 | for (i = 0; i < num_modes; ++i) { |
| 12489 | @@ -810,7 +1256,7 @@ static void adjust_refresh_rate(vout_dis |
| 12490 | if((best_id >= 0) && (display_state.display.hdmi.mode != supported_modes[best_id].code)) { |
| 12491 | msg_Info(vd, "Setting HDMI refresh rate to %"PRIu32, |
| 12492 | supported_modes[best_id].frame_rate); |
| 12493 | - vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, |
| 12494 | + vc_tv_hdmi_power_on_explicit_new_id(sys->display_id, HDMI_MODE_HDMI, |
| 12495 | supported_modes[best_id].group, |
| 12496 | supported_modes[best_id].code); |
| 12497 | } |
| 12498 | @@ -828,148 +1274,12 @@ static void adjust_refresh_rate(vout_dis |
| 12499 | } |
| 12500 | } |
| 12501 | |
| 12502 | -static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture) |
| 12503 | -{ |
| 12504 | - vout_display_sys_t *sys = vd->sys; |
| 12505 | - struct dmx_region_t **dmx_region = &sys->dmx_region; |
| 12506 | - struct dmx_region_t *unused_dmx_region; |
| 12507 | - DISPMANX_UPDATE_HANDLE_T update = 0; |
| 12508 | - picture_t *picture; |
| 12509 | - video_format_t *fmt; |
| 12510 | - struct dmx_region_t *dmx_region_next; |
| 12511 | - |
| 12512 | - if(subpicture) { |
| 12513 | - subpicture_region_t *region = subpicture->p_region; |
| 12514 | - while(region) { |
| 12515 | - picture = region->p_picture; |
| 12516 | - fmt = ®ion->fmt; |
| 12517 | - |
| 12518 | - if(!*dmx_region) { |
| 12519 | - if(!update) |
| 12520 | - update = vc_dispmanx_update_start(10); |
| 12521 | - *dmx_region = dmx_region_new(vd, update, region); |
| 12522 | - } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) || |
| 12523 | - ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) || |
| 12524 | - ((*dmx_region)->pos_x != region->i_x) || |
| 12525 | - ((*dmx_region)->pos_y != region->i_y) || |
| 12526 | - ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) { |
| 12527 | - dmx_region_next = (*dmx_region)->next; |
| 12528 | - if(!update) |
| 12529 | - update = vc_dispmanx_update_start(10); |
| 12530 | - dmx_region_delete(*dmx_region, update); |
| 12531 | - *dmx_region = dmx_region_new(vd, update, region); |
| 12532 | - (*dmx_region)->next = dmx_region_next; |
| 12533 | - } else if((*dmx_region)->picture != picture) { |
| 12534 | - if(!update) |
| 12535 | - update = vc_dispmanx_update_start(10); |
| 12536 | - dmx_region_update(*dmx_region, update, picture); |
| 12537 | - } |
| 12538 | - |
| 12539 | - dmx_region = &(*dmx_region)->next; |
| 12540 | - region = region->p_next; |
| 12541 | - } |
| 12542 | - } |
| 12543 | - |
| 12544 | - /* Remove remaining regions */ |
| 12545 | - unused_dmx_region = *dmx_region; |
| 12546 | - while(unused_dmx_region) { |
| 12547 | - dmx_region_next = unused_dmx_region->next; |
| 12548 | - if(!update) |
| 12549 | - update = vc_dispmanx_update_start(10); |
| 12550 | - dmx_region_delete(unused_dmx_region, update); |
| 12551 | - unused_dmx_region = dmx_region_next; |
| 12552 | - } |
| 12553 | - *dmx_region = NULL; |
| 12554 | - |
| 12555 | - if(update) |
| 12556 | - vc_dispmanx_update_submit_sync(update); |
| 12557 | -} |
| 12558 | - |
| 12559 | -static void close_dmx(vout_display_t *vd) |
| 12560 | -{ |
| 12561 | - vout_display_sys_t *sys = vd->sys; |
| 12562 | - DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10); |
| 12563 | - struct dmx_region_t *dmx_region = sys->dmx_region; |
| 12564 | - struct dmx_region_t *dmx_region_next; |
| 12565 | - |
| 12566 | - while(dmx_region) { |
| 12567 | - dmx_region_next = dmx_region->next; |
| 12568 | - dmx_region_delete(dmx_region, update); |
| 12569 | - dmx_region = dmx_region_next; |
| 12570 | - } |
| 12571 | - |
| 12572 | - vc_dispmanx_update_submit_sync(update); |
| 12573 | - sys->dmx_region = NULL; |
| 12574 | - |
| 12575 | - show_background(vd, false); |
| 12576 | - |
| 12577 | - vc_dispmanx_display_close(sys->dmx_handle); |
| 12578 | - sys->dmx_handle = DISPMANX_NO_HANDLE; |
| 12579 | -} |
| 12580 | - |
| 12581 | -static struct dmx_region_t *dmx_region_new(vout_display_t *vd, |
| 12582 | - DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region) |
| 12583 | -{ |
| 12584 | - vout_display_sys_t *sys = vd->sys; |
| 12585 | - video_format_t *fmt = ®ion->fmt; |
| 12586 | - struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t)); |
| 12587 | - uint32_t image_handle; |
| 12588 | - |
| 12589 | - dmx_region->pos_x = region->i_x; |
| 12590 | - dmx_region->pos_y = region->i_y; |
| 12591 | - |
| 12592 | - vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width, |
| 12593 | - fmt->i_visible_height); |
| 12594 | - vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16, |
| 12595 | - fmt->i_visible_height << 16); |
| 12596 | - vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y, |
| 12597 | - fmt->i_visible_width, fmt->i_visible_height); |
| 12598 | - |
| 12599 | - dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, |
| 12600 | - dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16), |
| 12601 | - dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16), |
| 12602 | - &image_handle); |
| 12603 | - vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32, |
| 12604 | - region->p_picture->p[0].i_pitch, |
| 12605 | - region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect); |
| 12606 | - |
| 12607 | - dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX; |
| 12608 | - dmx_region->alpha.opacity = region->i_alpha; |
| 12609 | - dmx_region->alpha.mask = DISPMANX_NO_HANDLE; |
| 12610 | - dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle, |
| 12611 | - sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource, |
| 12612 | - &dmx_region->src_rect, DISPMANX_PROTECTION_NONE, |
| 12613 | - &dmx_region->alpha, NULL, VC_IMAGE_ROT0); |
| 12614 | - |
| 12615 | - dmx_region->next = NULL; |
| 12616 | - dmx_region->picture = region->p_picture; |
| 12617 | - |
| 12618 | - return dmx_region; |
| 12619 | -} |
| 12620 | - |
| 12621 | -static void dmx_region_update(struct dmx_region_t *dmx_region, |
| 12622 | - DISPMANX_UPDATE_HANDLE_T update, picture_t *picture) |
| 12623 | -{ |
| 12624 | - vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32, |
| 12625 | - picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect); |
| 12626 | - vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource); |
| 12627 | - dmx_region->picture = picture; |
| 12628 | -} |
| 12629 | - |
| 12630 | -static void dmx_region_delete(struct dmx_region_t *dmx_region, |
| 12631 | - DISPMANX_UPDATE_HANDLE_T update) |
| 12632 | -{ |
| 12633 | - vc_dispmanx_element_remove(update, dmx_region->element); |
| 12634 | - vc_dispmanx_resource_delete(dmx_region->resource); |
| 12635 | - free(dmx_region); |
| 12636 | -} |
| 12637 | - |
| 12638 | static void maintain_phase_sync(vout_display_t *vd) |
| 12639 | { |
| 12640 | MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = { |
| 12641 | .hdr = { MMAL_PARAMETER_VIDEO_RENDER_STATS, sizeof(render_stats) }, |
| 12642 | }; |
| 12643 | - int32_t frame_duration = 1000000 / |
| 12644 | + int32_t frame_duration = CLOCK_FREQ / |
| 12645 | ((double)vd->sys->i_frame_rate / |
| 12646 | vd->sys->i_frame_rate_base); |
| 12647 | vout_display_sys_t *sys = vd->sys; |
| 12648 | @@ -1012,32 +1322,436 @@ static void maintain_phase_sync(vout_dis |
| 12649 | } |
| 12650 | } |
| 12651 | |
| 12652 | -static void show_background(vout_display_t *vd, bool enable) |
| 12653 | +static void CloseMmalVout(vlc_object_t *object) |
| 12654 | { |
| 12655 | - vout_display_sys_t *sys = vd->sys; |
| 12656 | - uint32_t image_ptr, color = 0xFF000000; |
| 12657 | - VC_RECT_T dst_rect, src_rect; |
| 12658 | - DISPMANX_UPDATE_HANDLE_T update; |
| 12659 | - |
| 12660 | - if (enable && !sys->bkg_element) { |
| 12661 | - sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1, |
| 12662 | - &image_ptr); |
| 12663 | - vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1); |
| 12664 | - vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32, |
| 12665 | - sizeof(color), &color, &dst_rect); |
| 12666 | - vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16); |
| 12667 | - vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0); |
| 12668 | - update = vc_dispmanx_update_start(0); |
| 12669 | - sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle, |
| 12670 | - sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect, |
| 12671 | - DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0); |
| 12672 | - vc_dispmanx_update_submit_sync(update); |
| 12673 | - } else if (!enable && sys->bkg_element) { |
| 12674 | - update = vc_dispmanx_update_start(0); |
| 12675 | - vc_dispmanx_element_remove(update, sys->bkg_element); |
| 12676 | - vc_dispmanx_resource_delete(sys->bkg_resource); |
| 12677 | - vc_dispmanx_update_submit_sync(update); |
| 12678 | - sys->bkg_element = DISPMANX_NO_HANDLE; |
| 12679 | - sys->bkg_resource = DISPMANX_NO_HANDLE; |
| 12680 | + vout_display_t * const vd = (vout_display_t *)object; |
| 12681 | + vout_display_sys_t * const sys = vd->sys; |
| 12682 | + char response[20]; /* answer is hvs_update_fields=%1d */ |
| 12683 | + |
| 12684 | +#if TRACE_ALL |
| 12685 | + msg_Dbg(vd, "<<< %s", __func__); |
| 12686 | +#endif |
| 12687 | + |
| 12688 | + kill_pool(sys); |
| 12689 | + |
| 12690 | + vc_tv_unregister_callback_full(tvservice_cb, vd); |
| 12691 | + |
| 12692 | + // Shouldn't be anything here - but just in case |
| 12693 | + for (unsigned int i = 0; i != SUBS_MAX; ++i) |
| 12694 | + if (sys->subpic_bufs[i] != NULL) |
| 12695 | + mmal_buffer_header_release(sys->subpic_bufs[i]); |
| 12696 | + |
| 12697 | + for (unsigned int i = 0; i != SUBS_MAX; ++i) { |
| 12698 | + vout_subpic_t * const sub = sys->subs + i; |
| 12699 | + if (sub->component != NULL) { |
| 12700 | + hw_mmal_subpic_close(VLC_OBJECT(vd), &sub->sub); |
| 12701 | + if (sub->component->control->is_enabled) |
| 12702 | + mmal_port_disable(sub->component->control); |
| 12703 | + if (sub->component->is_enabled) |
| 12704 | + mmal_component_disable(sub->component); |
| 12705 | + mmal_component_release(sub->component); |
| 12706 | + sub->component = NULL; |
| 12707 | + } |
| 12708 | } |
| 12709 | + |
| 12710 | + if (sys->input && sys->input->is_enabled) |
| 12711 | + mmal_port_disable(sys->input); |
| 12712 | + |
| 12713 | + if (sys->component && sys->component->control->is_enabled) |
| 12714 | + mmal_port_disable(sys->component->control); |
| 12715 | + |
| 12716 | + if (sys->copy_buf != NULL) |
| 12717 | + mmal_buffer_header_release(sys->copy_buf); |
| 12718 | + |
| 12719 | + if (sys->input != NULL && sys->copy_pool != NULL) |
| 12720 | + mmal_port_pool_destroy(sys->input, sys->copy_pool); |
| 12721 | + |
| 12722 | + if (sys->component && sys->component->is_enabled) |
| 12723 | + mmal_component_disable(sys->component); |
| 12724 | + |
| 12725 | + if (sys->pool) |
| 12726 | + mmal_pool_destroy(sys->pool); |
| 12727 | + |
| 12728 | + if (sys->component) |
| 12729 | + mmal_component_release(sys->component); |
| 12730 | + |
| 12731 | + isp_close(vd, sys); |
| 12732 | + |
| 12733 | + hw_mmal_vzc_pool_release(sys->vzc); |
| 12734 | + |
| 12735 | + vlc_mutex_destroy(&sys->manage_mutex); |
| 12736 | + |
| 12737 | + if (sys->native_interlaced) { |
| 12738 | + if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 || |
| 12739 | + response[18] != '0') |
| 12740 | + msg_Warn(vd, "Could not reset hvs field mode"); |
| 12741 | + } |
| 12742 | + |
| 12743 | + cma_vcsm_exit(sys->init_type);; |
| 12744 | + |
| 12745 | + free(sys); |
| 12746 | + |
| 12747 | +#if TRACE_ALL |
| 12748 | + msg_Dbg(vd, ">>> %s", __func__); |
| 12749 | +#endif |
| 12750 | +} |
| 12751 | + |
| 12752 | + |
| 12753 | +static const struct { |
| 12754 | + const char * name; |
| 12755 | + int num; |
| 12756 | +} display_name_to_num[] = { |
| 12757 | + {"auto", -1}, |
| 12758 | + {"hdmi-1", DISPMANX_ID_HDMI0}, |
| 12759 | + {"hdmi-2", DISPMANX_ID_HDMI1}, |
| 12760 | + {NULL, -2} |
| 12761 | +}; |
| 12762 | + |
| 12763 | +static const struct { |
| 12764 | + const char * name; |
| 12765 | + int transform_num; |
| 12766 | +} transform_name_to_num[] = { |
| 12767 | + {"auto", -1}, |
| 12768 | + {"0", MMAL_DISPLAY_ROT0}, |
| 12769 | + {"hflip", MMAL_DISPLAY_MIRROR_ROT0}, |
| 12770 | + {"vflip", MMAL_DISPLAY_MIRROR_ROT180}, |
| 12771 | + {"180", MMAL_DISPLAY_ROT180}, |
| 12772 | + {"transpose", MMAL_DISPLAY_MIRROR_ROT90}, |
| 12773 | + {"270", MMAL_DISPLAY_ROT270}, |
| 12774 | + {"90", MMAL_DISPLAY_ROT90}, |
| 12775 | + {"antitranspose", MMAL_DISPLAY_MIRROR_ROT270}, |
| 12776 | + {NULL, -2} |
| 12777 | +}; |
| 12778 | + |
| 12779 | +static int find_display_num(const char * const name) |
| 12780 | +{ |
| 12781 | + unsigned int i; |
| 12782 | + for (i = 0; display_name_to_num[i].name != NULL && strcasecmp(display_name_to_num[i].name, name) != 0; ++i) |
| 12783 | + /* Loop */; |
| 12784 | + return display_name_to_num[i].num; |
| 12785 | +} |
| 12786 | + |
| 12787 | +static int find_transform_num(const char * const name) |
| 12788 | +{ |
| 12789 | + unsigned int i; |
| 12790 | + for (i = 0; transform_name_to_num[i].name != NULL && strcasecmp(transform_name_to_num[i].name, name) != 0; ++i) |
| 12791 | + /* Loop */; |
| 12792 | + return transform_name_to_num[i].transform_num; |
| 12793 | +} |
| 12794 | + |
| 12795 | +#if HAVE_X11_XLIB_H |
| 12796 | +#include <X11/Xlib.h> |
| 12797 | +#include <X11/extensions/Xrandr.h> |
| 12798 | +static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd) |
| 12799 | +{ |
| 12800 | + Display * const x = XOpenDisplay(NULL); |
| 12801 | + Rotation cur_rot = 0; |
| 12802 | + MMAL_DISPLAYTRANSFORM_T trans; |
| 12803 | + |
| 12804 | + if (x == NULL) |
| 12805 | + return MMAL_DISPLAY_ROT0; |
| 12806 | + |
| 12807 | + XRRRotations(x, 0, &cur_rot); |
| 12808 | + XCloseDisplay(x); |
| 12809 | + |
| 12810 | + // Convert to MMAL |
| 12811 | + // xrandr seems to rotate the other way to mmal |
| 12812 | + |
| 12813 | + switch (cur_rot) |
| 12814 | + { |
| 12815 | + case 0: |
| 12816 | + case RR_Rotate_0: |
| 12817 | + trans = MMAL_DISPLAY_ROT0; |
| 12818 | + break; |
| 12819 | + case RR_Rotate_90: |
| 12820 | + trans = MMAL_DISPLAY_ROT270; |
| 12821 | + break; |
| 12822 | + case RR_Rotate_180: |
| 12823 | + trans = MMAL_DISPLAY_ROT180; |
| 12824 | + break; |
| 12825 | + case RR_Rotate_270: |
| 12826 | + trans = MMAL_DISPLAY_ROT90; |
| 12827 | + break; |
| 12828 | + case RR_Reflect_X: |
| 12829 | + trans = MMAL_DISPLAY_MIRROR_ROT0; |
| 12830 | + break; |
| 12831 | + case RR_Reflect_Y: |
| 12832 | + trans = MMAL_DISPLAY_MIRROR_ROT180; |
| 12833 | + break; |
| 12834 | + default: |
| 12835 | + msg_Info(vd, "Unexpected X rotation value: %#x", cur_rot); |
| 12836 | + trans = MMAL_DISPLAY_ROT0; |
| 12837 | + break; |
| 12838 | + } |
| 12839 | + |
| 12840 | + return trans; |
| 12841 | +} |
| 12842 | +#else |
| 12843 | +static MMAL_DISPLAYTRANSFORM_T get_xrandr_rotation(vout_display_t * const vd) |
| 12844 | +{ |
| 12845 | + VLC_UNUSED(vd); |
| 12846 | + return MMAL_DISPLAY_ROT0; |
| 12847 | +} |
| 12848 | +#endif |
| 12849 | + |
| 12850 | +static MMAL_RECT_T str_to_rect(const char * s) |
| 12851 | +{ |
| 12852 | + MMAL_RECT_T rect = {0}; |
| 12853 | + rect.width = strtoul(s, (char**)&s, 0); |
| 12854 | + if (*s == '\0') |
| 12855 | + return rect; |
| 12856 | + if (*s++ != 'x') |
| 12857 | + goto fail; |
| 12858 | + rect.height = strtoul(s, (char**)&s, 0); |
| 12859 | + if (*s == '\0') |
| 12860 | + return rect; |
| 12861 | + if (*s++ != '+') |
| 12862 | + goto fail; |
| 12863 | + rect.x = strtoul(s, (char**)&s, 0); |
| 12864 | + if (*s == '\0') |
| 12865 | + return rect; |
| 12866 | + if (*s++ != '+') |
| 12867 | + goto fail; |
| 12868 | + rect.y = strtoul(s, (char**)&s, 0); |
| 12869 | + if (*s != '\0') |
| 12870 | + goto fail; |
| 12871 | + return rect; |
| 12872 | + |
| 12873 | +fail: |
| 12874 | + return (MMAL_RECT_T){0,0,0,0}; |
| 12875 | +} |
| 12876 | + |
| 12877 | +static int OpenMmalVout(vlc_object_t *object) |
| 12878 | +{ |
| 12879 | + vout_display_t *vd = (vout_display_t *)object; |
| 12880 | + vout_display_sys_t *sys; |
| 12881 | + MMAL_STATUS_T status; |
| 12882 | + int ret = VLC_EGENERIC; |
| 12883 | + // At the moment all copy is via I420 |
| 12884 | + const bool needs_copy = !hw_mmal_chroma_is_mmal(vd->fmt.i_chroma); |
| 12885 | + const MMAL_FOURCC_T enc_in = needs_copy ? MMAL_ENCODING_I420 : |
| 12886 | + vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma); |
| 12887 | + |
| 12888 | +#if TRACE_ALL |
| 12889 | + msg_Dbg(vd, "<<< %s: o:%d", __func__, (int)vd->fmt.orientation); |
| 12890 | +#endif |
| 12891 | + |
| 12892 | + get_xrandr_rotation(vd); |
| 12893 | + |
| 12894 | + sys = calloc(1, sizeof(struct vout_display_sys_t)); |
| 12895 | + if (!sys) |
| 12896 | + return VLC_ENOMEM; |
| 12897 | + vd->sys = sys; |
| 12898 | + |
| 12899 | + vlc_mutex_init(&sys->manage_mutex); |
| 12900 | + |
| 12901 | + if ((sys->init_type = cma_vcsm_init()) == VCSM_INIT_NONE) |
| 12902 | + { |
| 12903 | + msg_Err(vd, "VCSM init fail"); |
| 12904 | + goto fail; |
| 12905 | + } |
| 12906 | + |
| 12907 | + vc_tv_register_callback(tvservice_cb, vd); |
| 12908 | + |
| 12909 | + sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME); |
| 12910 | + sys->transparent = var_InheritBool(vd, MMAL_VOUT_TRANSPARENT_NAME); |
| 12911 | + |
| 12912 | + { |
| 12913 | + const char *display_name = var_InheritString(vd, MMAL_DISPLAY_NAME); |
| 12914 | + int qt_num = var_InheritInteger(vd, "qt-fullscreen-screennumber" ); |
| 12915 | + int display_id = find_display_num(display_name); |
| 12916 | +// sys->display_id = display_id < 0 ? vc_tv_get_default_display_id() : display_id; |
| 12917 | + sys->display_id = display_id >= 0 ? display_id : |
| 12918 | + qt_num == 1 ? DISPMANX_ID_HDMI1 : DISPMANX_ID_HDMI; |
| 12919 | + if (display_id < -1) |
| 12920 | + msg_Warn(vd, "Unknown display device: '%s'", display_name); |
| 12921 | + else |
| 12922 | + msg_Dbg(vd, "Display device: %s, qt=%d id=%d display=%d", display_name, |
| 12923 | + qt_num, display_id, sys->display_id); |
| 12924 | + } |
| 12925 | + |
| 12926 | + { |
| 12927 | + const char *window_str = var_InheritString(vd, MMAL_VOUT_WINDOW_NAME); |
| 12928 | + sys->req_win = str_to_rect(window_str); |
| 12929 | + if (sys->req_win.width != 0) |
| 12930 | + msg_Dbg(vd, "Window: %dx%d @ %d,%d", |
| 12931 | + sys->req_win.width, sys->req_win.height, |
| 12932 | + sys->req_win.x, sys->req_win.y); |
| 12933 | + } |
| 12934 | + |
| 12935 | + { |
| 12936 | + const char *transform_name = var_InheritString(vd, MMAL_VOUT_TRANSFORM_NAME); |
| 12937 | + int transform_num = find_transform_num(transform_name); |
| 12938 | + sys->display_transform = transform_num < 0 ? |
| 12939 | + get_xrandr_rotation(vd) : |
| 12940 | + (MMAL_DISPLAYTRANSFORM_T)transform_num; |
| 12941 | + |
| 12942 | + if (transform_num < -1) |
| 12943 | + msg_Warn(vd, "Unknown vout transform: '%s'", transform_name); |
| 12944 | + else |
| 12945 | + msg_Dbg(vd, "Display transform: %s, mmal_display_transform=%d", |
| 12946 | + transform_name, (int)sys->display_transform); |
| 12947 | + |
| 12948 | + sys->video_transform = combine_transform( |
| 12949 | + vlc_to_mmal_transform(vd->fmt.orientation), sys->display_transform); |
| 12950 | + sys->dest_transform = transform_inverse(sys->display_transform); |
| 12951 | + } |
| 12952 | + |
| 12953 | + status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component); |
| 12954 | + if (status != MMAL_SUCCESS) { |
| 12955 | + msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)", |
| 12956 | + MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status)); |
| 12957 | + goto fail; |
| 12958 | + } |
| 12959 | + |
| 12960 | + sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; |
| 12961 | + status = mmal_port_enable(sys->component->control, vd_control_port_cb); |
| 12962 | + if (status != MMAL_SUCCESS) { |
| 12963 | + msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)", |
| 12964 | + sys->component->control->name, status, mmal_status_to_string(status)); |
| 12965 | + goto fail; |
| 12966 | + } |
| 12967 | + |
| 12968 | + sys->input = sys->component->input[0]; |
| 12969 | + sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd; |
| 12970 | + |
| 12971 | + sys->input->format->encoding = enc_in; |
| 12972 | + sys->input->format->encoding_variant = 0; |
| 12973 | + sys->i_planes = 1; |
| 12974 | + |
| 12975 | + display_set_format(vd, sys->input->format, want_isp(vd)); |
| 12976 | + |
| 12977 | + status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true); |
| 12978 | + if (status != MMAL_SUCCESS) { |
| 12979 | + msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)", |
| 12980 | + sys->input->name, status, mmal_status_to_string(status)); |
| 12981 | + goto fail; |
| 12982 | + } |
| 12983 | + |
| 12984 | + status = mmal_port_format_commit(sys->input); |
| 12985 | + if (status != MMAL_SUCCESS) { |
| 12986 | + msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)", |
| 12987 | + sys->input->name, status, mmal_status_to_string(status)); |
| 12988 | + goto fail; |
| 12989 | + } |
| 12990 | + |
| 12991 | + sys->input->buffer_size = sys->input->buffer_size_recommended; |
| 12992 | + |
| 12993 | + if (!needs_copy) { |
| 12994 | + sys->input->buffer_num = 30; |
| 12995 | + } |
| 12996 | + else { |
| 12997 | + sys->input->buffer_num = 2; |
| 12998 | + if ((sys->copy_pool = mmal_port_pool_create(sys->input, 2, sys->input->buffer_size)) == NULL) |
| 12999 | + { |
| 13000 | + msg_Err(vd, "Cannot create copy pool"); |
| 13001 | + goto fail; |
| 13002 | + } |
| 13003 | + } |
| 13004 | + |
| 13005 | + set_display_windows(vd, sys); |
| 13006 | + |
| 13007 | + configure_display(vd, vd->cfg, &vd->source); |
| 13008 | + |
| 13009 | + status = mmal_port_enable(sys->input, vd_input_port_cb); |
| 13010 | + if (status != MMAL_SUCCESS) { |
| 13011 | + msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)", |
| 13012 | + sys->input->name, status, mmal_status_to_string(status)); |
| 13013 | + goto fail; |
| 13014 | + } |
| 13015 | + |
| 13016 | + status = mmal_component_enable(sys->component); |
| 13017 | + if (status != MMAL_SUCCESS) { |
| 13018 | + msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)", |
| 13019 | + sys->component->name, status, mmal_status_to_string(status)); |
| 13020 | + goto fail; |
| 13021 | + } |
| 13022 | + |
| 13023 | + if ((sys->pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL) |
| 13024 | + { |
| 13025 | + msg_Err(vd, "Failed to create input pool"); |
| 13026 | + goto fail; |
| 13027 | + } |
| 13028 | + |
| 13029 | + for (unsigned int i = 0; i != SUBS_MAX; ++i) { |
| 13030 | + vout_subpic_t * const sub = sys->subs + i; |
| 13031 | + if ((status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sub->component)) != MMAL_SUCCESS) |
| 13032 | + { |
| 13033 | + msg_Dbg(vd, "Failed to create subpic component %d", i); |
| 13034 | + goto fail; |
| 13035 | + } |
| 13036 | + sub->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd; |
| 13037 | + if ((status = mmal_port_enable(sub->component->control, vd_control_port_cb)) != MMAL_SUCCESS) { |
| 13038 | + msg_Err(vd, "Failed to enable control port %s on sub %d (status=%"PRIx32" %s)", |
| 13039 | + sys->component->control->name, i, status, mmal_status_to_string(status)); |
| 13040 | + goto fail; |
| 13041 | + } |
| 13042 | + if ((status = hw_mmal_subpic_open(VLC_OBJECT(vd), &sub->sub, sub->component->input[0], |
| 13043 | + sys->display_id, sys->layer + i + 1)) != MMAL_SUCCESS) { |
| 13044 | + msg_Dbg(vd, "Failed to open subpic %d", i); |
| 13045 | + goto fail; |
| 13046 | + } |
| 13047 | + if ((status = mmal_component_enable(sub->component)) != MMAL_SUCCESS) |
| 13048 | + { |
| 13049 | + msg_Dbg(vd, "Failed to enable subpic component %d", i); |
| 13050 | + goto fail; |
| 13051 | + } |
| 13052 | + } |
| 13053 | + |
| 13054 | + // If we can't deal with it directly ask for I420 |
| 13055 | + vd->fmt.i_chroma = req_chroma(vd); |
| 13056 | + |
| 13057 | + vd->info = (vout_display_info_t){ |
| 13058 | + .is_slow = false, |
| 13059 | + .has_double_click = false, |
| 13060 | + .needs_hide_mouse = false, |
| 13061 | + .has_pictures_invalid = true, |
| 13062 | + .subpicture_chromas = hw_mmal_vzc_subpicture_chromas |
| 13063 | + }; |
| 13064 | + |
| 13065 | + vd->pool = vd_pool; |
| 13066 | + vd->prepare = vd_prepare; |
| 13067 | + vd->display = vd_display; |
| 13068 | + vd->control = vd_control; |
| 13069 | + |
| 13070 | + |
| 13071 | + msg_Dbg(vd, ">>> %s: ok", __func__); |
| 13072 | + return VLC_SUCCESS; |
| 13073 | + |
| 13074 | +fail: |
| 13075 | + CloseMmalVout(object); |
| 13076 | + |
| 13077 | + msg_Dbg(vd, ">>> %s: rv=%d", __func__, ret); |
| 13078 | + |
| 13079 | + return ret == VLC_SUCCESS ? VLC_EGENERIC : ret; |
| 13080 | } |
| 13081 | + |
| 13082 | +vlc_module_begin() |
| 13083 | + |
| 13084 | + add_submodule() |
| 13085 | + |
| 13086 | + set_shortname(N_("MMAL vout")) |
| 13087 | + set_description(N_("MMAL-based vout plugin for Raspberry Pi")) |
| 13088 | + set_capability("vout display", 16) // 1 point better than ASCII art |
| 13089 | + add_shortcut("mmal_vout") |
| 13090 | + set_category( CAT_VIDEO ) |
| 13091 | + set_subcategory( SUBCAT_VIDEO_VOUT ) |
| 13092 | + |
| 13093 | + add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false) |
| 13094 | + add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT, |
| 13095 | + MMAL_ADJUST_REFRESHRATE_LONGTEXT, false) |
| 13096 | + add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT, |
| 13097 | + MMAL_NATIVE_INTERLACE_LONGTEXT, false) |
| 13098 | + add_string(MMAL_DISPLAY_NAME, "auto", MMAL_DISPLAY_TEXT, |
| 13099 | + MMAL_DISPLAY_LONGTEXT, false) |
| 13100 | + add_string(MMAL_VOUT_TRANSFORM_NAME, "auto", MMAL_VOUT_TRANSFORM_TEXT, |
| 13101 | + MMAL_VOUT_TRANSFORM_LONGTEXT, false) |
| 13102 | + add_string(MMAL_VOUT_WINDOW_NAME, "fullscreen", MMAL_VOUT_WINDOW_TEXT, |
| 13103 | + MMAL_VOUT_WINDOW_LONGTEXT, false) |
| 13104 | + add_bool(MMAL_VOUT_TRANSPARENT_NAME, false, MMAL_VOUT_TRANSPARENT_TEXT, |
| 13105 | + MMAL_VOUT_TRANSPARENT_LONGTEXT, false) |
| 13106 | + set_callbacks(OpenMmalVout, CloseMmalVout) |
| 13107 | + |
| 13108 | +vlc_module_end() |
| 13109 | + |
| 13110 | + |
| 13111 | --- /dev/null |
| 13112 | +++ b/modules/hw/mmal/xsplitter.c |
| 13113 | @@ -0,0 +1,584 @@ |
| 13114 | +#ifdef HAVE_CONFIG_H |
| 13115 | +#include "config.h" |
| 13116 | +#endif |
| 13117 | + |
| 13118 | +#include <stdatomic.h> |
| 13119 | + |
| 13120 | +#include <vlc_common.h> |
| 13121 | +#include <vlc_plugin.h> |
| 13122 | +#include <vlc_threads.h> |
| 13123 | +#include <vlc_vout_display.h> |
| 13124 | +#include <vlc_modules.h> |
| 13125 | + |
| 13126 | +#include <bcm_host.h> |
| 13127 | +#include <interface/mmal/mmal.h> |
| 13128 | +#include <interface/mmal/util/mmal_util.h> |
| 13129 | +#include <interface/mmal/util/mmal_default_components.h> |
| 13130 | + |
| 13131 | +#include "mmal_picture.h" |
| 13132 | + |
| 13133 | +#define TRACE_ALL 0 |
| 13134 | + |
| 13135 | +typedef struct display_desc_s |
| 13136 | +{ |
| 13137 | + vout_display_t * vout; |
| 13138 | + unsigned int max_pels; |
| 13139 | +} display_desc_t; |
| 13140 | + |
| 13141 | +typedef struct mmal_x11_sys_s |
| 13142 | +{ |
| 13143 | + bool use_mmal; |
| 13144 | + display_desc_t * cur_desc; |
| 13145 | + display_desc_t mmal_desc; |
| 13146 | + display_desc_t x_desc; |
| 13147 | + uint32_t changed; |
| 13148 | + vlc_fourcc_t subpicture_chromas[16]; |
| 13149 | +} mmal_x11_sys_t; |
| 13150 | + |
| 13151 | +#define MAX_GL_PELS (1920*1080) |
| 13152 | +#define MAX_MMAL_PELS (4096*4096) // Should never be hit |
| 13153 | + |
| 13154 | +#if 0 |
| 13155 | +// Gen prog for the following table |
| 13156 | +// Not done inline in case we end up pulling in FP libs we don't want |
| 13157 | +#include <math.h> |
| 13158 | +#include <stdio.h> |
| 13159 | + |
| 13160 | +int main(int argc, char *argv[]) |
| 13161 | +{ |
| 13162 | + unsigned int i; |
| 13163 | + for (i = 0; i != 64; ++i) |
| 13164 | + { |
| 13165 | + printf(" [%2u]=%5u,", i, (unsigned int)(0.5 + (1/sqrt((i + 5)/4.0) * 65536.0))); |
| 13166 | + if (i % 4 == 3) |
| 13167 | + printf("\n"); |
| 13168 | + } |
| 13169 | +} |
| 13170 | +#endif |
| 13171 | + |
| 13172 | +static const uint16_t sqrt_tab[64] = { |
| 13173 | + [ 0]=58617, [ 1]=53510, [ 2]=49541, [ 3]=46341, |
| 13174 | + [ 4]=43691, [ 5]=41449, [ 6]=39520, [ 7]=37837, |
| 13175 | + [ 8]=36353, [ 9]=35030, [10]=33843, [11]=32768, |
| 13176 | + [12]=31790, [13]=30894, [14]=30070, [15]=29309, |
| 13177 | + [16]=28602, [17]=27945, [18]=27330, [19]=26755, |
| 13178 | + [20]=26214, [21]=25705, [22]=25225, [23]=24770, |
| 13179 | + [24]=24339, [25]=23930, [26]=23541, [27]=23170, |
| 13180 | + [28]=22817, [29]=22479, [30]=22155, [31]=21845, |
| 13181 | + [32]=21548, [33]=21263, [34]=20988, [35]=20724, |
| 13182 | + [36]=20470, [37]=20225, [38]=19988, [39]=19760, |
| 13183 | + [40]=19539, [41]=19326, [42]=19119, [43]=18919, |
| 13184 | + [44]=18725, [45]=18536, [46]=18354, [47]=18176, |
| 13185 | + [48]=18004, [49]=17837, [50]=17674, [51]=17515, |
| 13186 | + [52]=17361, [53]=17211, [54]=17064, [55]=16921, |
| 13187 | + [56]=16782, [57]=16646, [58]=16514, [59]=16384, |
| 13188 | + [60]=16257, [61]=16134, [62]=16013, [63]=15895 |
| 13189 | +}; |
| 13190 | +#define SQRT_MAX (sizeof(sqrt_tab)/sizeof(sqrt_tab[0]) - 1) |
| 13191 | + |
| 13192 | +static bool cpy_fmt_limit_size(const display_desc_t * const dd, |
| 13193 | + video_format_t * const dst, |
| 13194 | + const video_format_t * const src) |
| 13195 | +{ |
| 13196 | + const unsigned int src_pel = src->i_visible_width * src->i_visible_height; |
| 13197 | + |
| 13198 | + *dst = *src; |
| 13199 | + |
| 13200 | + if (src_pel <= dd->max_pels) |
| 13201 | + return false; |
| 13202 | + |
| 13203 | + // scaling factor sqrt(max_pel/cur_pel) |
| 13204 | + // sqrt done by lookup & 16 bit fixed-point maths - not exactly accurate but |
| 13205 | + // easily good enough & avoids floating point (which may be slow) |
| 13206 | + // src_pel > max_pel so n >= 0 |
| 13207 | + // Rounding should be such that exact sqrts work and everything else rounds |
| 13208 | + // down |
| 13209 | + unsigned int n = ((src_pel * 4 - 1) / dd->max_pels) - 4; |
| 13210 | + unsigned int scale = sqrt_tab[n >= SQRT_MAX ? SQRT_MAX : n]; |
| 13211 | + |
| 13212 | + // Rescale width - rounding up to 16 |
| 13213 | + unsigned int width = ((src->i_visible_width * scale + (16 << 16) - 1) >> 16) & ~15; |
| 13214 | + // Rescale height based on new width |
| 13215 | + unsigned int height = (src->i_visible_height * width + src->i_visible_width/2) / src->i_visible_width; |
| 13216 | + |
| 13217 | +// fprintf(stderr, "%dx%d -> %dx%d\n", src->i_visible_width, src->i_visible_height, width, height); |
| 13218 | + |
| 13219 | + dst->i_width = width; |
| 13220 | + dst->i_visible_width = width; |
| 13221 | + dst->i_height = height; |
| 13222 | + dst->i_visible_height = height; |
| 13223 | + return true; |
| 13224 | +} |
| 13225 | + |
| 13226 | +static void unload_display_module(vout_display_t * const x_vout) |
| 13227 | +{ |
| 13228 | + if (x_vout != NULL) { |
| 13229 | + if (x_vout->module != NULL) { |
| 13230 | + module_unneed(x_vout, x_vout->module); |
| 13231 | + } |
| 13232 | + vlc_object_release(x_vout); |
| 13233 | + } |
| 13234 | +} |
| 13235 | + |
| 13236 | +static void CloseMmalX11(vlc_object_t *object) |
| 13237 | +{ |
| 13238 | + vout_display_t * const vd = (vout_display_t *)object; |
| 13239 | + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; |
| 13240 | + |
| 13241 | + msg_Dbg(vd, "<<< %s", __func__); |
| 13242 | + |
| 13243 | + if (sys == NULL) |
| 13244 | + return; |
| 13245 | + |
| 13246 | + unload_display_module(sys->x_desc.vout); |
| 13247 | + |
| 13248 | + unload_display_module(sys->mmal_desc.vout); |
| 13249 | + |
| 13250 | + free(sys); |
| 13251 | + |
| 13252 | + msg_Dbg(vd, ">>> %s", __func__); |
| 13253 | +} |
| 13254 | + |
| 13255 | +static void mmal_x11_event(vout_display_t * x_vd, int cmd, va_list args) |
| 13256 | +{ |
| 13257 | + vout_display_t * const vd = x_vd->owner.sys; |
| 13258 | +#if TRACE_ALL |
| 13259 | + msg_Dbg(vd, "<<< %s (cmd=%d)", __func__, cmd); |
| 13260 | +#endif |
| 13261 | + |
| 13262 | + // Do not fall into the display assert if Invalid not supported |
| 13263 | + if (cmd == VOUT_DISPLAY_EVENT_PICTURES_INVALID && |
| 13264 | + !vd->info.has_pictures_invalid) |
| 13265 | + return; |
| 13266 | + |
| 13267 | + vd->owner.event(vd, cmd, args); |
| 13268 | +} |
| 13269 | + |
| 13270 | +static vout_window_t * mmal_x11_window_new(vout_display_t * x_vd, unsigned type) |
| 13271 | +{ |
| 13272 | + vout_display_t * const vd = x_vd->owner.sys; |
| 13273 | +#if TRACE_ALL |
| 13274 | + msg_Dbg(vd, "<<< %s (type=%d)", __func__, type); |
| 13275 | +#endif |
| 13276 | + return vd->owner.window_new(vd, type); |
| 13277 | +} |
| 13278 | + |
| 13279 | +static void mmal_x11_window_del(vout_display_t * x_vd, vout_window_t * win) |
| 13280 | +{ |
| 13281 | + vout_display_t * const vd = x_vd->owner.sys; |
| 13282 | +#if TRACE_ALL |
| 13283 | + msg_Dbg(vd, "<<< %s", __func__); |
| 13284 | +#endif |
| 13285 | + vd->owner.window_del(vd, win); |
| 13286 | +} |
| 13287 | + |
| 13288 | + |
| 13289 | +static int load_display_module(vout_display_t * const vd, |
| 13290 | + display_desc_t * const dd, |
| 13291 | + const char * const cap, |
| 13292 | + const char * const module_name) |
| 13293 | +{ |
| 13294 | + vout_display_t * const x_vout = vlc_object_create(vd, sizeof(*x_vout)); |
| 13295 | + |
| 13296 | + dd->vout = NULL; |
| 13297 | + if (!x_vout) |
| 13298 | + return -1; |
| 13299 | + |
| 13300 | + x_vout->owner.sys = vd; |
| 13301 | + x_vout->owner.event = mmal_x11_event; |
| 13302 | + x_vout->owner.window_new = mmal_x11_window_new; |
| 13303 | + x_vout->owner.window_del = mmal_x11_window_del; |
| 13304 | + |
| 13305 | + x_vout->cfg = vd->cfg; |
| 13306 | + x_vout->info = vd->info; |
| 13307 | + cpy_fmt_limit_size(dd, &x_vout->source, &vd->source); |
| 13308 | + cpy_fmt_limit_size(dd, &x_vout->fmt, &vd->fmt); |
| 13309 | + |
| 13310 | + if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL) |
| 13311 | + { |
| 13312 | + msg_Err(vd, "Failed to open Xsplitter:%s module", module_name); |
| 13313 | + goto fail; |
| 13314 | + } |
| 13315 | + |
| 13316 | + msg_Dbg(vd, "R/G/B: %08x/%08x/%08x", x_vout->fmt.i_rmask, x_vout->fmt.i_gmask, x_vout->fmt.i_bmask); |
| 13317 | + |
| 13318 | + dd->vout = x_vout; |
| 13319 | + return 0; |
| 13320 | + |
| 13321 | +fail: |
| 13322 | + vlc_object_release(x_vout); |
| 13323 | + return -1; |
| 13324 | +} |
| 13325 | + |
| 13326 | + |
| 13327 | +/* Return a pointer over the current picture_pool_t* (mandatory). |
| 13328 | + * |
| 13329 | + * For performance reasons, it is best to provide at least count |
| 13330 | + * pictures but it is not mandatory. |
| 13331 | + * You can return NULL when you cannot/do not want to allocate |
| 13332 | + * pictures. |
| 13333 | + * The vout display module keeps the ownership of the pool and can |
| 13334 | + * destroy it only when closing or on invalid pictures control. |
| 13335 | + */ |
| 13336 | +static picture_pool_t * mmal_x11_pool(vout_display_t * vd, unsigned count) |
| 13337 | +{ |
| 13338 | + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; |
| 13339 | + vout_display_t * const x_vd = sys->cur_desc->vout; |
| 13340 | +#if TRACE_ALL |
| 13341 | + char buf0[5]; |
| 13342 | + msg_Dbg(vd, "<<< %s (count=%d) %s:%dx%d->%s:%dx%d", __func__, count, |
| 13343 | + str_fourcc(buf0, vd->fmt.i_chroma), |
| 13344 | + vd->fmt.i_width, vd->fmt.i_height, |
| 13345 | + str_fourcc(buf0, x_vd->fmt.i_chroma), |
| 13346 | + x_vd->fmt.i_width, x_vd->fmt.i_height); |
| 13347 | +#endif |
| 13348 | + picture_pool_t * pool = x_vd->pool(x_vd, count); |
| 13349 | +#if TRACE_ALL |
| 13350 | + msg_Dbg(vd, ">>> %s: %p", __func__, pool); |
| 13351 | +#endif |
| 13352 | + return pool; |
| 13353 | +} |
| 13354 | + |
| 13355 | +/* Prepare a picture and an optional subpicture for display (optional). |
| 13356 | + * |
| 13357 | + * It is called before the next pf_display call to provide as much |
| 13358 | + * time as possible to prepare the given picture and the subpicture |
| 13359 | + * for display. |
| 13360 | + * You are guaranted that pf_display will always be called and using |
| 13361 | + * the exact same picture_t and subpicture_t. |
| 13362 | + * You cannot change the pixel content of the picture_t or of the |
| 13363 | + * subpicture_t. |
| 13364 | + */ |
| 13365 | +static void mmal_x11_prepare(vout_display_t * vd, picture_t * pic, subpicture_t * sub) |
| 13366 | +{ |
| 13367 | + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; |
| 13368 | + vout_display_t * const x_vd = sys->cur_desc->vout; |
| 13369 | +#if TRACE_ALL |
| 13370 | + msg_Dbg(vd, "<<< %s", __func__); |
| 13371 | +#endif |
| 13372 | + if (x_vd->prepare) |
| 13373 | + x_vd->prepare(x_vd, pic, sub); |
| 13374 | +} |
| 13375 | + |
| 13376 | +/* Display a picture and an optional subpicture (mandatory). |
| 13377 | + * |
| 13378 | + * The picture and the optional subpicture must be displayed as soon as |
| 13379 | + * possible. |
| 13380 | + * You cannot change the pixel content of the picture_t or of the |
| 13381 | + * subpicture_t. |
| 13382 | + * |
| 13383 | + * This function gives away the ownership of the picture and of the |
| 13384 | + * subpicture, so you must release them as soon as possible. |
| 13385 | + */ |
| 13386 | +static void mmal_x11_display(vout_display_t * vd, picture_t * pic, subpicture_t * sub) |
| 13387 | +{ |
| 13388 | + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; |
| 13389 | + vout_display_t * const x_vd = sys->cur_desc->vout; |
| 13390 | + |
| 13391 | +#if TRACE_ALL |
| 13392 | + const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic); |
| 13393 | + 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, |
| 13394 | + is_mmal_pic, sys->use_mmal); |
| 13395 | +#endif |
| 13396 | + |
| 13397 | + if (x_vd->fmt.i_chroma != pic->format.i_chroma || |
| 13398 | + x_vd->fmt.i_width != pic->format.i_width || |
| 13399 | + x_vd->fmt.i_height != pic->format.i_height) |
| 13400 | + { |
| 13401 | + msg_Dbg(vd, "%s: Picture dropped", __func__); |
| 13402 | + picture_Release(pic); |
| 13403 | + if (sub != NULL) |
| 13404 | + subpicture_Delete(sub); |
| 13405 | + return; |
| 13406 | + } |
| 13407 | + |
| 13408 | + x_vd->display(x_vd, pic, sub); |
| 13409 | +} |
| 13410 | + |
| 13411 | + |
| 13412 | +static int vout_display_Control(const display_desc_t * const dd, int query, ...) |
| 13413 | +{ |
| 13414 | + va_list args; |
| 13415 | + int result; |
| 13416 | + |
| 13417 | + va_start(args, query); |
| 13418 | + result = dd->vout->control(dd->vout, query, args); |
| 13419 | + va_end(args); |
| 13420 | + |
| 13421 | + return result; |
| 13422 | +} |
| 13423 | + |
| 13424 | +static bool want_mmal_vout(vout_display_t * const vd, const mmal_x11_sys_t * const sys) |
| 13425 | +{ |
| 13426 | + return sys->mmal_desc.vout != NULL && |
| 13427 | + (sys->x_desc.vout == NULL || var_InheritBool(vd, "fullscreen")); |
| 13428 | +} |
| 13429 | + |
| 13430 | +static inline int |
| 13431 | +up_rv(const int a, const int b) |
| 13432 | +{ |
| 13433 | + return a != 0 ? a : b; |
| 13434 | +} |
| 13435 | + |
| 13436 | +static int |
| 13437 | +reset_pictures(vout_display_t * const vd, const display_desc_t * const desc) |
| 13438 | +{ |
| 13439 | + int rv = 0; |
| 13440 | + VLC_UNUSED(vd); |
| 13441 | + if (desc->vout) |
| 13442 | + { |
| 13443 | + // If the display doesn't have has_pictures_invalid then it doesn't |
| 13444 | + // expect RESET_PICTURES |
| 13445 | + if (desc->vout->info.has_pictures_invalid) |
| 13446 | + vout_display_Control(desc, VOUT_DISPLAY_RESET_PICTURES); |
| 13447 | + } |
| 13448 | + return rv; |
| 13449 | +} |
| 13450 | + |
| 13451 | +static int |
| 13452 | +replay_controls(vout_display_t * const vd, const display_desc_t * const desc, const int32_t changed) |
| 13453 | +{ |
| 13454 | + if ((changed & (1 << VOUT_DISPLAY_CHANGE_DISPLAY_FILLED)) != 0) |
| 13455 | + vout_display_Control(desc, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, vd->cfg); |
| 13456 | + if ((changed & (1 << VOUT_DISPLAY_CHANGE_ZOOM)) != 0) |
| 13457 | + vout_display_Control(desc, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg); |
| 13458 | + if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) | |
| 13459 | + (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0) |
| 13460 | + cpy_fmt_limit_size(desc, &desc->vout->source, &vd->source); |
| 13461 | + if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT)) != 0) |
| 13462 | + vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT); |
| 13463 | + if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP)) != 0) |
| 13464 | + vout_display_Control(desc, VOUT_DISPLAY_CHANGE_SOURCE_CROP); |
| 13465 | + if ((changed & (1 << VOUT_DISPLAY_CHANGE_VIEWPOINT)) != 0) |
| 13466 | + vout_display_Control(desc, VOUT_DISPLAY_CHANGE_VIEWPOINT, vd->cfg); |
| 13467 | + return 0; |
| 13468 | +} |
| 13469 | + |
| 13470 | +/* Control on the module (mandatory) */ |
| 13471 | +static int mmal_x11_control(vout_display_t * vd, int ctl, va_list va) |
| 13472 | +{ |
| 13473 | + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; |
| 13474 | + display_desc_t *x_desc = sys->cur_desc; |
| 13475 | + int rv; |
| 13476 | +#if TRACE_ALL |
| 13477 | + msg_Dbg(vd, "<<< %s[%d] (ctl=%d)", __func__, sys->use_mmal, ctl); |
| 13478 | +#endif |
| 13479 | + // Remember what we've told this vd - unwanted ctls ignored on replay |
| 13480 | + if (ctl >= 0 && ctl <= 31) |
| 13481 | + sys->changed |= (1 << ctl); |
| 13482 | + |
| 13483 | + switch (ctl) { |
| 13484 | + case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE: |
| 13485 | + { |
| 13486 | + const vout_display_cfg_t * const cfg = va_arg(va, const vout_display_cfg_t *); |
| 13487 | + const bool want_mmal = want_mmal_vout(vd, sys); |
| 13488 | + const bool swap_vout = (sys->use_mmal != want_mmal); |
| 13489 | + display_desc_t * const new_desc = want_mmal ? &sys->mmal_desc : &sys->x_desc; |
| 13490 | + |
| 13491 | + msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d", |
| 13492 | + cfg->display.width, cfg->display.height, sys->mmal_desc.vout, want_mmal, |
| 13493 | + var_InheritBool(vd, "fullscreen")); |
| 13494 | + |
| 13495 | + // Repeat any control calls that we sent to the previous vd |
| 13496 | + if (swap_vout && sys->changed != 0) { |
| 13497 | + const uint32_t changed = sys->changed; |
| 13498 | + sys->changed = 0; |
| 13499 | + replay_controls(vd, new_desc, changed); |
| 13500 | + } |
| 13501 | + |
| 13502 | + if (swap_vout) { |
| 13503 | + if (sys->use_mmal) { |
| 13504 | + vout_display_Control(x_desc, VOUT_DISPLAY_CHANGE_MMAL_HIDE); |
| 13505 | + } |
| 13506 | + vout_display_SendEventPicturesInvalid(vd); |
| 13507 | + } |
| 13508 | + |
| 13509 | + rv = vout_display_Control(new_desc, ctl, cfg); |
| 13510 | + if (rv == VLC_SUCCESS) { |
| 13511 | + vd->fmt = new_desc->vout->fmt; |
| 13512 | + sys->cur_desc = new_desc; |
| 13513 | + sys->use_mmal = want_mmal; |
| 13514 | + } |
| 13515 | + |
| 13516 | + |
| 13517 | + break; |
| 13518 | + } |
| 13519 | + |
| 13520 | + case VOUT_DISPLAY_RESET_PICTURES: |
| 13521 | + { |
| 13522 | + char dbuf0[5], dbuf1[5], dbuf2[5]; |
| 13523 | + msg_Dbg(vd, "<<< %s: Pic reset: fmt: %s,%dx%d<-%s,%dx%d, source: %s,%dx%d/%dx%d", __func__, |
| 13524 | + str_fourcc(dbuf0, vd->fmt.i_chroma), vd->fmt.i_width, vd->fmt.i_height, |
| 13525 | + str_fourcc(dbuf1, x_desc->vout->fmt.i_chroma), x_desc->vout->fmt.i_width, x_desc->vout->fmt.i_height, |
| 13526 | + str_fourcc(dbuf2, vd->source.i_chroma), vd->source.i_width, vd->source.i_height, x_desc->vout->source.i_width, |
| 13527 | + x_desc->vout->source.i_height); |
| 13528 | + } |
| 13529 | + rv = reset_pictures(vd, &sys->x_desc); |
| 13530 | + rv = up_rv(rv, reset_pictures(vd, &sys->mmal_desc)); |
| 13531 | + |
| 13532 | + vd->fmt = x_desc->vout->fmt; |
| 13533 | + break; |
| 13534 | + |
| 13535 | + case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT: |
| 13536 | + case VOUT_DISPLAY_CHANGE_SOURCE_CROP: |
| 13537 | + cpy_fmt_limit_size(x_desc, &x_desc->vout->source, &vd->source); |
| 13538 | + |
| 13539 | + /* FALLTHRU */ |
| 13540 | + default: |
| 13541 | + rv = x_desc->vout->control(x_desc->vout, ctl, va); |
| 13542 | +// vd->fmt = x_vd->fmt; |
| 13543 | + break; |
| 13544 | + } |
| 13545 | +#if TRACE_ALL |
| 13546 | + msg_Dbg(vd, ">>> %s (rv=%d)", __func__, rv); |
| 13547 | +#endif |
| 13548 | + return rv; |
| 13549 | +} |
| 13550 | + |
| 13551 | +#define DO_MANAGE 0 |
| 13552 | + |
| 13553 | +#if DO_MANAGE |
| 13554 | +/* Manage pending event (optional) */ |
| 13555 | +static void mmal_x11_manage(vout_display_t * vd) |
| 13556 | +{ |
| 13557 | + mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys; |
| 13558 | + vout_display_t * const x_vd = sys->cur_desc->vout; |
| 13559 | +#if TRACE_ALL |
| 13560 | + msg_Dbg(vd, "<<< %s", __func__); |
| 13561 | +#endif |
| 13562 | + x_vd->manage(x_vd); |
| 13563 | +} |
| 13564 | +#endif |
| 13565 | + |
| 13566 | +static int OpenMmalX11(vlc_object_t *object) |
| 13567 | +{ |
| 13568 | + vout_display_t * const vd = (vout_display_t *)object; |
| 13569 | + mmal_x11_sys_t * const sys = calloc(1, sizeof(*sys)); |
| 13570 | + int ret = VLC_SUCCESS; |
| 13571 | + |
| 13572 | + if (sys == NULL) { |
| 13573 | + return VLC_EGENERIC; |
| 13574 | + } |
| 13575 | + vd->sys = (vout_display_sys_t *)sys; |
| 13576 | + |
| 13577 | + vd->info = (vout_display_info_t){ |
| 13578 | + .is_slow = false, |
| 13579 | + .has_double_click = false, |
| 13580 | + .needs_hide_mouse = false, |
| 13581 | + .has_pictures_invalid = true, |
| 13582 | + .subpicture_chromas = NULL |
| 13583 | + }; |
| 13584 | + |
| 13585 | + { |
| 13586 | + char dbuf0[5]; |
| 13587 | + msg_Dbg(vd, ">>> %s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__, |
| 13588 | + str_fourcc(dbuf0, vd->fmt.i_chroma), |
| 13589 | + vd->fmt.i_width, vd->fmt.i_height, |
| 13590 | + vd->fmt.i_x_offset, vd->fmt.i_y_offset, |
| 13591 | + vd->fmt.i_visible_width, vd->fmt.i_visible_height, |
| 13592 | + vd->fmt.i_sar_num, vd->fmt.i_sar_den); |
| 13593 | + } |
| 13594 | + |
| 13595 | + sys->x_desc.max_pels = MAX_GL_PELS; |
| 13596 | + sys->mmal_desc.max_pels = MAX_MMAL_PELS; |
| 13597 | + |
| 13598 | + if (load_display_module(vd, &sys->x_desc, "vout display", "opengles2") == 0) |
| 13599 | + { |
| 13600 | + msg_Dbg(vd, "Opengles2 output found"); |
| 13601 | + } |
| 13602 | + else |
| 13603 | + { |
| 13604 | + sys->x_desc.max_pels = MAX_MMAL_PELS; |
| 13605 | + if (load_display_module(vd, &sys->x_desc, "vout display", "xcb_x11") == 0) |
| 13606 | + msg_Dbg(vd, "X11 XCB output found"); |
| 13607 | + } |
| 13608 | + |
| 13609 | + if ((load_display_module(vd, &sys->mmal_desc, "vout display", "mmal_vout")) == 0) |
| 13610 | + msg_Dbg(vd, "MMAL output found"); |
| 13611 | + |
| 13612 | + if (sys->mmal_desc.vout == NULL && sys->x_desc.vout == NULL) { |
| 13613 | + char dbuf0[5], dbuf1[5]; |
| 13614 | + 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)); |
| 13615 | + goto fail; |
| 13616 | + } |
| 13617 | + |
| 13618 | + vd->pool = mmal_x11_pool; |
| 13619 | + vd->prepare = mmal_x11_prepare; |
| 13620 | + vd->display = mmal_x11_display; |
| 13621 | + vd->control = mmal_x11_control; |
| 13622 | +#if DO_MANAGE |
| 13623 | + vd->manage = mmal_x11_manage; |
| 13624 | +#endif |
| 13625 | + |
| 13626 | + if (want_mmal_vout(vd, sys)) { |
| 13627 | + sys->cur_desc = &sys->mmal_desc; |
| 13628 | + sys->use_mmal = true; |
| 13629 | + } |
| 13630 | + else { |
| 13631 | + sys->cur_desc = &sys->x_desc; |
| 13632 | + sys->use_mmal = false; |
| 13633 | + } |
| 13634 | + |
| 13635 | + if (sys->mmal_desc.vout == NULL || sys->x_desc.vout == NULL) { |
| 13636 | + vd->info = sys->cur_desc->vout->info; |
| 13637 | + vd->info.has_pictures_invalid = true; // Should make this unwanted |
| 13638 | + } |
| 13639 | + else { |
| 13640 | + // We have both - construct a combination |
| 13641 | + vd->info = (vout_display_info_t){ |
| 13642 | + .is_slow = false, |
| 13643 | + .has_double_click = sys->mmal_desc.vout->info.has_double_click || sys->x_desc.vout->info.has_double_click, |
| 13644 | + .needs_hide_mouse = sys->mmal_desc.vout->info.needs_hide_mouse || sys->x_desc.vout->info.needs_hide_mouse, |
| 13645 | + .has_pictures_invalid = true, |
| 13646 | + }; |
| 13647 | + // Construct intersection of subpicture chromas |
| 13648 | + // sys calloced so no need to add the terminating zero |
| 13649 | + if (sys->mmal_desc.vout->info.subpicture_chromas != NULL && sys->x_desc.vout->info.subpicture_chromas != NULL) { |
| 13650 | + unsigned int n = 0; |
| 13651 | + // N^2 - fix if we ever care |
| 13652 | + for (const vlc_fourcc_t * p1 = sys->mmal_desc.vout->info.subpicture_chromas; *p1 != 0 && n != 15; ++p1) { |
| 13653 | + for (const vlc_fourcc_t * p2 = sys->x_desc.vout->info.subpicture_chromas; *p2 != 0; ++p2) { |
| 13654 | + if (*p1 == *p2) { |
| 13655 | + sys->subpicture_chromas[n++] = *p1; |
| 13656 | + break; |
| 13657 | + } |
| 13658 | + } |
| 13659 | + } |
| 13660 | + if (n != 0) |
| 13661 | + vd->info.subpicture_chromas = sys->subpicture_chromas; |
| 13662 | + } |
| 13663 | + } |
| 13664 | + vd->fmt = sys->cur_desc->vout->fmt; |
| 13665 | + |
| 13666 | +#if TRACE_ALL |
| 13667 | + { |
| 13668 | + char dbuf0[5]; |
| 13669 | + msg_Dbg(vd, ">>> %s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d", __func__, |
| 13670 | + module_get_name(sys->cur_desc->vout->module, false), |
| 13671 | + str_fourcc(dbuf0, vd->fmt.i_chroma), |
| 13672 | + vd->fmt.i_width, vd->fmt.i_height, |
| 13673 | + vd->fmt.i_x_offset, vd->fmt.i_y_offset, |
| 13674 | + vd->fmt.i_visible_width, vd->fmt.i_visible_height, |
| 13675 | + vd->fmt.i_sar_num, vd->fmt.i_sar_den); |
| 13676 | + } |
| 13677 | +#endif |
| 13678 | + return VLC_SUCCESS; |
| 13679 | + |
| 13680 | +fail: |
| 13681 | + CloseMmalX11(VLC_OBJECT(vd)); |
| 13682 | + return ret == VLC_SUCCESS ? VLC_EGENERIC : ret; |
| 13683 | +} |
| 13684 | + |
| 13685 | + |
| 13686 | + |
| 13687 | + |
| 13688 | +vlc_module_begin() |
| 13689 | + set_shortname(N_("MMAL x11 splitter")) |
| 13690 | + set_description(N_("MMAL x11 splitter for Raspberry Pi")) |
| 13691 | + set_capability("vout display", 300) // Between GLES & GL |
| 13692 | + add_shortcut("mmal_x11") |
| 13693 | + set_category( CAT_VIDEO ) |
| 13694 | + set_subcategory( SUBCAT_VIDEO_VOUT ) |
| 13695 | + set_callbacks(OpenMmalX11, CloseMmalX11) |
| 13696 | +vlc_module_end() |
| 13697 | + |
| 13698 | --- a/modules/video_output/opengl/egl.c |
| 13699 | +++ b/modules/video_output/opengl/egl.c |
| 13700 | @@ -43,6 +43,8 @@ |
| 13701 | # include "../android/utils.h" |
| 13702 | #endif |
| 13703 | |
| 13704 | +#define REQUIRE_DMA_BUF_IMPORT 1 |
| 13705 | + |
| 13706 | typedef struct vlc_gl_sys_t |
| 13707 | { |
| 13708 | EGLDisplay display; |
| 13709 | @@ -355,6 +357,14 @@ static int Open (vlc_object_t *obj, cons |
| 13710 | goto error; |
| 13711 | } |
| 13712 | |
| 13713 | +#if REQUIRE_DMA_BUF_IMPORT |
| 13714 | + if (!CheckToken(ext, "EGL_EXT_image_dma_buf_import")) |
| 13715 | + { |
| 13716 | + msg_Dbg(obj, "No dma_buf_import - fall back to X"); |
| 13717 | + goto error; |
| 13718 | + } |
| 13719 | +#endif |
| 13720 | + |
| 13721 | const EGLint conf_attr[] = { |
| 13722 | EGL_RED_SIZE, 5, |
| 13723 | EGL_GREEN_SIZE, 5, |
| 13724 | --- a/src/input/decoder.c |
| 13725 | +++ b/src/input/decoder.c |
| 13726 | @@ -1995,6 +1995,7 @@ void input_DecoderDelete( decoder_t *p_d |
| 13727 | vlc_mutex_lock( &p_owner->lock ); |
| 13728 | p_owner->b_waiting = false; |
| 13729 | vlc_cond_signal( &p_owner->wait_request ); |
| 13730 | + vlc_mutex_unlock( &p_owner->lock ); |
| 13731 | |
| 13732 | /* If the video output is paused or slow, or if the picture pool size was |
| 13733 | * under-estimated (e.g. greedy video filter, buggy decoder...), the |
| 13734 | @@ -2005,7 +2006,6 @@ void input_DecoderDelete( decoder_t *p_d |
| 13735 | * worker threads (if any) and the decoder thread to terminate. */ |
| 13736 | if( p_owner->p_vout != NULL ) |
| 13737 | vout_Cancel( p_owner->p_vout, true ); |
| 13738 | - vlc_mutex_unlock( &p_owner->lock ); |
| 13739 | |
| 13740 | vlc_join( p_owner->thread, NULL ); |
| 13741 | |
| 13742 | --- a/src/misc/fourcc.c |
| 13743 | +++ b/src/misc/fourcc.c |
| 13744 | @@ -755,8 +755,13 @@ static const struct |
| 13745 | { { VLC_CODEC_VDPAU_VIDEO_420, VLC_CODEC_VDPAU_VIDEO_422, |
| 13746 | VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT }, |
| 13747 | FAKE_FMT() }, |
| 13748 | - { { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE, |
| 13749 | - VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE }, |
| 13750 | + { { VLC_CODEC_ANDROID_OPAQUE }, FAKE_FMT() }, |
| 13751 | + { { VLC_CODEC_MMAL_OPAQUE, VLC_CODEC_MMAL_ZC_SAND30 }, |
| 13752 | + FAKE_FMT() }, |
| 13753 | + { { VLC_CODEC_MMAL_ZC_I420, VLC_CODEC_MMAL_ZC_SAND8, |
| 13754 | + VLC_CODEC_MMAL_ZC_SAND10, VLC_CODEC_MMAL_ZC_RGB32 }, |
| 13755 | + FAKE_FMT() }, |
| 13756 | + { { VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE }, |
| 13757 | FAKE_FMT() }, |
| 13758 | { { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B }, |
| 13759 | FAKE_FMT() }, |
| 13760 | --- a/src/misc/picture.c |
| 13761 | +++ b/src/misc/picture.c |
| 13762 | @@ -365,10 +365,30 @@ void picture_CopyProperties( picture_t * |
| 13763 | p_dst->b_top_field_first = p_src->b_top_field_first; |
| 13764 | } |
| 13765 | |
| 13766 | +static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma) |
| 13767 | +{ |
| 13768 | + return i_chroma == VLC_CODEC_MMAL_OPAQUE || |
| 13769 | + i_chroma == VLC_CODEC_MMAL_ZC_I420 || |
| 13770 | + i_chroma == VLC_CODEC_MMAL_ZC_RGB32 || |
| 13771 | + i_chroma == VLC_CODEC_MMAL_ZC_SAND10 || |
| 13772 | + i_chroma == VLC_CODEC_MMAL_ZC_SAND30 || |
| 13773 | + i_chroma == VLC_CODEC_MMAL_ZC_SAND8; |
| 13774 | +} |
| 13775 | + |
| 13776 | void picture_CopyPixels( picture_t *p_dst, const picture_t *p_src ) |
| 13777 | { |
| 13778 | - for( int i = 0; i < p_src->i_planes ; i++ ) |
| 13779 | - plane_CopyPixels( p_dst->p+i, p_src->p+i ); |
| 13780 | + if( is_zc_chroma(p_src->format.i_chroma) ) |
| 13781 | + { |
| 13782 | + assert(p_dst->i_planes == 0); |
| 13783 | + p_dst->i_planes = p_src->i_planes; |
| 13784 | + for( int i = 0; i < p_src->i_planes; i++ ) |
| 13785 | + p_dst->p[i] = p_src->p[i]; |
| 13786 | + } |
| 13787 | + else |
| 13788 | + { |
| 13789 | + for( int i = 0; i < p_src->i_planes; i++ ) |
| 13790 | + plane_CopyPixels( p_dst->p+i, p_src->p+i ); |
| 13791 | + } |
| 13792 | |
| 13793 | assert( p_dst->context == NULL ); |
| 13794 | |
| 13795 | --- a/src/video_output/video_output.c |
| 13796 | +++ b/src/video_output/video_output.c |
| 13797 | @@ -964,6 +964,17 @@ static picture_t *ConvertRGB32AndBlend(v |
| 13798 | return NULL; |
| 13799 | } |
| 13800 | |
| 13801 | + |
| 13802 | +static inline bool is_zc_chroma(const vlc_fourcc_t i_chroma) |
| 13803 | +{ |
| 13804 | + return i_chroma == VLC_CODEC_MMAL_OPAQUE || |
| 13805 | + i_chroma == VLC_CODEC_MMAL_ZC_I420 || |
| 13806 | + i_chroma == VLC_CODEC_MMAL_ZC_RGB32 || |
| 13807 | + i_chroma == VLC_CODEC_MMAL_ZC_SAND10 || |
| 13808 | + i_chroma == VLC_CODEC_MMAL_ZC_SAND30 || |
| 13809 | + i_chroma == VLC_CODEC_MMAL_ZC_SAND8; |
| 13810 | +} |
| 13811 | + |
| 13812 | static int ThreadDisplayRenderPicture(vout_thread_t *vout, bool is_forced) |
| 13813 | { |
| 13814 | vout_thread_sys_t *sys = vout->p; |
| 13815 | @@ -1098,7 +1109,7 @@ static int ThreadDisplayRenderPicture(vo |
| 13816 | } |
| 13817 | |
| 13818 | assert(vout_IsDisplayFiltered(vd) == !sys->display.use_dr); |
| 13819 | - if (sys->display.use_dr && !is_direct) { |
| 13820 | + if (sys->display.use_dr && !is_direct && !is_zc_chroma(todisplay->format.i_chroma)) { |
| 13821 | picture_t *direct = NULL; |
| 13822 | if (likely(vout->p->display_pool != NULL)) |
| 13823 | direct = picture_pool_Get(vout->p->display_pool); |