You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
10197 lines
340 KiB
10197 lines
340 KiB
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -3419,6 +3419,9 @@
|
|
AC_ARG_ENABLE(mmal,
|
|
AS_HELP_STRING([--enable-mmal],
|
|
[Multi-Media Abstraction Layer (MMAL) hardware plugin (default enable)]))
|
|
+AC_ARG_ENABLE(mmal_avcodec,
|
|
+ AS_HELP_STRING([--enable-mmal-avcodec],
|
|
+ [Use MMAL enabled avcodec libs (default disable)]))
|
|
if test "${enable_mmal}" != "no"; then
|
|
VLC_SAVE_FLAGS
|
|
LDFLAGS="${LDFLAGS} -L/opt/vc/lib -lvchostif"
|
|
@@ -3429,7 +3432,7 @@
|
|
VLC_ADD_PLUGIN([mmal])
|
|
VLC_ADD_LDFLAGS([mmal],[ -L/opt/vc/lib ])
|
|
VLC_ADD_CFLAGS([mmal],[ -isystem /opt/vc/include -isystem /opt/vc/include/interface/vcos/pthreads -isystem /opt/vc/include/interface/vmcs_host/linux ])
|
|
- VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif ]) ], [
|
|
+ VLC_ADD_LIBS([mmal],[ -lbcm_host -lmmal -lmmal_core -lmmal_components -lmmal_util -lvchostif -lvchiq_arm -lvcsm ]) ], [
|
|
AS_IF([test "${enable_mmal}" = "yes"],
|
|
[ AC_MSG_ERROR([Cannot find bcm library...]) ],
|
|
[ AC_MSG_WARN([Cannot find bcm library...]) ])
|
|
@@ -3441,6 +3444,7 @@
|
|
VLC_RESTORE_FLAGS
|
|
fi
|
|
AM_CONDITIONAL([HAVE_MMAL], [test "${have_mmal}" = "yes"])
|
|
+AM_CONDITIONAL([HAVE_MMAL_AVCODEC], [test "${enable_mmal_avcodec}" = "yes"])
|
|
|
|
dnl
|
|
dnl evas plugin
|
|
--- a/include/vlc_fourcc.h
|
|
+++ b/include/vlc_fourcc.h
|
|
@@ -365,6 +365,9 @@
|
|
|
|
/* Broadcom MMAL opaque buffer type */
|
|
#define VLC_CODEC_MMAL_OPAQUE VLC_FOURCC('M','M','A','L')
|
|
+#define VLC_CODEC_MMAL_ZC_SAND8 VLC_FOURCC('Z','S','D','8')
|
|
+#define VLC_CODEC_MMAL_ZC_SAND10 VLC_FOURCC('Z','S','D','0')
|
|
+#define VLC_CODEC_MMAL_ZC_I420 VLC_FOURCC('Z','4','2','0')
|
|
|
|
/* DXVA2 opaque video surface for use with D3D9 */
|
|
#define VLC_CODEC_D3D9_OPAQUE VLC_FOURCC('D','X','A','9') /* 4:2:0 8 bpc */
|
|
--- a/modules/hw/mmal/Makefile.am
|
|
+++ b/modules/hw/mmal/Makefile.am
|
|
@@ -1,23 +1,39 @@
|
|
include $(top_srcdir)/modules/common.am
|
|
mmaldir = $(pluginsdir)/mmal
|
|
|
|
-AM_CFLAGS += $(CFLAGS_mmal)
|
|
-AM_LDFLAGS += -rpath '$(mmaldir)' $(LDFLAGS_mmal)
|
|
+AM_CFLAGS += -pthread $(CFLAGS_mmal)
|
|
+AM_LDFLAGS += -pthread -rpath '$(mmaldir)' $(LDFLAGS_mmal)
|
|
|
|
-libmmal_vout_plugin_la_SOURCES = vout.c mmal_picture.c mmal_picture.h
|
|
+libmmal_vout_plugin_la_SOURCES = vout.c subpic.c mmal_picture.c subpic.h mmal_picture.h
|
|
libmmal_vout_plugin_la_CFLAGS = $(AM_CFLAGS)
|
|
libmmal_vout_plugin_la_LDFLAGS = $(AM_LDFLAGS) -lm
|
|
libmmal_vout_plugin_la_LIBADD = $(LIBS_mmal)
|
|
mmal_LTLIBRARIES = libmmal_vout_plugin.la
|
|
|
|
-libmmal_codec_plugin_la_SOURCES = codec.c
|
|
+libmmal_codec_plugin_la_SOURCES = codec.c subpic.c mmal_picture.c blend_rgba_neon.S subpic.h mmal_picture.h
|
|
libmmal_codec_plugin_la_CFLAGS = $(AM_CFLAGS)
|
|
libmmal_codec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
|
|
libmmal_codec_plugin_la_LIBADD = $(LIBS_mmal)
|
|
mmal_LTLIBRARIES += libmmal_codec_plugin.la
|
|
|
|
-libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c
|
|
+libmmal_deinterlace_plugin_la_SOURCES = deinterlace.c mmal_picture.c mmal_picture.h
|
|
libmmal_deinterlace_plugin_la_CFLAGS = $(AM_CFLAGS)
|
|
libmmal_deinterlace_plugin_la_LDFLAGS = $(AM_LDFLAGS)
|
|
libmmal_deinterlace_plugin_la_LIBADD = $(LIBS_mmal)
|
|
mmal_LTLIBRARIES += libmmal_deinterlace_plugin.la
|
|
+
|
|
+libmmal_xsplitter_plugin_la_SOURCES = xsplitter.c mmal_picture.c mmal_picture.h
|
|
+libmmal_xsplitter_plugin_la_CFLAGS = $(AM_CFLAGS)
|
|
+libmmal_xsplitter_plugin_la_LDFLAGS = $(AM_LDFLAGS)
|
|
+libmmal_xsplitter_plugin_la_LIBADD = $(LIBS_mmal)
|
|
+mmal_LTLIBRARIES += libmmal_xsplitter_plugin.la
|
|
+
|
|
+if HAVE_MMAL_AVCODEC
|
|
+libmmal_avcodec_plugin_la_SOURCES = mmal_avcodec.c mmal_picture.c mmal_picture.h
|
|
+libmmal_avcodec_plugin_la_CFLAGS = $(AM_CFLAGS)
|
|
+libmmal_avcodec_plugin_la_LDFLAGS = $(AM_LDFLAGS)
|
|
+libmmal_avcodec_plugin_la_LIBADD = $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(LIBS_mmal)
|
|
+mmal_LTLIBRARIES += libmmal_avcodec_plugin.la
|
|
+endif
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/blend_rgba_neon.S
|
|
@@ -0,0 +1,200 @@
|
|
+ .syntax unified
|
|
+ .arm
|
|
+// .thumb
|
|
+ .text
|
|
+ .align 16
|
|
+ .arch armv7-a
|
|
+ .fpu neon-vfpv4
|
|
+
|
|
+@ blend_rgbx_rgba_neon
|
|
+
|
|
+@ Implements /255 as ((x * 257) + 0x8000) >> 16
|
|
+@ This generates something in the range [(x+126)/255, (x+127)/255] which is good enough
|
|
+
|
|
+@ There is advantage to aligning src and/or dest - dest gives a bit more due to being used twice
|
|
+
|
|
+
|
|
+
|
|
+@ [r0] RGBx dest loaded into d20-d23
|
|
+@ [r1] RGBA src merge loaded into d16-d19
|
|
+@ r2 plane alpha
|
|
+@ r3 count (pixels)
|
|
+
|
|
+.macro blend_main sR, sG, sB, sA, dR, dG, dB, dA
|
|
+
|
|
+ push { r4, lr }
|
|
+
|
|
+ vdup.u8 d7, r2
|
|
+
|
|
+ subs r3, #8
|
|
+ vmov.u8 d6, #0xff
|
|
+
|
|
+ blt 2f
|
|
+
|
|
+ @ If < 16 bytes to move then don't bother trying to align
|
|
+ @ (a) This means the the align doesn't need to worry about r3 underflow
|
|
+ @ (b) The overhead would be greater than any gain
|
|
+ cmp r3, #8
|
|
+ mov r4, r3
|
|
+ ble 1f
|
|
+
|
|
+ @ Align r1 on a 32 byte boundary
|
|
+ neg r3, r0
|
|
+ ubfx r3, r3, #2, #3
|
|
+
|
|
+ cmp r3, #0
|
|
+ blne 10f
|
|
+
|
|
+ sub r3, r4, r3
|
|
+
|
|
+1:
|
|
+ vld4.8 {d16, d17, d18, d19}, [r1]
|
|
+
|
|
+1:
|
|
+ @ Alpha ends up in d19
|
|
+ vmull.u8 q15, \sA, d7
|
|
+
|
|
+ vld4.8 {d20, d21, d22, d23}, [r0]
|
|
+
|
|
+ vsra.u16 q15, q15, #8
|
|
+ subs r3, #8
|
|
+ vrshrn.u16 d31, q15, #8
|
|
+ vsub.u8 d30, d6, d31
|
|
+
|
|
+ vmull.u8 q12, \sR, d31
|
|
+ vmull.u8 q13, \sG, d31
|
|
+ vmull.u8 q14, \sB, d31
|
|
+ addge r1, #32
|
|
+
|
|
+ vmlal.u8 q12, \dR, d30
|
|
+ vmlal.u8 q13, \dG, d30
|
|
+ vmlal.u8 q14, \dB, d30
|
|
+ vld4.8 {d16, d17, d18, d19}, [r1]
|
|
+
|
|
+ vsra.u16 q12, q12, #8 @ * 257/256
|
|
+ vsra.u16 q13, q13, #8
|
|
+ vsra.u16 q14, q14, #8
|
|
+
|
|
+ vrshrn.u16 \dR, q12, #8
|
|
+ vrshrn.u16 \dG, q13, #8
|
|
+ vrshrn.u16 \dB, q14, #8
|
|
+ vmov.u8 \dA, #0xff
|
|
+
|
|
+ vst4.8 {d20, d21, d22, d23}, [r0]!
|
|
+ bge 1b
|
|
+ add r1, #32
|
|
+
|
|
+2:
|
|
+ cmp r3, #-8
|
|
+ blgt 10f
|
|
+
|
|
+ pop { r4, pc }
|
|
+
|
|
+
|
|
+// Partial version
|
|
+// Align @ start & deal with tail
|
|
+10:
|
|
+ lsls r2, r3, #30 @ b2 -> C, b1 -> N
|
|
+ mov r2, r0
|
|
+ bcc 1f
|
|
+ vld4.8 {d16[0], d17[0], d18[0], d19[0]}, [r1]!
|
|
+ vld4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]!
|
|
+ vld4.8 {d16[1], d17[1], d18[1], d19[1]}, [r1]!
|
|
+ vld4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]!
|
|
+ vld4.8 {d16[2], d17[2], d18[2], d19[2]}, [r1]!
|
|
+ vld4.8 {d20[2], d21[2], d22[2], d23[2]}, [r0]!
|
|
+ vld4.8 {d16[3], d17[3], d18[3], d19[3]}, [r1]!
|
|
+ vld4.8 {d20[3], d21[3], d22[3], d23[3]}, [r0]!
|
|
+1:
|
|
+ bpl 1f
|
|
+ vld4.8 {d16[4], d17[4], d18[4], d19[4]}, [r1]!
|
|
+ vld4.8 {d20[4], d21[4], d22[4], d23[4]}, [r0]!
|
|
+ vld4.8 {d16[5], d17[5], d18[5], d19[5]}, [r1]!
|
|
+ vld4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]!
|
|
+1:
|
|
+ tst r3, #1
|
|
+ beq 1f
|
|
+ vld4.8 {d16[6], d17[6], d18[6], d19[6]}, [r1]!
|
|
+ vld4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]!
|
|
+1:
|
|
+ @ Alpha ends up in d19
|
|
+
|
|
+ vmull.u8 q15, \sA, d7
|
|
+ vsra.u16 q15, q15, #8
|
|
+ vrshrn.u16 d31, q15, #8
|
|
+ vsub.u8 d30, d6, d31
|
|
+
|
|
+ vmull.u8 q12, \sR, d31
|
|
+ vmull.u8 q13, \sG, d31
|
|
+ vmull.u8 q14, \sB, d31
|
|
+
|
|
+ vmlal.u8 q12, \dR, d30
|
|
+ vmlal.u8 q13, \dG, d30
|
|
+ vmlal.u8 q14, \dB, d30
|
|
+
|
|
+ vsra.u16 q12, q12, #8
|
|
+ vsra.u16 q13, q13, #8
|
|
+ vsra.u16 q14, q14, #8
|
|
+
|
|
+ vrshrn.u16 \dR, q12, #8
|
|
+ vrshrn.u16 \dG, q13, #8
|
|
+ vrshrn.u16 \dB, q14, #8
|
|
+ vmov.u8 \dA, #0xff
|
|
+
|
|
+ mov r0, r2
|
|
+ lsls r2, r3, #30 @ b2 -> C, b1 -> N
|
|
+ mov r2, r0
|
|
+ bcc 1f
|
|
+ vst4.8 {d20[0], d21[0], d22[0], d23[0]}, [r0]!
|
|
+ vst4.8 {d20[1], d21[1], d22[1], d23[1]}, [r0]!
|
|
+ vst4.8 {d20[2], d21[2], d22[2], d23[2]}, [r0]!
|
|
+ vst4.8 {d20[3], d21[3], d22[3], d23[3]}, [r0]!
|
|
+1:
|
|
+ bpl 1f
|
|
+ vst4.8 {d20[4], d21[4], d22[4], d23[4]}, [r0]!
|
|
+ vst4.8 {d20[5], d21[5], d22[5], d23[5]}, [r0]!
|
|
+1:
|
|
+ tst r3, #1
|
|
+ beq 1f
|
|
+ vst4.8 {d20[6], d21[6], d22[6], d23[6]}, [r0]!
|
|
+1:
|
|
+ bx lr
|
|
+
|
|
+.endm
|
|
+
|
|
+
|
|
+@ [r0] RGBx dest (Byte order: R, G, B, x)
|
|
+@ [r1] RGBA src merge (Byte order: R, G, B, A)
|
|
+@ r2 plane alpha
|
|
+@ r3 count (pixels)
|
|
+
|
|
+@ Whilst specified as RGBx+RGBA the only important part is the position of
|
|
+@ alpha, the other components are all treated the same
|
|
+
|
|
+@ [r0] RGBx dest (Byte order: R, G, B, x)
|
|
+@ [r1] RGBA src merge (Byte order: R, G, B, A) - same as above
|
|
+@ r2 plane alpha
|
|
+@ r3 count (pixels)
|
|
+ .align 16
|
|
+ .global blend_rgbx_rgba_neon
|
|
+#ifdef __ELF__
|
|
+ .type blend_rgbx_rgba_neon, %function
|
|
+#endif
|
|
+blend_rgbx_rgba_neon:
|
|
+ blend_main d16, d17, d18, d19, d20, d21, d22, d23
|
|
+
|
|
+
|
|
+@ [r0] RGBx dest (Byte order: R, G, B, x)
|
|
+@ [r1] RGBA src merge (Byte order: B, G, R, A) - B / R swapped
|
|
+@ r2 plane alpha
|
|
+@ r3 count (pixels)
|
|
+ .align 16
|
|
+ .global blend_bgrx_rgba_neon
|
|
+#ifdef __ELF__
|
|
+ .type blend_bgrx_rgba_neon, %function
|
|
+#endif
|
|
+blend_bgrx_rgba_neon:
|
|
+ blend_main d18, d17, d16, d19, d20, d21, d22, d23
|
|
+
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/blend_rgba_neon.h
|
|
@@ -0,0 +1,17 @@
|
|
+#ifndef HW_MMAL_BLEND_RGBA_NEON_H
|
|
+#define HW_MMAL_BLEND_RGBA_NEON_H
|
|
+
|
|
+#ifdef __cplusplus
|
|
+extern "C" {
|
|
+#endif
|
|
+
|
|
+typedef void blend_neon_fn(void * dest, const void * src, int alpha, unsigned int n);
|
|
+extern blend_neon_fn blend_rgbx_rgba_neon;
|
|
+extern blend_neon_fn blend_bgrx_rgba_neon;
|
|
+
|
|
+#ifdef __cplusplus
|
|
+}
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/blend_test.c
|
|
@@ -0,0 +1,180 @@
|
|
+#include <stdio.h>
|
|
+#include <stdint.h>
|
|
+#include <memory.h>
|
|
+
|
|
+#include "blend_rgba_neon.h"
|
|
+
|
|
+#define RPI_PROFILE 1
|
|
+#define RPI_PROC_ALLOC 1
|
|
+#include "rpi_prof.h"
|
|
+
|
|
+static inline unsigned div255(unsigned v)
|
|
+{
|
|
+ // This models what we we do in the asm for / 255
|
|
+ // It generates something in the range [(i+126)/255, (i+127)/255] which is good enough
|
|
+ return ((v * 257) + 0x8000) >> 16;
|
|
+}
|
|
+
|
|
+static inline unsigned int a_merge(unsigned int dst, unsigned src, unsigned f)
|
|
+{
|
|
+ return div255((255 - f) * (dst) + src * f);
|
|
+}
|
|
+
|
|
+
|
|
+static void merge_line(void * dest, const void * src, int alpha, unsigned int n)
|
|
+{
|
|
+ unsigned int i;
|
|
+ const uint8_t * s_data = src;
|
|
+ uint8_t * d_data = dest;
|
|
+
|
|
+ for (i = 0; i != n; ++i) {
|
|
+ const uint32_t s_pel = ((const uint32_t *)s_data)[i];
|
|
+ const uint32_t d_pel = ((const uint32_t *)d_data)[i];
|
|
+ const unsigned int a = div255(alpha * (s_pel >> 24));
|
|
+ ((uint32_t *)d_data)[i] = 0xff000000 |
|
|
+ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 16) & 0xff, a) << 16) |
|
|
+ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) |
|
|
+ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 0) & 0xff, a) << 0 );
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+// Merge RGBA with BGRA
|
|
+static void merge_line2(void * dest, const void * src, int alpha, unsigned int n)
|
|
+{
|
|
+ unsigned int i;
|
|
+ const uint8_t * s_data = src;
|
|
+ uint8_t * d_data = dest;
|
|
+
|
|
+ for (i = 0; i != n; ++i) {
|
|
+ const uint32_t s_pel = ((const uint32_t *)s_data)[i];
|
|
+ const uint32_t d_pel = ((const uint32_t *)d_data)[i];
|
|
+ const unsigned int a = div255(alpha * (s_pel >> 24));
|
|
+ ((uint32_t *)d_data)[i] = 0xff000000 |
|
|
+ (a_merge((d_pel >> 0) & 0xff, (s_pel >> 16) & 0xff, a) << 0 ) |
|
|
+ (a_merge((d_pel >> 8) & 0xff, (s_pel >> 8) & 0xff, a) << 8 ) |
|
|
+ (a_merge((d_pel >> 16) & 0xff, (s_pel >> 0) & 0xff, a) << 16);
|
|
+ }
|
|
+}
|
|
+
|
|
+#define BUF_SIZE 256
|
|
+#define BUF_SLACK 16
|
|
+#define BUF_ALIGN 64
|
|
+#define BUF_ALLOC (BUF_SIZE + 2*BUF_SLACK + BUF_ALIGN)
|
|
+
|
|
+static void test_line(const uint32_t * const dx, const unsigned int d_off,
|
|
+ const uint32_t * const sx, const unsigned int s_off,
|
|
+ const unsigned int alpha, const unsigned int len, const int prof_no)
|
|
+{
|
|
+ uint32_t d0_buf[BUF_ALLOC];
|
|
+ uint32_t d1_buf[BUF_ALLOC];
|
|
+ const uint32_t * const s0 = sx + s_off;
|
|
+
|
|
+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
|
|
+ uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
|
|
+ unsigned int i;
|
|
+
|
|
+ memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4);
|
|
+ memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4);
|
|
+
|
|
+ merge_line(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
|
|
+
|
|
+ PROFILE_START();
|
|
+ blend_rgbx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
|
|
+ PROFILE_ACC_N(prof_no);
|
|
+
|
|
+ for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) {
|
|
+ if (d0[i] != d1[i]) {
|
|
+ printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void test_line2(const uint32_t * const dx, const unsigned int d_off,
|
|
+ const uint32_t * const sx, const unsigned int s_off,
|
|
+ const unsigned int alpha, const unsigned int len, const int prof_no)
|
|
+{
|
|
+ uint32_t d0_buf[BUF_ALLOC];
|
|
+ uint32_t d1_buf[BUF_ALLOC];
|
|
+ const uint32_t * const s0 = sx + s_off;
|
|
+
|
|
+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
|
|
+ uint32_t * const d1 = (uint32_t *)(((uintptr_t)d1_buf + (BUF_ALIGN - 1)) & ~(BUF_ALIGN - 1)) + d_off;
|
|
+ unsigned int i;
|
|
+
|
|
+ memcpy(d0, dx, (BUF_SIZE + BUF_SLACK*2)*4);
|
|
+ memcpy(d1, dx, (BUF_SIZE + BUF_SLACK*2)*4);
|
|
+
|
|
+ merge_line2(d0 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
|
|
+
|
|
+ PROFILE_START();
|
|
+ blend_bgrx_rgba_neon(d1 + BUF_SLACK, s0 + BUF_SLACK, alpha, len);
|
|
+ PROFILE_ACC_N(prof_no);
|
|
+
|
|
+ for (i = 0; i != BUF_SIZE + BUF_SLACK*2; ++i) {
|
|
+ if (d0[i] != d1[i]) {
|
|
+ printf("%3d: %08x + %08x * %02x: %08x / %08x: len=%d\n", (int)(i - BUF_SLACK), dx[i], s0[i], alpha, d0[i], d1[i], len);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+int main(int argc, char *argv[])
|
|
+{
|
|
+ unsigned int i, j;
|
|
+ uint32_t d0_buf[BUF_ALLOC];
|
|
+ uint32_t s0_buf[BUF_ALLOC];
|
|
+
|
|
+ uint32_t * const d0 = (uint32_t *)(((uintptr_t)d0_buf + 63) & ~63) + 0;
|
|
+ uint32_t * const s0 = (uint32_t *)(((uintptr_t)s0_buf + 63) & ~63) + 0;
|
|
+
|
|
+ PROFILE_INIT();
|
|
+
|
|
+ for (i = 0; i != 255*255; ++i) {
|
|
+ unsigned int a = div255(i);
|
|
+ unsigned int b = (i + 127)/255;
|
|
+ unsigned int c = (i + 126)/255;
|
|
+ if (a != b && a != c)
|
|
+ printf("%d/255: %d != %d/%d\n", i, a, b, c);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i != BUF_ALLOC; ++i) {
|
|
+ d0_buf[i] = 0xff00 | i;
|
|
+ s0_buf[i] = (i << 24) | 0x40ffc0;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i != 256; ++i) {
|
|
+ test_line(d0, 0, s0, 0, i, 256, -1);
|
|
+ }
|
|
+ for (i = 0; i != 256; ++i) {
|
|
+ test_line(d0, 0, s0, 0, 128, i, -1);
|
|
+ }
|
|
+
|
|
+ for (j = 0; j != 16; ++j) {
|
|
+ for (i = 0; i != 256; ++i) {
|
|
+ test_line(d0, j & 3, s0, j >> 2, i, 256, j);
|
|
+ }
|
|
+ PROFILE_PRINTF_N(j);
|
|
+ PROFILE_CLEAR_N(j);
|
|
+ }
|
|
+ printf("Done 1\n");
|
|
+
|
|
+ for (i = 0; i != 256; ++i) {
|
|
+ test_line2(d0, 0, s0, 0, i, 256, -1);
|
|
+ }
|
|
+ for (i = 0; i != 256; ++i) {
|
|
+ test_line2(d0, 0, s0, 0, 128, i, -1);
|
|
+ }
|
|
+
|
|
+ for (j = 0; j != 16; ++j) {
|
|
+ for (i = 0; i != 256; ++i) {
|
|
+ test_line2(d0, j & 3, s0, j >> 2, i, 256, j);
|
|
+ }
|
|
+ PROFILE_PRINTF_N(j);
|
|
+ }
|
|
+ printf("Done 2\n");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
--- a/modules/hw/mmal/codec.c
|
|
+++ b/modules/hw/mmal/codec.c
|
|
@@ -26,10 +26,12 @@
|
|
#include "config.h"
|
|
#endif
|
|
|
|
+#include <stdatomic.h>
|
|
+
|
|
#include <vlc_common.h>
|
|
-#include <vlc_atomic.h>
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_codec.h>
|
|
+#include <vlc_filter.h>
|
|
#include <vlc_threads.h>
|
|
|
|
#include <bcm_host.h>
|
|
@@ -38,255 +40,393 @@
|
|
#include <interface/mmal/util/mmal_default_components.h>
|
|
|
|
#include "mmal_picture.h"
|
|
+#include "subpic.h"
|
|
+#include "blend_rgba_neon.h"
|
|
+
|
|
+#define TRACE_ALL 0
|
|
|
|
/*
|
|
* This seems to be a bit high, but reducing it causes instabilities
|
|
*/
|
|
-#define NUM_EXTRA_BUFFERS 5
|
|
+#define NUM_EXTRA_BUFFERS 3
|
|
+//#define NUM_EXTRA_BUFFERS 6
|
|
+//#define NUM_EXTRA_BUFFERS 10
|
|
#define NUM_DECODER_BUFFER_HEADERS 30
|
|
|
|
#define MIN_NUM_BUFFERS_IN_TRANSIT 2
|
|
|
|
+#define MMAL_SLICE_HEIGHT 16
|
|
+#define MMAL_ALIGN_W 32
|
|
+#define MMAL_ALIGN_H 16
|
|
+
|
|
#define MMAL_OPAQUE_NAME "mmal-opaque"
|
|
#define MMAL_OPAQUE_TEXT N_("Decode frames directly into RPI VideoCore instead of host memory.")
|
|
#define MMAL_OPAQUE_LONGTEXT N_("Decode frames directly into RPI VideoCore instead of host memory. This option must only be used with the MMAL video output plugin.")
|
|
|
|
-static int OpenDecoder(decoder_t *dec);
|
|
-static void CloseDecoder(decoder_t *dec);
|
|
+#define MMAL_RESIZE_NAME "mmal-resize"
|
|
+#define MMAL_RESIZE_TEXT N_("Use mmal resizer rather than hvs.")
|
|
+#define MMAL_RESIZE_LONGTEXT N_("Use mmal resizer rather than isp. This uses less gpu memory than the ISP but is slower.")
|
|
+
|
|
+#define MMAL_ISP_NAME "mmal-isp"
|
|
+#define MMAL_ISP_TEXT N_("Use mmal isp rather than hvs.")
|
|
+#define MMAL_ISP_LONGTEXT N_("Use mmal isp rather than hvs. This may be faster but has no blend.")
|
|
|
|
-vlc_module_begin()
|
|
- set_shortname(N_("MMAL decoder"))
|
|
- set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
|
|
- set_capability("video decoder", 90)
|
|
- add_shortcut("mmal_decoder")
|
|
- add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
|
|
- set_callbacks(OpenDecoder, CloseDecoder)
|
|
-vlc_module_end()
|
|
|
|
-struct decoder_sys_t {
|
|
- bool opaque;
|
|
+typedef struct decoder_sys_t
|
|
+{
|
|
MMAL_COMPONENT_T *component;
|
|
MMAL_PORT_T *input;
|
|
MMAL_POOL_T *input_pool;
|
|
MMAL_PORT_T *output;
|
|
- MMAL_POOL_T *output_pool; /* only used for non-opaque mode */
|
|
+ hw_mmal_port_pool_ref_t *ppr;
|
|
MMAL_ES_FORMAT_T *output_format;
|
|
- vlc_sem_t sem;
|
|
|
|
+ MMAL_STATUS_T err_stream;
|
|
bool b_top_field_first;
|
|
bool b_progressive;
|
|
|
|
+ bool b_flushed;
|
|
+
|
|
+ // Lock to avoid pic update & allocate happenening simultainiously
|
|
+ // * We should be able to arrange life s.t. this isn't needed
|
|
+ // but while we are confused apply belt & braces
|
|
+ vlc_mutex_t pic_lock;
|
|
+
|
|
/* statistics */
|
|
- int output_in_transit;
|
|
- int input_in_transit;
|
|
atomic_bool started;
|
|
+} decoder_sys_t;
|
|
+
|
|
+
|
|
+typedef struct supported_mmal_enc_s {
|
|
+ struct {
|
|
+ MMAL_PARAMETER_HEADER_T header;
|
|
+ MMAL_FOURCC_T encodings[64];
|
|
+ } supported;
|
|
+ int n;
|
|
+} supported_mmal_enc_t;
|
|
+
|
|
+static supported_mmal_enc_t supported_mmal_enc =
|
|
+{
|
|
+ {{MMAL_PARAMETER_SUPPORTED_ENCODINGS, sizeof(((supported_mmal_enc_t *)0)->supported)}, {0}},
|
|
+ -1
|
|
};
|
|
|
|
-/* Utilities */
|
|
-static int change_output_format(decoder_t *dec);
|
|
-static int send_output_buffer(decoder_t *dec);
|
|
-static void fill_output_port(decoder_t *dec);
|
|
-
|
|
-/* VLC decoder callback */
|
|
-static int decode(decoder_t *dec, block_t *block);
|
|
-static void flush_decoder(decoder_t *dec);
|
|
-
|
|
-/* MMAL callbacks */
|
|
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
|
|
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
|
|
-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
|
|
+static inline char safe_char(const unsigned int c0)
|
|
+{
|
|
+ const unsigned int c = c0 & 0xff;
|
|
+ return c > ' ' && c < 0x7f ? c : '.';
|
|
+}
|
|
|
|
-static int OpenDecoder(decoder_t *dec)
|
|
+static const char * str_fourcc(char * buf, unsigned int fcc)
|
|
{
|
|
- int ret = VLC_SUCCESS;
|
|
- decoder_sys_t *sys;
|
|
- MMAL_PARAMETER_UINT32_T extra_buffers;
|
|
- MMAL_STATUS_T status;
|
|
+ if (fcc == 0)
|
|
+ return "----";
|
|
+ buf[0] = safe_char(fcc >> 0);
|
|
+ buf[1] = safe_char(fcc >> 8);
|
|
+ buf[2] = safe_char(fcc >> 16);
|
|
+ buf[3] = safe_char(fcc >> 24);
|
|
+ buf[4] = 0;
|
|
+ return buf;
|
|
+}
|
|
|
|
- if (dec->fmt_in.i_codec != VLC_CODEC_MPGV &&
|
|
- dec->fmt_in.i_codec != VLC_CODEC_H264)
|
|
- return VLC_EGENERIC;
|
|
+static bool is_enc_supported(const MMAL_FOURCC_T fcc)
|
|
+{
|
|
+ int i;
|
|
|
|
- sys = calloc(1, sizeof(decoder_sys_t));
|
|
- if (!sys) {
|
|
- ret = VLC_ENOMEM;
|
|
- goto out;
|
|
+ if (fcc == 0)
|
|
+ return false;
|
|
+ if (supported_mmal_enc.n == -1)
|
|
+ return true; // Unknown - say OK
|
|
+ for (i = 0; i < supported_mmal_enc.n; ++i) {
|
|
+ if (supported_mmal_enc.supported.encodings[i] == fcc)
|
|
+ return true;
|
|
}
|
|
- dec->p_sys = sys;
|
|
+ return false;
|
|
+}
|
|
|
|
- sys->opaque = var_InheritBool(dec, MMAL_OPAQUE_NAME);
|
|
- bcm_host_init();
|
|
+static bool set_and_test_enc_supported(MMAL_PORT_T * port, const MMAL_FOURCC_T fcc)
|
|
+{
|
|
+ if (supported_mmal_enc.n >= 0)
|
|
+ /* already done */;
|
|
+ else if (mmal_port_parameter_get(port, (MMAL_PARAMETER_HEADER_T *)&supported_mmal_enc.supported) != MMAL_SUCCESS)
|
|
+ supported_mmal_enc.n = 0;
|
|
+ else
|
|
+ supported_mmal_enc.n = (supported_mmal_enc.supported.header.size - sizeof(supported_mmal_enc.supported.header)) /
|
|
+ sizeof(supported_mmal_enc.supported.encodings[0]);
|
|
|
|
- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
|
|
- MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+ return is_enc_supported(fcc);
|
|
+}
|
|
|
|
- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
|
|
- status = mmal_port_enable(sys->component->control, control_port_cb);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
|
|
- sys->component->control->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
+static MMAL_FOURCC_T vlc_to_mmal_es_fourcc(const unsigned int fcc)
|
|
+{
|
|
+ switch (fcc){
|
|
+ case VLC_CODEC_MJPG:
|
|
+ return MMAL_ENCODING_MJPEG;
|
|
+ case VLC_CODEC_MP1V:
|
|
+ return MMAL_ENCODING_MP1V;
|
|
+ case VLC_CODEC_MPGV:
|
|
+ case VLC_CODEC_MP2V:
|
|
+ return MMAL_ENCODING_MP2V;
|
|
+ case VLC_CODEC_H263:
|
|
+ return MMAL_ENCODING_H263;
|
|
+ case VLC_CODEC_MP4V:
|
|
+ return MMAL_ENCODING_MP4V;
|
|
+ case VLC_CODEC_H264:
|
|
+ return MMAL_ENCODING_H264;
|
|
+ case VLC_CODEC_VP6:
|
|
+ return MMAL_ENCODING_VP6;
|
|
+ case VLC_CODEC_VP8:
|
|
+ return MMAL_ENCODING_VP8;
|
|
+ case VLC_CODEC_WMV1:
|
|
+ return MMAL_ENCODING_WMV1;
|
|
+ case VLC_CODEC_WMV2:
|
|
+ return MMAL_ENCODING_WMV2;
|
|
+ case VLC_CODEC_WMV3:
|
|
+ return MMAL_ENCODING_WMV3;
|
|
+ case VLC_CODEC_VC1:
|
|
+ return MMAL_ENCODING_WVC1;
|
|
+ case VLC_CODEC_THEORA:
|
|
+ return MMAL_ENCODING_THEORA;
|
|
+ default:
|
|
+ break;
|
|
}
|
|
+ return 0;
|
|
+}
|
|
|
|
- sys->input = sys->component->input[0];
|
|
- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
|
|
- if (dec->fmt_in.i_codec == VLC_CODEC_MPGV)
|
|
- sys->input->format->encoding = MMAL_ENCODING_MP2V;
|
|
- else
|
|
- sys->input->format->encoding = MMAL_ENCODING_H264;
|
|
+static MMAL_FOURCC_T pic_to_slice_mmal_fourcc(const MMAL_FOURCC_T fcc)
|
|
+{
|
|
+ switch (fcc){
|
|
+ case MMAL_ENCODING_I420:
|
|
+ return MMAL_ENCODING_I420_SLICE;
|
|
+ case MMAL_ENCODING_I422:
|
|
+ return MMAL_ENCODING_I422_SLICE;
|
|
+ case MMAL_ENCODING_ARGB:
|
|
+ return MMAL_ENCODING_ARGB_SLICE;
|
|
+ case MMAL_ENCODING_RGBA:
|
|
+ return MMAL_ENCODING_RGBA_SLICE;
|
|
+ case MMAL_ENCODING_ABGR:
|
|
+ return MMAL_ENCODING_ABGR_SLICE;
|
|
+ case MMAL_ENCODING_BGRA:
|
|
+ return MMAL_ENCODING_BGRA_SLICE;
|
|
+ case MMAL_ENCODING_RGB16:
|
|
+ return MMAL_ENCODING_RGB16_SLICE;
|
|
+ case MMAL_ENCODING_RGB24:
|
|
+ return MMAL_ENCODING_RGB24_SLICE;
|
|
+ case MMAL_ENCODING_RGB32:
|
|
+ return MMAL_ENCODING_RGB32_SLICE;
|
|
+ case MMAL_ENCODING_BGR16:
|
|
+ return MMAL_ENCODING_BGR16_SLICE;
|
|
+ case MMAL_ENCODING_BGR24:
|
|
+ return MMAL_ENCODING_BGR24_SLICE;
|
|
+ case MMAL_ENCODING_BGR32:
|
|
+ return MMAL_ENCODING_BGR32_SLICE;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
|
|
- if (dec->fmt_in.i_codec == VLC_CODEC_H264) {
|
|
- if (dec->fmt_in.i_extra > 0) {
|
|
- status = mmal_format_extradata_alloc(sys->input->format,
|
|
- dec->fmt_in.i_extra);
|
|
- if (status == MMAL_SUCCESS) {
|
|
- memcpy(sys->input->format->extradata, dec->fmt_in.p_extra,
|
|
- dec->fmt_in.i_extra);
|
|
- sys->input->format->extradata_size = dec->fmt_in.i_extra;
|
|
- } else {
|
|
- msg_Err(dec, "Failed to allocate extra format data on input port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- }
|
|
+#define DEBUG_SQUARES 0
|
|
+#if DEBUG_SQUARES
|
|
+static void draw_square(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h, uint32_t val)
|
|
+{
|
|
+ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x;
|
|
+ unsigned int i;
|
|
+ for (i = 0; i != h; ++i) {
|
|
+ unsigned int j;
|
|
+ for (j = 0; j != w; ++j) {
|
|
+ p[j] = val;
|
|
}
|
|
+ p += pic_stride;
|
|
}
|
|
+}
|
|
+#endif
|
|
|
|
- status = mmal_port_format_commit(sys->input);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
+#if 0
|
|
+static inline void draw_line(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int len, int inc)
|
|
+{
|
|
+ uint32_t * p = (uint32_t *)pic_buf + y * pic_stride + x;
|
|
+ while (len-- != 0) {
|
|
+ *p = ~0U;
|
|
+ p += inc;
|
|
}
|
|
- sys->input->buffer_size = sys->input->buffer_size_recommended;
|
|
- sys->input->buffer_num = sys->input->buffer_num_recommended;
|
|
+}
|
|
|
|
- status = mmal_port_enable(sys->input, input_port_cb);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
|
|
- sys->output = sys->component->output[0];
|
|
- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
|
|
+static void draw_corners(void * pic_buf, size_t pic_stride, unsigned int x, unsigned int y, unsigned int w, unsigned int h)
|
|
+{
|
|
+ const unsigned int len = 20;
|
|
+ draw_line(pic_buf, pic_stride, x, y, len, 1);
|
|
+ draw_line(pic_buf, pic_stride, x, y, len, pic_stride);
|
|
+ draw_line(pic_buf, pic_stride, x + w - 1, y, len, -1);
|
|
+ draw_line(pic_buf, pic_stride, x + w - 1, y, len, pic_stride);
|
|
+ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -1);
|
|
+ draw_line(pic_buf, pic_stride, x + w - 1, y + h - 1, len, -(int)pic_stride);
|
|
+ draw_line(pic_buf, pic_stride, x, y + h - 1, len, 1);
|
|
+ draw_line(pic_buf, pic_stride, x, y + h - 1, len, -(int)pic_stride);
|
|
+}
|
|
+#endif
|
|
|
|
- if (sys->opaque) {
|
|
- extra_buffers.hdr.id = MMAL_PARAMETER_EXTRA_BUFFERS;
|
|
- extra_buffers.hdr.size = sizeof(MMAL_PARAMETER_UINT32_T);
|
|
- extra_buffers.value = NUM_EXTRA_BUFFERS;
|
|
- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(dec, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
|
|
- status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+// Buffer either attached to pic or released
|
|
+static picture_t * alloc_opaque_pic(decoder_t * const dec, MMAL_BUFFER_HEADER_T * const buf)
|
|
+{
|
|
+ decoder_sys_t *const dec_sys = dec->p_sys;
|
|
|
|
- msg_Dbg(dec, "Activate zero-copy for output port");
|
|
- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
|
|
- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
|
|
- 1
|
|
- };
|
|
+ vlc_mutex_lock(&dec_sys->pic_lock);
|
|
+ picture_t * const pic = decoder_NewPicture(dec);
|
|
+ vlc_mutex_unlock(&dec_sys->pic_lock);
|
|
|
|
- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(dec, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
|
|
- sys->output->name, status, mmal_status_to_string(status));
|
|
- goto out;
|
|
- }
|
|
- }
|
|
+ if (pic == NULL)
|
|
+ goto fail1;
|
|
|
|
- status = mmal_port_enable(sys->output, output_port_cb);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(dec, "Failed to enable output port %s (status=%"PRIx32" %s)",
|
|
- sys->output->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
+ if (buf->length == 0) {
|
|
+ msg_Err(dec, "%s: Empty buffer", __func__);
|
|
+ goto fail2;
|
|
}
|
|
|
|
- status = mmal_component_enable(sys->component);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
|
|
- sys->component->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+ if ((pic->context = hw_mmal_gen_context(MMAL_ENCODING_OPAQUE, buf, dec_sys->ppr)) == NULL)
|
|
+ goto fail2;
|
|
|
|
- sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0);
|
|
+ buf_to_pic_copy_props(pic, buf);
|
|
|
|
- if (sys->opaque) {
|
|
- dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
|
|
- dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
|
|
- } else {
|
|
- dec->fmt_out.i_codec = VLC_CODEC_I420;
|
|
- dec->fmt_out.video.i_chroma = VLC_CODEC_I420;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
|
|
+#endif
|
|
+
|
|
+ return pic;
|
|
+
|
|
+fail2:
|
|
+ picture_Release(pic);
|
|
+fail1:
|
|
+ // Recycle rather than release to avoid buffer starvation if NewPic fails
|
|
+ hw_mmal_port_pool_ref_recycle(dec_sys->ppr, buf);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
+{
|
|
+ decoder_t *dec = (decoder_t *)port->userdata;
|
|
+ MMAL_STATUS_T status;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "<<< %s: cmd=%d, data=%p", __func__, buffer->cmd, buffer->data);
|
|
+#endif
|
|
+
|
|
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
|
|
+ status = *(uint32_t *)buffer->data;
|
|
+ dec->p_sys->err_stream = status;
|
|
+ msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
|
|
+ mmal_status_to_string(status));
|
|
}
|
|
|
|
- dec->pf_decode = decode;
|
|
- dec->pf_flush = flush_decoder;
|
|
+ mmal_buffer_header_release(buffer);
|
|
+}
|
|
|
|
- vlc_sem_init(&sys->sem, 0);
|
|
+static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
+{
|
|
+ block_t * const block = (block_t *)buffer->user_data;
|
|
|
|
-out:
|
|
- if (ret != VLC_SUCCESS)
|
|
- CloseDecoder(dec);
|
|
+ (void)port; // Unused
|
|
|
|
- return ret;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg((decoder_t *)port->userdata, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
|
|
+ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
|
|
+#endif
|
|
+
|
|
+ mmal_buffer_header_reset(buffer);
|
|
+ mmal_buffer_header_release(buffer);
|
|
+
|
|
+ if (block != NULL)
|
|
+ block_Release(block);
|
|
}
|
|
|
|
-static void CloseDecoder(decoder_t *dec)
|
|
+static void decoder_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
{
|
|
- decoder_sys_t *sys = dec->p_sys;
|
|
- MMAL_BUFFER_HEADER_T *buffer;
|
|
+ decoder_t * const dec = (decoder_t *)port->userdata;
|
|
|
|
- if (!sys)
|
|
+ if (buffer->cmd == 0 && buffer->length != 0)
|
|
+ {
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "<<< %s: cmd=%d, data=%p, len=%d/%d, pts=%lld", __func__,
|
|
+ buffer->cmd, buffer->data, buffer->length, buffer->alloc_size, (long long)buffer->pts);
|
|
+#endif
|
|
+
|
|
+ picture_t *pic = alloc_opaque_pic(dec, buffer);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "flags=%#x, video flags=%#x", buffer->flags, buffer->type->video.flags);
|
|
+#endif
|
|
+ if (pic == NULL)
|
|
+ msg_Err(dec, "Failed to allocate new picture");
|
|
+ else
|
|
+ decoder_QueueVideo(dec, pic);
|
|
+ // Buffer released or attached to pic - do not release again
|
|
return;
|
|
+ }
|
|
|
|
- if (sys->component && sys->component->control->is_enabled)
|
|
- mmal_port_disable(sys->component->control);
|
|
+ if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED)
|
|
+ {
|
|
+ decoder_sys_t * const sys = dec->p_sys;
|
|
+ MMAL_EVENT_FORMAT_CHANGED_T * const fmt = mmal_event_format_changed_get(buffer);
|
|
+ MMAL_ES_FORMAT_T * const format = mmal_format_alloc();
|
|
|
|
- if (sys->input && sys->input->is_enabled)
|
|
- mmal_port_disable(sys->input);
|
|
+ if (format == NULL)
|
|
+ msg_Err(dec, "Failed to allocate new format");
|
|
+ else
|
|
+ {
|
|
+ mmal_format_full_copy(format, fmt->format);
|
|
+ format->encoding = MMAL_ENCODING_OPAQUE;
|
|
|
|
- if (sys->output && sys->output->is_enabled)
|
|
- mmal_port_disable(sys->output);
|
|
+ if (sys->output_format != NULL)
|
|
+ mmal_format_free(sys->output_format);
|
|
|
|
- if (sys->component && sys->component->is_enabled)
|
|
- mmal_component_disable(sys->component);
|
|
+ sys->output_format = format;
|
|
+ }
|
|
+ }
|
|
+ else if (buffer->cmd != 0) {
|
|
+ char buf0[5];
|
|
+ msg_Warn(dec, "Unexpected output cb event: %s", str_fourcc(buf0, buffer->cmd));
|
|
+ }
|
|
|
|
- if (sys->input_pool)
|
|
- mmal_pool_destroy(sys->input_pool);
|
|
+ // If we get here then we were flushing (cmd == 0 && len == 0) or
|
|
+ // that was an EVENT - in either case we want to release the buffer
|
|
+ // back to its pool rather than recycle it.
|
|
+ mmal_buffer_header_reset(buffer);
|
|
+ buffer->user_data = NULL;
|
|
+ mmal_buffer_header_release(buffer);
|
|
+}
|
|
|
|
- if (sys->output_format)
|
|
- mmal_format_free(sys->output_format);
|
|
|
|
- if (sys->output_pool)
|
|
- mmal_pool_destroy(sys->output_pool);
|
|
|
|
- if (sys->component)
|
|
- mmal_component_release(sys->component);
|
|
+static void fill_output_port(decoder_t *dec)
|
|
+{
|
|
+ decoder_sys_t *sys = dec->p_sys;
|
|
|
|
- vlc_sem_destroy(&sys->sem);
|
|
- free(sys);
|
|
+ if (decoder_UpdateVideoFormat(dec) != 0)
|
|
+ {
|
|
+ // If we have a new format don't bother stuffing the buffer
|
|
+ // We should get a reset RSN
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "%s: Updated", __func__);
|
|
+#endif
|
|
|
|
- bcm_host_deinit();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ hw_mmal_port_pool_ref_fill(sys->ppr);
|
|
+ return;
|
|
}
|
|
|
|
static int change_output_format(decoder_t *dec)
|
|
{
|
|
MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T interlace_type;
|
|
- decoder_sys_t *sys = dec->p_sys;
|
|
+ decoder_sys_t * const sys = dec->p_sys;
|
|
MMAL_STATUS_T status;
|
|
- int pool_size;
|
|
int ret = 0;
|
|
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "%s: <<<", __func__);
|
|
+#endif
|
|
+
|
|
if (atomic_load(&sys->started)) {
|
|
mmal_format_full_copy(sys->output->format, sys->output_format);
|
|
status = mmal_port_format_commit(sys->output);
|
|
@@ -300,7 +440,9 @@
|
|
}
|
|
|
|
port_reset:
|
|
+#if TRACE_ALL
|
|
msg_Dbg(dec, "%s: Do full port reset", __func__);
|
|
+#endif
|
|
status = mmal_port_disable(sys->output);
|
|
if (status != MMAL_SUCCESS) {
|
|
msg_Err(dec, "Failed to disable output port (status=%"PRIx32" %s)",
|
|
@@ -318,18 +460,10 @@
|
|
goto out;
|
|
}
|
|
|
|
- if (sys->opaque) {
|
|
- sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
|
|
- pool_size = NUM_DECODER_BUFFER_HEADERS;
|
|
- } else {
|
|
- sys->output->buffer_num = __MAX(sys->output->buffer_num_recommended,
|
|
- MIN_NUM_BUFFERS_IN_TRANSIT);
|
|
- pool_size = sys->output->buffer_num;
|
|
- }
|
|
-
|
|
+ sys->output->buffer_num = NUM_DECODER_BUFFER_HEADERS;
|
|
sys->output->buffer_size = sys->output->buffer_size_recommended;
|
|
|
|
- status = mmal_port_enable(sys->output, output_port_cb);
|
|
+ status = mmal_port_enable(sys->output, decoder_output_cb);
|
|
if (status != MMAL_SUCCESS) {
|
|
msg_Err(dec, "Failed to enable output port (status=%"PRIx32" %s)",
|
|
status, mmal_status_to_string(status));
|
|
@@ -338,25 +472,14 @@
|
|
}
|
|
|
|
if (!atomic_load(&sys->started)) {
|
|
- if (!sys->opaque) {
|
|
- sys->output_pool = mmal_port_pool_create(sys->output, pool_size, 0);
|
|
- msg_Dbg(dec, "Created output pool with %d pictures", sys->output_pool->headers_num);
|
|
- }
|
|
-
|
|
atomic_store(&sys->started, true);
|
|
|
|
/* we need one picture from vout for each buffer header on the output
|
|
* port */
|
|
- dec->i_extra_picture_buffers = pool_size;
|
|
-
|
|
- /* remove what VLC core reserves as it is part of the pool_size
|
|
- * already */
|
|
- if (dec->fmt_in.i_codec == VLC_CODEC_H264)
|
|
- dec->i_extra_picture_buffers -= 19;
|
|
- else
|
|
- dec->i_extra_picture_buffers -= 3;
|
|
-
|
|
+ dec->i_extra_picture_buffers = 10;
|
|
+#if TRACE_ALL
|
|
msg_Dbg(dec, "Request %d extra pictures", dec->i_extra_picture_buffers);
|
|
+#endif
|
|
}
|
|
|
|
apply_fmt:
|
|
@@ -382,12 +505,19 @@
|
|
sys->b_progressive = (interlace_type.eMode == MMAL_InterlaceProgressive);
|
|
sys->b_top_field_first = sys->b_progressive ? true :
|
|
(interlace_type.eMode == MMAL_InterlaceFieldsInterleavedUpperFirst);
|
|
+#if TRACE_ALL
|
|
msg_Dbg(dec, "Detected %s%s video (%d)",
|
|
sys->b_progressive ? "progressive" : "interlaced",
|
|
sys->b_progressive ? "" : (sys->b_top_field_first ? " tff" : " bff"),
|
|
interlace_type.eMode);
|
|
+#endif
|
|
}
|
|
|
|
+ // Tell the rest of the world we have changed format
|
|
+ vlc_mutex_lock(&sys->pic_lock);
|
|
+ ret = decoder_UpdateVideoFormat(dec);
|
|
+ vlc_mutex_unlock(&sys->pic_lock);
|
|
+
|
|
out:
|
|
mmal_format_free(sys->output_format);
|
|
sys->output_format = NULL;
|
|
@@ -395,144 +525,85 @@
|
|
return ret;
|
|
}
|
|
|
|
-static int send_output_buffer(decoder_t *dec)
|
|
+static MMAL_STATUS_T
|
|
+set_extradata_and_commit(decoder_t * const dec, decoder_sys_t * const sys)
|
|
{
|
|
- decoder_sys_t *sys = dec->p_sys;
|
|
- MMAL_BUFFER_HEADER_T *buffer;
|
|
- picture_sys_t *p_sys;
|
|
- picture_t *picture = NULL;
|
|
MMAL_STATUS_T status;
|
|
- unsigned buffer_size = 0;
|
|
- int ret = 0;
|
|
-
|
|
- if (!sys->output->is_enabled)
|
|
- return VLC_EGENERIC;
|
|
-
|
|
- /* If local output pool is allocated, use it - this is only the case for
|
|
- * non-opaque modes */
|
|
- if (sys->output_pool) {
|
|
- buffer = mmal_queue_get(sys->output_pool->queue);
|
|
- if (!buffer) {
|
|
- msg_Warn(dec, "Failed to get new buffer");
|
|
- return VLC_EGENERIC;
|
|
- }
|
|
- }
|
|
-
|
|
- if (!decoder_UpdateVideoFormat(dec))
|
|
- picture = decoder_NewPicture(dec);
|
|
- if (!picture) {
|
|
- msg_Warn(dec, "Failed to get new picture");
|
|
- ret = -1;
|
|
- goto err;
|
|
- }
|
|
-
|
|
- p_sys = picture->p_sys;
|
|
- for (int i = 0; i < picture->i_planes; i++)
|
|
- buffer_size += picture->p[i].i_lines * picture->p[i].i_pitch;
|
|
-
|
|
- if (sys->output_pool) {
|
|
- mmal_buffer_header_reset(buffer);
|
|
- buffer->alloc_size = sys->output->buffer_size;
|
|
- if (buffer_size < sys->output->buffer_size) {
|
|
- msg_Err(dec, "Retrieved picture with too small data block (%d < %d)",
|
|
- buffer_size, sys->output->buffer_size);
|
|
- ret = VLC_EGENERIC;
|
|
- goto err;
|
|
- }
|
|
-
|
|
- if (!sys->opaque)
|
|
- buffer->data = picture->p[0].p_pixels;
|
|
- } else {
|
|
- buffer = p_sys->buffer;
|
|
- if (!buffer) {
|
|
- msg_Warn(dec, "Picture has no buffer attached");
|
|
- picture_Release(picture);
|
|
- return VLC_EGENERIC;
|
|
- }
|
|
- buffer->data = p_sys->buffer->data;
|
|
- }
|
|
- buffer->user_data = picture;
|
|
- buffer->cmd = 0;
|
|
|
|
- status = mmal_port_send_buffer(sys->output, buffer);
|
|
+ status = mmal_port_format_commit(sys->input);
|
|
if (status != MMAL_SUCCESS) {
|
|
- msg_Err(dec, "Failed to send buffer to output port (status=%"PRIx32" %s)",
|
|
- status, mmal_status_to_string(status));
|
|
- ret = -1;
|
|
- goto err;
|
|
- }
|
|
- atomic_fetch_add(&sys->output_in_transit, 1);
|
|
-
|
|
- return ret;
|
|
-
|
|
-err:
|
|
- if (picture)
|
|
- picture_Release(picture);
|
|
- if (sys->output_pool && buffer) {
|
|
- buffer->data = NULL;
|
|
- mmal_buffer_header_release(buffer);
|
|
+ msg_Err(dec, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, status, mmal_status_to_string(status));
|
|
}
|
|
- return ret;
|
|
+ return status;
|
|
}
|
|
|
|
-static void fill_output_port(decoder_t *dec)
|
|
+static MMAL_STATUS_T decoder_send_extradata(decoder_t * const dec, decoder_sys_t *const sys)
|
|
{
|
|
- decoder_sys_t *sys = dec->p_sys;
|
|
-
|
|
- unsigned max_buffers_in_transit = 0;
|
|
- int buffers_available = 0;
|
|
- int buffers_to_send = 0;
|
|
- int i;
|
|
+ if (dec->fmt_in.i_codec == VLC_CODEC_H264 &&
|
|
+ dec->fmt_in.i_extra > 0)
|
|
+ {
|
|
+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(sys->input_pool->queue);
|
|
+ MMAL_STATUS_T status;
|
|
+
|
|
+ mmal_buffer_header_reset(buf);
|
|
+ buf->cmd = 0;
|
|
+ buf->user_data = NULL;
|
|
+ buf->alloc_size = sys->input->buffer_size;
|
|
+ buf->length = dec->fmt_in.i_extra;
|
|
+ buf->data = dec->fmt_in.p_extra;
|
|
+ buf->flags = MMAL_BUFFER_HEADER_FLAG_CONFIG;
|
|
|
|
- if (sys->output_pool) {
|
|
- max_buffers_in_transit = __MAX(sys->output_pool->headers_num,
|
|
- MIN_NUM_BUFFERS_IN_TRANSIT);
|
|
- buffers_available = mmal_queue_length(sys->output_pool->queue);
|
|
- } else {
|
|
- max_buffers_in_transit = NUM_DECODER_BUFFER_HEADERS;
|
|
- buffers_available = NUM_DECODER_BUFFER_HEADERS - atomic_load(&sys->output_in_transit);
|
|
+ status = mmal_port_send_buffer(sys->input, buf);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(dec, "Failed to send extradata buffer to input port (status=%"PRIx32" %s)",
|
|
+ status, mmal_status_to_string(status));
|
|
+ return status;
|
|
+ }
|
|
}
|
|
- buffers_to_send = max_buffers_in_transit - atomic_load(&sys->output_in_transit);
|
|
-
|
|
- if (buffers_to_send > buffers_available)
|
|
- buffers_to_send = buffers_available;
|
|
|
|
-#ifndef NDEBUG
|
|
- msg_Dbg(dec, "Send %d buffers to output port (available: %d, "
|
|
- "in_transit: %d, buffer_num: %d)",
|
|
- buffers_to_send, buffers_available,
|
|
- atomic_load(&sys->output_in_transit),
|
|
- sys->output->buffer_num);
|
|
-#endif
|
|
- for (i = 0; i < buffers_to_send; ++i)
|
|
- if (send_output_buffer(dec) < 0)
|
|
- break;
|
|
+ return MMAL_SUCCESS;
|
|
}
|
|
|
|
static void flush_decoder(decoder_t *dec)
|
|
{
|
|
- decoder_sys_t *sys = dec->p_sys;
|
|
- MMAL_BUFFER_HEADER_T *buffer;
|
|
- MMAL_STATUS_T status;
|
|
+ decoder_sys_t *const sys = dec->p_sys;
|
|
|
|
- msg_Dbg(dec, "Flushing decoder ports...");
|
|
- mmal_port_flush(sys->output);
|
|
- mmal_port_flush(sys->input);
|
|
-
|
|
- while (atomic_load(&sys->output_in_transit) ||
|
|
- atomic_load(&sys->input_in_transit))
|
|
- vlc_sem_wait(&sys->sem);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "%s: <<<", __func__);
|
|
+#endif
|
|
+
|
|
+ if (!sys->b_flushed) {
|
|
+ mmal_port_disable(sys->input);
|
|
+ mmal_port_disable(sys->output);
|
|
+ // We can leave the input disabled, but we want the output enabled
|
|
+ // in order to sink any buffers returning from other modules
|
|
+ mmal_port_enable(sys->output, decoder_output_cb);
|
|
+ sys->b_flushed = true;
|
|
+ }
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "%s: >>>", __func__);
|
|
+#endif
|
|
}
|
|
|
|
static int decode(decoder_t *dec, block_t *block)
|
|
{
|
|
decoder_sys_t *sys = dec->p_sys;
|
|
MMAL_BUFFER_HEADER_T *buffer;
|
|
- bool need_flush = false;
|
|
uint32_t len;
|
|
uint32_t flags = 0;
|
|
MMAL_STATUS_T status;
|
|
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "<<< %s: %lld/%lld", __func__, block == NULL ? -1LL : block->i_dts, block == NULL ? -1LL : block->i_pts);
|
|
+#endif
|
|
+
|
|
+ if (sys->err_stream != MMAL_SUCCESS) {
|
|
+ msg_Err(dec, "MMAL error reported by ctrl");
|
|
+ flush_decoder(dec);
|
|
+ return VLCDEC_ECRITICAL; /// I think they are all fatal
|
|
+ }
|
|
+
|
|
/*
|
|
* Configure output port if necessary
|
|
*/
|
|
@@ -541,18 +612,50 @@
|
|
msg_Err(dec, "Failed to change output port format");
|
|
}
|
|
|
|
- if (!block)
|
|
- goto out;
|
|
+ if (block == NULL)
|
|
+ return VLCDEC_SUCCESS;
|
|
|
|
/*
|
|
* Check whether full flush is required
|
|
*/
|
|
- if (block && block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
|
|
+ if (block->i_flags & BLOCK_FLAG_DISCONTINUITY) {
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "%s: >>> Discontinuity", __func__);
|
|
+#endif
|
|
flush_decoder(dec);
|
|
+ }
|
|
+
|
|
+ if (block->i_buffer == 0)
|
|
+ {
|
|
block_Release(block);
|
|
return VLCDEC_SUCCESS;
|
|
}
|
|
|
|
+ // Reenable stuff if the last thing we did was flush
|
|
+ if (!sys->output->is_enabled &&
|
|
+ (status = mmal_port_enable(sys->output, decoder_output_cb)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(dec, "Output port enable failed");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if (!sys->input->is_enabled)
|
|
+ {
|
|
+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+
|
|
+ if ((status = mmal_port_enable(sys->input, input_port_cb)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(dec, "Input port enable failed");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ // *** We cannot get a picture to put the result in 'till we have
|
|
+ // reported the size & the output stages have been set up
|
|
if (atomic_load(&sys->started))
|
|
fill_output_port(dec);
|
|
|
|
@@ -563,18 +666,21 @@
|
|
if (block->i_flags & BLOCK_FLAG_CORRUPTED)
|
|
flags |= MMAL_BUFFER_HEADER_FLAG_CORRUPTED;
|
|
|
|
- while (block && block->i_buffer > 0) {
|
|
- buffer = mmal_queue_timedwait(sys->input_pool->queue, 100);
|
|
+ while (block != NULL)
|
|
+ {
|
|
+ buffer = mmal_queue_wait(sys->input_pool->queue);
|
|
if (!buffer) {
|
|
msg_Err(dec, "Failed to retrieve buffer header for input data");
|
|
- need_flush = true;
|
|
- break;
|
|
+ goto fail;
|
|
}
|
|
+
|
|
mmal_buffer_header_reset(buffer);
|
|
buffer->cmd = 0;
|
|
- buffer->pts = block->i_pts != 0 ? block->i_pts : block->i_dts;
|
|
+ buffer->pts = block->i_pts != VLC_TICK_INVALID ? block->i_pts :
|
|
+ block->i_dts != VLC_TICK_INVALID ? block->i_dts : MMAL_TIME_UNKNOWN;
|
|
buffer->dts = block->i_dts;
|
|
buffer->alloc_size = sys->input->buffer_size;
|
|
+ buffer->user_data = NULL;
|
|
|
|
len = block->i_buffer;
|
|
if (len > buffer->alloc_size)
|
|
@@ -590,89 +696,1443 @@
|
|
}
|
|
buffer->flags = flags;
|
|
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "%s: -- Send buffer: len=%d", __func__, len);
|
|
+#endif
|
|
status = mmal_port_send_buffer(sys->input, buffer);
|
|
if (status != MMAL_SUCCESS) {
|
|
msg_Err(dec, "Failed to send buffer to input port (status=%"PRIx32" %s)",
|
|
status, mmal_status_to_string(status));
|
|
- break;
|
|
+ goto fail;
|
|
}
|
|
- atomic_fetch_add(&sys->input_in_transit, 1);
|
|
+
|
|
+ // Reset flushed flag once we have sent a buf
|
|
+ sys->b_flushed = false;
|
|
}
|
|
+ return VLCDEC_SUCCESS;
|
|
|
|
-out:
|
|
- if (need_flush)
|
|
- flush_decoder(dec);
|
|
+fail:
|
|
+ flush_decoder(dec);
|
|
+ return VLCDEC_ECRITICAL;
|
|
|
|
- return VLCDEC_SUCCESS;
|
|
}
|
|
|
|
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
+
|
|
+static void CloseDecoder(decoder_t *dec)
|
|
{
|
|
- decoder_t *dec = (decoder_t *)port->userdata;
|
|
+ decoder_sys_t *sys = dec->p_sys;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, "%s: <<<", __func__);
|
|
+#endif
|
|
+
|
|
+ if (!sys)
|
|
+ return;
|
|
+
|
|
+ if (sys->component != NULL) {
|
|
+ if (sys->input->is_enabled)
|
|
+ mmal_port_disable(sys->input);
|
|
+
|
|
+ if (sys->output->is_enabled)
|
|
+ mmal_port_disable(sys->output);
|
|
+
|
|
+ if (sys->component->control->is_enabled)
|
|
+ mmal_port_disable(sys->component->control);
|
|
+
|
|
+ if (sys->component->is_enabled)
|
|
+ mmal_component_disable(sys->component);
|
|
+
|
|
+ mmal_component_release(sys->component);
|
|
+ }
|
|
+
|
|
+ if (sys->input_pool != NULL)
|
|
+ mmal_pool_destroy(sys->input_pool);
|
|
+
|
|
+ if (sys->output_format != NULL)
|
|
+ mmal_format_free(sys->output_format);
|
|
+
|
|
+ hw_mmal_port_pool_ref_release(sys->ppr, false);
|
|
+
|
|
+ vlc_mutex_destroy(&sys->pic_lock);
|
|
+ free(sys);
|
|
+
|
|
+ bcm_host_deinit();
|
|
+}
|
|
+
|
|
+static int OpenDecoder(decoder_t *dec)
|
|
+{
|
|
+ int ret = VLC_EGENERIC;
|
|
+ decoder_sys_t *sys;
|
|
MMAL_STATUS_T status;
|
|
+ const MMAL_FOURCC_T in_fcc = vlc_to_mmal_es_fourcc(dec->fmt_in.i_codec);
|
|
+
|
|
+#if TRACE_ALL
|
|
+ {
|
|
+ char buf1[5], buf2[5], buf2a[5];
|
|
+ char buf3[5], buf4[5];
|
|
+ msg_Dbg(dec, "%s: <<< (%s/%s)[%s] %dx%d -> (%s/%s) %dx%d", __func__,
|
|
+ str_fourcc(buf1, dec->fmt_in.i_codec),
|
|
+ str_fourcc(buf2, dec->fmt_in.video.i_chroma),
|
|
+ str_fourcc(buf2a, in_fcc),
|
|
+ dec->fmt_in.video.i_width, dec->fmt_in.video.i_height,
|
|
+ str_fourcc(buf3, dec->fmt_out.i_codec),
|
|
+ str_fourcc(buf4, dec->fmt_out.video.i_chroma),
|
|
+ dec->fmt_out.video.i_width, dec->fmt_out.video.i_height);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (!is_enc_supported(in_fcc))
|
|
+ return VLC_EGENERIC;
|
|
+
|
|
+ sys = calloc(1, sizeof(decoder_sys_t));
|
|
+ if (!sys) {
|
|
+ ret = VLC_ENOMEM;
|
|
+ goto fail;
|
|
+ }
|
|
+ dec->p_sys = sys;
|
|
+ vlc_mutex_init(&sys->pic_lock);
|
|
+
|
|
+ bcm_host_init();
|
|
+
|
|
+ sys->err_stream = MMAL_SUCCESS;
|
|
+
|
|
+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &sys->component);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(dec, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
|
|
+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ sys->input = sys->component->input[0];
|
|
+ sys->output = sys->component->output[0];
|
|
+
|
|
+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
|
|
+ sys->input->format->encoding = in_fcc;
|
|
+
|
|
+ if (!set_and_test_enc_supported(sys->input, in_fcc)) {
|
|
+#if TRACE_ALL
|
|
+ char cbuf[5];
|
|
+ msg_Dbg(dec, "Format not supported: %s", str_fourcc(cbuf, in_fcc));
|
|
+#endif
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)dec;
|
|
+ status = mmal_port_enable(sys->component->control, control_port_cb);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(dec, "Failed to enable control port %s (status=%"PRIx32" %s)",
|
|
+ sys->component->control->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if ((status = set_extradata_and_commit(dec, sys)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+
|
|
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
|
|
+ sys->input->buffer_num = sys->input->buffer_num_recommended;
|
|
+
|
|
+ status = mmal_port_enable(sys->input, input_port_cb);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(dec, "Failed to enable input port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(dec), &sys->ppr,
|
|
+ sys->output, NUM_EXTRA_BUFFERS, decoder_output_cb)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+
|
|
+ status = mmal_component_enable(sys->component);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(dec, "Failed to enable component %s (status=%"PRIx32" %s)",
|
|
+ sys->component->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if ((sys->input_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
|
|
+ {
|
|
+ msg_Err(dec, "Failed to create input pool");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ sys->b_flushed = true;
|
|
+ dec->fmt_out.i_codec = VLC_CODEC_MMAL_OPAQUE;
|
|
+ dec->fmt_out.video.i_chroma = VLC_CODEC_MMAL_OPAQUE;
|
|
+
|
|
+ if ((status = decoder_send_extradata(dec, sys)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+
|
|
+ dec->pf_decode = decode;
|
|
+ dec->pf_flush = flush_decoder;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(dec, ">>> %s: ok", __func__);
|
|
+#endif
|
|
+ return 0;
|
|
+
|
|
+fail:
|
|
+ CloseDecoder(dec);
|
|
+#if TRACE_ALL
|
|
+msg_Dbg(dec, ">>> %s: FAIL: ret=%d", __func__, ret);
|
|
+#endif
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+// ----------------------------
|
|
+
|
|
+#define CONV_MAX_LATENCY 1 // In frames
|
|
+
|
|
+typedef struct pic_fifo_s {
|
|
+ picture_t * head;
|
|
+ picture_t * tail;
|
|
+} pic_fifo_t;
|
|
+
|
|
+static inline picture_t * pic_fifo_get(pic_fifo_t * const pf)
|
|
+{
|
|
+ picture_t * const pic = pf->head;;
|
|
+ if (pic != NULL) {
|
|
+ pf->head = pic->p_next;
|
|
+ pic->p_next = NULL;
|
|
+ }
|
|
+ return pic;
|
|
+}
|
|
+
|
|
+static inline picture_t * pic_fifo_get_all(pic_fifo_t * const pf)
|
|
+{
|
|
+ picture_t * const pic = pf->head;;
|
|
+ pf->head = NULL;
|
|
+ return pic;
|
|
+}
|
|
+
|
|
+static inline void pic_fifo_release_all(pic_fifo_t * const pf)
|
|
+{
|
|
+ picture_t * pic;
|
|
+ while ((pic = pic_fifo_get(pf)) != NULL) {
|
|
+ picture_Release(pic);
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline void pic_fifo_init(pic_fifo_t * const pf)
|
|
+{
|
|
+ pf->head = NULL;
|
|
+ pf->tail = NULL; // Not strictly needed
|
|
+}
|
|
+
|
|
+static inline void pic_fifo_put(pic_fifo_t * const pf, picture_t * pic)
|
|
+{
|
|
+ pic->p_next = NULL;
|
|
+ if (pf->head == NULL)
|
|
+ pf->head = pic;
|
|
+ else
|
|
+ pf->tail->p_next = pic;
|
|
+ pf->tail = pic;
|
|
+}
|
|
+
|
|
+#define SUBS_MAX 3
|
|
+
|
|
+typedef enum filter_resizer_e {
|
|
+ FILTER_RESIZER_RESIZER,
|
|
+ FILTER_RESIZER_ISP,
|
|
+ FILTER_RESIZER_HVS
|
|
+} filter_resizer_t;
|
|
+
|
|
+typedef struct conv_frame_stash_s
|
|
+{
|
|
+ mtime_t pts;
|
|
+ MMAL_BUFFER_HEADER_T * sub_bufs[SUBS_MAX];
|
|
+} conv_frame_stash_t;
|
|
+
|
|
+typedef struct filter_sys_t {
|
|
+ filter_resizer_t resizer_type;
|
|
+ MMAL_COMPONENT_T *component;
|
|
+ MMAL_PORT_T *input;
|
|
+ MMAL_PORT_T *output;
|
|
+ MMAL_POOL_T *out_pool; // Free output buffers
|
|
+ MMAL_POOL_T *in_pool; // Input pool to get BH for replication
|
|
+
|
|
+ subpic_reg_stash_t subs[SUBS_MAX];
|
|
+
|
|
+ pic_fifo_t ret_pics;
|
|
+
|
|
+ vlc_sem_t sem;
|
|
+ vlc_mutex_t lock;
|
|
+
|
|
+ bool b_top_field_first;
|
|
+ bool b_progressive;
|
|
+
|
|
+ MMAL_STATUS_T err_stream;
|
|
+ int in_count;
|
|
+
|
|
+ bool is_sliced;
|
|
+ bool out_fmt_set;
|
|
+ bool latency_set;
|
|
+ const char * component_name;
|
|
+ MMAL_PORT_BH_CB_T in_port_cb_fn;
|
|
+ MMAL_PORT_BH_CB_T out_port_cb_fn;
|
|
+
|
|
+ uint64_t frame_seq;
|
|
+ conv_frame_stash_t stash[16];
|
|
+
|
|
+ // Slice specific tracking stuff
|
|
+ struct {
|
|
+ pic_fifo_t pics;
|
|
+ unsigned int line; // Lines filled
|
|
+ } slice;
|
|
+
|
|
+} filter_sys_t;
|
|
+
|
|
+
|
|
+static MMAL_STATUS_T pic_to_format(MMAL_ES_FORMAT_T * const es_fmt, const picture_t * const pic)
|
|
+{
|
|
+ unsigned int bpp = (pic->format.i_bits_per_pixel + 7) >> 3;
|
|
+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
|
|
+
|
|
+ if (bpp < 1 || bpp > 4)
|
|
+ return MMAL_EINVAL;
|
|
+
|
|
+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
|
|
+ es_fmt->encoding = vlc_to_mmal_video_fourcc(&pic->format);
|
|
+ es_fmt->encoding_variant = 0;
|
|
+
|
|
+ // Fill in crop etc.
|
|
+ vlc_to_mmal_video_fmt(es_fmt, &pic->format);
|
|
+ // Override width / height with strides
|
|
+ v_fmt->width = pic->p[0].i_pitch / bpp;
|
|
+ v_fmt->height = pic->p[0].i_lines;
|
|
+ return MMAL_SUCCESS;
|
|
+}
|
|
+
|
|
+
|
|
+static MMAL_STATUS_T conv_enable_in(filter_t * const p_filter, filter_sys_t * const sys)
|
|
+{
|
|
+ MMAL_STATUS_T err = MMAL_SUCCESS;
|
|
+
|
|
+ if (!sys->input->is_enabled &&
|
|
+ (err = mmal_port_enable(sys->input, sys->in_port_cb_fn)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, err, mmal_status_to_string(err));
|
|
+ }
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static MMAL_STATUS_T conv_enable_out(filter_t * const p_filter, filter_sys_t * const sys)
|
|
+{
|
|
+ MMAL_STATUS_T err = MMAL_SUCCESS;
|
|
+
|
|
+ if (!sys->output->is_enabled &&
|
|
+ (err = mmal_port_enable(sys->output, sys->out_port_cb_fn)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
|
|
+ sys->output->name, err, mmal_status_to_string(err));
|
|
+ }
|
|
+ return err;
|
|
+}
|
|
+
|
|
+static void conv_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
+{
|
|
+ filter_t * const p_filter = (filter_t *)port->userdata;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "%s: <<< cmd=%d, data=%p, pic=%p", __func__, buffer->cmd, buffer->data, buffer->user_data);
|
|
+#endif
|
|
|
|
if (buffer->cmd == MMAL_EVENT_ERROR) {
|
|
- status = *(uint32_t *)buffer->data;
|
|
- msg_Err(dec, "MMAL error %"PRIx32" \"%s\"", status,
|
|
+ MMAL_STATUS_T status = *(uint32_t *)buffer->data;
|
|
+
|
|
+ p_filter->p_sys->err_stream = status;
|
|
+
|
|
+ msg_Err(p_filter, "MMAL error %"PRIx32" \"%s\"", status,
|
|
mmal_status_to_string(status));
|
|
}
|
|
|
|
mmal_buffer_header_release(buffer);
|
|
}
|
|
|
|
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
+static void conv_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
|
|
{
|
|
- block_t *block = (block_t *)buffer->user_data;
|
|
- decoder_t *dec = (decoder_t *)port->userdata;
|
|
- decoder_sys_t *sys = dec->p_sys;
|
|
- buffer->user_data = NULL;
|
|
+#if TRACE_ALL
|
|
+ picture_context_t * ctx = buf->user_data;
|
|
+// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
|
|
+
|
|
+ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, ctx=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
|
|
+ __func__, buf->cmd, ctx, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
|
|
+#else
|
|
+ VLC_UNUSED(port);
|
|
+#endif
|
|
+
|
|
+ mmal_buffer_header_release(buf);
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void conv_out_q_pic(filter_sys_t * const sys, picture_t * const pic)
|
|
+{
|
|
+ pic->p_next = NULL;
|
|
+
|
|
+ vlc_mutex_lock(&sys->lock);
|
|
+ pic_fifo_put(&sys->ret_pics, pic);
|
|
+ vlc_mutex_unlock(&sys->lock);
|
|
|
|
- mmal_buffer_header_release(buffer);
|
|
- if (block)
|
|
- block_Release(block);
|
|
- atomic_fetch_sub(&sys->input_in_transit, 1);
|
|
vlc_sem_post(&sys->sem);
|
|
}
|
|
|
|
-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
+static void conv_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
|
|
{
|
|
- decoder_t *dec = (decoder_t *)port->userdata;
|
|
- decoder_sys_t *sys = dec->p_sys;
|
|
- picture_t *picture;
|
|
- MMAL_EVENT_FORMAT_CHANGED_T *fmt;
|
|
- MMAL_ES_FORMAT_T *format;
|
|
-
|
|
- if (buffer->cmd == 0) {
|
|
- picture = (picture_t *)buffer->user_data;
|
|
- if (buffer->length > 0) {
|
|
- picture->date = buffer->pts;
|
|
- picture->b_progressive = sys->b_progressive;
|
|
- picture->b_top_field_first = sys->b_top_field_first;
|
|
- decoder_QueueVideo(dec, picture);
|
|
- } else {
|
|
- picture_Release(picture);
|
|
- if (sys->output_pool) {
|
|
- buffer->user_data = NULL;
|
|
- buffer->alloc_size = 0;
|
|
- buffer->data = NULL;
|
|
- mmal_buffer_header_release(buffer);
|
|
+ filter_t * const p_filter = (filter_t *)port->userdata;
|
|
+ filter_sys_t * const sys = p_filter->p_sys;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld/%lld", __func__,
|
|
+ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size,
|
|
+ (long long)buf->pts, (long long)sys->stash[(unsigned int)(buf->pts & 0xf)].pts);
|
|
+#endif
|
|
+ if (buf->cmd == 0) {
|
|
+ picture_t * const pic = (picture_t *)buf->user_data;
|
|
+
|
|
+ if (pic == NULL) {
|
|
+ msg_Err(p_filter, "%s: Buffer has no attached picture", __func__);
|
|
+ }
|
|
+ else if (buf->data == NULL || buf->length == 0)
|
|
+ {
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
|
|
+#endif
|
|
+ picture_Release(pic);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ buf_to_pic_copy_props(pic, buf);
|
|
+
|
|
+// draw_corners(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, pic->p[0].i_visible_pitch / 4, pic->p[0].i_visible_lines);
|
|
+#if DEBUG_SQUARES
|
|
+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 0, 0, 32, 32, 0xffff0000);
|
|
+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 32, 0, 32, 32, 0xff00ff00);
|
|
+ draw_square(pic->p[0].p_pixels, pic->p[0].i_pitch / 4, 64, 0, 32, 32, 0xff0000ff);
|
|
+#endif
|
|
+ conv_out_q_pic(sys, pic);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ buf->user_data = NULL; // Zap here to make sure we can't reuse later
|
|
+ mmal_buffer_header_release(buf);
|
|
+}
|
|
+
|
|
+
|
|
+static void slice_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
|
|
+{
|
|
+ filter_t * const p_filter = (filter_t *)port->userdata;
|
|
+ filter_sys_t * const sys = p_filter->p_sys;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "<<< %s: cmd=%d, flags=%#x, pic=%p, data=%p, len=%d/%d, pts=%lld", __func__,
|
|
+ buf->cmd, buf->flags, buf->user_data, buf->data, buf->length, buf->alloc_size, (long long)buf->pts);
|
|
+#endif
|
|
+
|
|
+ if (buf->cmd != 0)
|
|
+ {
|
|
+ mmal_buffer_header_release(buf);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (buf->data == NULL || buf->length == 0)
|
|
+ {
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "%s: Buffer has no data", __func__);
|
|
+#endif
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ // Got slice
|
|
+ picture_t *pic = sys->slice.pics.head;
|
|
+ const unsigned int scale_lines = sys->output->format->es->video.height; // Expected lines of callback
|
|
+
|
|
+ if (pic == NULL) {
|
|
+ msg_Err(p_filter, "No output picture");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ // Copy lines
|
|
+ // * single plane only - fix for I420
|
|
+ {
|
|
+ const unsigned int scale_n = __MIN(scale_lines - sys->slice.line, MMAL_SLICE_HEIGHT);
|
|
+ const unsigned int pic_lines = pic->p[0].i_lines;
|
|
+ const unsigned int copy_n = sys->slice.line + scale_n <= pic_lines ? scale_n :
|
|
+ sys->slice.line >= pic_lines ? 0 :
|
|
+ pic_lines - sys->slice.line;
|
|
+
|
|
+ const unsigned int src_stride = buf->type->video.pitch[0];
|
|
+ const unsigned int dst_stride = pic->p[0].i_pitch;
|
|
+ uint8_t *dst = pic->p[0].p_pixels + sys->slice.line * dst_stride;
|
|
+ const uint8_t *src = buf->data + buf->type->video.offset[0];
|
|
+
|
|
+ if (src_stride == dst_stride) {
|
|
+ if (copy_n != 0)
|
|
+ memcpy(dst, src, src_stride * copy_n);
|
|
}
|
|
+ else {
|
|
+ unsigned int i;
|
|
+ for (i = 0; i != copy_n; ++i) {
|
|
+ memcpy(dst, src, __MIN(dst_stride, src_stride));
|
|
+ dst += dst_stride;
|
|
+ src += src_stride;
|
|
+ }
|
|
+ }
|
|
+ sys->slice.line += scale_n;
|
|
}
|
|
- atomic_fetch_sub(&sys->output_in_transit, 1);
|
|
- vlc_sem_post(&sys->sem);
|
|
- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
|
|
- fmt = mmal_event_format_changed_get(buffer);
|
|
|
|
- format = mmal_format_alloc();
|
|
- mmal_format_full_copy(format, fmt->format);
|
|
+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) != 0 || sys->slice.line >= scale_lines) {
|
|
|
|
- if (sys->opaque)
|
|
- format->encoding = MMAL_ENCODING_OPAQUE;
|
|
+ if ((buf->flags & MMAL_BUFFER_HEADER_FLAG_FRAME_END) == 0 || sys->slice.line != scale_lines) {
|
|
+ // Stuff doesn't add up...
|
|
+ msg_Err(p_filter, "Line count (%d/%d) & EOF disagree (flags=%#x)", sys->slice.line, scale_lines, buf->flags);
|
|
+ goto fail;
|
|
+ }
|
|
+ else {
|
|
+ sys->slice.line = 0;
|
|
+
|
|
+ vlc_mutex_lock(&sys->lock);
|
|
+ pic_fifo_get(&sys->slice.pics); // Remove head from Q
|
|
+ vlc_mutex_unlock(&sys->lock);
|
|
+
|
|
+ buf_to_pic_copy_props(pic, buf);
|
|
+ conv_out_q_pic(sys, pic);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Put back
|
|
+ buf->user_data = NULL; // Zap here to make sure we can't reuse later
|
|
+ mmal_buffer_header_reset(buf);
|
|
+
|
|
+ if (mmal_port_send_buffer(sys->output, buf) != MMAL_SUCCESS) {
|
|
+ mmal_buffer_header_release(buf);
|
|
+ }
|
|
+ return;
|
|
+
|
|
+fail:
|
|
+ sys->err_stream = MMAL_EIO;
|
|
+ vlc_sem_post(&sys->sem); // If we were waiting then break us out - the flush should fix sem values
|
|
+}
|
|
+
|
|
+
|
|
+static void conv_flush(filter_t * p_filter)
|
|
+{
|
|
+ filter_sys_t * const sys = p_filter->p_sys;
|
|
+ unsigned int i;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "<<< %s", __func__);
|
|
+#endif
|
|
+
|
|
+ if (sys->resizer_type == FILTER_RESIZER_HVS)
|
|
+ {
|
|
+ for (i = 0; i != SUBS_MAX; ++i) {
|
|
+ hw_mmal_subpic_flush(VLC_OBJECT(p_filter), sys->subs + i);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (sys->input != NULL && sys->input->is_enabled)
|
|
+ mmal_port_disable(sys->input);
|
|
+
|
|
+ if (sys->output != NULL && sys->output->is_enabled)
|
|
+ mmal_port_disable(sys->output);
|
|
+
|
|
+ // Free up anything we may have already lying around
|
|
+ // Don't need lock as the above disables should have prevented anything
|
|
+ // happening in the background
|
|
+
|
|
+ for (i = 0; i != 16; ++i) {
|
|
+ conv_frame_stash_t *const stash = sys->stash + i;
|
|
+ unsigned int sub_no;
|
|
+
|
|
+ stash->pts = MMAL_TIME_UNKNOWN;
|
|
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
|
|
+ if (stash->sub_bufs[sub_no] != NULL) {
|
|
+ mmal_buffer_header_release(stash->sub_bufs[sub_no]);
|
|
+ stash->sub_bufs[sub_no] = NULL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pic_fifo_release_all(&sys->slice.pics);
|
|
+ pic_fifo_release_all(&sys->ret_pics);
|
|
+
|
|
+ // Reset sem values - easiest & most reliable way is to just kill & re-init
|
|
+ // This will also dig us out of situations where we have got out of sync somehow
|
|
+ vlc_sem_destroy(&sys->sem);
|
|
+ vlc_sem_init(&sys->sem, CONV_MAX_LATENCY);
|
|
+
|
|
+ // No buffers in either port now
|
|
+ sys->in_count = 0;
|
|
+
|
|
+ // Reset error status
|
|
+ sys->err_stream = MMAL_SUCCESS;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, ">>> %s", __func__);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void conv_flush_passthrough(filter_t * p_filter)
|
|
+{
|
|
+ VLC_UNUSED(p_filter);
|
|
+}
|
|
+
|
|
+static void conv_stash_fixup(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const p_pic)
|
|
+{
|
|
+ conv_frame_stash_t * const stash = sys->stash + (p_pic->date & 0xf);
|
|
+ unsigned int sub_no;
|
|
+ VLC_UNUSED(p_filter);
|
|
+
|
|
+ p_pic->date = stash->pts;
|
|
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
|
|
+ if (stash->sub_bufs[sub_no] != NULL) {
|
|
+ // **** Do stashed blend
|
|
+ // **** Aaargh, bother... need to rescale subs too
|
|
+
|
|
+ mmal_buffer_header_release(stash->sub_bufs[sub_no]);
|
|
+ stash->sub_bufs[sub_no] = NULL;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static MMAL_STATUS_T conv_set_output(filter_t * const p_filter, filter_sys_t * const sys, picture_t * const pic)
|
|
+{
|
|
+ MMAL_STATUS_T status;
|
|
+
|
|
+ sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
|
|
+ sys->output->format->type = MMAL_ES_TYPE_VIDEO;
|
|
+ sys->output->format->encoding = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
|
|
+ sys->output->format->encoding_variant = 0;
|
|
+ vlc_to_mmal_video_fmt(sys->output->format, &p_filter->fmt_out.video);
|
|
+
|
|
+ // Override default format width/height if we have a pic we need to match
|
|
+ if (pic != NULL)
|
|
+ {
|
|
+ if ((status = pic_to_format(sys->output->format, pic)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ char cbuf[5];
|
|
+ msg_Err(p_filter, "Bad format desc: %s, bits=%d", str_fourcc(cbuf, pic->format.i_chroma), pic->format.i_bits_per_pixel);
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ MMAL_VIDEO_FORMAT_T *fmt = &sys->output->format->es->video;
|
|
+ msg_Dbg(p_filter, "%s: %dx%d [(0,0) %dx%d]", __func__, fmt->width, fmt->height, fmt->crop.width, fmt->crop.height);
|
|
+ }
|
|
+
|
|
+ mmal_log_dump_format(sys->output->format);
|
|
+
|
|
+ status = mmal_port_format_commit(sys->output);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(p_filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
|
|
+ sys->output->name, status, mmal_status_to_string(status));
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ sys->output->buffer_num = __MAX(sys->is_sliced ? 16 : 2, sys->output->buffer_num_recommended);
|
|
+ sys->output->buffer_size = sys->output->buffer_size_recommended;
|
|
+
|
|
+ if ((status = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS)
|
|
+ return status;
|
|
|
|
- sys->output_format = format;
|
|
+ return MMAL_SUCCESS;
|
|
+}
|
|
+
|
|
+static picture_t *conv_filter(filter_t *p_filter, picture_t *p_pic)
|
|
+{
|
|
+ filter_sys_t * const sys = p_filter->p_sys;
|
|
+ picture_t * ret_pics;
|
|
+ MMAL_STATUS_T err;
|
|
+ const uint64_t frame_seq = ++sys->frame_seq;
|
|
+ conv_frame_stash_t * const stash = sys->stash + (frame_seq & 0xf);
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "<<< %s", __func__);
|
|
+#endif
|
|
+#if 0
|
|
+ {
|
|
+ char dbuf0[5], dbuf1[5];
|
|
+ msg_Dbg(p_filter, "%s: %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] sar:%d/%d", __func__,
|
|
+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
|
|
+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
|
|
+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
|
|
+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
|
|
+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
|
|
+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
|
|
+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
|
|
+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (sys->err_stream != MMAL_SUCCESS) {
|
|
+ goto stream_fail;
|
|
+ }
|
|
+
|
|
+ if (p_pic->context == NULL) {
|
|
+ msg_Dbg(p_filter, "%s: No context", __func__);
|
|
+ }
|
|
+ else if (sys->resizer_type == FILTER_RESIZER_HVS)
|
|
+ {
|
|
+ unsigned int sub_no = 0;
|
|
+
|
|
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
|
|
+ int rv;
|
|
+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(p_filter), p_pic, sub_no, sys->subs + sub_no,
|
|
+ &sys->output->format->es->video.crop, frame_seq)) == 0)
|
|
+ break;
|
|
+ else if (rv < 0)
|
|
+ goto fail;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ unsigned int sub_no = 0;
|
|
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
|
|
+ if ((stash->sub_bufs[sub_no] = hw_mmal_pic_sub_buf_get(p_pic, sub_no)) != NULL) {
|
|
+ mmal_buffer_header_acquire(stash->sub_bufs[sub_no]);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!sys->out_fmt_set) {
|
|
+ sys->out_fmt_set = true;
|
|
+
|
|
+ if (sys->is_sliced) {
|
|
+ // If zc then we will do stride conversion when we copy to arm side
|
|
+ // so no need to worry about actual pic dimensions here
|
|
+ if ((err = conv_set_output(p_filter, sys, NULL)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+ }
|
|
+ else {
|
|
+ picture_t *pic = filter_NewPicture(p_filter);
|
|
+ err = conv_set_output(p_filter, sys, pic);
|
|
+ picture_Release(pic);
|
|
+ if (err != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ msg_Dbg(p_filter, "Outpool: zc=%d, num=%d, size=%d", sys->is_sliced, sys->output->buffer_num, sys->output->buffer_size);
|
|
+ sys->out_pool = sys->is_sliced ?
|
|
+ mmal_port_pool_create(sys->output, sys->output->buffer_num, sys->output->buffer_size) :
|
|
+ mmal_pool_create(sys->output->buffer_num, 0);
|
|
+
|
|
+ if (sys->out_pool == NULL) {
|
|
+ msg_Err(p_filter, "Failed to create output pool");
|
|
+ goto fail;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Reenable stuff if the last thing we did was flush
|
|
+ if ((err = conv_enable_out(p_filter, sys)) != MMAL_SUCCESS ||
|
|
+ (err = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+
|
|
+ // If ZC then we need to allocate the out pic before we stuff the input
|
|
+ if (sys->is_sliced) {
|
|
+ MMAL_BUFFER_HEADER_T * out_buf;
|
|
+ picture_t * const out_pic = filter_NewPicture(p_filter);
|
|
+
|
|
+ if (out_pic == NULL)
|
|
+ {
|
|
+ msg_Err(p_filter, "Failed to alloc required filter output pic");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ vlc_mutex_lock(&sys->lock);
|
|
+ pic_fifo_put(&sys->slice.pics, out_pic);
|
|
+ vlc_mutex_unlock(&sys->lock);
|
|
+
|
|
+ // Poke any returned pic buffers into output
|
|
+ // In general this should only happen immediately after enable
|
|
+ while ((out_buf = mmal_queue_get(sys->out_pool->queue)) != NULL)
|
|
+ mmal_port_send_buffer(sys->output, out_buf);
|
|
+
|
|
+ ++sys->in_count;
|
|
+ }
|
|
+
|
|
+ // Stuff into input
|
|
+ // We assume the BH is already set up with values reflecting pic date etc.
|
|
+ {
|
|
+ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "In buf send: pic=%p, buf=%p, user=%p, pts=%lld/%lld",
|
|
+ p_pic, pic_buf, pic_buf->user_data, (long long)frame_seq, (long long)p_pic->date);
|
|
+#endif
|
|
+ if (pic_buf == NULL) {
|
|
+ msg_Err(p_filter, "Pic has no attached buffer");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ stash->pts = p_pic->date;
|
|
+ if ((err = port_send_replicated(sys->input, sys->in_pool, pic_buf, frame_seq)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Send buffer to input failed");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ picture_Release(p_pic);
|
|
+ p_pic = NULL;
|
|
+ --sys->in_count;
|
|
+ }
|
|
+
|
|
+ if (!sys->is_sliced) {
|
|
+ MMAL_BUFFER_HEADER_T * out_buf;
|
|
+
|
|
+ while ((out_buf = sys->in_count < 0 ?
|
|
+ mmal_queue_wait(sys->out_pool->queue) : mmal_queue_get(sys->out_pool->queue)) != NULL)
|
|
+ {
|
|
+ picture_t * const out_pic = filter_NewPicture(p_filter);
|
|
+
|
|
+ if (out_pic == NULL) {
|
|
+ msg_Warn(p_filter, "Failed to alloc new filter output pic");
|
|
+ mmal_buffer_header_release(out_buf);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ char dbuf0[5];
|
|
+ msg_Dbg(p_filter, "out_pic %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d",
|
|
+ str_fourcc(dbuf0, out_pic->format.i_chroma),
|
|
+ out_pic->format.i_width, out_pic->format.i_height,
|
|
+ out_pic->format.i_x_offset, out_pic->format.i_y_offset,
|
|
+ out_pic->format.i_visible_width, out_pic->format.i_visible_height,
|
|
+ out_pic->format.i_sar_num, out_pic->format.i_sar_den);
|
|
+#endif
|
|
+
|
|
+ mmal_buffer_header_reset(out_buf);
|
|
+ out_buf->user_data = out_pic;
|
|
+ out_buf->data = out_pic->p[0].p_pixels;
|
|
+ out_buf->alloc_size = out_pic->p[0].i_pitch * out_pic->p[0].i_lines;
|
|
+ //**** stride ????
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "Out buf send: pic=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
|
|
+ p_pic, out_buf->user_data, out_buf->flags,
|
|
+ out_buf->length, out_buf->alloc_size, (long long)out_buf->pts);
|
|
+#endif
|
|
+
|
|
+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Send buffer to output failed");
|
|
+ mmal_buffer_header_release(out_buf);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ++sys->in_count;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (sys->in_count < 0)
|
|
+ {
|
|
+ msg_Err(p_filter, "Buffer count somehow negative");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ // Avoid being more than 1 pic behind
|
|
+ vlc_sem_wait(&sys->sem);
|
|
+
|
|
+ // Set sem for delayed scale if not already set
|
|
+ if (!sys->latency_set) {
|
|
+ unsigned int i;
|
|
+ sys->latency_set = true;
|
|
+ for (i = 0; i != CONV_MAX_LATENCY; ++i) {
|
|
+ vlc_sem_post(&sys->sem);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Return all pending buffers
|
|
+ vlc_mutex_lock(&sys->lock);
|
|
+ ret_pics = pic_fifo_get_all(&sys->ret_pics);
|
|
+ vlc_mutex_unlock(&sys->lock);
|
|
+
|
|
+ if (sys->err_stream != MMAL_SUCCESS)
|
|
+ goto stream_fail;
|
|
+
|
|
+ // Sink as many sem posts as we have pics
|
|
+ // (shouldn't normally wait, but there is a small race)
|
|
+ if (ret_pics != NULL)
|
|
+ {
|
|
+ picture_t *next_pic = ret_pics->p_next;
|
|
+
|
|
+ conv_stash_fixup(p_filter, sys, ret_pics);
|
|
+#if 0
|
|
+ char dbuf0[5];
|
|
+
|
|
+ msg_Dbg(p_filter, "pic_out %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d",
|
|
+ str_fourcc(dbuf0, ret_pics->format.i_chroma),
|
|
+ ret_pics->format.i_width, ret_pics->format.i_height,
|
|
+ ret_pics->format.i_x_offset, ret_pics->format.i_y_offset,
|
|
+ ret_pics->format.i_visible_width, ret_pics->format.i_visible_height,
|
|
+ ret_pics->format.i_sar_num, ret_pics->format.i_sar_den);
|
|
+#endif
|
|
+ while (next_pic != NULL) {
|
|
+ vlc_sem_wait(&sys->sem);
|
|
+ conv_stash_fixup(p_filter, sys, next_pic);
|
|
+ next_pic = next_pic->p_next;
|
|
+ }
|
|
+ }
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
|
|
+#endif
|
|
|
|
- mmal_buffer_header_release(buffer);
|
|
+ return ret_pics;
|
|
+
|
|
+stream_fail:
|
|
+ msg_Err(p_filter, "MMAL error reported by callback");
|
|
+fail:
|
|
+ if (p_pic != NULL)
|
|
+ picture_Release(p_pic);
|
|
+ conv_flush(p_filter);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static picture_t *conv_filter_passthrough(filter_t *p_filter, picture_t *p_pic)
|
|
+{
|
|
+ VLC_UNUSED(p_filter);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "<<< %s", __func__);
|
|
+#endif
|
|
+ return p_pic;
|
|
+}
|
|
+
|
|
+static void CloseConverter(vlc_object_t * obj)
|
|
+{
|
|
+ filter_t * const p_filter = (filter_t *)obj;
|
|
+ filter_sys_t * const sys = p_filter->p_sys;
|
|
+ unsigned int i;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(obj, "<<< %s", __func__);
|
|
+#endif
|
|
+
|
|
+ if (sys == NULL)
|
|
+ return;
|
|
+
|
|
+ // Disables input & output ports
|
|
+ conv_flush(p_filter);
|
|
+
|
|
+ if (sys->component && sys->component->control->is_enabled)
|
|
+ mmal_port_disable(sys->component->control);
|
|
+
|
|
+ if (sys->component && sys->component->is_enabled)
|
|
+ mmal_component_disable(sys->component);
|
|
+
|
|
+ if (sys->resizer_type == FILTER_RESIZER_HVS)
|
|
+ {
|
|
+ for (i = 0; i != SUBS_MAX; ++i) {
|
|
+ hw_mmal_subpic_close(VLC_OBJECT(p_filter), sys->subs + i);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (sys->out_pool)
|
|
+ {
|
|
+ if (sys->is_sliced)
|
|
+ mmal_port_pool_destroy(sys->output, sys->out_pool);
|
|
+ else
|
|
+ mmal_pool_destroy(sys->out_pool);
|
|
+ }
|
|
+
|
|
+ if (sys->in_pool != NULL)
|
|
+ mmal_pool_destroy(sys->in_pool);
|
|
+
|
|
+ if (sys->component)
|
|
+ mmal_component_release(sys->component);
|
|
+
|
|
+ vlc_sem_destroy(&sys->sem);
|
|
+ vlc_mutex_destroy(&sys->lock);
|
|
+
|
|
+ free(sys);
|
|
+}
|
|
+
|
|
+
|
|
+static int open_converter_passthrough(filter_t * const p_filter)
|
|
+{
|
|
+ {
|
|
+ char dbuf0[5], dbuf1[5];
|
|
+ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d", __func__,
|
|
+ "passthrough",
|
|
+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma),
|
|
+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
|
|
+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
|
|
+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
|
|
+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
|
|
+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma),
|
|
+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
|
|
+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
|
|
+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
|
|
+ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
|
|
+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den);
|
|
+ }
|
|
+
|
|
+
|
|
+ p_filter->pf_video_filter = conv_filter_passthrough;
|
|
+ p_filter->pf_flush = conv_flush_passthrough;
|
|
+ return VLC_SUCCESS;
|
|
+}
|
|
+
|
|
+static int OpenConverter(vlc_object_t * obj)
|
|
+{
|
|
+ filter_t * const p_filter = (filter_t *)obj;
|
|
+ int ret = VLC_EGENERIC;
|
|
+ filter_sys_t *sys;
|
|
+ MMAL_STATUS_T status;
|
|
+ MMAL_FOURCC_T enc_out;
|
|
+ const MMAL_FOURCC_T enc_in = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video);
|
|
+ bool use_resizer;
|
|
+ bool use_isp;
|
|
+ int gpu_mem;
|
|
+
|
|
+ if ((enc_in != MMAL_ENCODING_OPAQUE &&
|
|
+ enc_in != MMAL_ENCODING_YUVUV128 &&
|
|
+ enc_in != MMAL_ENCODING_YUVUV64_10) ||
|
|
+ (enc_out = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video)) == 0)
|
|
+ return VLC_EGENERIC;
|
|
+
|
|
+ if (enc_in == enc_out) {
|
|
+ return open_converter_passthrough(p_filter);
|
|
+ }
|
|
+
|
|
+ use_resizer = var_InheritBool(p_filter, MMAL_RESIZE_NAME);
|
|
+ use_isp = var_InheritBool(p_filter, MMAL_ISP_NAME);
|
|
+
|
|
+retry:
|
|
+ // Must use ISP - HVS can't do this, nor can resizer
|
|
+ if (enc_in == MMAL_ENCODING_YUVUV64_10) {
|
|
+ // If resizer selected then just give up
|
|
+ if (use_resizer)
|
|
+ return VLC_EGENERIC;
|
|
+ // otherwise downgrade HVS to ISP
|
|
+ use_isp = true;
|
|
+ }
|
|
+
|
|
+ if (use_resizer) {
|
|
+ // use resizer overrides use_isp
|
|
+ use_isp = false;
|
|
+ }
|
|
+
|
|
+ // Check we have a sliced version of the fourcc if we want the resizer
|
|
+ if (use_resizer &&
|
|
+ (enc_out = pic_to_slice_mmal_fourcc(enc_out)) == 0) {
|
|
+ return VLC_EGENERIC;
|
|
+ }
|
|
+
|
|
+ gpu_mem = hw_mmal_get_gpu_mem();
|
|
+
|
|
+ {
|
|
+ char dbuf0[5], dbuf1[5], dbuf2[5], dbuf3[5];
|
|
+ msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %d/%d] sar:%d/%d->%s/%s,%dx%d [(%d,%d) %dx%d] rgb:%#x:%#x:%#x sar:%d/%d (gpu=%d)", __func__,
|
|
+ use_resizer ? "resize" : use_isp ? "isp" : "hvs",
|
|
+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), str_fourcc(dbuf2, enc_in),
|
|
+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
|
|
+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
|
|
+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
|
|
+ p_filter->fmt_in.video.i_sar_num, p_filter->fmt_in.video.i_sar_den,
|
|
+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), str_fourcc(dbuf3, enc_out),
|
|
+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
|
|
+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
|
|
+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height,
|
|
+ p_filter->fmt_out.video.i_rmask, p_filter->fmt_out.video.i_gmask, p_filter->fmt_out.video.i_bmask,
|
|
+ p_filter->fmt_out.video.i_sar_num, p_filter->fmt_out.video.i_sar_den,
|
|
+ gpu_mem);
|
|
+ }
|
|
+
|
|
+ sys = calloc(1, sizeof(filter_sys_t));
|
|
+ if (!sys) {
|
|
+ ret = VLC_ENOMEM;
|
|
+ goto fail;
|
|
+ }
|
|
+ p_filter->p_sys = sys;
|
|
+
|
|
+ // Init stuff the we destroy unconditionaly in Close first
|
|
+ vlc_mutex_init(&sys->lock);
|
|
+ vlc_sem_init(&sys->sem, 0);
|
|
+ sys->err_stream = MMAL_SUCCESS;
|
|
+ pic_fifo_init(&sys->ret_pics);
|
|
+ pic_fifo_init(&sys->slice.pics);
|
|
+
|
|
+ sys->in_port_cb_fn = conv_input_port_cb;
|
|
+ if (use_resizer) {
|
|
+ sys->resizer_type = FILTER_RESIZER_RESIZER;
|
|
+ sys->is_sliced = true;
|
|
+ sys->component_name = MMAL_COMPONENT_DEFAULT_RESIZER;
|
|
+ sys->out_port_cb_fn = slice_output_port_cb;
|
|
+ }
|
|
+ else if (use_isp) {
|
|
+ sys->resizer_type = FILTER_RESIZER_ISP;
|
|
+ sys->is_sliced = false; // Copy directly into filter picture
|
|
+ sys->component_name = MMAL_COMPONENT_ISP_RESIZER;
|
|
+ sys->out_port_cb_fn = conv_output_port_cb;
|
|
} else {
|
|
- mmal_buffer_header_release(buffer);
|
|
+ sys->resizer_type = FILTER_RESIZER_HVS;
|
|
+ sys->is_sliced = false; // Copy directly into filter picture
|
|
+ sys->component_name = MMAL_COMPONENT_HVS;
|
|
+ sys->out_port_cb_fn = conv_output_port_cb;
|
|
+ }
|
|
+
|
|
+ status = mmal_component_create(sys->component_name, &sys->component);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ if (!use_isp && !use_resizer) {
|
|
+ msg_Warn(p_filter, "Failed to rcreate HVS resizer - retrying with ISP");
|
|
+ CloseConverter(obj);
|
|
+ use_isp = true;
|
|
+ goto retry;
|
|
+ }
|
|
+ msg_Err(p_filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
|
|
+ MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
}
|
|
+ sys->output = sys->component->output[0];
|
|
+ sys->input = sys->component->input[0];
|
|
+
|
|
+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
|
|
+ status = mmal_port_enable(sys->component->control, conv_control_port_cb);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(p_filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
|
|
+ sys->component->control->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)p_filter;
|
|
+ sys->input->format->type = MMAL_ES_TYPE_VIDEO;
|
|
+ sys->input->format->encoding = enc_in;
|
|
+ sys->input->format->encoding_variant = MMAL_ENCODING_I420;
|
|
+ vlc_to_mmal_video_fmt(sys->input->format, &p_filter->fmt_in.video);
|
|
+ port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, 1);
|
|
+
|
|
+ mmal_log_dump_format(sys->input->format);
|
|
+
|
|
+ status = mmal_port_format_commit(sys->input);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(p_filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
|
|
+ sys->input->buffer_num = NUM_DECODER_BUFFER_HEADERS;
|
|
+
|
|
+ if ((status = conv_enable_in(p_filter, sys)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+
|
|
+ port_parameter_set_bool(sys->output, MMAL_PARAMETER_ZERO_COPY, sys->is_sliced);
|
|
+
|
|
+ status = mmal_component_enable(sys->component);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(p_filter, "Failed to enable component %s (status=%"PRIx32" %s)",
|
|
+ sys->component->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
|
|
+ {
|
|
+ msg_Err(p_filter, "Failed to create input pool");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if (sys->resizer_type == FILTER_RESIZER_HVS)
|
|
+ {
|
|
+ unsigned int i;
|
|
+ for (i = 0; i != SUBS_MAX; ++i) {
|
|
+ if (hw_mmal_subpic_open(VLC_OBJECT(p_filter), sys->subs + i, sys->component->input[i + 1], i + 1) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Failed to open subpic %d", i);
|
|
+ goto fail;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ p_filter->pf_video_filter = conv_filter;
|
|
+ p_filter->pf_flush = conv_flush;
|
|
+ // video_drain NIF in filter structure
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, ">>> %s: ok", __func__);
|
|
+#endif
|
|
+
|
|
+ return VLC_SUCCESS;
|
|
+
|
|
+fail:
|
|
+ CloseConverter(obj);
|
|
+
|
|
+ if (!use_resizer && status == MMAL_ENOMEM) {
|
|
+ use_resizer = true;
|
|
+ msg_Warn(p_filter, "Lack of memory to use HVS/ISP: trying resizer");
|
|
+ goto retry;
|
|
+ }
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, ">>> %s: FAIL: %d", __func__, ret);
|
|
+#endif
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+typedef struct blend_sys_s {
|
|
+ vzc_pool_ctl_t * vzc;
|
|
+ const picture_t * last_dst; // Not a ref, just a hint that we have a new pic
|
|
+} blend_sys_t;
|
|
+
|
|
+static void FilterBlendMmal(filter_t *p_filter,
|
|
+ picture_t *dst, const picture_t * src,
|
|
+ int x_offset, int y_offset, int alpha)
|
|
+{
|
|
+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "%s (%d,%d:%d) pic=%p, pts=%lld, force=%d", __func__, x_offset, y_offset, alpha, src, src->date, src->b_force);
|
|
+#endif
|
|
+ // If nothing to do then do nothing
|
|
+ if (alpha == 0 ||
|
|
+ src->format.i_visible_height == 0 ||
|
|
+ src->format.i_visible_width == 0)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (dst->context == NULL)
|
|
+ msg_Err(p_filter, "MMAL pic missing context");
|
|
+ else
|
|
+ {
|
|
+ // cast away src const so we can ref it
|
|
+ MMAL_BUFFER_HEADER_T *buf = hw_mmal_vzc_buf_from_pic(sys->vzc, (picture_t *)src, dst,
|
|
+ dst != sys->last_dst || !hw_mmal_pic_has_sub_bufs(dst));
|
|
+ if (buf == NULL) {
|
|
+ msg_Err(p_filter, "Failed to allocate vzc buffer for subpic");
|
|
+ return;
|
|
+ }
|
|
+ MMAL_DISPLAYREGION_T * const reg = hw_mmal_vzc_buf_region(buf);
|
|
+
|
|
+ reg->set |=
|
|
+ MMAL_DISPLAY_SET_ALPHA | MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_DEST_RECT;
|
|
+
|
|
+ reg->fullscreen = 0;
|
|
+
|
|
+ reg->alpha = (uint32_t)(alpha & 0xff) | (1U << 31);
|
|
+
|
|
+ hw_mmal_vzc_buf_set_dest_rect(buf, x_offset, y_offset, src->format.i_visible_width, src->format.i_visible_height);
|
|
+
|
|
+ reg->dest_rect = (MMAL_RECT_T){0, 0, 0, 0};
|
|
+
|
|
+ hw_mmal_pic_sub_buf_add(dst, buf);
|
|
+
|
|
+ sys->last_dst = dst;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void FlushBlendMmal(filter_t * p_filter)
|
|
+{
|
|
+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
|
|
+ sys->last_dst = NULL;
|
|
+ hw_mmal_vzc_pool_flush(sys->vzc);
|
|
+}
|
|
+
|
|
+static int OpenBlendMmal(vlc_object_t *object)
|
|
+{
|
|
+ filter_t * const p_filter = (filter_t *)object;
|
|
+ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
|
|
+
|
|
+ if ((vfcc_dst != VLC_CODEC_MMAL_OPAQUE &&
|
|
+ vfcc_dst != VLC_CODEC_MMAL_ZC_SAND8 &&
|
|
+ vfcc_dst != VLC_CODEC_MMAL_ZC_SAND10) ||
|
|
+ !hw_mmal_vzc_subpic_fmt_valid(&p_filter->fmt_in.video))
|
|
+ {
|
|
+ return VLC_EGENERIC;
|
|
+ }
|
|
+
|
|
+ {
|
|
+ char dbuf0[5], dbuf1[5];
|
|
+ msg_Dbg(p_filter, "%s: (%s) %s,%dx%d [(%d,%d) %dx%d]->%s,%dx%d [(%d,%d) %dx%d]", __func__,
|
|
+ "blend",
|
|
+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma), p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
|
|
+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
|
|
+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
|
|
+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma), p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
|
|
+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
|
|
+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
|
|
+ }
|
|
+
|
|
+ {
|
|
+ blend_sys_t * const sys = calloc(1, sizeof (*sys));
|
|
+ if (sys == NULL)
|
|
+ return VLC_ENOMEM;
|
|
+ if ((sys->vzc = hw_mmal_vzc_pool_new()) == NULL)
|
|
+ {
|
|
+ free(sys);
|
|
+ return VLC_ENOMEM;
|
|
+ }
|
|
+ p_filter->p_sys = (filter_sys_t *)sys;
|
|
+ }
|
|
+
|
|
+ p_filter->pf_video_blend = FilterBlendMmal;
|
|
+ p_filter->pf_flush = FlushBlendMmal;
|
|
+
|
|
+ return VLC_SUCCESS;
|
|
+}
|
|
+
|
|
+static void CloseBlendMmal(vlc_object_t *object)
|
|
+{
|
|
+ filter_t * const p_filter = (filter_t *)object;
|
|
+ blend_sys_t * const sys = (blend_sys_t *)p_filter->p_sys;
|
|
+
|
|
+ hw_mmal_vzc_pool_release(sys->vzc);
|
|
+ free(sys);
|
|
+}
|
|
+
|
|
+// ---------------------------------------------------------------------------
|
|
+
|
|
+static void FilterBlendNeon(filter_t *p_filter,
|
|
+ picture_t *dst_pic, const picture_t * src_pic,
|
|
+ int x_offset, int y_offset, int alpha)
|
|
+{
|
|
+ const uint8_t * s_data;
|
|
+ uint8_t * d_data;
|
|
+ int width = src_pic->format.i_visible_width;
|
|
+ int height = src_pic->format.i_visible_height;
|
|
+ blend_neon_fn *const blend_fn = (blend_neon_fn * )p_filter->p_sys;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "%s (%d,%d:%d) pic=%p, pts=%lld, force=%d", __func__, x_offset, y_offset, alpha, src_pic, src_pic->date, src_pic->b_force);
|
|
+#endif
|
|
+
|
|
+ if (alpha == 0 ||
|
|
+ src_pic->format.i_visible_height == 0 ||
|
|
+ src_pic->format.i_visible_width == 0)
|
|
+ {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ x_offset += dst_pic->format.i_x_offset;
|
|
+ y_offset += dst_pic->format.i_y_offset;
|
|
+
|
|
+ // Deal with R/B overrun
|
|
+ if (x_offset + width >= (int)(dst_pic->format.i_x_offset + dst_pic->format.i_visible_width))
|
|
+ width = dst_pic->format.i_x_offset + dst_pic->format.i_visible_width - x_offset;
|
|
+ if (y_offset + height >= (int)(dst_pic->format.i_y_offset + dst_pic->format.i_visible_height))
|
|
+ height = dst_pic->format.i_y_offset + dst_pic->format.i_visible_height - y_offset;
|
|
+
|
|
+ if (width <= 0 || height <= 0) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // *** L/U overrun
|
|
+
|
|
+ s_data = src_pic->p[0].p_pixels +
|
|
+ src_pic->p[0].i_pixel_pitch * src_pic->format.i_x_offset +
|
|
+ src_pic->p[0].i_pitch * src_pic->format.i_y_offset;
|
|
+ d_data = dst_pic->p[0].p_pixels +
|
|
+ dst_pic->p[0].i_pixel_pitch * x_offset +
|
|
+ dst_pic->p[0].i_pitch * y_offset;
|
|
+
|
|
+
|
|
+ do {
|
|
+ blend_fn(d_data, s_data, alpha, width);
|
|
+ s_data += src_pic->p[0].i_pitch;
|
|
+ d_data += dst_pic->p[0].i_pitch;
|
|
+ } while (--height > 0);
|
|
+}
|
|
+
|
|
+static void CloseBlendNeon(vlc_object_t *object)
|
|
+{
|
|
+ VLC_UNUSED(object);
|
|
}
|
|
+
|
|
+static int OpenBlendNeon(vlc_object_t *object)
|
|
+{
|
|
+ filter_t * const p_filter = (filter_t *)object;
|
|
+ const vlc_fourcc_t vfcc_dst = p_filter->fmt_out.video.i_chroma;
|
|
+ MMAL_FOURCC_T mfcc_src = vlc_to_mmal_video_fourcc(&p_filter->fmt_in.video);
|
|
+ MMAL_FOURCC_T mfcc_dst = vlc_to_mmal_video_fourcc(&p_filter->fmt_out.video);
|
|
+ blend_neon_fn * blend_fn = (blend_neon_fn *)0;
|
|
+
|
|
+ // Non-alpha RGB only for dest
|
|
+ if (vfcc_dst != VLC_CODEC_RGB32)
|
|
+ return VLC_EGENERIC;
|
|
+
|
|
+ // Check we have appropriate blend fn (mmal doesn't have a non-alpha RGB32)
|
|
+ switch (mfcc_src) {
|
|
+ case MMAL_ENCODING_RGBA:
|
|
+ if (mfcc_dst == MMAL_ENCODING_RGBA)
|
|
+ blend_fn = blend_rgbx_rgba_neon;
|
|
+ else if (mfcc_dst == MMAL_ENCODING_BGRA)
|
|
+ blend_fn = blend_bgrx_rgba_neon;
|
|
+ break;
|
|
+
|
|
+ case MMAL_ENCODING_BGRA:
|
|
+ if (mfcc_dst == MMAL_ENCODING_BGRA)
|
|
+ blend_fn = blend_rgbx_rgba_neon;
|
|
+ else if (mfcc_dst == MMAL_ENCODING_RGBA)
|
|
+ blend_fn = blend_bgrx_rgba_neon;
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (blend_fn == (blend_neon_fn *)0)
|
|
+ return VLC_EGENERIC;
|
|
+
|
|
+ p_filter->p_sys = (void *)blend_fn;
|
|
+ p_filter->pf_video_blend = FilterBlendNeon;
|
|
+
|
|
+ {
|
|
+ char dbuf0[5], dbuf1[5];
|
|
+ char dbuf0a[5], dbuf1a[5];
|
|
+ msg_Dbg(p_filter, "%s: (%s) %s/%s,%dx%d [(%d,%d) %dx%d]->%s/%s,%dx%d [(%d,%d) %dx%d]", __func__,
|
|
+ "blend",
|
|
+ str_fourcc(dbuf0, p_filter->fmt_in.video.i_chroma),
|
|
+ str_fourcc(dbuf0a, mfcc_src),
|
|
+ p_filter->fmt_in.video.i_width, p_filter->fmt_in.video.i_height,
|
|
+ p_filter->fmt_in.video.i_x_offset, p_filter->fmt_in.video.i_y_offset,
|
|
+ p_filter->fmt_in.video.i_visible_width, p_filter->fmt_in.video.i_visible_height,
|
|
+ str_fourcc(dbuf1, p_filter->fmt_out.video.i_chroma),
|
|
+ str_fourcc(dbuf1a, mfcc_dst),
|
|
+ p_filter->fmt_out.video.i_width, p_filter->fmt_out.video.i_height,
|
|
+ p_filter->fmt_out.video.i_x_offset, p_filter->fmt_out.video.i_y_offset,
|
|
+ p_filter->fmt_out.video.i_visible_width, p_filter->fmt_out.video.i_visible_height);
|
|
+ }
|
|
+
|
|
+ return VLC_SUCCESS;
|
|
+}
|
|
+
|
|
+vlc_module_begin()
|
|
+ set_category( CAT_INPUT )
|
|
+ set_subcategory( SUBCAT_INPUT_VCODEC )
|
|
+ set_shortname(N_("MMAL decoder"))
|
|
+ set_description(N_("MMAL-based decoder plugin for Raspberry Pi"))
|
|
+ set_capability("video decoder", 90)
|
|
+ add_shortcut("mmal_decoder")
|
|
+ add_bool(MMAL_OPAQUE_NAME, true, MMAL_OPAQUE_TEXT, MMAL_OPAQUE_LONGTEXT, false)
|
|
+ set_callbacks(OpenDecoder, CloseDecoder)
|
|
+
|
|
+ add_submodule()
|
|
+ set_category( CAT_VIDEO )
|
|
+ set_subcategory( SUBCAT_VIDEO_VFILTER )
|
|
+ set_shortname(N_("MMAL converterer"))
|
|
+ set_description(N_("MMAL conversion filter"))
|
|
+ add_shortcut("mmal_converter")
|
|
+ set_capability( "video converter", 900 )
|
|
+ add_bool(MMAL_RESIZE_NAME, /* default */ false, MMAL_RESIZE_TEXT, MMAL_RESIZE_LONGTEXT, /* advanced option */ false)
|
|
+ add_bool(MMAL_ISP_NAME, /* default */ false, MMAL_ISP_TEXT, MMAL_ISP_LONGTEXT, /* advanced option */ false)
|
|
+ set_callbacks(OpenConverter, CloseConverter)
|
|
+
|
|
+ add_submodule()
|
|
+ set_category( CAT_VIDEO )
|
|
+ set_subcategory( SUBCAT_VIDEO_VFILTER )
|
|
+ set_description(N_("Video pictures blending for MMAL"))
|
|
+ add_shortcut("mmal_blend")
|
|
+ set_capability("video blending", 120)
|
|
+ set_callbacks(OpenBlendMmal, CloseBlendMmal)
|
|
+
|
|
+ add_submodule()
|
|
+ set_category( CAT_VIDEO )
|
|
+ set_subcategory( SUBCAT_VIDEO_VFILTER )
|
|
+ set_description(N_("Video pictures blending for neon"))
|
|
+ add_shortcut("neon_blend")
|
|
+ set_capability("video blending", 110)
|
|
+ set_callbacks(OpenBlendNeon, CloseBlendNeon)
|
|
+
|
|
+vlc_module_end()
|
|
+
|
|
+
|
|
--- a/modules/hw/mmal/deinterlace.c
|
|
+++ b/modules/hw/mmal/deinterlace.c
|
|
@@ -26,11 +26,12 @@
|
|
#include "config.h"
|
|
#endif
|
|
|
|
-#include <vlc_picture_pool.h>
|
|
+#include <stdatomic.h>
|
|
+
|
|
#include <vlc_common.h>
|
|
+#include <vlc_picture_pool.h>
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_filter.h>
|
|
-#include <vlc_atomic.h>
|
|
|
|
#include "mmal_picture.h"
|
|
|
|
@@ -41,466 +42,569 @@
|
|
|
|
#define MIN_NUM_BUFFERS_IN_TRANSIT 2
|
|
|
|
-#define MMAL_DEINTERLACE_QPU "mmal-deinterlace-adv-qpu"
|
|
-#define MMAL_DEINTERLACE_QPU_TEXT N_("Use QPUs for advanced HD deinterlacing.")
|
|
-#define MMAL_DEINTERLACE_QPU_LONGTEXT N_("Make use of the QPUs to allow higher quality deinterlacing of HD content.")
|
|
+#define MMAL_DEINTERLACE_NO_QPU "mmal-deinterlace-no-qpu"
|
|
+#define MMAL_DEINTERLACE_NO_QPU_TEXT N_("Do not use QPUs for advanced HD deinterlacing.")
|
|
+#define MMAL_DEINTERLACE_NO_QPU_LONGTEXT N_("Do not make use of the QPUs to allow higher quality deinterlacing of HD content.")
|
|
|
|
-static int Open(filter_t *filter);
|
|
-static void Close(filter_t *filter);
|
|
+#define MMAL_DEINTERLACE_ADV "mmal-deinterlace-adv"
|
|
+#define MMAL_DEINTERLACE_ADV_TEXT N_("Force advanced deinterlace")
|
|
+#define MMAL_DEINTERLACE_ADV_LONGTEXT N_("Force advanced deinterlace")
|
|
|
|
-vlc_module_begin()
|
|
- set_shortname(N_("MMAL deinterlace"))
|
|
- set_description(N_("MMAL-based deinterlace filter plugin"))
|
|
- set_capability("video filter", 0)
|
|
- set_category(CAT_VIDEO)
|
|
- set_subcategory(SUBCAT_VIDEO_VFILTER)
|
|
- set_callbacks(Open, Close)
|
|
- add_shortcut("deinterlace")
|
|
- add_bool(MMAL_DEINTERLACE_QPU, false, MMAL_DEINTERLACE_QPU_TEXT,
|
|
- MMAL_DEINTERLACE_QPU_LONGTEXT, true);
|
|
-vlc_module_end()
|
|
+#define MMAL_DEINTERLACE_FAST "mmal-deinterlace-fast"
|
|
+#define MMAL_DEINTERLACE_FAST_TEXT N_("Force fast deinterlace")
|
|
+#define MMAL_DEINTERLACE_FAST_LONGTEXT N_("Force fast deinterlace")
|
|
+
|
|
+#define MMAL_DEINTERLACE_NONE "mmal-deinterlace-none"
|
|
+#define MMAL_DEINTERLACE_NONE_TEXT N_("Force no deinterlace")
|
|
+#define MMAL_DEINTERLACE_NONE_LONGTEXT N_("Force no interlace. Simply strips off the interlace markers and passes the frame straight through. "\
|
|
+ "This is the default for > SD if < 96M gpu-mem")
|
|
+
|
|
+#define MMAL_DEINTERLACE_HALF_RATE "mmal-deinterlace-half-rate"
|
|
+#define MMAL_DEINTERLACE_HALF_RATE_TEXT N_("Halve output framerate")
|
|
+#define MMAL_DEINTERLACE_HALF_RATE_LONGTEXT N_("Halve output framerate. 1 output frame for each pair of interlaced fields input")
|
|
+
|
|
+#define MMAL_DEINTERLACE_FULL_RATE "mmal-deinterlace-full-rate"
|
|
+#define MMAL_DEINTERLACE_FULL_RATE_TEXT N_("Full output framerate")
|
|
+#define MMAL_DEINTERLACE_FULL_RATE_LONGTEXT N_("Full output framerate. 1 output frame for each interlaced field input")
|
|
|
|
-struct filter_sys_t {
|
|
+
|
|
+typedef struct filter_sys_t
|
|
+{
|
|
MMAL_COMPONENT_T *component;
|
|
MMAL_PORT_T *input;
|
|
MMAL_PORT_T *output;
|
|
+ MMAL_POOL_T *in_pool;
|
|
+ hw_mmal_port_pool_ref_t *out_ppr;
|
|
|
|
- MMAL_QUEUE_T *filtered_pictures;
|
|
- vlc_sem_t sem;
|
|
+ MMAL_QUEUE_T * out_q;
|
|
|
|
- atomic_bool started;
|
|
+ bool half_rate;
|
|
+ bool use_qpu;
|
|
+ bool use_fast;
|
|
+ bool use_passthrough;
|
|
+ unsigned int seq_in;
|
|
+ unsigned int seq_out;
|
|
+} filter_sys_t;
|
|
|
|
- /* statistics */
|
|
- int output_in_transit;
|
|
- int input_in_transit;
|
|
-};
|
|
-
|
|
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
|
|
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
|
|
-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
|
|
-static picture_t *deinterlace(filter_t *filter, picture_t *picture);
|
|
-static void flush(filter_t *filter);
|
|
|
|
#define MMAL_COMPONENT_DEFAULT_DEINTERLACE "vc.ril.image_fx"
|
|
|
|
-static int Open(filter_t *filter)
|
|
-{
|
|
- int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
|
|
- (int64_t)1000000 * filter->fmt_in.video.i_frame_rate_base /
|
|
- filter->fmt_in.video.i_frame_rate : 0;
|
|
- bool use_qpu = var_InheritBool(filter, MMAL_DEINTERLACE_QPU);
|
|
+#define TRACE_ALL 0
|
|
|
|
- MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
|
|
- { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
|
|
- MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
|
|
- 4,
|
|
- { 3, frame_duration, 0, use_qpu }
|
|
- };
|
|
|
|
- int ret = VLC_SUCCESS;
|
|
- MMAL_STATUS_T status;
|
|
- filter_sys_t *sys;
|
|
|
|
- msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!",
|
|
- frame_duration, use_qpu ? "used" : "unused");
|
|
+// Buffer attached to pic on success, is still valid on failure
|
|
+static picture_t * di_alloc_opaque(filter_t * const p_filter, MMAL_BUFFER_HEADER_T * const buf)
|
|
+{
|
|
+ filter_sys_t *const filter_sys = p_filter->p_sys;
|
|
+ picture_t * const pic = filter_NewPicture(p_filter);
|
|
|
|
- if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
|
|
- return VLC_EGENERIC;
|
|
+ if (pic == NULL)
|
|
+ goto fail1;
|
|
|
|
- if (filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
|
|
- return VLC_EGENERIC;
|
|
+ if (buf->length == 0) {
|
|
+ msg_Err(p_filter, "%s: Empty buffer", __func__);
|
|
+ goto fail2;
|
|
+ }
|
|
|
|
- sys = calloc(1, sizeof(filter_sys_t));
|
|
- if (!sys)
|
|
- return VLC_ENOMEM;
|
|
- filter->p_sys = sys;
|
|
+ if ((pic->context = hw_mmal_gen_context(MMAL_ENCODING_OPAQUE, buf, filter_sys->out_ppr)) == NULL)
|
|
+ goto fail2;
|
|
|
|
- bcm_host_init();
|
|
+ buf_to_pic_copy_props(pic, buf);
|
|
|
|
- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
|
|
- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "pic: prog=%d, tff=%d, date=%lld", pic->b_progressive, pic->b_top_field_first, (long long)pic->date);
|
|
+#endif
|
|
|
|
- status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
|
|
- MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+ return pic;
|
|
|
|
- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
|
|
- status = mmal_port_enable(sys->component->control, control_port_cb);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
|
|
- sys->component->control->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+fail2:
|
|
+ picture_Release(pic);
|
|
+fail1:
|
|
+// mmal_buffer_header_release(buf);
|
|
+ return NULL;
|
|
+}
|
|
|
|
- sys->input = sys->component->input[0];
|
|
- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
|
|
- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
|
|
- sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
|
|
- sys->input->format->es->video.width = filter->fmt_in.video.i_width;
|
|
- sys->input->format->es->video.height = filter->fmt_in.video.i_height;
|
|
- sys->input->format->es->video.crop.x = 0;
|
|
- sys->input->format->es->video.crop.y = 0;
|
|
- sys->input->format->es->video.crop.width = filter->fmt_in.video.i_width;
|
|
- sys->input->format->es->video.crop.height = filter->fmt_in.video.i_height;
|
|
- sys->input->format->es->video.par.num = filter->fmt_in.video.i_sar_num;
|
|
- sys->input->format->es->video.par.den = filter->fmt_in.video.i_sar_den;
|
|
+static void di_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
+{
|
|
+#if TRACE_ALL
|
|
+ pic_ctx_mmal_t * ctx = buffer->user_data;
|
|
+// filter_sys_t *const sys = ((filter_t *)port->userdata)->p_sys;
|
|
+
|
|
+ msg_Dbg((filter_t *)port->userdata, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buffer->cmd, ctx, buffer,
|
|
+ buffer->flags, (long long)buffer->pts);
|
|
+#else
|
|
+ VLC_UNUSED(port);
|
|
+#endif
|
|
|
|
- es_format_Copy(&filter->fmt_out, &filter->fmt_in);
|
|
- filter->fmt_out.video.i_frame_rate *= 2;
|
|
+ mmal_buffer_header_release(buffer);
|
|
|
|
- status = mmal_port_format_commit(sys->input);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg((filter_t *)port->userdata, ">>> %s", __func__);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void di_output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
|
|
+{
|
|
+ if (buf->cmd == 0 && buf->length != 0)
|
|
+ {
|
|
+ // The filter structure etc. should always exist if we have contents
|
|
+ // but might not on later flushes as we shut down
|
|
+ filter_t * const p_filter = (filter_t *)port->userdata;
|
|
+ filter_sys_t * const sys = p_filter->p_sys;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts);
|
|
+#endif
|
|
+ mmal_queue_put(sys->out_q, buf);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, ">>> %s: out Q len=%d", __func__, mmal_queue_length(sys->out_q));
|
|
+#endif
|
|
}
|
|
- sys->input->buffer_size = sys->input->buffer_size_recommended;
|
|
- sys->input->buffer_num = sys->input->buffer_num_recommended;
|
|
+ else
|
|
+ {
|
|
+ mmal_buffer_header_reset(buf);
|
|
+ mmal_buffer_header_release(buf);
|
|
+ }
|
|
+}
|
|
|
|
- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
|
|
- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
|
|
- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
|
|
- 1
|
|
- };
|
|
+static MMAL_STATUS_T fill_output_from_q(filter_t * const p_filter, filter_sys_t * const sys, MMAL_QUEUE_T * const q)
|
|
+{
|
|
+ MMAL_BUFFER_HEADER_T * out_buf;
|
|
|
|
- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- goto out;
|
|
+ while ((out_buf = mmal_queue_get(q)) != NULL)
|
|
+ {
|
|
+ MMAL_STATUS_T err;
|
|
+ if ((err = mmal_port_send_buffer(sys->output, out_buf)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Send buffer to output failed");
|
|
+ mmal_queue_put_back(q, out_buf);
|
|
+ return err;
|
|
}
|
|
}
|
|
+ return MMAL_SUCCESS;
|
|
+}
|
|
|
|
- status = mmal_port_enable(sys->input, input_port_cb);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+static inline unsigned int seq_inc(unsigned int x)
|
|
+{
|
|
+ return x + 1 >= 16 ? 1 : x + 1;
|
|
+}
|
|
|
|
- sys->output = sys->component->output[0];
|
|
- sys->output->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
|
|
- mmal_format_full_copy(sys->output->format, sys->input->format);
|
|
+static inline unsigned int seq_delta(unsigned int sseq, unsigned int fseq)
|
|
+{
|
|
+ return fseq == 0 ? 0 : fseq <= sseq ? sseq - fseq : 15 - (fseq - sseq);
|
|
+}
|
|
|
|
- status = mmal_port_format_commit(sys->output);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to commit format for output port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+static picture_t *deinterlace(filter_t * p_filter, picture_t * p_pic)
|
|
+{
|
|
+ filter_sys_t * const sys = p_filter->p_sys;
|
|
+ picture_t *ret_pics = NULL;
|
|
+ MMAL_STATUS_T err;
|
|
|
|
- sys->output->buffer_num = 3;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "<<< %s", __func__);
|
|
+#endif
|
|
|
|
- if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE) {
|
|
- MMAL_PARAMETER_UINT32_T extra_buffers = {
|
|
- { MMAL_PARAMETER_EXTRA_BUFFERS, sizeof(MMAL_PARAMETER_UINT32_T) },
|
|
- 5
|
|
- };
|
|
- status = mmal_port_parameter_set(sys->output, &extra_buffers.hdr);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
|
|
- status, mmal_status_to_string(status));
|
|
- goto out;
|
|
+ // Reenable stuff if the last thing we did was flush
|
|
+ // Output should always be enabled
|
|
+ if (!sys->input->is_enabled &&
|
|
+ (err = mmal_port_enable(sys->input, di_input_port_cb)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Input port enable failed");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ // Fill output from anything that has turned up in pool Q
|
|
+ if (hw_mmal_port_pool_ref_fill(sys->out_ppr) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Out port fill fail");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ // Stuff into input
|
|
+ // We assume the BH is already set up with values reflecting pic date etc.
|
|
+ {
|
|
+ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic);
|
|
+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->in_pool->queue);
|
|
+
|
|
+ if ((err = mmal_buffer_header_replicate(buf, pic_buf)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Failed to replicate input buffer: %d", err);
|
|
+ goto fail;
|
|
}
|
|
|
|
- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
|
|
- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
|
|
- 1
|
|
- };
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "In buf send: pic=%p, buf=%p/%p, ctx=%p, flags=%#x, len=%d/%d, pts=%lld",
|
|
+ p_pic, pic_buf, buf, pic_buf->user_data, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
|
|
+#endif
|
|
|
|
- status = mmal_port_parameter_set(sys->output, &zero_copy.hdr);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
|
|
- sys->output->name, status, mmal_status_to_string(status));
|
|
- goto out;
|
|
+ picture_Release(p_pic);
|
|
+
|
|
+ // Add a sequence to the flags so we can track what we have actually
|
|
+ // deinterlaced
|
|
+ buf->flags = (buf->flags & ~(0xfU * MMAL_BUFFER_HEADER_FLAG_USER0)) | (sys->seq_in * (MMAL_BUFFER_HEADER_FLAG_USER0));
|
|
+ sys->seq_in = seq_inc(sys->seq_in);
|
|
+
|
|
+ if ((err = mmal_port_send_buffer(sys->input, buf)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Send buffer to input failed");
|
|
+ goto fail;
|
|
}
|
|
}
|
|
|
|
- status = mmal_port_enable(sys->output, output_port_cb);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to enable output port %s (status=%"PRIx32" %s)",
|
|
- sys->output->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+ // Return anything that is in the out Q
|
|
+ {
|
|
+ MMAL_BUFFER_HEADER_T * out_buf;
|
|
+ picture_t ** pp_pic = &ret_pics;
|
|
+
|
|
+ // Advanced di has a 3 frame latency, so if the seq delta is greater
|
|
+ // than that then we are expecting at least two frames of output. Wait
|
|
+ // for one of those.
|
|
+ while ((out_buf = (seq_delta(sys->seq_in, sys->seq_out) > 3 ? mmal_queue_wait(sys->out_q) : mmal_queue_get(sys->out_q))) != NULL)
|
|
+ {
|
|
+ picture_t * const out_pic = di_alloc_opaque(p_filter, out_buf);
|
|
+ const unsigned int seq_out = (out_buf->flags / MMAL_BUFFER_HEADER_FLAG_USER0) & 0xf;
|
|
+
|
|
+ if (out_pic == NULL) {
|
|
+ msg_Warn(p_filter, "Failed to alloc new filter output pic");
|
|
+ mmal_queue_put_back(sys->out_q, out_buf);
|
|
+ break;
|
|
+ }
|
|
|
|
- status = mmal_component_enable(sys->component);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
|
|
- sys->component->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "-- %s: Q pic=%p: seq_in=%d, seq_out=%d, delta=%d", __func__, out_pic, sys->seq_in, seq_out, seq_delta(sys->seq_in, seq_out));
|
|
+#endif
|
|
|
|
- sys->filtered_pictures = mmal_queue_create();
|
|
+ *pp_pic = out_pic;
|
|
+ pp_pic = &out_pic->p_next;
|
|
|
|
- filter->pf_video_filter = deinterlace;
|
|
- filter->pf_flush = flush;
|
|
+ // Ignore 0 seqs
|
|
+ // Don't think these should actually happen
|
|
+ if (seq_out != 0)
|
|
+ sys->seq_out = seq_out;
|
|
+ }
|
|
+ }
|
|
|
|
- vlc_sem_init(&sys->sem, 0);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, ">>> %s: pic=%p", __func__, ret_pics);
|
|
+#endif
|
|
|
|
-out:
|
|
- if (ret != VLC_SUCCESS)
|
|
- Close(filter);
|
|
+ return ret_pics;
|
|
|
|
- return ret;
|
|
+fail:
|
|
+ picture_Release(p_pic);
|
|
+ return NULL;
|
|
}
|
|
|
|
-static void Close(filter_t *filter)
|
|
+static void di_flush(filter_t *p_filter)
|
|
{
|
|
- filter_sys_t *sys = filter->p_sys;
|
|
- MMAL_BUFFER_HEADER_T *buffer;
|
|
+ filter_sys_t * const sys = p_filter->p_sys;
|
|
|
|
- if (!sys)
|
|
- return;
|
|
-
|
|
- if (sys->component && sys->component->control->is_enabled)
|
|
- mmal_port_disable(sys->component->control);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "<<< %s", __func__);
|
|
+#endif
|
|
|
|
- if (sys->input && sys->input->is_enabled)
|
|
+ if (sys->input != NULL && sys->input->is_enabled)
|
|
mmal_port_disable(sys->input);
|
|
|
|
- if (sys->output && sys->output->is_enabled)
|
|
+ if (sys->output != NULL && sys->output->is_enabled)
|
|
+ {
|
|
+ // Wedge anything we've got into the output port as that will free the underlying buffers
|
|
+ fill_output_from_q(p_filter, sys, sys->out_q);
|
|
+
|
|
mmal_port_disable(sys->output);
|
|
|
|
- if (sys->component && sys->component->is_enabled)
|
|
- mmal_component_disable(sys->component);
|
|
+ // If that dumped anything real into the out_q then have another go
|
|
+ if (mmal_queue_length(sys->out_q) != 0)
|
|
+ {
|
|
+ mmal_port_enable(sys->output, di_output_port_cb);
|
|
+ fill_output_from_q(p_filter, sys, sys->out_q);
|
|
+ mmal_port_disable(sys->output);
|
|
+ // Out q should now be empty & should remain so until the input is reenabled
|
|
+ }
|
|
+ mmal_port_enable(sys->output, di_output_port_cb);
|
|
|
|
- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
|
|
- picture_t *pic = (picture_t *)buffer->user_data;
|
|
- picture_Release(pic);
|
|
+ // Leaving the input disabled is fine - but we want to leave the output enabled
|
|
+ // so we can retrieve buffers that are still bound to pictures
|
|
}
|
|
|
|
- if (sys->filtered_pictures)
|
|
- mmal_queue_destroy(sys->filtered_pictures);
|
|
+ sys->seq_in = 1;
|
|
+ sys->seq_out = 1;
|
|
|
|
- if (sys->component)
|
|
- mmal_component_release(sys->component);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, ">>> %s", __func__);
|
|
+#endif
|
|
+}
|
|
|
|
- vlc_sem_destroy(&sys->sem);
|
|
- free(sys);
|
|
|
|
- bcm_host_deinit();
|
|
+static void pass_flush(filter_t *p_filter)
|
|
+{
|
|
+ // Nothing to do
|
|
+ VLC_UNUSED(p_filter);
|
|
+}
|
|
+
|
|
+static picture_t * pass_deinterlace(filter_t * p_filter, picture_t * p_pic)
|
|
+{
|
|
+ VLC_UNUSED(p_filter);
|
|
+
|
|
+ p_pic->b_progressive = true;
|
|
+ return p_pic;
|
|
}
|
|
|
|
-static int send_output_buffer(filter_t *filter)
|
|
+
|
|
+static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
{
|
|
- filter_sys_t *sys = filter->p_sys;
|
|
- MMAL_BUFFER_HEADER_T *buffer;
|
|
+ filter_t *filter = (filter_t *)port->userdata;
|
|
MMAL_STATUS_T status;
|
|
- picture_t *picture;
|
|
- int ret = 0;
|
|
|
|
- if (!sys->output->is_enabled) {
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
|
|
+ status = *(uint32_t *)buffer->data;
|
|
+ msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
|
|
+ mmal_status_to_string(status));
|
|
}
|
|
|
|
- picture = filter_NewPicture(filter);
|
|
- if (!picture) {
|
|
- msg_Warn(filter, "Failed to get new picture");
|
|
- ret = -1;
|
|
- goto out;
|
|
- }
|
|
- picture->format.i_frame_rate = filter->fmt_out.video.i_frame_rate;
|
|
- picture->format.i_frame_rate_base = filter->fmt_out.video.i_frame_rate_base;
|
|
+ mmal_buffer_header_reset(buffer);
|
|
+ mmal_buffer_header_release(buffer);
|
|
+}
|
|
+
|
|
+static void CloseMmalDeinterlace(filter_t *filter)
|
|
+{
|
|
+ filter_sys_t * const sys = filter->p_sys;
|
|
|
|
- buffer = picture->p_sys->buffer;
|
|
- buffer->user_data = picture;
|
|
- buffer->cmd = 0;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(filter, "<<< %s", __func__);
|
|
+#endif
|
|
|
|
- mmal_picture_lock(picture);
|
|
+ if (sys == NULL)
|
|
+ return;
|
|
|
|
- status = mmal_port_send_buffer(sys->output, buffer);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to send buffer to output port (status=%"PRIx32" %s)",
|
|
- status, mmal_status_to_string(status));
|
|
- mmal_buffer_header_release(buffer);
|
|
- picture_Release(picture);
|
|
- ret = -1;
|
|
- } else {
|
|
- atomic_fetch_add(&sys->output_in_transit, 1);
|
|
- vlc_sem_post(&sys->sem);
|
|
+ if (sys->use_passthrough)
|
|
+ {
|
|
+ free(sys);
|
|
+ return;
|
|
}
|
|
|
|
-out:
|
|
- return ret;
|
|
+ di_flush(filter);
|
|
+
|
|
+ if (sys->component && sys->component->control->is_enabled)
|
|
+ mmal_port_disable(sys->component->control);
|
|
+
|
|
+ if (sys->component && sys->component->is_enabled)
|
|
+ mmal_component_disable(sys->component);
|
|
+
|
|
+ if (sys->in_pool != NULL)
|
|
+ mmal_pool_destroy(sys->in_pool);
|
|
+
|
|
+ hw_mmal_port_pool_ref_release(sys->out_ppr, false);
|
|
+ // Once we exit filter & sys are invalid so mark as such
|
|
+ sys->output->userdata = NULL;
|
|
+
|
|
+ if (sys->out_q != NULL)
|
|
+ mmal_queue_destroy(sys->out_q);
|
|
+
|
|
+ if (sys->component)
|
|
+ mmal_component_release(sys->component);
|
|
+
|
|
+ free(sys);
|
|
+
|
|
+ bcm_host_deinit();
|
|
}
|
|
|
|
-static void fill_output_port(filter_t *filter)
|
|
+
|
|
+static int OpenMmalDeinterlace(filter_t *filter)
|
|
{
|
|
- filter_sys_t *sys = filter->p_sys;
|
|
- /* allow at least 2 buffers in transit */
|
|
- unsigned max_buffers_in_transit = __MAX(2, MIN_NUM_BUFFERS_IN_TRANSIT);
|
|
- int buffers_available = sys->output->buffer_num -
|
|
- atomic_load(&sys->output_in_transit) -
|
|
- mmal_queue_length(sys->filtered_pictures);
|
|
- int buffers_to_send = max_buffers_in_transit - sys->output_in_transit;
|
|
- int i;
|
|
+ int32_t frame_duration = filter->fmt_in.video.i_frame_rate != 0 ?
|
|
+ CLOCK_FREQ * filter->fmt_in.video.i_frame_rate_base /
|
|
+ filter->fmt_in.video.i_frame_rate : 0;
|
|
+
|
|
+ int ret = VLC_EGENERIC;
|
|
+ MMAL_STATUS_T status;
|
|
+ filter_sys_t *sys;
|
|
+
|
|
+ msg_Dbg(filter, "<<< %s", __func__);
|
|
|
|
- if (buffers_to_send > buffers_available)
|
|
- buffers_to_send = buffers_available;
|
|
+ if (filter->fmt_in.video.i_chroma != VLC_CODEC_MMAL_OPAQUE ||
|
|
+ filter->fmt_out.video.i_chroma != VLC_CODEC_MMAL_OPAQUE)
|
|
+ return VLC_EGENERIC;
|
|
|
|
-#ifndef NDEBUG
|
|
- msg_Dbg(filter, "Send %d buffers to output port (available: %d, in_transit: %d, buffer_num: %d)",
|
|
- buffers_to_send, buffers_available, sys->output_in_transit,
|
|
- sys->output->buffer_num);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(filter, "Try to open mmal_deinterlace filter. frame_duration: %d, QPU %s!",
|
|
+ frame_duration, use_qpu ? "used" : "unused");
|
|
#endif
|
|
- for (i = 0; i < buffers_to_send; ++i) {
|
|
- if (send_output_buffer(filter) < 0)
|
|
- break;
|
|
+
|
|
+ sys = calloc(1, sizeof(filter_sys_t));
|
|
+ if (!sys)
|
|
+ return VLC_ENOMEM;
|
|
+ filter->p_sys = sys;
|
|
+
|
|
+ sys->seq_in = 1;
|
|
+ sys->seq_out = 1;
|
|
+ sys->half_rate = false;
|
|
+ sys->use_qpu = true;
|
|
+ sys->use_fast = false;
|
|
+ sys->use_passthrough = false;
|
|
+
|
|
+ if (filter->fmt_in.video.i_width * filter->fmt_in.video.i_height > 768 * 576)
|
|
+ {
|
|
+ // We get stressed if we have to try too hard - so make life easier
|
|
+ sys->half_rate = true;
|
|
+ // Also check we actually have enough memory to do this
|
|
+ if (hw_mmal_get_gpu_mem() < (96 << 20))
|
|
+ sys->use_passthrough = true;
|
|
+
|
|
}
|
|
-}
|
|
|
|
-static picture_t *deinterlace(filter_t *filter, picture_t *picture)
|
|
-{
|
|
- filter_sys_t *sys = filter->p_sys;
|
|
- MMAL_BUFFER_HEADER_T *buffer;
|
|
- picture_t *out_picture = NULL;
|
|
- picture_t *ret = NULL;
|
|
- MMAL_STATUS_T status;
|
|
- unsigned i = 0;
|
|
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NO_QPU))
|
|
+ sys->use_qpu = false;
|
|
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_ADV))
|
|
+ {
|
|
+ sys->use_fast = false;
|
|
+ sys->use_passthrough = false;
|
|
+ }
|
|
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FAST))
|
|
+ {
|
|
+ sys->use_fast = true;
|
|
+ sys->use_passthrough = false;
|
|
+ }
|
|
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_NONE))
|
|
+ sys->use_passthrough = true;
|
|
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_FULL_RATE))
|
|
+ sys->half_rate = false;
|
|
+ if (var_InheritBool(filter, MMAL_DEINTERLACE_HALF_RATE))
|
|
+ sys->half_rate = true;
|
|
+
|
|
+ if (sys->use_passthrough)
|
|
+ {
|
|
+ filter->pf_video_filter = pass_deinterlace;
|
|
+ filter->pf_flush = pass_flush;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ bcm_host_init();
|
|
|
|
- fill_output_port(filter);
|
|
+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_DEINTERLACE, &sys->component);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(filter, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
|
|
+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- buffer = picture->p_sys->buffer;
|
|
- buffer->user_data = picture;
|
|
- buffer->pts = picture->date;
|
|
- buffer->cmd = 0;
|
|
+ {
|
|
+ const MMAL_PARAMETER_IMAGEFX_PARAMETERS_T imfx_param = {
|
|
+ { MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, sizeof(imfx_param) },
|
|
+ sys->use_fast ?
|
|
+ MMAL_PARAM_IMAGEFX_DEINTERLACE_FAST :
|
|
+ MMAL_PARAM_IMAGEFX_DEINTERLACE_ADV,
|
|
+ 4,
|
|
+ { 5 /* Frame type: mixed */, frame_duration, sys->half_rate, sys->use_qpu }
|
|
+ };
|
|
|
|
- if (!picture->p_sys->displayed) {
|
|
- status = mmal_port_send_buffer(sys->input, buffer);
|
|
+ status = mmal_port_parameter_set(sys->component->output[0], &imfx_param.hdr);
|
|
if (status != MMAL_SUCCESS) {
|
|
- msg_Err(filter, "Failed to send buffer to input port (status=%"PRIx32" %s)",
|
|
- status, mmal_status_to_string(status));
|
|
- picture_Release(picture);
|
|
- } else {
|
|
- picture->p_sys->displayed = true;
|
|
- atomic_fetch_add(&sys->input_in_transit, 1);
|
|
- vlc_sem_post(&sys->sem);
|
|
+ msg_Err(filter, "Failed to configure MMAL component %s (status=%"PRIx32" %s)",
|
|
+ MMAL_COMPONENT_DEFAULT_DEINTERLACE, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
}
|
|
- } else {
|
|
- picture_Release(picture);
|
|
}
|
|
|
|
- /*
|
|
- * Send output buffers
|
|
- */
|
|
- while(atomic_load(&sys->started) && i < 2) {
|
|
- if (buffer = mmal_queue_timedwait(sys->filtered_pictures, 2000)) {
|
|
- i++;
|
|
- if (!out_picture) {
|
|
- out_picture = (picture_t *)buffer->user_data;
|
|
- ret = out_picture;
|
|
- } else {
|
|
- out_picture->p_next = (picture_t *)buffer->user_data;
|
|
- out_picture = out_picture->p_next;
|
|
- }
|
|
- out_picture->date = buffer->pts;
|
|
- } else {
|
|
- msg_Dbg(filter, "Failed waiting for filtered picture");
|
|
- break;
|
|
- }
|
|
+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
|
|
+ status = mmal_port_enable(sys->component->control, control_port_cb);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(filter, "Failed to enable control port %s (status=%"PRIx32" %s)",
|
|
+ sys->component->control->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
}
|
|
- if (out_picture)
|
|
- out_picture->p_next = NULL;
|
|
|
|
- return ret;
|
|
-}
|
|
+ sys->input = sys->component->input[0];
|
|
+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)filter;
|
|
+ if (filter->fmt_in.i_codec == VLC_CODEC_MMAL_OPAQUE)
|
|
+ sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
|
|
+ vlc_to_mmal_video_fmt(sys->input->format, &filter->fmt_in.video);
|
|
|
|
-static void flush(filter_t *filter)
|
|
-{
|
|
- filter_sys_t *sys = filter->p_sys;
|
|
- MMAL_BUFFER_HEADER_T *buffer;
|
|
+ es_format_Copy(&filter->fmt_out, &filter->fmt_in);
|
|
+ if (!sys->half_rate)
|
|
+ filter->fmt_out.video.i_frame_rate *= 2;
|
|
|
|
- msg_Dbg(filter, "flush deinterlace filter");
|
|
+ status = mmal_port_format_commit(sys->input);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(filter, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
|
|
+ sys->input->buffer_num = 30;
|
|
+// sys->input->buffer_num = sys->input->buffer_num_recommended;
|
|
|
|
- msg_Dbg(filter, "flush: flush ports (input: %d, output: %d in transit)",
|
|
- sys->input_in_transit, sys->output_in_transit);
|
|
- mmal_port_flush(sys->output);
|
|
- mmal_port_flush(sys->input);
|
|
-
|
|
- msg_Dbg(filter, "flush: wait for all buffers to be returned");
|
|
- while (atomic_load(&sys->input_in_transit) ||
|
|
- atomic_load(&sys->output_in_transit))
|
|
- vlc_sem_wait(&sys->sem);
|
|
-
|
|
- while ((buffer = mmal_queue_get(sys->filtered_pictures))) {
|
|
- picture_t *pic = (picture_t *)buffer->user_data;
|
|
- msg_Dbg(filter, "flush: release already filtered pic %p",
|
|
- (void *)pic);
|
|
- picture_Release(pic);
|
|
+ if ((sys->in_pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
|
|
+ {
|
|
+ msg_Err(filter, "Failed to create input pool");
|
|
+ goto fail;
|
|
}
|
|
- atomic_store(&sys->started, false);
|
|
- msg_Dbg(filter, "flush: done");
|
|
-}
|
|
|
|
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
-{
|
|
- filter_t *filter = (filter_t *)port->userdata;
|
|
- MMAL_STATUS_T status;
|
|
+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(filter, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- if (buffer->cmd == MMAL_EVENT_ERROR) {
|
|
- status = *(uint32_t *)buffer->data;
|
|
- msg_Err(filter, "MMAL error %"PRIx32" \"%s\"", status,
|
|
- mmal_status_to_string(status));
|
|
+ status = mmal_port_enable(sys->input, di_input_port_cb);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(filter, "Failed to enable input port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
}
|
|
|
|
- mmal_buffer_header_release(buffer);
|
|
-}
|
|
|
|
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
-{
|
|
- picture_t *picture = (picture_t *)buffer->user_data;
|
|
- filter_t *filter = (filter_t *)port->userdata;
|
|
- filter_sys_t *sys = filter->p_sys;
|
|
+ if ((sys->out_q = mmal_queue_create()) == NULL)
|
|
+ {
|
|
+ msg_Err(filter, "Failed to create out Q");
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- if (picture) {
|
|
- picture_Release(picture);
|
|
- } else {
|
|
- msg_Warn(filter, "Got buffer without picture on input port - OOOPS");
|
|
- mmal_buffer_header_release(buffer);
|
|
+ sys->output = sys->component->output[0];
|
|
+ mmal_format_full_copy(sys->output->format, sys->input->format);
|
|
+
|
|
+ if ((status = hw_mmal_opaque_output(VLC_OBJECT(filter), &sys->out_ppr, sys->output, 5, di_output_port_cb)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+
|
|
+ status = mmal_component_enable(sys->component);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(filter, "Failed to enable component %s (status=%"PRIx32" %s)",
|
|
+ sys->component->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
}
|
|
|
|
- atomic_fetch_sub(&sys->input_in_transit, 1);
|
|
- vlc_sem_post(&sys->sem);
|
|
+ filter->pf_video_filter = deinterlace;
|
|
+ filter->pf_flush = di_flush;
|
|
+ return 0;
|
|
+
|
|
+fail:
|
|
+ CloseMmalDeinterlace(filter);
|
|
+ return ret;
|
|
}
|
|
|
|
-static void output_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
-{
|
|
- filter_t *filter = (filter_t *)port->userdata;
|
|
- filter_sys_t *sys = filter->p_sys;
|
|
- picture_t *picture;
|
|
+vlc_module_begin()
|
|
+ set_shortname(N_("MMAL deinterlace"))
|
|
+ set_description(N_("MMAL-based deinterlace filter plugin"))
|
|
+ set_capability("video filter", 900)
|
|
+ set_category(CAT_VIDEO)
|
|
+ set_subcategory(SUBCAT_VIDEO_VFILTER)
|
|
+ set_callbacks(OpenMmalDeinterlace, CloseMmalDeinterlace)
|
|
+ add_shortcut("deinterlace")
|
|
+ add_bool(MMAL_DEINTERLACE_NO_QPU, false, MMAL_DEINTERLACE_NO_QPU_TEXT,
|
|
+ MMAL_DEINTERLACE_NO_QPU_LONGTEXT, true);
|
|
+ add_bool(MMAL_DEINTERLACE_ADV, false, MMAL_DEINTERLACE_ADV_TEXT,
|
|
+ MMAL_DEINTERLACE_ADV_LONGTEXT, true);
|
|
+ add_bool(MMAL_DEINTERLACE_FAST, false, MMAL_DEINTERLACE_FAST_TEXT,
|
|
+ MMAL_DEINTERLACE_FAST_LONGTEXT, true);
|
|
+ add_bool(MMAL_DEINTERLACE_NONE, false, MMAL_DEINTERLACE_NONE_TEXT,
|
|
+ MMAL_DEINTERLACE_NONE_LONGTEXT, true);
|
|
+ add_bool(MMAL_DEINTERLACE_HALF_RATE, false, MMAL_DEINTERLACE_HALF_RATE_TEXT,
|
|
+ MMAL_DEINTERLACE_HALF_RATE_LONGTEXT, true);
|
|
+ add_bool(MMAL_DEINTERLACE_FULL_RATE, false, MMAL_DEINTERLACE_FULL_RATE_TEXT,
|
|
+ MMAL_DEINTERLACE_FULL_RATE_LONGTEXT, true);
|
|
+
|
|
+vlc_module_end()
|
|
|
|
- if (buffer->cmd == 0) {
|
|
- if (buffer->length > 0) {
|
|
- atomic_store(&sys->started, true);
|
|
- mmal_queue_put(sys->filtered_pictures, buffer);
|
|
- picture = (picture_t *)buffer->user_data;
|
|
- } else {
|
|
- picture = (picture_t *)buffer->user_data;
|
|
- picture_Release(picture);
|
|
- }
|
|
|
|
- atomic_fetch_sub(&sys->output_in_transit, 1);
|
|
- vlc_sem_post(&sys->sem);
|
|
- } else if (buffer->cmd == MMAL_EVENT_FORMAT_CHANGED) {
|
|
- msg_Warn(filter, "MMAL_EVENT_FORMAT_CHANGED seen but not handled");
|
|
- mmal_buffer_header_release(buffer);
|
|
- } else {
|
|
- mmal_buffer_header_release(buffer);
|
|
- }
|
|
-}
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/mmal_avcodec.c
|
|
@@ -0,0 +1,2275 @@
|
|
+/*****************************************************************************
|
|
+ * video.c: video decoder using the libavcodec library
|
|
+ *****************************************************************************
|
|
+ * Copyright (C) 1999-2001 VLC authors and VideoLAN
|
|
+ * $Id$
|
|
+ *
|
|
+ * Authors: Laurent Aimar <fenrir@via.ecp.fr>
|
|
+ * Gildas Bazin <gbazin@videolan.org>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU Lesser General Public License as published by
|
|
+ * the Free Software Foundation; either version 2.1 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU Lesser General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Lesser General Public License
|
|
+ * along with this program; if not, write to the Free Software Foundation,
|
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
+ *****************************************************************************/
|
|
+
|
|
+/*****************************************************************************
|
|
+ * Preamble
|
|
+ *****************************************************************************/
|
|
+#ifdef HAVE_CONFIG_H
|
|
+# include "config.h"
|
|
+#endif
|
|
+
|
|
+#include <vlc_plugin.h>
|
|
+#include <vlc_common.h>
|
|
+#include <vlc_codec.h>
|
|
+#include <vlc_avcodec.h>
|
|
+#include <vlc_cpu.h>
|
|
+#include <vlc_atomic.h>
|
|
+#include <assert.h>
|
|
+
|
|
+#include "../../codec/avcodec/avcommon.h"
|
|
+
|
|
+#include <interface/mmal/mmal.h>
|
|
+
|
|
+#include <libavcodec/avcodec.h>
|
|
+#include <libavutil/mem.h>
|
|
+#include <libavutil/pixdesc.h>
|
|
+#include <libavcodec/rpi_zc.h>
|
|
+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
|
|
+#include <libavutil/mastering_display_metadata.h>
|
|
+#endif
|
|
+
|
|
+#include "mmal_picture.h"
|
|
+
|
|
+#define TRACE_ALL 0
|
|
+
|
|
+#define AVPROVIDER(lib) ((lib##_VERSION_MICRO < 100) ? "libav" : "ffmpeg")
|
|
+
|
|
+#ifdef HAVE_LIBAVCODEC_AVCODEC_H
|
|
+#include <libavcodec/avcodec.h>
|
|
+
|
|
+/* LIBAVCODEC_VERSION_CHECK checks for the right version of libav and FFmpeg
|
|
+ * a is the major version
|
|
+ * b and c the minor and micro versions of libav
|
|
+ * d and e the minor and micro versions of FFmpeg */
|
|
+#define LIBAVCODEC_VERSION_CHECK( a, b, c, d, e ) \
|
|
+ ( (LIBAVCODEC_VERSION_MICRO < 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \
|
|
+ (LIBAVCODEC_VERSION_MICRO >= 100 && LIBAVCODEC_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) )
|
|
+
|
|
+#ifndef AV_CODEC_FLAG_OUTPUT_CORRUPT
|
|
+# define AV_CODEC_FLAG_OUTPUT_CORRUPT CODEC_FLAG_OUTPUT_CORRUPT
|
|
+#endif
|
|
+#ifndef AV_CODEC_FLAG_GRAY
|
|
+# define AV_CODEC_FLAG_GRAY CODEC_FLAG_GRAY
|
|
+#endif
|
|
+#ifndef AV_CODEC_FLAG_DR1
|
|
+# define AV_CODEC_FLAG_DR1 CODEC_FLAG_DR1
|
|
+#endif
|
|
+#ifndef AV_CODEC_FLAG_DELAY
|
|
+# define AV_CODEC_FLAG_DELAY CODEC_FLAG_DELAY
|
|
+#endif
|
|
+#ifndef AV_CODEC_FLAG2_FAST
|
|
+# define AV_CODEC_FLAG2_FAST CODEC_FLAG2_FAST
|
|
+#endif
|
|
+#ifndef FF_INPUT_BUFFER_PADDING_SIZE
|
|
+# define FF_INPUT_BUFFER_PADDING_SIZE AV_INPUT_BUFFER_PADDING_SIZE
|
|
+#endif
|
|
+#ifndef AV_CODEC_FLAG_INTERLACED_DCT
|
|
+# define AV_CODEC_FLAG_INTERLACED_DCT CODEC_FLAG_INTERLACED_DCT
|
|
+#endif
|
|
+#ifndef AV_CODEC_FLAG_INTERLACED_ME
|
|
+# define AV_CODEC_FLAG_INTERLACED_ME CODEC_FLAG_INTERLACED_ME
|
|
+#endif
|
|
+#ifndef AV_CODEC_FLAG_GLOBAL_HEADER
|
|
+# define AV_CODEC_FLAG_GLOBAL_HEADER CODEC_FLAG_GLOBAL_HEADER
|
|
+#endif
|
|
+#ifndef AV_CODEC_FLAG_LOW_DELAY
|
|
+# define AV_CODEC_FLAG_LOW_DELAY CODEC_FLAG_LOW_DELAY
|
|
+#endif
|
|
+#ifndef AV_CODEC_CAP_SMALL_LAST_FRAME
|
|
+# define AV_CODEC_CAP_SMALL_LAST_FRAME CODEC_CAP_SMALL_LAST_FRAME
|
|
+#endif
|
|
+#ifndef AV_INPUT_BUFFER_MIN_SIZE
|
|
+# define AV_INPUT_BUFFER_MIN_SIZE FF_MIN_BUFFER_SIZE
|
|
+#endif
|
|
+#ifndef FF_MAX_B_FRAMES
|
|
+# define FF_MAX_B_FRAMES 16 // FIXME: remove this
|
|
+#endif
|
|
+
|
|
+#endif /* HAVE_LIBAVCODEC_AVCODEC_H */
|
|
+
|
|
+#ifdef HAVE_LIBAVUTIL_AVUTIL_H
|
|
+# include <libavutil/avutil.h>
|
|
+
|
|
+/* LIBAVUTIL_VERSION_CHECK checks for the right version of libav and FFmpeg
|
|
+ * a is the major version
|
|
+ * b and c the minor and micro versions of libav
|
|
+ * d and e the minor and micro versions of FFmpeg */
|
|
+#define LIBAVUTIL_VERSION_CHECK( a, b, c, d, e ) \
|
|
+ ( (LIBAVUTIL_VERSION_MICRO < 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \
|
|
+ (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) )
|
|
+
|
|
+#if !LIBAVUTIL_VERSION_CHECK( 52, 11, 0, 32, 100 )
|
|
+# define AV_PIX_FMT_FLAG_HWACCEL PIX_FMT_HWACCEL
|
|
+#endif
|
|
+
|
|
+#endif /* HAVE_LIBAVUTIL_AVUTIL_H */
|
|
+
|
|
+#if LIBAVUTIL_VERSION_MAJOR >= 55
|
|
+# define FF_API_AUDIOCONVERT 1
|
|
+#endif
|
|
+
|
|
+/* libavutil/pixfmt.h */
|
|
+#ifndef PixelFormat
|
|
+# define PixelFormat AVPixelFormat
|
|
+#endif
|
|
+
|
|
+#ifdef HAVE_LIBAVFORMAT_AVFORMAT_H
|
|
+# include <libavformat/avformat.h>
|
|
+
|
|
+#define LIBAVFORMAT_VERSION_CHECK( a, b, c, d, e ) \
|
|
+ ( (LIBAVFORMAT_VERSION_MICRO < 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, b, c ) ) || \
|
|
+ (LIBAVFORMAT_VERSION_MICRO >= 100 && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT( a, d, e ) ) )
|
|
+
|
|
+#endif
|
|
+
|
|
+/*****************************************************************************
|
|
+ * Codec fourcc -> libavcodec Codec_id mapping
|
|
+ * Sorted by AVCodecID enumeration order
|
|
+ *****************************************************************************/
|
|
+struct vlc_avcodec_fourcc
|
|
+{
|
|
+ vlc_fourcc_t i_fourcc;
|
|
+ unsigned i_codec;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Video Codecs
|
|
+ */
|
|
+static const struct vlc_avcodec_fourcc video_codecs[] =
|
|
+{
|
|
+ { VLC_CODEC_MP1V, AV_CODEC_ID_MPEG1VIDEO },
|
|
+ { VLC_CODEC_MP2V, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */
|
|
+ { VLC_CODEC_MPGV, AV_CODEC_ID_MPEG2VIDEO }, /* prefer MPEG2 over MPEG1 */
|
|
+ /* AV_CODEC_ID_MPEG2VIDEO_XVMC */
|
|
+ { VLC_CODEC_H261, AV_CODEC_ID_H261 },
|
|
+ { VLC_CODEC_H263, AV_CODEC_ID_H263 },
|
|
+ { VLC_CODEC_RV10, AV_CODEC_ID_RV10 },
|
|
+ { VLC_CODEC_RV13, AV_CODEC_ID_RV10 },
|
|
+ { VLC_CODEC_RV20, AV_CODEC_ID_RV20 },
|
|
+ { VLC_CODEC_MJPG, AV_CODEC_ID_MJPEG },
|
|
+ { VLC_CODEC_MJPGB, AV_CODEC_ID_MJPEGB },
|
|
+ { VLC_CODEC_LJPG, AV_CODEC_ID_LJPEG },
|
|
+ { VLC_CODEC_SP5X, AV_CODEC_ID_SP5X },
|
|
+ { VLC_CODEC_JPEGLS, AV_CODEC_ID_JPEGLS },
|
|
+ { VLC_CODEC_MP4V, AV_CODEC_ID_MPEG4 },
|
|
+ /* AV_CODEC_ID_RAWVIDEO */
|
|
+ { VLC_CODEC_DIV1, AV_CODEC_ID_MSMPEG4V1 },
|
|
+ { VLC_CODEC_DIV2, AV_CODEC_ID_MSMPEG4V2 },
|
|
+ { VLC_CODEC_DIV3, AV_CODEC_ID_MSMPEG4V3 },
|
|
+ { VLC_CODEC_WMV1, AV_CODEC_ID_WMV1 },
|
|
+ { VLC_CODEC_WMV2, AV_CODEC_ID_WMV2 },
|
|
+ { VLC_CODEC_H263P, AV_CODEC_ID_H263P },
|
|
+ { VLC_CODEC_H263I, AV_CODEC_ID_H263I },
|
|
+ { VLC_CODEC_FLV1, AV_CODEC_ID_FLV1 },
|
|
+ { VLC_CODEC_SVQ1, AV_CODEC_ID_SVQ1 },
|
|
+ { VLC_CODEC_SVQ3, AV_CODEC_ID_SVQ3 },
|
|
+ { VLC_CODEC_DV, AV_CODEC_ID_DVVIDEO },
|
|
+ { VLC_CODEC_HUFFYUV, AV_CODEC_ID_HUFFYUV },
|
|
+ { VLC_CODEC_CYUV, AV_CODEC_ID_CYUV },
|
|
+ { VLC_CODEC_H264, AV_CODEC_ID_H264 },
|
|
+ { VLC_CODEC_INDEO3, AV_CODEC_ID_INDEO3 },
|
|
+ { VLC_CODEC_VP3, AV_CODEC_ID_VP3 },
|
|
+ { VLC_CODEC_THEORA, AV_CODEC_ID_THEORA },
|
|
+#if ( !defined( WORDS_BIGENDIAN ) )
|
|
+ /* Asus Video (Another thing that doesn't work on PPC) */
|
|
+ { VLC_CODEC_ASV1, AV_CODEC_ID_ASV1 },
|
|
+ { VLC_CODEC_ASV2, AV_CODEC_ID_ASV2 },
|
|
+#endif
|
|
+ { VLC_CODEC_FFV1, AV_CODEC_ID_FFV1 },
|
|
+ { VLC_CODEC_4XM, AV_CODEC_ID_4XM },
|
|
+ { VLC_CODEC_VCR1, AV_CODEC_ID_VCR1 },
|
|
+ { VLC_CODEC_CLJR, AV_CODEC_ID_CLJR },
|
|
+ { VLC_CODEC_MDEC, AV_CODEC_ID_MDEC },
|
|
+ { VLC_CODEC_ROQ, AV_CODEC_ID_ROQ },
|
|
+ { VLC_CODEC_INTERPLAY, AV_CODEC_ID_INTERPLAY_VIDEO },
|
|
+ { VLC_CODEC_XAN_WC3, AV_CODEC_ID_XAN_WC3 },
|
|
+ { VLC_CODEC_XAN_WC4, AV_CODEC_ID_XAN_WC4 },
|
|
+ { VLC_CODEC_RPZA, AV_CODEC_ID_RPZA },
|
|
+ { VLC_CODEC_CINEPAK, AV_CODEC_ID_CINEPAK },
|
|
+ { VLC_CODEC_WS_VQA, AV_CODEC_ID_WS_VQA },
|
|
+ { VLC_CODEC_MSRLE, AV_CODEC_ID_MSRLE },
|
|
+ { VLC_CODEC_MSVIDEO1, AV_CODEC_ID_MSVIDEO1 },
|
|
+ { VLC_CODEC_IDCIN, AV_CODEC_ID_IDCIN },
|
|
+ { VLC_CODEC_8BPS, AV_CODEC_ID_8BPS },
|
|
+ { VLC_CODEC_SMC, AV_CODEC_ID_SMC },
|
|
+ { VLC_CODEC_FLIC, AV_CODEC_ID_FLIC },
|
|
+ { VLC_CODEC_TRUEMOTION1, AV_CODEC_ID_TRUEMOTION1 },
|
|
+ { VLC_CODEC_VMDVIDEO, AV_CODEC_ID_VMDVIDEO },
|
|
+ { VLC_CODEC_LCL_MSZH, AV_CODEC_ID_MSZH },
|
|
+ { VLC_CODEC_LCL_ZLIB, AV_CODEC_ID_ZLIB },
|
|
+ { VLC_CODEC_QTRLE, AV_CODEC_ID_QTRLE },
|
|
+ { VLC_CODEC_TSCC, AV_CODEC_ID_TSCC },
|
|
+ { VLC_CODEC_ULTI, AV_CODEC_ID_ULTI },
|
|
+ { VLC_CODEC_QDRAW, AV_CODEC_ID_QDRAW },
|
|
+ { VLC_CODEC_VIXL, AV_CODEC_ID_VIXL },
|
|
+ { VLC_CODEC_QPEG, AV_CODEC_ID_QPEG },
|
|
+ { VLC_CODEC_PNG, AV_CODEC_ID_PNG },
|
|
+ { VLC_CODEC_PPM, AV_CODEC_ID_PPM },
|
|
+ /* AV_CODEC_ID_PBM */
|
|
+ { VLC_CODEC_PGM, AV_CODEC_ID_PGM },
|
|
+ { VLC_CODEC_PGMYUV, AV_CODEC_ID_PGMYUV },
|
|
+ { VLC_CODEC_PAM, AV_CODEC_ID_PAM },
|
|
+ { VLC_CODEC_FFVHUFF, AV_CODEC_ID_FFVHUFF },
|
|
+ { VLC_CODEC_RV30, AV_CODEC_ID_RV30 },
|
|
+ { VLC_CODEC_RV40, AV_CODEC_ID_RV40 },
|
|
+ { VLC_CODEC_VC1, AV_CODEC_ID_VC1 },
|
|
+ { VLC_CODEC_WMVA, AV_CODEC_ID_VC1 },
|
|
+ { VLC_CODEC_WMV3, AV_CODEC_ID_WMV3 },
|
|
+ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3 },
|
|
+ { VLC_CODEC_LOCO, AV_CODEC_ID_LOCO },
|
|
+ { VLC_CODEC_WNV1, AV_CODEC_ID_WNV1 },
|
|
+ { VLC_CODEC_AASC, AV_CODEC_ID_AASC },
|
|
+ { VLC_CODEC_INDEO2, AV_CODEC_ID_INDEO2 },
|
|
+ { VLC_CODEC_FRAPS, AV_CODEC_ID_FRAPS },
|
|
+ { VLC_CODEC_TRUEMOTION2, AV_CODEC_ID_TRUEMOTION2 },
|
|
+ { VLC_CODEC_BMP, AV_CODEC_ID_BMP },
|
|
+ { VLC_CODEC_CSCD, AV_CODEC_ID_CSCD },
|
|
+ { VLC_CODEC_MMVIDEO, AV_CODEC_ID_MMVIDEO },
|
|
+ { VLC_CODEC_ZMBV, AV_CODEC_ID_ZMBV },
|
|
+ { VLC_CODEC_AVS, AV_CODEC_ID_AVS },
|
|
+ { VLC_CODEC_SMACKVIDEO, AV_CODEC_ID_SMACKVIDEO },
|
|
+ { VLC_CODEC_NUV, AV_CODEC_ID_NUV },
|
|
+ { VLC_CODEC_KMVC, AV_CODEC_ID_KMVC },
|
|
+ { VLC_CODEC_FLASHSV, AV_CODEC_ID_FLASHSV },
|
|
+ { VLC_CODEC_CAVS, AV_CODEC_ID_CAVS },
|
|
+ { VLC_CODEC_JPEG2000, AV_CODEC_ID_JPEG2000 },
|
|
+ { VLC_CODEC_VMNC, AV_CODEC_ID_VMNC },
|
|
+ { VLC_CODEC_VP5, AV_CODEC_ID_VP5 },
|
|
+ { VLC_CODEC_VP6, AV_CODEC_ID_VP6 },
|
|
+ { VLC_CODEC_VP6F, AV_CODEC_ID_VP6F },
|
|
+ { VLC_CODEC_TARGA, AV_CODEC_ID_TARGA },
|
|
+ { VLC_CODEC_DSICINVIDEO, AV_CODEC_ID_DSICINVIDEO },
|
|
+ { VLC_CODEC_TIERTEXSEQVIDEO, AV_CODEC_ID_TIERTEXSEQVIDEO },
|
|
+ { VLC_CODEC_TIFF, AV_CODEC_ID_TIFF },
|
|
+ { VLC_CODEC_GIF, AV_CODEC_ID_GIF },
|
|
+ { VLC_CODEC_DXA, AV_CODEC_ID_DXA },
|
|
+ { VLC_CODEC_DNXHD, AV_CODEC_ID_DNXHD },
|
|
+ { VLC_CODEC_THP, AV_CODEC_ID_THP },
|
|
+ { VLC_CODEC_SGI, AV_CODEC_ID_SGI },
|
|
+ { VLC_CODEC_C93, AV_CODEC_ID_C93 },
|
|
+ { VLC_CODEC_BETHSOFTVID, AV_CODEC_ID_BETHSOFTVID },
|
|
+ /* AV_CODEC_ID_PTX */
|
|
+ { VLC_CODEC_TXD, AV_CODEC_ID_TXD },
|
|
+ { VLC_CODEC_VP6A, AV_CODEC_ID_VP6A },
|
|
+ { VLC_CODEC_AMV, AV_CODEC_ID_AMV },
|
|
+ { VLC_CODEC_VB, AV_CODEC_ID_VB },
|
|
+ { VLC_CODEC_PCX, AV_CODEC_ID_PCX },
|
|
+ /* AV_CODEC_ID_SUNRAST */
|
|
+ { VLC_CODEC_INDEO4, AV_CODEC_ID_INDEO4 },
|
|
+ { VLC_CODEC_INDEO5, AV_CODEC_ID_INDEO5 },
|
|
+ { VLC_CODEC_MIMIC, AV_CODEC_ID_MIMIC },
|
|
+ { VLC_CODEC_RL2, AV_CODEC_ID_RL2 },
|
|
+ { VLC_CODEC_ESCAPE124, AV_CODEC_ID_ESCAPE124 },
|
|
+ { VLC_CODEC_DIRAC, AV_CODEC_ID_DIRAC },
|
|
+ { VLC_CODEC_BFI, AV_CODEC_ID_BFI },
|
|
+ { VLC_CODEC_CMV, AV_CODEC_ID_CMV },
|
|
+ { VLC_CODEC_MOTIONPIXELS, AV_CODEC_ID_MOTIONPIXELS },
|
|
+ { VLC_CODEC_TGV, AV_CODEC_ID_TGV },
|
|
+ { VLC_CODEC_TGQ, AV_CODEC_ID_TGQ },
|
|
+ { VLC_CODEC_TQI, AV_CODEC_ID_TQI },
|
|
+ { VLC_CODEC_AURA, AV_CODEC_ID_AURA },
|
|
+ /* AV_CODEC_ID_AURA2 */
|
|
+ /* AV_CODEC_ID_V210X */
|
|
+ { VLC_CODEC_TMV, AV_CODEC_ID_TMV },
|
|
+ { VLC_CODEC_V210, AV_CODEC_ID_V210 },
|
|
+ /* AV_CODEC_ID_DPX */
|
|
+ { VLC_CODEC_MAD, AV_CODEC_ID_MAD },
|
|
+ { VLC_CODEC_FRWU, AV_CODEC_ID_FRWU },
|
|
+ { VLC_CODEC_FLASHSV2, AV_CODEC_ID_FLASHSV2 },
|
|
+ /* AV_CODEC_ID_CDGRAPHICS */
|
|
+ /* AV_CODEC_ID_R210 */
|
|
+ { VLC_CODEC_ANM, AV_CODEC_ID_ANM },
|
|
+ { VLC_CODEC_BINKVIDEO, AV_CODEC_ID_BINKVIDEO },
|
|
+ /* AV_CODEC_ID_IFF_ILBM */
|
|
+ /* AV_CODEC_ID_IFF_BYTERUN1 */
|
|
+ { VLC_CODEC_KGV1, AV_CODEC_ID_KGV1 },
|
|
+ { VLC_CODEC_YOP, AV_CODEC_ID_YOP },
|
|
+ { VLC_CODEC_VP8, AV_CODEC_ID_VP8 },
|
|
+ /* AV_CODEC_ID_PICTOR */
|
|
+ /* AV_CODEC_ID_ANSI */
|
|
+ /* AV_CODEC_ID_A64_MULTI */
|
|
+ /* AV_CODEC_ID_A64_MULTI5 */
|
|
+ /* AV_CODEC_ID_R10K */
|
|
+ { VLC_CODEC_MXPEG, AV_CODEC_ID_MXPEG },
|
|
+ { VLC_CODEC_LAGARITH, AV_CODEC_ID_LAGARITH },
|
|
+ { VLC_CODEC_PRORES, AV_CODEC_ID_PRORES },
|
|
+ { VLC_CODEC_JV, AV_CODEC_ID_JV },
|
|
+ { VLC_CODEC_DFA, AV_CODEC_ID_DFA },
|
|
+ { VLC_CODEC_WMVP, AV_CODEC_ID_WMV3IMAGE },
|
|
+ { VLC_CODEC_WMVP2, AV_CODEC_ID_VC1IMAGE },
|
|
+ { VLC_CODEC_UTVIDEO, AV_CODEC_ID_UTVIDEO },
|
|
+ { VLC_CODEC_BMVVIDEO, AV_CODEC_ID_BMV_VIDEO },
|
|
+ { VLC_CODEC_VBLE, AV_CODEC_ID_VBLE },
|
|
+ { VLC_CODEC_DXTORY, AV_CODEC_ID_DXTORY },
|
|
+ /* AV_CODEC_ID_V410 */
|
|
+ /* AV_CODEC_ID_XWD */
|
|
+ { VLC_CODEC_CDXL, AV_CODEC_ID_CDXL },
|
|
+ /* AV_CODEC_ID_XBM */
|
|
+ /* AV_CODEC_ID_ZEROCODEC */
|
|
+ { VLC_CODEC_MSS1, AV_CODEC_ID_MSS1 },
|
|
+ { VLC_CODEC_MSA1, AV_CODEC_ID_MSA1 },
|
|
+ { VLC_CODEC_TSC2, AV_CODEC_ID_TSCC2 },
|
|
+ { VLC_CODEC_MTS2, AV_CODEC_ID_MTS2 },
|
|
+ { VLC_CODEC_CLLC, AV_CODEC_ID_CLLC },
|
|
+ { VLC_CODEC_MSS2, AV_CODEC_ID_MSS2 },
|
|
+ { VLC_CODEC_VP9, AV_CODEC_ID_VP9 },
|
|
+#if LIBAVCODEC_VERSION_CHECK( 57, 26, 0, 83, 101 )
|
|
+ { VLC_CODEC_AV1, AV_CODEC_ID_AV1 },
|
|
+#endif
|
|
+ { VLC_CODEC_ICOD, AV_CODEC_ID_AIC },
|
|
+ /* AV_CODEC_ID_ESCAPE130 */
|
|
+ { VLC_CODEC_G2M4, AV_CODEC_ID_G2M },
|
|
+ { VLC_CODEC_G2M2, AV_CODEC_ID_G2M },
|
|
+ { VLC_CODEC_G2M3, AV_CODEC_ID_G2M },
|
|
+ /* AV_CODEC_ID_WEBP */
|
|
+ { VLC_CODEC_HNM4_VIDEO, AV_CODEC_ID_HNM4_VIDEO },
|
|
+ { VLC_CODEC_HEVC, AV_CODEC_ID_HEVC },
|
|
+
|
|
+ { VLC_CODEC_FIC , AV_CODEC_ID_FIC },
|
|
+ /* AV_CODEC_ID_ALIAS_PIX */
|
|
+ /* AV_CODEC_ID_BRENDER_PIX */
|
|
+ /* AV_CODEC_ID_PAF_VIDEO */
|
|
+ /* AV_CODEC_ID_EXR */
|
|
+
|
|
+ { VLC_CODEC_VP7 , AV_CODEC_ID_VP7 },
|
|
+ /* AV_CODEC_ID_SANM */
|
|
+ /* AV_CODEC_ID_SGIRLE */
|
|
+ /* AV_CODEC_ID_MVC1 */
|
|
+ /* AV_CODEC_ID_MVC2 */
|
|
+ { VLC_CODEC_HQX, AV_CODEC_ID_HQX },
|
|
+
|
|
+ { VLC_CODEC_TDSC, AV_CODEC_ID_TDSC },
|
|
+
|
|
+ { VLC_CODEC_HQ_HQA, AV_CODEC_ID_HQ_HQA },
|
|
+
|
|
+ { VLC_CODEC_HAP, AV_CODEC_ID_HAP },
|
|
+ /* AV_CODEC_ID_DDS */
|
|
+
|
|
+ { VLC_CODEC_DXV, AV_CODEC_ID_DXV },
|
|
+
|
|
+ /* ffmpeg only: AV_CODEC_ID_BRENDER_PIX */
|
|
+ /* ffmpeg only: AV_CODEC_ID_Y41P */
|
|
+ /* ffmpeg only: AV_CODEC_ID_EXR */
|
|
+ /* ffmpeg only: AV_CODEC_ID_AVRP */
|
|
+ /* ffmpeg only: AV_CODEC_ID_012V */
|
|
+ /* ffmpeg only: AV_CODEC_ID_AVUI */
|
|
+ /* ffmpeg only: AV_CODEC_ID_AYUV */
|
|
+ /* ffmpeg only: AV_CODEC_ID_TARGA_Y216 */
|
|
+ /* ffmpeg only: AV_CODEC_ID_V308 */
|
|
+ /* ffmpeg only: AV_CODEC_ID_V408 */
|
|
+ /* ffmpeg only: AV_CODEC_ID_YUV4 */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SANM */
|
|
+ /* ffmpeg only: AV_CODEC_ID_PAF_VIDEO */
|
|
+ /* ffmpeg only: AV_CODEC_ID_AVRN */
|
|
+ /* ffmpeg only: AV_CODEC_ID_CPIA */
|
|
+ /* ffmpeg only: AV_CODEC_ID_XFACE */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SGIRLE */
|
|
+ /* ffmpeg only: AV_CODEC_ID_MVC1 */
|
|
+ /* ffmpeg only: AV_CODEC_ID_MVC2 */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SNOW */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SMVJPEG */
|
|
+
|
|
+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 24, 102 )
|
|
+ { VLC_CODEC_CINEFORM, AV_CODEC_ID_CFHD },
|
|
+#endif
|
|
+
|
|
+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 70, 100 )
|
|
+ { VLC_CODEC_PIXLET, AV_CODEC_ID_PIXLET },
|
|
+#endif
|
|
+
|
|
+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 101 )
|
|
+ { VLC_CODEC_SPEEDHQ, AV_CODEC_ID_SPEEDHQ },
|
|
+#endif
|
|
+
|
|
+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 79, 100 )
|
|
+ { VLC_CODEC_FMVC, AV_CODEC_ID_FMVC },
|
|
+#endif
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Audio Codecs
|
|
+ */
|
|
+static const struct vlc_avcodec_fourcc audio_codecs[] =
|
|
+{
|
|
+ /* PCM */
|
|
+ { VLC_CODEC_S16L, AV_CODEC_ID_PCM_S16LE },
|
|
+ { VLC_CODEC_S16B, AV_CODEC_ID_PCM_S16BE },
|
|
+ { VLC_CODEC_U16L, AV_CODEC_ID_PCM_U16LE },
|
|
+ { VLC_CODEC_U16B, AV_CODEC_ID_PCM_U16BE },
|
|
+ { VLC_CODEC_S8, AV_CODEC_ID_PCM_S8 },
|
|
+ { VLC_CODEC_U8, AV_CODEC_ID_PCM_U8 },
|
|
+ { VLC_CODEC_MULAW, AV_CODEC_ID_PCM_MULAW },
|
|
+ { VLC_CODEC_ALAW, AV_CODEC_ID_PCM_ALAW },
|
|
+ { VLC_CODEC_S32L, AV_CODEC_ID_PCM_S32LE },
|
|
+ { VLC_CODEC_S32B, AV_CODEC_ID_PCM_S32BE },
|
|
+ { VLC_CODEC_U32L, AV_CODEC_ID_PCM_U32LE },
|
|
+ { VLC_CODEC_U32B, AV_CODEC_ID_PCM_U32BE },
|
|
+ { VLC_CODEC_S24L, AV_CODEC_ID_PCM_S24LE },
|
|
+ { VLC_CODEC_S24B, AV_CODEC_ID_PCM_S24BE },
|
|
+ { VLC_CODEC_U24L, AV_CODEC_ID_PCM_U24LE },
|
|
+ { VLC_CODEC_U24B, AV_CODEC_ID_PCM_U24BE },
|
|
+ { VLC_CODEC_S24DAUD, AV_CODEC_ID_PCM_S24DAUD },
|
|
+ /* AV_CODEC_ID_PCM_ZORK */
|
|
+ { VLC_CODEC_S16L_PLANAR, AV_CODEC_ID_PCM_S16LE_PLANAR },
|
|
+ /* AV_CODEC_ID_PCM_DVD */
|
|
+ { VLC_CODEC_F32B, AV_CODEC_ID_PCM_F32BE },
|
|
+ { VLC_CODEC_F32L, AV_CODEC_ID_PCM_F32LE },
|
|
+ { VLC_CODEC_F64B, AV_CODEC_ID_PCM_F64BE },
|
|
+ { VLC_CODEC_F64L, AV_CODEC_ID_PCM_F64LE },
|
|
+ { VLC_CODEC_BD_LPCM, AV_CODEC_ID_PCM_BLURAY },
|
|
+ /* AV_CODEC_ID_PCM_LXF */
|
|
+ /* AV_CODEC_ID_S302M */
|
|
+ /* AV_CODEC_ID_PCM_S8_PLANAR */
|
|
+ /* AV_CODEC_ID_PCM_S24LE_PLANAR */
|
|
+ /* AV_CODEC_ID_PCM_S32LE_PLANAR */
|
|
+ /* ffmpeg only: AV_CODEC_ID_PCM_S16BE_PLANAR */
|
|
+
|
|
+ /* ADPCM */
|
|
+ { VLC_CODEC_ADPCM_IMA_QT, AV_CODEC_ID_ADPCM_IMA_QT },
|
|
+ { VLC_CODEC_ADPCM_IMA_WAV, AV_CODEC_ID_ADPCM_IMA_WAV },
|
|
+ /* AV_CODEC_ID_ADPCM_IMA_DK3 */
|
|
+ /* AV_CODEC_ID_ADPCM_IMA_DK4 */
|
|
+ { VLC_CODEC_ADPCM_IMA_WS, AV_CODEC_ID_ADPCM_IMA_WS },
|
|
+ /* AV_CODEC_ID_ADPCM_IMA_SMJPEG */
|
|
+ { VLC_CODEC_ADPCM_MS, AV_CODEC_ID_ADPCM_MS },
|
|
+ { VLC_CODEC_ADPCM_4XM, AV_CODEC_ID_ADPCM_4XM },
|
|
+ { VLC_CODEC_ADPCM_XA, AV_CODEC_ID_ADPCM_XA },
|
|
+ { VLC_CODEC_ADPCM_ADX, AV_CODEC_ID_ADPCM_ADX },
|
|
+ { VLC_CODEC_ADPCM_EA, AV_CODEC_ID_ADPCM_EA },
|
|
+ { VLC_CODEC_ADPCM_G726, AV_CODEC_ID_ADPCM_G726 },
|
|
+ { VLC_CODEC_ADPCM_CREATIVE, AV_CODEC_ID_ADPCM_CT },
|
|
+ { VLC_CODEC_ADPCM_SWF, AV_CODEC_ID_ADPCM_SWF },
|
|
+ { VLC_CODEC_ADPCM_YAMAHA, AV_CODEC_ID_ADPCM_YAMAHA },
|
|
+ { VLC_CODEC_ADPCM_SBPRO_4, AV_CODEC_ID_ADPCM_SBPRO_4 },
|
|
+ { VLC_CODEC_ADPCM_SBPRO_3, AV_CODEC_ID_ADPCM_SBPRO_3 },
|
|
+ { VLC_CODEC_ADPCM_SBPRO_2, AV_CODEC_ID_ADPCM_SBPRO_2 },
|
|
+ { VLC_CODEC_ADPCM_THP, AV_CODEC_ID_ADPCM_THP },
|
|
+ { VLC_CODEC_ADPCM_IMA_AMV, AV_CODEC_ID_ADPCM_IMA_AMV },
|
|
+ { VLC_CODEC_ADPCM_EA_R1, AV_CODEC_ID_ADPCM_EA_R1 },
|
|
+ /* AV_CODEC_ID_ADPCM_EA_R3 */
|
|
+ /* AV_CODEC_ID_ADPCM_EA_R2 */
|
|
+ { VLC_CODEC_ADPCM_IMA_EA_SEAD, AV_CODEC_ID_ADPCM_IMA_EA_SEAD },
|
|
+ /* AV_CODEC_ID_ADPCM_IMA_EA_EACS */
|
|
+ /* AV_CODEC_ID_ADPCM_EA_XAS */
|
|
+ /* AV_CODEC_ID_ADPCM_EA_MAXIS_XA */
|
|
+ /* AV_CODEC_ID_ADPCM_IMA_ISS */
|
|
+ { VLC_CODEC_ADPCM_G722, AV_CODEC_ID_ADPCM_G722 },
|
|
+ { VLC_CODEC_ADPCM_IMA_APC, AV_CODEC_ID_ADPCM_IMA_APC },
|
|
+ /* ffmpeg only: AV_CODEC_ID_VIMA */
|
|
+ /* ffmpeg only: AV_CODEC_ID_ADPCM_AFC */
|
|
+ /* ffmpeg only: AV_CODEC_ID_ADPCM_IMA_OKI */
|
|
+ /* ffmpeg only: AV_CODEC_ID_ADPCM_DTK */
|
|
+ /* ffmpeg only: AV_CODEC_ID_ADPCM_IMA_RAD */
|
|
+ /* ffmpeg only: AV_CODEC_ID_ADPCM_G726LE */
|
|
+
|
|
+ /* AMR */
|
|
+ { VLC_CODEC_AMR_NB, AV_CODEC_ID_AMR_NB },
|
|
+ { VLC_CODEC_AMR_WB, AV_CODEC_ID_AMR_WB },
|
|
+
|
|
+ /* RealAudio */
|
|
+ { VLC_CODEC_RA_144, AV_CODEC_ID_RA_144 },
|
|
+ { VLC_CODEC_RA_288, AV_CODEC_ID_RA_288 },
|
|
+
|
|
+ /* DPCM */
|
|
+ { VLC_CODEC_ROQ_DPCM, AV_CODEC_ID_ROQ_DPCM },
|
|
+ { VLC_CODEC_INTERPLAY_DPCM, AV_CODEC_ID_INTERPLAY_DPCM },
|
|
+ /* AV_CODEC_ID_XAN_DPCM */
|
|
+ /* AV_CODEC_ID_SOL_DPCM */
|
|
+
|
|
+ /* audio codecs */
|
|
+ { VLC_CODEC_MPGA, AV_CODEC_ID_MP2 },
|
|
+ { VLC_CODEC_MP2, AV_CODEC_ID_MP2 },
|
|
+ { VLC_CODEC_MP3, AV_CODEC_ID_MP3 },
|
|
+ { VLC_CODEC_MP4A, AV_CODEC_ID_AAC },
|
|
+ { VLC_CODEC_A52, AV_CODEC_ID_AC3 },
|
|
+ { VLC_CODEC_DTS, AV_CODEC_ID_DTS },
|
|
+ { VLC_CODEC_VORBIS, AV_CODEC_ID_VORBIS },
|
|
+ { VLC_CODEC_DVAUDIO, AV_CODEC_ID_DVAUDIO },
|
|
+ { VLC_CODEC_WMA1, AV_CODEC_ID_WMAV1 },
|
|
+ { VLC_CODEC_WMA2, AV_CODEC_ID_WMAV2 },
|
|
+ { VLC_CODEC_MACE3, AV_CODEC_ID_MACE3 },
|
|
+ { VLC_CODEC_MACE6, AV_CODEC_ID_MACE6 },
|
|
+ { VLC_CODEC_VMDAUDIO, AV_CODEC_ID_VMDAUDIO },
|
|
+ { VLC_CODEC_FLAC, AV_CODEC_ID_FLAC },
|
|
+ /* AV_CODEC_ID_MP3ADU */
|
|
+ /* AV_CODEC_ID_MP3ON4 */
|
|
+ { VLC_CODEC_SHORTEN, AV_CODEC_ID_SHORTEN },
|
|
+ { VLC_CODEC_ALAC, AV_CODEC_ID_ALAC },
|
|
+ /* AV_CODEC_ID_WESTWOOD_SND1 */
|
|
+ { VLC_CODEC_GSM, AV_CODEC_ID_GSM },
|
|
+ { VLC_CODEC_QDM2, AV_CODEC_ID_QDM2 },
|
|
+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 100 )
|
|
+ { VLC_CODEC_QDMC, AV_CODEC_ID_QDMC },
|
|
+#endif
|
|
+ { VLC_CODEC_COOK, AV_CODEC_ID_COOK },
|
|
+ { VLC_CODEC_TRUESPEECH, AV_CODEC_ID_TRUESPEECH },
|
|
+ { VLC_CODEC_TTA, AV_CODEC_ID_TTA },
|
|
+ { VLC_CODEC_SMACKAUDIO, AV_CODEC_ID_SMACKAUDIO },
|
|
+ { VLC_CODEC_QCELP, AV_CODEC_ID_QCELP },
|
|
+ { VLC_CODEC_WAVPACK, AV_CODEC_ID_WAVPACK },
|
|
+ { VLC_CODEC_DSICINAUDIO, AV_CODEC_ID_DSICINAUDIO },
|
|
+ { VLC_CODEC_IMC, AV_CODEC_ID_IMC },
|
|
+ { VLC_CODEC_MUSEPACK7, AV_CODEC_ID_MUSEPACK7 },
|
|
+ { VLC_CODEC_MLP, AV_CODEC_ID_MLP },
|
|
+ { VLC_CODEC_GSM_MS, AV_CODEC_ID_GSM_MS },
|
|
+ { VLC_CODEC_ATRAC3, AV_CODEC_ID_ATRAC3 },
|
|
+ { VLC_CODEC_APE, AV_CODEC_ID_APE },
|
|
+ { VLC_CODEC_NELLYMOSER, AV_CODEC_ID_NELLYMOSER },
|
|
+ { VLC_CODEC_MUSEPACK8, AV_CODEC_ID_MUSEPACK8 },
|
|
+ { VLC_CODEC_SPEEX, AV_CODEC_ID_SPEEX },
|
|
+ { VLC_CODEC_WMAS, AV_CODEC_ID_WMAVOICE },
|
|
+ { VLC_CODEC_WMAP, AV_CODEC_ID_WMAPRO },
|
|
+ { VLC_CODEC_WMAL, AV_CODEC_ID_WMALOSSLESS },
|
|
+ { VLC_CODEC_ATRAC3P, AV_CODEC_ID_ATRAC3P },
|
|
+ { VLC_CODEC_EAC3, AV_CODEC_ID_EAC3 },
|
|
+ { VLC_CODEC_SIPR, AV_CODEC_ID_SIPR },
|
|
+ /* AV_CODEC_ID_MP1 */
|
|
+ { VLC_CODEC_TWINVQ, AV_CODEC_ID_TWINVQ },
|
|
+ { VLC_CODEC_TRUEHD, AV_CODEC_ID_TRUEHD },
|
|
+ { VLC_CODEC_ALS, AV_CODEC_ID_MP4ALS },
|
|
+ { VLC_CODEC_ATRAC1, AV_CODEC_ID_ATRAC1 },
|
|
+ { VLC_CODEC_BINKAUDIO_RDFT, AV_CODEC_ID_BINKAUDIO_RDFT },
|
|
+ { VLC_CODEC_BINKAUDIO_DCT, AV_CODEC_ID_BINKAUDIO_DCT },
|
|
+ { VLC_CODEC_MP4A, AV_CODEC_ID_AAC_LATM },
|
|
+ /* AV_CODEC_ID_QDMC */
|
|
+ /* AV_CODEC_ID_CELT */
|
|
+ { VLC_CODEC_G723_1, AV_CODEC_ID_G723_1 },
|
|
+ /* AV_CODEC_ID_G729 */
|
|
+ /* AV_CODEC_ID_8SVX_EXP */
|
|
+ /* AV_CODEC_ID_8SVX_FIB */
|
|
+ { VLC_CODEC_BMVAUDIO, AV_CODEC_ID_BMV_AUDIO },
|
|
+ { VLC_CODEC_RALF, AV_CODEC_ID_RALF },
|
|
+ { VLC_CODEC_INDEO_AUDIO, AV_CODEC_ID_IAC },
|
|
+ /* AV_CODEC_ID_ILBC */
|
|
+ { VLC_CODEC_OPUS, AV_CODEC_ID_OPUS },
|
|
+ /* AV_CODEC_ID_COMFORT_NOISE */
|
|
+ { VLC_CODEC_TAK, AV_CODEC_ID_TAK },
|
|
+ { VLC_CODEC_METASOUND, AV_CODEC_ID_METASOUND },
|
|
+ /* AV_CODEC_ID_PAF_AUDIO */
|
|
+ { VLC_CODEC_ON2AVC, AV_CODEC_ID_ON2AVC },
|
|
+
|
|
+ /* ffmpeg only: AV_CODEC_ID_FFWAVESYNTH */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SONIC */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SONIC_LS */
|
|
+ /* ffmpeg only: AV_CODEC_ID_PAF_AUDIO */
|
|
+ /* ffmpeg only: AV_CODEC_ID_EVRC */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SMV */
|
|
+};
|
|
+
|
|
+/* Subtitle streams */
|
|
+static const struct vlc_avcodec_fourcc spu_codecs[] =
|
|
+{
|
|
+ { VLC_CODEC_SPU, AV_CODEC_ID_DVD_SUBTITLE },
|
|
+ { VLC_CODEC_DVBS, AV_CODEC_ID_DVB_SUBTITLE },
|
|
+ { VLC_CODEC_SUBT, AV_CODEC_ID_TEXT },
|
|
+ { VLC_CODEC_XSUB, AV_CODEC_ID_XSUB },
|
|
+ { VLC_CODEC_SSA, AV_CODEC_ID_SSA },
|
|
+ /* AV_CODEC_ID_MOV_TEXT */
|
|
+ { VLC_CODEC_BD_PG, AV_CODEC_ID_HDMV_PGS_SUBTITLE },
|
|
+#if LIBAVCODEC_VERSION_CHECK( 57, 999, 999, 71, 100 )
|
|
+ { VLC_CODEC_BD_TEXT, AV_CODEC_ID_HDMV_TEXT_SUBTITLE },
|
|
+#endif
|
|
+ { VLC_CODEC_TELETEXT, AV_CODEC_ID_DVB_TELETEXT },
|
|
+ /* AV_CODEC_ID_SRT */
|
|
+ /* ffmpeg only: AV_CODEC_ID_MICRODVD */
|
|
+ /* ffmpeg only: AV_CODEC_ID_EIA_608 */
|
|
+ /* ffmpeg only: AV_CODEC_ID_JACOSUB */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SAMI */
|
|
+ /* ffmpeg only: AV_CODEC_ID_REALTEXT */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SUBVIEWER1 */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SUBVIEWER */
|
|
+ /* ffmpeg only: AV_CODEC_ID_SUBRIP */
|
|
+ /* ffmpeg only: AV_CODEC_ID_WEBVTT */
|
|
+ /* ffmpeg only: AV_CODEC_ID_MPL2 */
|
|
+ /* ffmpeg only: AV_CODEC_ID_VPLAYER */
|
|
+ /* ffmpeg only: AV_CODEC_ID_PJS */
|
|
+ /* ffmpeg only: AV_CODEC_ID_ASS */
|
|
+};
|
|
+
|
|
+static bool GetFfmpegCodec( enum es_format_category_e cat, vlc_fourcc_t i_fourcc,
|
|
+ unsigned *pi_ffmpeg_codec, const char **ppsz_name )
|
|
+{
|
|
+ const struct vlc_avcodec_fourcc *base;
|
|
+ size_t count;
|
|
+
|
|
+ switch( cat )
|
|
+ {
|
|
+ case VIDEO_ES:
|
|
+ base = video_codecs;
|
|
+ count = ARRAY_SIZE(video_codecs);
|
|
+ break;
|
|
+ case AUDIO_ES:
|
|
+ base = audio_codecs;
|
|
+ count = ARRAY_SIZE(audio_codecs);
|
|
+ break;
|
|
+ case SPU_ES:
|
|
+ base = spu_codecs;
|
|
+ count = ARRAY_SIZE(spu_codecs);
|
|
+ break;
|
|
+ default:
|
|
+ base = NULL;
|
|
+ count = 0;
|
|
+ }
|
|
+
|
|
+ i_fourcc = vlc_fourcc_GetCodec( cat, i_fourcc );
|
|
+
|
|
+ for( size_t i = 0; i < count; i++ )
|
|
+ {
|
|
+ if( base[i].i_fourcc == i_fourcc )
|
|
+ {
|
|
+ if( pi_ffmpeg_codec != NULL )
|
|
+ *pi_ffmpeg_codec = base[i].i_codec;
|
|
+ if( ppsz_name )
|
|
+ *ppsz_name = vlc_fourcc_GetDescription( cat, i_fourcc );
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+ * Chroma fourcc -> libavutil pixfmt mapping
|
|
+ *****************************************************************************/
|
|
+#if defined(WORDS_BIGENDIAN)
|
|
+# define VLC_RGB_ES( fcc, leid, beid ) \
|
|
+ { fcc, beid, 0, 0, 0 },
|
|
+#else
|
|
+# define VLC_RGB_ES( fcc, leid, beid ) \
|
|
+ { fcc, leid, 0, 0, 0 },
|
|
+#endif
|
|
+
|
|
+#define VLC_RGB( fcc, leid, beid, rmask, gmask, bmask ) \
|
|
+ { fcc, leid, rmask, gmask, bmask }, \
|
|
+ { fcc, beid, bmask, gmask, rmask }, \
|
|
+ VLC_RGB_ES( fcc, leid, beid )
|
|
+
|
|
+
|
|
+static const struct
|
|
+{
|
|
+ vlc_fourcc_t i_chroma;
|
|
+ int i_chroma_id;
|
|
+ uint32_t i_rmask;
|
|
+ uint32_t i_gmask;
|
|
+ uint32_t i_bmask;
|
|
+
|
|
+} chroma_table[] =
|
|
+{
|
|
+ // Sand
|
|
+// {VLC_CODEC_MMAL_OPAQUE, AV_PIX_FMT_SAND128, 0, 0, 0 },
|
|
+ {VLC_CODEC_MMAL_ZC_SAND8, AV_PIX_FMT_SAND128, 0, 0, 0 },
|
|
+ {VLC_CODEC_MMAL_ZC_SAND10, AV_PIX_FMT_SAND64_10, 0, 0, 0 },
|
|
+
|
|
+ /* Planar YUV formats */
|
|
+ {VLC_CODEC_I444, AV_PIX_FMT_YUV444P, 0, 0, 0 },
|
|
+ {VLC_CODEC_J444, AV_PIX_FMT_YUVJ444P, 0, 0, 0 },
|
|
+
|
|
+ {VLC_CODEC_I440, AV_PIX_FMT_YUV440P, 0, 0, 0 },
|
|
+ {VLC_CODEC_J440, AV_PIX_FMT_YUVJ440P, 0, 0, 0 },
|
|
+
|
|
+ {VLC_CODEC_I422, AV_PIX_FMT_YUV422P, 0, 0, 0 },
|
|
+ {VLC_CODEC_J422, AV_PIX_FMT_YUVJ422P, 0, 0, 0 },
|
|
+
|
|
+ {VLC_CODEC_I420, AV_PIX_FMT_YUV420P, 0, 0, 0 },
|
|
+ {VLC_CODEC_YV12, AV_PIX_FMT_YUV420P, 0, 0, 0 },
|
|
+ {VLC_FOURCC('I','Y','U','V'), AV_PIX_FMT_YUV420P, 0, 0, 0 },
|
|
+ {VLC_CODEC_J420, AV_PIX_FMT_YUVJ420P, 0, 0, 0 },
|
|
+ {VLC_CODEC_I411, AV_PIX_FMT_YUV411P, 0, 0, 0 },
|
|
+ {VLC_CODEC_I410, AV_PIX_FMT_YUV410P, 0, 0, 0 },
|
|
+ {VLC_FOURCC('Y','V','U','9'), AV_PIX_FMT_YUV410P, 0, 0, 0 },
|
|
+
|
|
+ {VLC_CODEC_NV12, AV_PIX_FMT_NV12, 0, 0, 0 },
|
|
+ {VLC_CODEC_NV21, AV_PIX_FMT_NV21, 0, 0, 0 },
|
|
+
|
|
+ {VLC_CODEC_I420_9L, AV_PIX_FMT_YUV420P9LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I420_9B, AV_PIX_FMT_YUV420P9BE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I420_10L, AV_PIX_FMT_YUV420P10LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I420_10B, AV_PIX_FMT_YUV420P10BE, 0, 0, 0 },
|
|
+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) )
|
|
+ {VLC_CODEC_I420_12L, AV_PIX_FMT_YUV420P12LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I420_12B, AV_PIX_FMT_YUV420P12BE, 0, 0, 0 },
|
|
+#endif
|
|
+ {VLC_CODEC_I420_16L, AV_PIX_FMT_YUV420P16LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I420_16B, AV_PIX_FMT_YUV420P16BE, 0, 0, 0 },
|
|
+#ifdef AV_PIX_FMT_P010
|
|
+ {VLC_CODEC_P010, AV_PIX_FMT_P010, 0, 0, 0 },
|
|
+#endif
|
|
+
|
|
+ {VLC_CODEC_I422_9L, AV_PIX_FMT_YUV422P9LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I422_9B, AV_PIX_FMT_YUV422P9BE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I422_10L, AV_PIX_FMT_YUV422P10LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I422_10B, AV_PIX_FMT_YUV422P10BE, 0, 0, 0 },
|
|
+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) )
|
|
+ {VLC_CODEC_I422_12L, AV_PIX_FMT_YUV422P12LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I422_12B, AV_PIX_FMT_YUV422P12BE, 0, 0, 0 },
|
|
+#endif
|
|
+
|
|
+ {VLC_CODEC_YUV420A, AV_PIX_FMT_YUVA420P, 0, 0, 0 },
|
|
+ {VLC_CODEC_YUV422A, AV_PIX_FMT_YUVA422P, 0, 0, 0 },
|
|
+ {VLC_CODEC_YUVA, AV_PIX_FMT_YUVA444P, 0, 0, 0 },
|
|
+
|
|
+ {VLC_CODEC_YUVA_444_10L, AV_PIX_FMT_YUVA444P10LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_YUVA_444_10B, AV_PIX_FMT_YUVA444P10BE, 0, 0, 0 },
|
|
+
|
|
+ {VLC_CODEC_I444_9L, AV_PIX_FMT_YUV444P9LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I444_9B, AV_PIX_FMT_YUV444P9BE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I444_10L, AV_PIX_FMT_YUV444P10LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I444_10B, AV_PIX_FMT_YUV444P10BE, 0, 0, 0 },
|
|
+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 54, 17, 100 ) )
|
|
+ {VLC_CODEC_I444_12L, AV_PIX_FMT_YUV444P12LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I444_12B, AV_PIX_FMT_YUV444P12BE, 0, 0, 0 },
|
|
+#endif
|
|
+ {VLC_CODEC_I444_16L, AV_PIX_FMT_YUV444P16LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_I444_16B, AV_PIX_FMT_YUV444P16BE, 0, 0, 0 },
|
|
+
|
|
+ /* Packed YUV formats */
|
|
+ {VLC_CODEC_YUYV, AV_PIX_FMT_YUYV422, 0, 0, 0 },
|
|
+ {VLC_FOURCC('Y','U','Y','V'), AV_PIX_FMT_YUYV422, 0, 0, 0 },
|
|
+ {VLC_CODEC_UYVY, AV_PIX_FMT_UYVY422, 0, 0, 0 },
|
|
+ {VLC_CODEC_YVYU, AV_PIX_FMT_YVYU422, 0, 0, 0 },
|
|
+ {VLC_FOURCC('Y','4','1','1'), AV_PIX_FMT_UYYVYY411, 0, 0, 0 },
|
|
+
|
|
+ /* Packed RGB formats */
|
|
+ VLC_RGB( VLC_FOURCC('R','G','B','4'), AV_PIX_FMT_RGB4, AV_PIX_FMT_BGR4, 0x10, 0x06, 0x01 )
|
|
+ VLC_RGB( VLC_CODEC_RGB8, AV_PIX_FMT_RGB8, AV_PIX_FMT_BGR8, 0xC0, 0x38, 0x07 )
|
|
+
|
|
+ VLC_RGB( VLC_CODEC_RGB15, AV_PIX_FMT_RGB555, AV_PIX_FMT_BGR555, 0x7c00, 0x03e0, 0x001f )
|
|
+ VLC_RGB( VLC_CODEC_RGB16, AV_PIX_FMT_RGB565, AV_PIX_FMT_BGR565, 0xf800, 0x07e0, 0x001f )
|
|
+ VLC_RGB( VLC_CODEC_RGB24, AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, 0xff0000, 0x00ff00, 0x0000ff )
|
|
+
|
|
+ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_RGB32, AV_PIX_FMT_BGR32, 0x00ff0000, 0x0000ff00, 0x000000ff )
|
|
+ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_RGB32_1, AV_PIX_FMT_BGR32_1, 0xff000000, 0x00ff0000, 0x0000ff00 )
|
|
+
|
|
+#ifdef AV_PIX_FMT_0BGR32
|
|
+ VLC_RGB( VLC_CODEC_RGB32, AV_PIX_FMT_0BGR32, AV_PIX_FMT_0RGB32, 0x000000ff, 0x0000ff00, 0x00ff0000 )
|
|
+#endif
|
|
+
|
|
+ {VLC_CODEC_RGBA, AV_PIX_FMT_RGBA, 0, 0, 0 },
|
|
+ {VLC_CODEC_ARGB, AV_PIX_FMT_ARGB, 0, 0, 0 },
|
|
+ {VLC_CODEC_BGRA, AV_PIX_FMT_BGRA, 0, 0, 0 },
|
|
+ {VLC_CODEC_GREY, AV_PIX_FMT_GRAY8, 0, 0, 0},
|
|
+
|
|
+ /* Paletized RGB */
|
|
+ {VLC_CODEC_RGBP, AV_PIX_FMT_PAL8, 0, 0, 0},
|
|
+
|
|
+ {VLC_CODEC_GBR_PLANAR, AV_PIX_FMT_GBRP, 0, 0, 0 },
|
|
+ {VLC_CODEC_GBR_PLANAR_9L, AV_PIX_FMT_GBRP9LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_GBR_PLANAR_9B, AV_PIX_FMT_GBRP9BE, 0, 0, 0 },
|
|
+ {VLC_CODEC_GBR_PLANAR_10L, AV_PIX_FMT_GBRP10LE, 0, 0, 0 },
|
|
+ {VLC_CODEC_GBR_PLANAR_10B, AV_PIX_FMT_GBRP10BE, 0, 0, 0 },
|
|
+
|
|
+ /* XYZ */
|
|
+#if LIBAVUTIL_VERSION_CHECK(52, 10, 0, 25, 100)
|
|
+ {VLC_CODEC_XYZ12, AV_PIX_FMT_XYZ12, 0xfff0, 0xfff0, 0xfff0},
|
|
+#endif
|
|
+ { 0, 0, 0, 0, 0 }
|
|
+};
|
|
+
|
|
+static int GetVlcChroma( video_format_t *fmt, int i_ffmpeg_chroma )
|
|
+{
|
|
+ /* TODO FIXME for rgb format we HAVE to set rgb mask/shift */
|
|
+ for( int i = 0; chroma_table[i].i_chroma != 0; i++ )
|
|
+ {
|
|
+ if( chroma_table[i].i_chroma_id == i_ffmpeg_chroma )
|
|
+ {
|
|
+ fmt->i_rmask = chroma_table[i].i_rmask;
|
|
+ fmt->i_gmask = chroma_table[i].i_gmask;
|
|
+ fmt->i_bmask = chroma_table[i].i_bmask;
|
|
+ fmt->i_chroma = chroma_table[i].i_chroma;
|
|
+ return VLC_SUCCESS;
|
|
+ }
|
|
+ }
|
|
+ return VLC_EGENERIC;
|
|
+}
|
|
+
|
|
+//#include "../codec/cc.h"
|
|
+
|
|
+static AVCodecContext *ffmpeg_AllocContext( decoder_t *p_dec,
|
|
+ const AVCodec **restrict codecp )
|
|
+{
|
|
+ unsigned i_codec_id;
|
|
+ const char *psz_namecodec;
|
|
+ const AVCodec *p_codec = NULL;
|
|
+
|
|
+ /* *** determine codec type *** */
|
|
+ if( !GetFfmpegCodec( p_dec->fmt_in.i_cat, p_dec->fmt_in.i_codec,
|
|
+ &i_codec_id, &psz_namecodec ) )
|
|
+ return NULL;
|
|
+
|
|
+ msg_Dbg( p_dec, "using %s %s", AVPROVIDER(LIBAVCODEC), LIBAVCODEC_IDENT );
|
|
+
|
|
+ /* Initialization must be done before avcodec_find_decoder() */
|
|
+ vlc_init_avcodec(VLC_OBJECT(p_dec));
|
|
+
|
|
+ /* *** ask ffmpeg for a decoder *** */
|
|
+ char *psz_decoder = var_InheritString( p_dec, "avcodec-codec" );
|
|
+ if( psz_decoder != NULL )
|
|
+ {
|
|
+ p_codec = avcodec_find_decoder_by_name( psz_decoder );
|
|
+ if( !p_codec )
|
|
+ msg_Err( p_dec, "Decoder `%s' not found", psz_decoder );
|
|
+ else if( p_codec->id != i_codec_id )
|
|
+ {
|
|
+ msg_Err( p_dec, "Decoder `%s' can't handle %4.4s",
|
|
+ psz_decoder, (char*)&p_dec->fmt_in.i_codec );
|
|
+ p_codec = NULL;
|
|
+ }
|
|
+ free( psz_decoder );
|
|
+ }
|
|
+ if( !p_codec )
|
|
+ p_codec = avcodec_find_decoder( i_codec_id );
|
|
+ if( !p_codec )
|
|
+ {
|
|
+ msg_Dbg( p_dec, "codec not found (%s)", psz_namecodec );
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ *codecp = p_codec;
|
|
+
|
|
+ /* *** get a p_context *** */
|
|
+ AVCodecContext *avctx = avcodec_alloc_context3(p_codec);
|
|
+ if( unlikely(avctx == NULL) )
|
|
+ return NULL;
|
|
+
|
|
+ avctx->debug = var_InheritInteger( p_dec, "avcodec-debug" );
|
|
+ avctx->opaque = p_dec;
|
|
+ return avctx;
|
|
+}
|
|
+
|
|
+static int ffmpeg_OpenCodec( decoder_t *p_dec, AVCodecContext *ctx,
|
|
+ const AVCodec *codec )
|
|
+{
|
|
+ char *psz_opts = var_InheritString( p_dec, "avcodec-options" );
|
|
+ AVDictionary *options = NULL;
|
|
+ int ret;
|
|
+
|
|
+ if (psz_opts) {
|
|
+ vlc_av_get_options(psz_opts, &options);
|
|
+ free(psz_opts);
|
|
+ }
|
|
+
|
|
+ if (av_rpi_zc_init(ctx) != 0)
|
|
+ {
|
|
+ msg_Err(p_dec, "Failed to init AV ZC");
|
|
+ return VLC_EGENERIC;
|
|
+ }
|
|
+
|
|
+ vlc_avcodec_lock();
|
|
+ ret = avcodec_open2( ctx, codec, options ? &options : NULL );
|
|
+ vlc_avcodec_unlock();
|
|
+
|
|
+ AVDictionaryEntry *t = NULL;
|
|
+ while ((t = av_dict_get(options, "", t, AV_DICT_IGNORE_SUFFIX))) {
|
|
+ msg_Err( p_dec, "Unknown option \"%s\"", t->key );
|
|
+ }
|
|
+ av_dict_free(&options);
|
|
+
|
|
+ if( ret < 0 )
|
|
+ {
|
|
+ msg_Err( p_dec, "cannot start codec (%s)", codec->name );
|
|
+ return VLC_EGENERIC;
|
|
+ }
|
|
+
|
|
+ msg_Dbg( p_dec, "codec (%s) started", codec->name );
|
|
+ return VLC_SUCCESS;
|
|
+}
|
|
+
|
|
+
|
|
+/*****************************************************************************
|
|
+ * decoder_sys_t : decoder descriptor
|
|
+ *****************************************************************************/
|
|
+struct decoder_sys_t
|
|
+{
|
|
+ AVCodecContext *p_context;
|
|
+ const AVCodec *p_codec;
|
|
+
|
|
+ /* Video decoder specific part */
|
|
+ date_t pts;
|
|
+
|
|
+ /* Closed captions for decoders */
|
|
+// cc_data_t cc;
|
|
+
|
|
+ /* for frame skipping algo */
|
|
+ bool b_hurry_up;
|
|
+ bool b_show_corrupted;
|
|
+ bool b_from_preroll;
|
|
+ enum AVDiscard i_skip_frame;
|
|
+
|
|
+ /* how many decoded frames are late */
|
|
+ int i_late_frames;
|
|
+ mtime_t i_late_frames_start;
|
|
+ mtime_t i_last_late_delay;
|
|
+
|
|
+ /* for direct rendering */
|
|
+ bool b_direct_rendering;
|
|
+ atomic_bool b_dr_failure;
|
|
+
|
|
+ /* Hack to force display of still pictures */
|
|
+ bool b_first_frame;
|
|
+
|
|
+
|
|
+ /* */
|
|
+ bool palette_sent;
|
|
+
|
|
+ /* VA API */
|
|
+// vlc_va_t *p_va;
|
|
+ enum AVPixelFormat pix_fmt;
|
|
+ int profile;
|
|
+ int level;
|
|
+
|
|
+ MMAL_POOL_T * out_pool;
|
|
+};
|
|
+
|
|
+/*****************************************************************************
|
|
+ * Local prototypes
|
|
+ *****************************************************************************/
|
|
+static void ffmpeg_InitCodec ( decoder_t * );
|
|
+static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *,
|
|
+ const enum PixelFormat * );
|
|
+static int DecodeVideo( decoder_t *, block_t * );
|
|
+static void Flush( decoder_t * );
|
|
+
|
|
+static uint32_t ffmpeg_CodecTag( vlc_fourcc_t fcc )
|
|
+{
|
|
+ uint8_t *p = (uint8_t*)&fcc;
|
|
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+ * Local Functions
|
|
+ *****************************************************************************/
|
|
+
|
|
+/**
|
|
+ * Sets the decoder output format.
|
|
+ */
|
|
+static int lavc_GetVideoFormat(decoder_t *dec, video_format_t *restrict fmt,
|
|
+ AVCodecContext *ctx, enum AVPixelFormat pix_fmt,
|
|
+ enum AVPixelFormat sw_pix_fmt)
|
|
+{
|
|
+ int width = ctx->coded_width;
|
|
+ int height = ctx->coded_height;
|
|
+
|
|
+ video_format_Init(fmt, 0);
|
|
+
|
|
+ if (pix_fmt == sw_pix_fmt)
|
|
+ { /* software decoding */
|
|
+ int aligns[AV_NUM_DATA_POINTERS];
|
|
+
|
|
+ if (GetVlcChroma(fmt, pix_fmt))
|
|
+ return -1;
|
|
+
|
|
+ /* The libavcodec palette can only be fetched when the first output
|
|
+ * frame is decoded. Assume that the current chroma is RGB32 while we
|
|
+ * are waiting for a valid palette. Indeed, fmt_out.video.p_palette
|
|
+ * doesn't trigger a new vout request, but a new chroma yes. */
|
|
+ if (pix_fmt == AV_PIX_FMT_PAL8 && !dec->fmt_out.video.p_palette)
|
|
+ fmt->i_chroma = VLC_CODEC_RGB32;
|
|
+
|
|
+ avcodec_align_dimensions2(ctx, &width, &height, aligns);
|
|
+ }
|
|
+// else /* hardware decoding */
|
|
+// fmt->i_chroma = vlc_va_GetChroma(pix_fmt, sw_pix_fmt);
|
|
+
|
|
+ if( width == 0 || height == 0 || width > 8192 || height > 8192 ||
|
|
+ width < ctx->width || height < ctx->height )
|
|
+ {
|
|
+ msg_Err(dec, "Invalid frame size %dx%d vsz %dx%d",
|
|
+ width, height, ctx->width, ctx->height );
|
|
+ return -1; /* invalid display size */
|
|
+ }
|
|
+
|
|
+ fmt->i_width = width;
|
|
+ fmt->i_height = height;
|
|
+ fmt->i_visible_width = ctx->width;
|
|
+ fmt->i_visible_height = ctx->height;
|
|
+
|
|
+ /* If an aspect-ratio was specified in the input format then force it */
|
|
+ if (dec->fmt_in.video.i_sar_num > 0 && dec->fmt_in.video.i_sar_den > 0)
|
|
+ {
|
|
+ fmt->i_sar_num = dec->fmt_in.video.i_sar_num;
|
|
+ fmt->i_sar_den = dec->fmt_in.video.i_sar_den;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ fmt->i_sar_num = ctx->sample_aspect_ratio.num;
|
|
+ fmt->i_sar_den = ctx->sample_aspect_ratio.den;
|
|
+
|
|
+ if (fmt->i_sar_num == 0 || fmt->i_sar_den == 0)
|
|
+ fmt->i_sar_num = fmt->i_sar_den = 1;
|
|
+ }
|
|
+
|
|
+ if (dec->fmt_in.video.i_frame_rate > 0
|
|
+ && dec->fmt_in.video.i_frame_rate_base > 0)
|
|
+ {
|
|
+ fmt->i_frame_rate = dec->fmt_in.video.i_frame_rate;
|
|
+ fmt->i_frame_rate_base = dec->fmt_in.video.i_frame_rate_base;
|
|
+ }
|
|
+ else if (ctx->framerate.num > 0 && ctx->framerate.den > 0)
|
|
+ {
|
|
+ fmt->i_frame_rate = ctx->framerate.num;
|
|
+ fmt->i_frame_rate_base = ctx->framerate.den;
|
|
+# if LIBAVCODEC_VERSION_MICRO < 100
|
|
+ // for some reason libav don't thinkg framerate presents actually same thing as in ffmpeg
|
|
+ fmt->i_frame_rate_base *= __MAX(ctx->ticks_per_frame, 1);
|
|
+# endif
|
|
+ }
|
|
+ else if (ctx->time_base.num > 0 && ctx->time_base.den > 0)
|
|
+ {
|
|
+ fmt->i_frame_rate = ctx->time_base.den;
|
|
+ fmt->i_frame_rate_base = ctx->time_base.num
|
|
+ * __MAX(ctx->ticks_per_frame, 1);
|
|
+ }
|
|
+
|
|
+ if( ctx->color_range == AVCOL_RANGE_JPEG )
|
|
+ fmt->b_color_range_full = true;
|
|
+
|
|
+ switch( ctx->colorspace )
|
|
+ {
|
|
+ case AVCOL_SPC_BT709:
|
|
+ fmt->space = COLOR_SPACE_BT709;
|
|
+ break;
|
|
+ case AVCOL_SPC_SMPTE170M:
|
|
+ case AVCOL_SPC_BT470BG:
|
|
+ fmt->space = COLOR_SPACE_BT601;
|
|
+ break;
|
|
+ case AVCOL_SPC_BT2020_NCL:
|
|
+ case AVCOL_SPC_BT2020_CL:
|
|
+ fmt->space = COLOR_SPACE_BT2020;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch( ctx->color_trc )
|
|
+ {
|
|
+ case AVCOL_TRC_LINEAR:
|
|
+ fmt->transfer = TRANSFER_FUNC_LINEAR;
|
|
+ break;
|
|
+ case AVCOL_TRC_GAMMA22:
|
|
+ fmt->transfer = TRANSFER_FUNC_SRGB;
|
|
+ break;
|
|
+ case AVCOL_TRC_BT709:
|
|
+ fmt->transfer = TRANSFER_FUNC_BT709;
|
|
+ break;
|
|
+ case AVCOL_TRC_SMPTE170M:
|
|
+ case AVCOL_TRC_BT2020_10:
|
|
+ case AVCOL_TRC_BT2020_12:
|
|
+ fmt->transfer = TRANSFER_FUNC_BT2020;
|
|
+ break;
|
|
+#if LIBAVUTIL_VERSION_CHECK( 55, 14, 0, 31, 100)
|
|
+ case AVCOL_TRC_ARIB_STD_B67:
|
|
+ fmt->transfer = TRANSFER_FUNC_ARIB_B67;
|
|
+ break;
|
|
+#endif
|
|
+#if LIBAVUTIL_VERSION_CHECK( 55, 17, 0, 37, 100)
|
|
+ case AVCOL_TRC_SMPTE2084:
|
|
+ fmt->transfer = TRANSFER_FUNC_SMPTE_ST2084;
|
|
+ break;
|
|
+ case AVCOL_TRC_SMPTE240M:
|
|
+ fmt->transfer = TRANSFER_FUNC_SMPTE_240;
|
|
+ break;
|
|
+ case AVCOL_TRC_GAMMA28:
|
|
+ fmt->transfer = TRANSFER_FUNC_BT470_BG;
|
|
+ break;
|
|
+#endif
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch( ctx->color_primaries )
|
|
+ {
|
|
+ case AVCOL_PRI_BT709:
|
|
+ fmt->primaries = COLOR_PRIMARIES_BT709;
|
|
+ break;
|
|
+ case AVCOL_PRI_BT470BG:
|
|
+ fmt->primaries = COLOR_PRIMARIES_BT601_625;
|
|
+ break;
|
|
+ case AVCOL_PRI_SMPTE170M:
|
|
+ case AVCOL_PRI_SMPTE240M:
|
|
+ fmt->primaries = COLOR_PRIMARIES_BT601_525;
|
|
+ break;
|
|
+ case AVCOL_PRI_BT2020:
|
|
+ fmt->primaries = COLOR_PRIMARIES_BT2020;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ switch( ctx->chroma_sample_location )
|
|
+ {
|
|
+ case AVCHROMA_LOC_LEFT:
|
|
+ fmt->chroma_location = CHROMA_LOCATION_LEFT;
|
|
+ break;
|
|
+ case AVCHROMA_LOC_CENTER:
|
|
+ fmt->chroma_location = CHROMA_LOCATION_CENTER;
|
|
+ break;
|
|
+ case AVCHROMA_LOC_TOPLEFT:
|
|
+ fmt->chroma_location = CHROMA_LOCATION_TOP_LEFT;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int lavc_UpdateVideoFormat(decoder_t *dec, AVCodecContext *ctx,
|
|
+ enum AVPixelFormat fmt,
|
|
+ enum AVPixelFormat swfmt)
|
|
+{
|
|
+ video_format_t fmt_out;
|
|
+ int val;
|
|
+
|
|
+ val = lavc_GetVideoFormat(dec, &fmt_out, ctx, fmt, swfmt);
|
|
+ if (val)
|
|
+ return val;
|
|
+
|
|
+ /* always have date in fields/ticks units */
|
|
+ if(dec->p_sys->pts.i_divider_num)
|
|
+ date_Change(&dec->p_sys->pts, fmt_out.i_frame_rate *
|
|
+ __MAX(ctx->ticks_per_frame, 1),
|
|
+ fmt_out.i_frame_rate_base);
|
|
+ else
|
|
+ date_Init(&dec->p_sys->pts, fmt_out.i_frame_rate *
|
|
+ __MAX(ctx->ticks_per_frame, 1),
|
|
+ fmt_out.i_frame_rate_base);
|
|
+
|
|
+ fmt_out.p_palette = dec->fmt_out.video.p_palette;
|
|
+ dec->fmt_out.video.p_palette = NULL;
|
|
+
|
|
+ es_format_Change(&dec->fmt_out, VIDEO_ES, fmt_out.i_chroma);
|
|
+ dec->fmt_out.video = fmt_out;
|
|
+ dec->fmt_out.video.orientation = dec->fmt_in.video.orientation;
|
|
+ dec->fmt_out.video.projection_mode = dec->fmt_in.video.projection_mode;
|
|
+ dec->fmt_out.video.multiview_mode = dec->fmt_in.video.multiview_mode;
|
|
+ dec->fmt_out.video.pose = dec->fmt_in.video.pose;
|
|
+ if ( dec->fmt_in.video.mastering.max_luminance )
|
|
+ dec->fmt_out.video.mastering = dec->fmt_in.video.mastering;
|
|
+ dec->fmt_out.video.lighting = dec->fmt_in.video.lighting;
|
|
+
|
|
+ return decoder_UpdateVideoFormat(dec);
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+ * Flush:
|
|
+ *****************************************************************************/
|
|
+static void Flush( decoder_t *p_dec )
|
|
+{
|
|
+ decoder_sys_t *p_sys = p_dec->p_sys;
|
|
+ AVCodecContext *p_context = p_sys->p_context;
|
|
+
|
|
+ date_Set(&p_sys->pts, VLC_TS_INVALID); /* To make sure we recover properly */
|
|
+ p_sys->i_late_frames = 0;
|
|
+// cc_Flush( &p_sys->cc );
|
|
+
|
|
+ /* Abort pictures in order to unblock all avcodec workers threads waiting
|
|
+ * for a picture. This will avoid a deadlock between avcodec_flush_buffers
|
|
+ * and workers threads */
|
|
+ decoder_AbortPictures( p_dec, true );
|
|
+
|
|
+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
|
|
+ if( avcodec_is_open( p_context ) )
|
|
+ avcodec_flush_buffers( p_context );
|
|
+
|
|
+ /* Reset cancel state to false */
|
|
+ decoder_AbortPictures( p_dec, false );
|
|
+}
|
|
+
|
|
+static bool check_block_validity( decoder_sys_t *p_sys, block_t *block )
|
|
+{
|
|
+ if( !block)
|
|
+ return true;
|
|
+
|
|
+ if( block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED) )
|
|
+ {
|
|
+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
|
|
+// cc_Flush( &p_sys->cc );
|
|
+
|
|
+ p_sys->i_late_frames = 0;
|
|
+ if( block->i_flags & BLOCK_FLAG_CORRUPTED )
|
|
+ {
|
|
+ block_Release( block );
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ return true;
|
|
+}
|
|
+
|
|
+static bool check_block_being_late( decoder_sys_t *p_sys, block_t *block, mtime_t current_time)
|
|
+{
|
|
+ if( !block )
|
|
+ return false;
|
|
+ if( block->i_flags & BLOCK_FLAG_PREROLL )
|
|
+ {
|
|
+ /* Do not care about late frames when prerolling
|
|
+ * TODO avoid decoding of non reference frame
|
|
+ * (ie all B except for H264 where it depends only on nal_ref_idc) */
|
|
+ p_sys->i_late_frames = 0;
|
|
+ p_sys->b_from_preroll = true;
|
|
+ p_sys->i_last_late_delay = INT64_MAX;
|
|
+ }
|
|
+
|
|
+ if( p_sys->i_late_frames <= 0 )
|
|
+ return false;
|
|
+
|
|
+ if( current_time - p_sys->i_late_frames_start > (5*CLOCK_FREQ))
|
|
+ {
|
|
+ date_Set( &p_sys->pts, VLC_TS_INVALID ); /* To make sure we recover properly */
|
|
+ block_Release( block );
|
|
+ p_sys->i_late_frames--;
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static bool check_frame_should_be_dropped( decoder_sys_t *p_sys, AVCodecContext *p_context, bool *b_need_output_picture )
|
|
+{
|
|
+ if( p_sys->i_late_frames <= 4)
|
|
+ return false;
|
|
+
|
|
+ *b_need_output_picture = false;
|
|
+ if( p_sys->i_late_frames < 12 )
|
|
+ {
|
|
+ p_context->skip_frame =
|
|
+ (p_sys->i_skip_frame <= AVDISCARD_NONREF) ?
|
|
+ AVDISCARD_NONREF : p_sys->i_skip_frame;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* picture too late, won't decode
|
|
+ * but break picture until a new I, and for mpeg4 ...*/
|
|
+ p_sys->i_late_frames--; /* needed else it will never be decrease */
|
|
+ return true;
|
|
+ }
|
|
+ return false;
|
|
+}
|
|
+
|
|
+static void interpolate_next_pts( decoder_t *p_dec, AVFrame *frame )
|
|
+{
|
|
+ decoder_sys_t *p_sys = p_dec->p_sys;
|
|
+ AVCodecContext *p_context = p_sys->p_context;
|
|
+
|
|
+ if( date_Get( &p_sys->pts ) == VLC_TS_INVALID ||
|
|
+ p_sys->pts.i_divider_num == 0 )
|
|
+ return;
|
|
+
|
|
+ int i_tick = p_context->ticks_per_frame;
|
|
+ if( i_tick <= 0 )
|
|
+ i_tick = 1;
|
|
+
|
|
+ /* interpolate the next PTS */
|
|
+ date_Increment( &p_sys->pts, i_tick + frame->repeat_pict );
|
|
+}
|
|
+
|
|
+static void update_late_frame_count( decoder_t *p_dec, block_t *p_block, mtime_t current_time, mtime_t i_pts )
|
|
+{
|
|
+ decoder_sys_t *p_sys = p_dec->p_sys;
|
|
+ /* Update frame late count (except when doing preroll) */
|
|
+ mtime_t i_display_date = VLC_TS_INVALID;
|
|
+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
|
|
+ i_display_date = decoder_GetDisplayDate( p_dec, i_pts );
|
|
+
|
|
+ if( i_display_date > VLC_TS_INVALID && i_display_date <= current_time )
|
|
+ {
|
|
+ /* Out of preroll, consider only late frames on rising delay */
|
|
+ if( p_sys->b_from_preroll )
|
|
+ {
|
|
+ if( p_sys->i_last_late_delay > current_time - i_display_date )
|
|
+ {
|
|
+ p_sys->i_last_late_delay = current_time - i_display_date;
|
|
+ return;
|
|
+ }
|
|
+ p_sys->b_from_preroll = false;
|
|
+ }
|
|
+
|
|
+ p_sys->i_late_frames++;
|
|
+ if( p_sys->i_late_frames == 1 )
|
|
+ p_sys->i_late_frames_start = current_time;
|
|
+
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ p_sys->i_late_frames = 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static int DecodeSidedata( decoder_t *p_dec, const AVFrame *frame, picture_t *p_pic )
|
|
+{
|
|
+// decoder_sys_t *p_sys = p_dec->p_sys;
|
|
+ bool format_changed = false;
|
|
+
|
|
+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 16, 101 ) )
|
|
+#define FROM_AVRAT(default_factor, avrat) \
|
|
+(uint64_t)(default_factor) * (avrat).num / (avrat).den
|
|
+ const AVFrameSideData *metadata =
|
|
+ av_frame_get_side_data( frame,
|
|
+ AV_FRAME_DATA_MASTERING_DISPLAY_METADATA );
|
|
+ if ( metadata )
|
|
+ {
|
|
+ const AVMasteringDisplayMetadata *hdr_meta =
|
|
+ (const AVMasteringDisplayMetadata *) metadata->data;
|
|
+ if ( hdr_meta->has_luminance )
|
|
+ {
|
|
+#define ST2086_LUMA_FACTOR 10000
|
|
+ p_pic->format.mastering.max_luminance =
|
|
+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->max_luminance);
|
|
+ p_pic->format.mastering.min_luminance =
|
|
+ FROM_AVRAT(ST2086_LUMA_FACTOR, hdr_meta->min_luminance);
|
|
+ }
|
|
+ if ( hdr_meta->has_primaries )
|
|
+ {
|
|
+#define ST2086_RED 2
|
|
+#define ST2086_GREEN 0
|
|
+#define ST2086_BLUE 1
|
|
+#define LAV_RED 0
|
|
+#define LAV_GREEN 1
|
|
+#define LAV_BLUE 2
|
|
+#define ST2086_PRIM_FACTOR 50000
|
|
+ p_pic->format.mastering.primaries[ST2086_RED*2 + 0] =
|
|
+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][0]);
|
|
+ p_pic->format.mastering.primaries[ST2086_RED*2 + 1] =
|
|
+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_RED][1]);
|
|
+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 0] =
|
|
+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][0]);
|
|
+ p_pic->format.mastering.primaries[ST2086_GREEN*2 + 1] =
|
|
+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_GREEN][1]);
|
|
+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 0] =
|
|
+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][0]);
|
|
+ p_pic->format.mastering.primaries[ST2086_BLUE*2 + 1] =
|
|
+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->display_primaries[LAV_BLUE][1]);
|
|
+ p_pic->format.mastering.white_point[0] =
|
|
+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[0]);
|
|
+ p_pic->format.mastering.white_point[1] =
|
|
+ FROM_AVRAT(ST2086_PRIM_FACTOR, hdr_meta->white_point[1]);
|
|
+ }
|
|
+
|
|
+ if ( memcmp( &p_dec->fmt_out.video.mastering,
|
|
+ &p_pic->format.mastering,
|
|
+ sizeof(p_pic->format.mastering) ) )
|
|
+ {
|
|
+ p_dec->fmt_out.video.mastering = p_pic->format.mastering;
|
|
+ format_changed = true;
|
|
+ }
|
|
+#undef FROM_AVRAT
|
|
+ }
|
|
+#endif
|
|
+#if (LIBAVUTIL_VERSION_MICRO >= 100 && LIBAVUTIL_VERSION_INT >= AV_VERSION_INT( 55, 60, 100 ) )
|
|
+ const AVFrameSideData *metadata_lt =
|
|
+ av_frame_get_side_data( frame,
|
|
+ AV_FRAME_DATA_CONTENT_LIGHT_LEVEL );
|
|
+ if ( metadata_lt )
|
|
+ {
|
|
+ const AVContentLightMetadata *light_meta =
|
|
+ (const AVContentLightMetadata *) metadata_lt->data;
|
|
+ p_pic->format.lighting.MaxCLL = light_meta->MaxCLL;
|
|
+ p_pic->format.lighting.MaxFALL = light_meta->MaxFALL;
|
|
+ if ( memcmp( &p_dec->fmt_out.video.lighting,
|
|
+ &p_pic->format.lighting,
|
|
+ sizeof(p_pic->format.lighting) ) )
|
|
+ {
|
|
+ p_dec->fmt_out.video.lighting = p_pic->format.lighting;
|
|
+ format_changed = true;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ if (format_changed && decoder_UpdateVideoFormat( p_dec ))
|
|
+ return -1;
|
|
+#if 0
|
|
+ const AVFrameSideData *p_avcc = av_frame_get_side_data( frame, AV_FRAME_DATA_A53_CC );
|
|
+ if( p_avcc )
|
|
+ {
|
|
+ cc_Extract( &p_sys->cc, CC_PAYLOAD_RAW, true, p_avcc->data, p_avcc->size );
|
|
+ if( p_sys->cc.b_reorder || p_sys->cc.i_data )
|
|
+ {
|
|
+ block_t *p_cc = block_Alloc( p_sys->cc.i_data );
|
|
+ if( p_cc )
|
|
+ {
|
|
+ memcpy( p_cc->p_buffer, p_sys->cc.p_data, p_sys->cc.i_data );
|
|
+ if( p_sys->cc.b_reorder )
|
|
+ p_cc->i_dts = p_cc->i_pts = p_pic->date;
|
|
+ else
|
|
+ p_cc->i_pts = p_cc->i_dts;
|
|
+ decoder_cc_desc_t desc;
|
|
+ desc.i_608_channels = p_sys->cc.i_608channels;
|
|
+ desc.i_708_channels = p_sys->cc.i_708channels;
|
|
+ desc.i_reorder_depth = 4;
|
|
+ decoder_QueueCc( p_dec, p_cc, &desc );
|
|
+ }
|
|
+ cc_Flush( &p_sys->cc );
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+static int OpenVideoCodec( decoder_t *p_dec )
|
|
+{
|
|
+ decoder_sys_t *p_sys = p_dec->p_sys;
|
|
+ AVCodecContext *ctx = p_sys->p_context;
|
|
+ const AVCodec *codec = p_sys->p_codec;
|
|
+ int ret;
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_dec, "<<< %s", __func__);
|
|
+#endif
|
|
+
|
|
+ if( ctx->extradata_size <= 0 )
|
|
+ {
|
|
+ if( codec->id == AV_CODEC_ID_VC1 ||
|
|
+ codec->id == AV_CODEC_ID_THEORA )
|
|
+ {
|
|
+ msg_Warn( p_dec, "waiting for extra data for codec %s",
|
|
+ codec->name );
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ctx->width = p_dec->fmt_in.video.i_visible_width;
|
|
+ ctx->height = p_dec->fmt_in.video.i_visible_height;
|
|
+
|
|
+ ctx->coded_width = p_dec->fmt_in.video.i_width;
|
|
+ ctx->coded_height = p_dec->fmt_in.video.i_height;
|
|
+
|
|
+ ctx->bits_per_coded_sample = p_dec->fmt_in.video.i_bits_per_pixel;
|
|
+ p_sys->pix_fmt = AV_PIX_FMT_NONE;
|
|
+ p_sys->profile = -1;
|
|
+ p_sys->level = -1;
|
|
+// cc_Init( &p_sys->cc );
|
|
+
|
|
+ set_video_color_settings( &p_dec->fmt_in.video, ctx );
|
|
+
|
|
+ ret = ffmpeg_OpenCodec( p_dec, ctx, codec );
|
|
+ if( ret < 0 )
|
|
+ return ret;
|
|
+
|
|
+ switch( ctx->active_thread_type )
|
|
+ {
|
|
+ case FF_THREAD_FRAME:
|
|
+ msg_Dbg( p_dec, "using frame thread mode with %d threads",
|
|
+ ctx->thread_count );
|
|
+ break;
|
|
+ case FF_THREAD_SLICE:
|
|
+ msg_Dbg( p_dec, "using slice thread mode with %d threads",
|
|
+ ctx->thread_count );
|
|
+ break;
|
|
+ case 0:
|
|
+ if( ctx->thread_count > 1 )
|
|
+ msg_Warn( p_dec, "failed to enable threaded decoding" );
|
|
+ break;
|
|
+ default:
|
|
+ msg_Warn( p_dec, "using unknown thread mode with %d threads",
|
|
+ ctx->thread_count );
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static MMAL_BOOL_T
|
|
+zc_buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata)
|
|
+{
|
|
+ const AVRpiZcRefPtr fr_ref = userdata;
|
|
+ VLC_UNUSED(buf);
|
|
+
|
|
+ av_rpi_zc_unref(fr_ref);
|
|
+
|
|
+ return MMAL_FALSE;
|
|
+}
|
|
+
|
|
+static MMAL_FOURCC_T
|
|
+avfmt_to_mmal(const int avfmt)
|
|
+{
|
|
+ switch( avfmt )
|
|
+ {
|
|
+ case AV_PIX_FMT_SAND128:
|
|
+ return MMAL_ENCODING_YUVUV128;
|
|
+ case AV_PIX_FMT_SAND64_10:
|
|
+ return MMAL_ENCODING_YUVUV64_10;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return MMAL_ENCODING_UNKNOWN;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+ * DecodeBlock: Called to decode one or more frames
|
|
+ *****************************************************************************/
|
|
+
|
|
+
|
|
+// Returns
|
|
+// -ve error
|
|
+// 0 Need more input (EAGAIN)
|
|
+// 1 Frame decoded (dropped or Qed)
|
|
+// 2 Decode err
|
|
+// 3 EOF
|
|
+
|
|
+static int rx_frame(decoder_t * const p_dec, decoder_sys_t * const p_sys, AVCodecContext * const p_context)
|
|
+{
|
|
+ AVFrame * frame = av_frame_alloc();
|
|
+ picture_t * p_pic = NULL;
|
|
+ int ret;
|
|
+
|
|
+ if (frame == NULL)
|
|
+ return VLC_ENOMEM;
|
|
+
|
|
+ ret = avcodec_receive_frame(p_context, frame);
|
|
+
|
|
+ if (ret != 0)
|
|
+ {
|
|
+ av_frame_free(&frame);
|
|
+ switch (ret)
|
|
+ {
|
|
+ case AVERROR(EAGAIN):
|
|
+ return 0;
|
|
+
|
|
+ case AVERROR(ENOMEM):
|
|
+ case AVERROR(EINVAL):
|
|
+ msg_Err(p_dec, "avcodec_receive_frame critical error");
|
|
+ return VLC_EGENERIC;
|
|
+
|
|
+ case AVERROR_EOF:
|
|
+ msg_Dbg(p_dec, "Rx EOF");
|
|
+ avcodec_flush_buffers(p_context);
|
|
+ return 2;
|
|
+
|
|
+ default:
|
|
+ msg_Warn(p_dec, "Decode error: %d", ret);
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Compute the PTS */
|
|
+#ifdef FF_API_PKT_PTS
|
|
+ mtime_t i_pts = frame->pts;
|
|
+#else
|
|
+ mtime_t i_pts = frame->pkt_pts;
|
|
+#endif
|
|
+ if (i_pts == AV_NOPTS_VALUE )
|
|
+ i_pts = frame->pkt_dts;
|
|
+
|
|
+ if( i_pts == AV_NOPTS_VALUE )
|
|
+ i_pts = date_Get( &p_sys->pts );
|
|
+
|
|
+ /* Interpolate the next PTS */
|
|
+ if( i_pts > VLC_TS_INVALID )
|
|
+ date_Set( &p_sys->pts, i_pts );
|
|
+
|
|
+ interpolate_next_pts( p_dec, frame );
|
|
+
|
|
+// update_late_frame_count( p_dec, p_block, current_time, i_pts); //*********************
|
|
+
|
|
+ if( ( /* !p_sys->p_va && */ !frame->linesize[0] ) ||
|
|
+ ( p_dec->b_frame_drop_allowed && (frame->flags & AV_FRAME_FLAG_CORRUPT) &&
|
|
+ !p_sys->b_show_corrupted ) )
|
|
+ {
|
|
+ msg_Dbg(p_dec, "Frame drop");
|
|
+ av_frame_free(&frame);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ lavc_UpdateVideoFormat(p_dec, p_context, p_context->pix_fmt,
|
|
+ p_context->pix_fmt);
|
|
+
|
|
+ {
|
|
+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_wait(p_sys->out_pool->queue);
|
|
+// MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(p_sys->out_pool->queue);
|
|
+ if (buf == NULL) {
|
|
+ msg_Err(p_dec, "MMAL buffer alloc failure");
|
|
+ av_frame_free(&frame);
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ mmal_buffer_header_reset(buf); // length, offset, flags, pts, dts
|
|
+ buf->cmd = 0;
|
|
+ buf->user_data = NULL;
|
|
+
|
|
+ {
|
|
+ const AVRpiZcRefPtr fr_buf = av_rpi_zc_ref(p_context, frame, frame->format, 0);
|
|
+
|
|
+ if (fr_buf == NULL) {
|
|
+ mmal_buffer_header_release(buf);
|
|
+ av_frame_free(&frame);
|
|
+ return VLC_ENOMEM;
|
|
+ }
|
|
+
|
|
+ const intptr_t vc_handle = (intptr_t)av_rpi_zc_vc_handle(fr_buf);
|
|
+
|
|
+ buf->data = (uint8_t *)vc_handle; // Cast our handle to a pointer for mmal - 2 steps to avoid gcc warnings
|
|
+ buf->offset = av_rpi_zc_offset(fr_buf);
|
|
+ buf->length = av_rpi_zc_length(fr_buf);
|
|
+ buf->alloc_size = av_rpi_zc_numbytes(fr_buf);
|
|
+ buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
|
|
+
|
|
+ mmal_buffer_header_pre_release_cb_set(buf, zc_buf_pre_release_cb, fr_buf);
|
|
+ }
|
|
+
|
|
+ p_pic = decoder_NewPicture(p_dec); // *** Really want an empy pic
|
|
+ if (p_pic == NULL)
|
|
+ {
|
|
+ msg_Err(p_dec, "Picture alloc failure");
|
|
+ mmal_buffer_header_release(buf);
|
|
+ av_frame_free(&frame);
|
|
+ return VLC_ENOMEM;
|
|
+ }
|
|
+
|
|
+ p_pic->context = hw_mmal_gen_context(avfmt_to_mmal(frame->format), buf, NULL);
|
|
+ }
|
|
+
|
|
+
|
|
+ if( !p_dec->fmt_in.video.i_sar_num || !p_dec->fmt_in.video.i_sar_den )
|
|
+ {
|
|
+ /* Fetch again the aspect ratio in case it changed */
|
|
+ p_dec->fmt_out.video.i_sar_num
|
|
+ = p_context->sample_aspect_ratio.num;
|
|
+ p_dec->fmt_out.video.i_sar_den
|
|
+ = p_context->sample_aspect_ratio.den;
|
|
+
|
|
+ if( !p_dec->fmt_out.video.i_sar_num || !p_dec->fmt_out.video.i_sar_den )
|
|
+ {
|
|
+ p_dec->fmt_out.video.i_sar_num = 1;
|
|
+ p_dec->fmt_out.video.i_sar_den = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ p_pic->date = i_pts;
|
|
+ /* Hack to force display of still pictures */
|
|
+ p_pic->b_force = p_sys->b_first_frame;
|
|
+ p_pic->i_nb_fields = 2 + frame->repeat_pict;
|
|
+ p_pic->b_progressive = !frame->interlaced_frame;
|
|
+ p_pic->b_top_field_first = frame->top_field_first;
|
|
+
|
|
+ if (DecodeSidedata(p_dec, frame, p_pic))
|
|
+ i_pts = VLC_TS_INVALID;
|
|
+
|
|
+ av_frame_free(&frame);
|
|
+
|
|
+ p_sys->b_first_frame = false;
|
|
+ decoder_QueueVideo(p_dec, p_pic);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+static int DecodeVideo( decoder_t *p_dec, block_t * p_block)
|
|
+{
|
|
+ decoder_sys_t *p_sys = p_dec->p_sys;
|
|
+ AVCodecContext *p_context = p_sys->p_context;
|
|
+ /* Boolean if we assume that we should get valid pic as result */
|
|
+ bool b_need_output_picture = true;
|
|
+
|
|
+ /* Boolean for END_OF_SEQUENCE */
|
|
+ bool eos_spotted = false;
|
|
+ mtime_t current_time;
|
|
+ int rv = VLCDEC_SUCCESS;
|
|
+
|
|
+ if( !p_context->extradata_size && p_dec->fmt_in.i_extra )
|
|
+ {
|
|
+ ffmpeg_InitCodec( p_dec );
|
|
+ if( !avcodec_is_open( p_context ) )
|
|
+ OpenVideoCodec( p_dec );
|
|
+ }
|
|
+
|
|
+ if(!p_block && !(p_sys->p_codec->capabilities & AV_CODEC_CAP_DELAY) )
|
|
+ return VLCDEC_SUCCESS;
|
|
+
|
|
+ if( !avcodec_is_open( p_context ) )
|
|
+ {
|
|
+ if( p_block )
|
|
+ block_Release( p_block );
|
|
+ return VLCDEC_ECRITICAL;
|
|
+ }
|
|
+
|
|
+ if( !check_block_validity( p_sys, p_block ) )
|
|
+ return VLCDEC_SUCCESS;
|
|
+
|
|
+ current_time = mdate();
|
|
+ if( p_dec->b_frame_drop_allowed && check_block_being_late( p_sys, p_block, current_time) )
|
|
+ {
|
|
+ msg_Err( p_dec, "more than 5 seconds of late video -> "
|
|
+ "dropping frame (computer too slow ?)" );
|
|
+ return VLCDEC_SUCCESS;
|
|
+ }
|
|
+
|
|
+
|
|
+ /* A good idea could be to decode all I pictures and see for the other */
|
|
+ b_need_output_picture = true;
|
|
+
|
|
+ /* Defaults that if we aren't in prerolling, we want output picture
|
|
+ same for if we are flushing (p_block==NULL) */
|
|
+ if( !p_block || !(p_block->i_flags & BLOCK_FLAG_PREROLL) )
|
|
+ b_need_output_picture = true;
|
|
+ else
|
|
+ b_need_output_picture = false;
|
|
+
|
|
+ /* Change skip_frame config only if hurry_up is enabled */
|
|
+ if( p_sys->b_hurry_up )
|
|
+ {
|
|
+ p_context->skip_frame = p_sys->i_skip_frame;
|
|
+
|
|
+ /* Check also if we should/can drop the block and move to next block
|
|
+ as trying to catchup the speed*/
|
|
+ if( p_dec->b_frame_drop_allowed &&
|
|
+ check_frame_should_be_dropped( p_sys, p_context, &b_need_output_picture ) )
|
|
+ {
|
|
+ if( p_block )
|
|
+ block_Release( p_block );
|
|
+ msg_Warn( p_dec, "More than 11 late frames, dropping frame" );
|
|
+ return VLCDEC_SUCCESS;
|
|
+ }
|
|
+ }
|
|
+ if( !b_need_output_picture )
|
|
+ {
|
|
+ p_context->skip_frame = __MAX( p_context->skip_frame,
|
|
+ AVDISCARD_NONREF );
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Do the actual decoding now */
|
|
+
|
|
+ /* Don't forget that libavcodec requires a little more bytes
|
|
+ * that the real frame size */
|
|
+ if( p_block && p_block->i_buffer > 0 )
|
|
+ {
|
|
+ eos_spotted = ( p_block->i_flags & BLOCK_FLAG_END_OF_SEQUENCE ) != 0;
|
|
+
|
|
+ p_block = block_Realloc( p_block, 0,
|
|
+ p_block->i_buffer + FF_INPUT_BUFFER_PADDING_SIZE );
|
|
+ if( !p_block )
|
|
+ return VLCDEC_ECRITICAL;
|
|
+
|
|
+ p_block->i_buffer -= FF_INPUT_BUFFER_PADDING_SIZE;
|
|
+ memset( p_block->p_buffer + p_block->i_buffer, 0,
|
|
+ FF_INPUT_BUFFER_PADDING_SIZE );
|
|
+ }
|
|
+
|
|
+ AVPacket pkt = {.data = NULL, .size = 0};
|
|
+
|
|
+ av_init_packet( &pkt );
|
|
+ if( p_block && p_block->i_buffer > 0 )
|
|
+ {
|
|
+ pkt.data = p_block->p_buffer;
|
|
+ pkt.size = p_block->i_buffer;
|
|
+ pkt.pts = p_block->i_pts > VLC_TS_INVALID ? p_block->i_pts : AV_NOPTS_VALUE;
|
|
+ pkt.dts = p_block->i_dts > VLC_TS_INVALID ? p_block->i_dts : AV_NOPTS_VALUE;
|
|
+ }
|
|
+
|
|
+ if( !p_sys->palette_sent )
|
|
+ {
|
|
+ uint8_t *pal = av_packet_new_side_data(&pkt, AV_PKT_DATA_PALETTE, AVPALETTE_SIZE);
|
|
+ if (pal) {
|
|
+ memcpy(pal, p_dec->fmt_in.video.p_palette->palette, AVPALETTE_SIZE);
|
|
+ p_sys->palette_sent = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+#if LIBAVCODEC_VERSION_CHECK( 57, 0, 0xFFFFFFFFU, 64, 101 )
|
|
+ if( !b_need_output_picture )
|
|
+ pkt.flags |= AV_PKT_FLAG_DISCARD;
|
|
+#endif
|
|
+
|
|
+ int ret = avcodec_send_packet(p_context, &pkt);
|
|
+
|
|
+ if (ret == AVERROR(EAGAIN))
|
|
+ {
|
|
+ // Cannot send more data until output drained - so do drain
|
|
+ while (rx_frame(p_dec, p_sys, p_context) == 1)
|
|
+ /* Loop */;
|
|
+
|
|
+ // And try again - should not fail the same way
|
|
+ ret = avcodec_send_packet(p_context, &pkt);
|
|
+ }
|
|
+
|
|
+ // Now done with pkt & block
|
|
+ av_packet_unref(&pkt);
|
|
+ if (p_block != NULL)
|
|
+ {
|
|
+ block_Release( p_block );
|
|
+ p_block = NULL;
|
|
+ }
|
|
+
|
|
+ if (ret != 0)
|
|
+ {
|
|
+ if (ret == AVERROR(ENOMEM) || ret == AVERROR(EINVAL))
|
|
+ {
|
|
+ msg_Err(p_dec, "avcodec_send_packet critical error");
|
|
+ rv = VLCDEC_ECRITICAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ while (rx_frame(p_dec, p_sys, p_context) == 1)
|
|
+ /* Loop */;
|
|
+
|
|
+ if (eos_spotted)
|
|
+ p_sys->b_first_frame = true;
|
|
+
|
|
+ return rv;
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+ * EndVideo: decoder destruction
|
|
+ *****************************************************************************
|
|
+ * This function is called when the thread ends after a successful
|
|
+ * initialization.
|
|
+ *****************************************************************************/
|
|
+static void MmalAvcodecCloseDecoder(vlc_object_t *obj)
|
|
+{
|
|
+ decoder_t *p_dec = (decoder_t *)obj;
|
|
+ decoder_sys_t *p_sys = p_dec->p_sys;
|
|
+ AVCodecContext *ctx = p_sys->p_context;
|
|
+// void *hwaccel_context;
|
|
+
|
|
+ /* do not flush buffers if codec hasn't been opened (theora/vorbis/VC1) */
|
|
+ if( avcodec_is_open( ctx ) )
|
|
+ avcodec_flush_buffers( ctx );
|
|
+
|
|
+// cc_Flush( &p_sys->cc );
|
|
+
|
|
+ avcodec_close(ctx);
|
|
+ av_rpi_zc_uninit(ctx);
|
|
+
|
|
+// hwaccel_context = ctx->hwaccel_context;
|
|
+ avcodec_free_context( &ctx );
|
|
+
|
|
+ if( p_sys->out_pool != NULL )
|
|
+ mmal_pool_destroy(p_sys->out_pool);
|
|
+
|
|
+// if( p_sys->p_va )
|
|
+// vlc_va_Delete( p_sys->p_va, &hwaccel_context );
|
|
+
|
|
+ free( p_sys );
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+ * ffmpeg_InitCodec: setup codec extra initialization data for ffmpeg
|
|
+ *****************************************************************************/
|
|
+static void ffmpeg_InitCodec( decoder_t *p_dec )
|
|
+{
|
|
+ decoder_sys_t *p_sys = p_dec->p_sys;
|
|
+ size_t i_size = p_dec->fmt_in.i_extra;
|
|
+
|
|
+ if( !i_size ) return;
|
|
+
|
|
+ if( p_sys->p_codec->id == AV_CODEC_ID_SVQ3 )
|
|
+ {
|
|
+ uint8_t *p;
|
|
+
|
|
+ p_sys->p_context->extradata_size = i_size + 12;
|
|
+ p = p_sys->p_context->extradata =
|
|
+ av_malloc( p_sys->p_context->extradata_size +
|
|
+ FF_INPUT_BUFFER_PADDING_SIZE );
|
|
+ if( !p )
|
|
+ return;
|
|
+
|
|
+ memcpy( &p[0], "SVQ3", 4 );
|
|
+ memset( &p[4], 0, 8 );
|
|
+ memcpy( &p[12], p_dec->fmt_in.p_extra, i_size );
|
|
+
|
|
+ /* Now remove all atoms before the SMI one */
|
|
+ if( p_sys->p_context->extradata_size > 0x5a &&
|
|
+ strncmp( (char*)&p[0x56], "SMI ", 4 ) )
|
|
+ {
|
|
+ uint8_t *psz = &p[0x52];
|
|
+
|
|
+ while( psz < &p[p_sys->p_context->extradata_size - 8] )
|
|
+ {
|
|
+ uint_fast32_t atom_size = GetDWBE( psz );
|
|
+ if( atom_size <= 1 )
|
|
+ {
|
|
+ /* FIXME handle 1 as long size */
|
|
+ break;
|
|
+ }
|
|
+ if( !strncmp( (char*)&psz[4], "SMI ", 4 ) )
|
|
+ {
|
|
+ memmove( &p[0x52], psz,
|
|
+ &p[p_sys->p_context->extradata_size] - psz );
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ psz += atom_size;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ p_sys->p_context->extradata_size = i_size;
|
|
+ p_sys->p_context->extradata =
|
|
+ av_malloc( i_size + FF_INPUT_BUFFER_PADDING_SIZE );
|
|
+ if( p_sys->p_context->extradata )
|
|
+ {
|
|
+ memcpy( p_sys->p_context->extradata,
|
|
+ p_dec->fmt_in.p_extra, i_size );
|
|
+ memset( p_sys->p_context->extradata + i_size,
|
|
+ 0, FF_INPUT_BUFFER_PADDING_SIZE );
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static enum PixelFormat ffmpeg_GetFormat( AVCodecContext *p_context,
|
|
+ const enum PixelFormat *pi_fmt )
|
|
+{
|
|
+ decoder_t *p_dec = p_context->opaque;
|
|
+ decoder_sys_t *p_sys = p_dec->p_sys;
|
|
+ video_format_t fmt;
|
|
+
|
|
+ /* Enumerate available formats */
|
|
+ enum PixelFormat swfmt = avcodec_default_get_format(p_context, pi_fmt);
|
|
+// bool can_hwaccel = false;
|
|
+
|
|
+ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++)
|
|
+ {
|
|
+ const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(pi_fmt[i]);
|
|
+ if (dsc == NULL)
|
|
+ continue;
|
|
+ bool hwaccel = (dsc->flags & AV_PIX_FMT_FLAG_HWACCEL) != 0;
|
|
+
|
|
+ msg_Dbg( p_dec, "available %sware decoder output format %d (%s)",
|
|
+ hwaccel ? "hard" : "soft", pi_fmt[i], dsc->name );
|
|
+// if (hwaccel)
|
|
+// can_hwaccel = true;
|
|
+ }
|
|
+
|
|
+ /* If the format did not actually change (e.g. seeking), try to reuse the
|
|
+ * existing output format, and if present, hardware acceleration back-end.
|
|
+ * This avoids resetting the pipeline downstream. This also avoids
|
|
+ * needlessly probing for hardware acceleration support. */
|
|
+ if (p_sys->pix_fmt != AV_PIX_FMT_NONE
|
|
+ && lavc_GetVideoFormat(p_dec, &fmt, p_context, p_sys->pix_fmt, swfmt) == 0
|
|
+ && fmt.i_width == p_dec->fmt_out.video.i_width
|
|
+ && fmt.i_height == p_dec->fmt_out.video.i_height
|
|
+ && p_context->profile == p_sys->profile
|
|
+ && p_context->level <= p_sys->level)
|
|
+ {
|
|
+ for (size_t i = 0; pi_fmt[i] != AV_PIX_FMT_NONE; i++)
|
|
+ if (pi_fmt[i] == p_sys->pix_fmt)
|
|
+ {
|
|
+ msg_Dbg(p_dec, "reusing decoder output format %d", pi_fmt[i]);
|
|
+ return p_sys->pix_fmt;
|
|
+ }
|
|
+ }
|
|
+
|
|
+// if (p_sys->p_va != NULL)
|
|
+// {
|
|
+// msg_Err(p_dec, "existing hardware acceleration cannot be reused");
|
|
+// vlc_va_Delete(p_sys->p_va, &p_context->hwaccel_context);
|
|
+// p_sys->p_va = NULL;
|
|
+// }
|
|
+
|
|
+ p_sys->profile = p_context->profile;
|
|
+ p_sys->level = p_context->level;
|
|
+
|
|
+#if 1
|
|
+ return swfmt;
|
|
+#else
|
|
+ if (!can_hwaccel)
|
|
+ return swfmt;
|
|
+
|
|
+#if (LIBAVCODEC_VERSION_MICRO >= 100) \
|
|
+ && (LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 83, 101))
|
|
+ if (p_context->active_thread_type)
|
|
+ {
|
|
+ msg_Warn(p_dec, "thread type %d: disabling hardware acceleration",
|
|
+ p_context->active_thread_type);
|
|
+ return swfmt;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ wait_mt(p_sys);
|
|
+
|
|
+ static const enum PixelFormat hwfmts[] =
|
|
+ {
|
|
+#ifdef _WIN32
|
|
+#if LIBAVUTIL_VERSION_CHECK(54, 13, 1, 24, 100)
|
|
+ AV_PIX_FMT_D3D11VA_VLD,
|
|
+#endif
|
|
+ AV_PIX_FMT_DXVA2_VLD,
|
|
+#endif
|
|
+ AV_PIX_FMT_VAAPI_VLD,
|
|
+#if (LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(52, 4, 0))
|
|
+ AV_PIX_FMT_VDPAU,
|
|
+#endif
|
|
+ AV_PIX_FMT_NONE,
|
|
+ };
|
|
+
|
|
+ for( size_t i = 0; hwfmts[i] != AV_PIX_FMT_NONE; i++ )
|
|
+ {
|
|
+ enum PixelFormat hwfmt = AV_PIX_FMT_NONE;
|
|
+ for( size_t j = 0; hwfmt == AV_PIX_FMT_NONE && pi_fmt[j] != AV_PIX_FMT_NONE; j++ )
|
|
+ if( hwfmts[i] == pi_fmt[j] )
|
|
+ hwfmt = hwfmts[i];
|
|
+
|
|
+ if( hwfmt == AV_PIX_FMT_NONE )
|
|
+ continue;
|
|
+
|
|
+ p_dec->fmt_out.video.i_chroma = vlc_va_GetChroma(hwfmt, swfmt);
|
|
+ if (p_dec->fmt_out.video.i_chroma == 0)
|
|
+ continue; /* Unknown brand of hardware acceleration */
|
|
+ if (p_context->width == 0 || p_context->height == 0)
|
|
+ { /* should never happen */
|
|
+ msg_Err(p_dec, "unspecified video dimensions");
|
|
+ continue;
|
|
+ }
|
|
+ const AVPixFmtDescriptor *dsc = av_pix_fmt_desc_get(hwfmt);
|
|
+ msg_Dbg(p_dec, "trying format %s", dsc ? dsc->name : "unknown");
|
|
+ if (lavc_UpdateVideoFormat(p_dec, p_context, hwfmt, swfmt))
|
|
+ continue; /* Unsupported brand of hardware acceleration */
|
|
+ post_mt(p_sys);
|
|
+
|
|
+ picture_t *test_pic = decoder_NewPicture(p_dec);
|
|
+ assert(!test_pic || test_pic->format.i_chroma == p_dec->fmt_out.video.i_chroma);
|
|
+ vlc_va_t *va = vlc_va_New(VLC_OBJECT(p_dec), p_context, hwfmt,
|
|
+ &p_dec->fmt_in,
|
|
+ test_pic ? test_pic->p_sys : NULL);
|
|
+ if (test_pic)
|
|
+ picture_Release(test_pic);
|
|
+ if (va == NULL)
|
|
+ {
|
|
+ wait_mt(p_sys);
|
|
+ continue; /* Unsupported codec profile or such */
|
|
+ }
|
|
+
|
|
+ if (va->description != NULL)
|
|
+ msg_Info(p_dec, "Using %s for hardware decoding", va->description);
|
|
+
|
|
+ p_sys->p_va = va;
|
|
+ p_sys->pix_fmt = hwfmt;
|
|
+ p_context->draw_horiz_band = NULL;
|
|
+ return hwfmt;
|
|
+ }
|
|
+
|
|
+ post_mt(p_sys);
|
|
+ /* Fallback to default behaviour */
|
|
+ p_sys->pix_fmt = swfmt;
|
|
+ return swfmt;
|
|
+#endif
|
|
+}
|
|
+
|
|
+/*****************************************************************************
|
|
+ * InitVideo: initialize the video decoder
|
|
+ *****************************************************************************
|
|
+ * the ffmpeg codec will be opened, some memory allocated. The vout is not yet
|
|
+ * opened (done after the first decoded frame).
|
|
+ *****************************************************************************/
|
|
+
|
|
+/*****************************************************************************
|
|
+ * ffmpeg_OpenCodec:
|
|
+ *****************************************************************************/
|
|
+
|
|
+static int MmalAvcodecOpenDecoder( vlc_object_t *obj )
|
|
+{
|
|
+ decoder_t *p_dec = (decoder_t *)obj;
|
|
+ const AVCodec *p_codec;
|
|
+
|
|
+ if (p_dec->fmt_in.i_codec != VLC_CODEC_HEVC)
|
|
+ return VLC_EGENERIC;
|
|
+
|
|
+ AVCodecContext *p_context = ffmpeg_AllocContext( p_dec, &p_codec );
|
|
+ if( p_context == NULL )
|
|
+ return VLC_EGENERIC;
|
|
+
|
|
+ int i_val;
|
|
+
|
|
+ /* Allocate the memory needed to store the decoder's structure */
|
|
+ decoder_sys_t *p_sys = calloc( 1, sizeof(*p_sys) );
|
|
+ if( unlikely(p_sys == NULL) )
|
|
+ {
|
|
+ avcodec_free_context( &p_context );
|
|
+ return VLC_ENOMEM;
|
|
+ }
|
|
+
|
|
+ p_dec->p_sys = p_sys;
|
|
+ p_sys->p_context = p_context;
|
|
+ p_sys->p_codec = p_codec;
|
|
+// p_sys->p_va = NULL;
|
|
+
|
|
+ /* ***** Fill p_context with init values ***** */
|
|
+ p_context->codec_tag = ffmpeg_CodecTag( p_dec->fmt_in.i_original_fourcc ?
|
|
+ p_dec->fmt_in.i_original_fourcc : p_dec->fmt_in.i_codec );
|
|
+
|
|
+ /* ***** Get configuration of ffmpeg plugin ***** */
|
|
+ p_context->workaround_bugs =
|
|
+ var_InheritInteger( p_dec, "avcodec-workaround-bugs" );
|
|
+ p_context->err_recognition =
|
|
+ var_InheritInteger( p_dec, "avcodec-error-resilience" );
|
|
+
|
|
+ if( var_CreateGetBool( p_dec, "grayscale" ) )
|
|
+ p_context->flags |= AV_CODEC_FLAG_GRAY;
|
|
+
|
|
+ /* ***** Output always the frames ***** */
|
|
+ p_context->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
|
|
+
|
|
+ i_val = var_CreateGetInteger( p_dec, "avcodec-skiploopfilter" );
|
|
+ if( i_val >= 4 ) p_context->skip_loop_filter = AVDISCARD_ALL;
|
|
+ else if( i_val == 3 ) p_context->skip_loop_filter = AVDISCARD_NONKEY;
|
|
+ else if( i_val == 2 ) p_context->skip_loop_filter = AVDISCARD_BIDIR;
|
|
+ else if( i_val == 1 ) p_context->skip_loop_filter = AVDISCARD_NONREF;
|
|
+ else p_context->skip_loop_filter = AVDISCARD_DEFAULT;
|
|
+
|
|
+ if( var_CreateGetBool( p_dec, "avcodec-fast" ) )
|
|
+ p_context->flags2 |= AV_CODEC_FLAG2_FAST;
|
|
+
|
|
+ /* ***** libavcodec frame skipping ***** */
|
|
+ p_sys->b_hurry_up = var_CreateGetBool( p_dec, "avcodec-hurry-up" );
|
|
+ p_sys->b_show_corrupted = var_CreateGetBool( p_dec, "avcodec-corrupted" );
|
|
+
|
|
+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-frame" );
|
|
+ if( i_val >= 4 ) p_sys->i_skip_frame = AVDISCARD_ALL;
|
|
+ else if( i_val == 3 ) p_sys->i_skip_frame = AVDISCARD_NONKEY;
|
|
+ else if( i_val == 2 ) p_sys->i_skip_frame = AVDISCARD_BIDIR;
|
|
+ else if( i_val == 1 ) p_sys->i_skip_frame = AVDISCARD_NONREF;
|
|
+ else if( i_val == -1 ) p_sys->i_skip_frame = AVDISCARD_NONE;
|
|
+ else p_sys->i_skip_frame = AVDISCARD_DEFAULT;
|
|
+ p_context->skip_frame = p_sys->i_skip_frame;
|
|
+
|
|
+ i_val = var_CreateGetInteger( p_dec, "avcodec-skip-idct" );
|
|
+ if( i_val >= 4 ) p_context->skip_idct = AVDISCARD_ALL;
|
|
+ else if( i_val == 3 ) p_context->skip_idct = AVDISCARD_NONKEY;
|
|
+ else if( i_val == 2 ) p_context->skip_idct = AVDISCARD_BIDIR;
|
|
+ else if( i_val == 1 ) p_context->skip_idct = AVDISCARD_NONREF;
|
|
+ else if( i_val == -1 ) p_context->skip_idct = AVDISCARD_NONE;
|
|
+ else p_context->skip_idct = AVDISCARD_DEFAULT;
|
|
+
|
|
+ /* ***** libavcodec direct rendering ***** */
|
|
+ p_sys->b_direct_rendering = false;
|
|
+ atomic_init(&p_sys->b_dr_failure, false);
|
|
+ if( var_CreateGetBool( p_dec, "avcodec-dr" ) &&
|
|
+ (p_codec->capabilities & AV_CODEC_CAP_DR1) &&
|
|
+ /* No idea why ... but this fixes flickering on some TSCC streams */
|
|
+ p_sys->p_codec->id != AV_CODEC_ID_TSCC &&
|
|
+ p_sys->p_codec->id != AV_CODEC_ID_CSCD &&
|
|
+ p_sys->p_codec->id != AV_CODEC_ID_CINEPAK )
|
|
+ {
|
|
+ /* Some codecs set pix_fmt only after the 1st frame has been decoded,
|
|
+ * so we need to do another check in ffmpeg_GetFrameBuf() */
|
|
+ p_sys->b_direct_rendering = true;
|
|
+ }
|
|
+
|
|
+ p_context->get_format = ffmpeg_GetFormat;
|
|
+ /* Always use our get_buffer wrapper so we can calculate the
|
|
+ * PTS correctly */
|
|
+// p_context->get_buffer2 = lavc_GetFrame;
|
|
+// p_context->opaque = p_dec;
|
|
+
|
|
+ int i_thread_count = var_InheritInteger( p_dec, "avcodec-threads" );
|
|
+ if( i_thread_count <= 0 )
|
|
+ i_thread_count = 6;
|
|
+#if 0
|
|
+ if( i_thread_count <= 0 )
|
|
+ {
|
|
+ i_thread_count = vlc_GetCPUCount();
|
|
+ if( i_thread_count > 1 )
|
|
+ i_thread_count++;
|
|
+
|
|
+ //FIXME: take in count the decoding time
|
|
+#if VLC_WINSTORE_APP
|
|
+ i_thread_count = __MIN( i_thread_count, 6 );
|
|
+#else
|
|
+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 10 : 6 );
|
|
+#endif
|
|
+ }
|
|
+ i_thread_count = __MIN( i_thread_count, p_codec->id == AV_CODEC_ID_HEVC ? 32 : 16 );
|
|
+#endif
|
|
+ msg_Dbg( p_dec, "allowing %d thread(s) for decoding", i_thread_count );
|
|
+ p_context->thread_count = i_thread_count;
|
|
+ p_context->thread_safe_callbacks = true;
|
|
+
|
|
+ p_context->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
|
|
+
|
|
+ if( p_context->thread_type & FF_THREAD_FRAME )
|
|
+ p_dec->i_extra_picture_buffers = 2 * p_context->thread_count;
|
|
+
|
|
+ /* ***** misc init ***** */
|
|
+ date_Init(&p_sys->pts, 1, 30001);
|
|
+ date_Set(&p_sys->pts, VLC_TS_INVALID);
|
|
+ p_sys->b_first_frame = true;
|
|
+ p_sys->i_late_frames = 0;
|
|
+ p_sys->b_from_preroll = false;
|
|
+
|
|
+ /* Set output properties */
|
|
+ if( GetVlcChroma( &p_dec->fmt_out.video, p_context->pix_fmt ) != VLC_SUCCESS )
|
|
+ {
|
|
+ /* we are doomed. but not really, because most codecs set their pix_fmt later on */
|
|
+ p_dec->fmt_out.i_codec = VLC_CODEC_I420;
|
|
+ }
|
|
+ p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma;
|
|
+
|
|
+ p_dec->fmt_out.video.orientation = p_dec->fmt_in.video.orientation;
|
|
+
|
|
+ if( p_dec->fmt_in.video.p_palette ) {
|
|
+ p_sys->palette_sent = false;
|
|
+ p_dec->fmt_out.video.p_palette = malloc( sizeof(video_palette_t) );
|
|
+ if( p_dec->fmt_out.video.p_palette )
|
|
+ *p_dec->fmt_out.video.p_palette = *p_dec->fmt_in.video.p_palette;
|
|
+ } else
|
|
+ p_sys->palette_sent = true;
|
|
+
|
|
+ /* ***** init this codec with special data ***** */
|
|
+ ffmpeg_InitCodec( p_dec );
|
|
+
|
|
+ /* ***** Open the codec ***** */
|
|
+ if( OpenVideoCodec( p_dec ) < 0 )
|
|
+ {
|
|
+ free( p_sys );
|
|
+ avcodec_free_context( &p_context );
|
|
+ return VLC_EGENERIC;
|
|
+ }
|
|
+
|
|
+ if ((p_sys->out_pool = mmal_pool_create(5, 0)) == NULL)
|
|
+ {
|
|
+ msg_Err(p_dec, "Failed to create mmal buffer pool");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ p_dec->pf_decode = DecodeVideo;
|
|
+ p_dec->pf_flush = Flush;
|
|
+
|
|
+ /* XXX: Writing input format makes little sense. */
|
|
+ if( p_context->profile != FF_PROFILE_UNKNOWN )
|
|
+ p_dec->fmt_in.i_profile = p_context->profile;
|
|
+ if( p_context->level != FF_LEVEL_UNKNOWN )
|
|
+ p_dec->fmt_in.i_level = p_context->level;
|
|
+ return VLC_SUCCESS;
|
|
+
|
|
+fail:
|
|
+ MmalAvcodecCloseDecoder(VLC_OBJECT(p_dec));
|
|
+ return VLC_EGENERIC;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+vlc_module_begin()
|
|
+ set_category( CAT_INPUT )
|
|
+ set_subcategory( SUBCAT_INPUT_VCODEC )
|
|
+ set_shortname(N_("MMAL avcodec"))
|
|
+ set_description(N_("MMAL buffered avcodec "))
|
|
+ set_capability("video decoder", 800)
|
|
+ add_shortcut("mmal_avcodec")
|
|
+ set_callbacks(MmalAvcodecOpenDecoder, MmalAvcodecCloseDecoder)
|
|
+vlc_module_end()
|
|
+
|
|
--- a/modules/hw/mmal/mmal_picture.c
|
|
+++ b/modules/hw/mmal/mmal_picture.c
|
|
@@ -21,25 +21,961 @@
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
+// We would really like to use vlc_thread.h but the detach thread stuff can't be
|
|
+// used here :-(
|
|
+#include <pthread.h>
|
|
+
|
|
+#include <stdatomic.h>
|
|
+
|
|
#include <vlc_common.h>
|
|
#include <vlc_picture.h>
|
|
#include <interface/mmal/mmal.h>
|
|
+#include <interface/mmal/util/mmal_util.h>
|
|
+#include <interface/mmal/util/mmal_default_components.h>
|
|
+#include <interface/vmcs_host/vcgencmd.h>
|
|
+#include <interface/vcsm/user-vcsm.h>
|
|
|
|
#include "mmal_picture.h"
|
|
|
|
-int mmal_picture_lock(picture_t *picture)
|
|
+
|
|
+static void flush_range(void * start, size_t len)
|
|
+{
|
|
+ struct vcsm_user_clean_invalid2_s * b = calloc(1,
|
|
+ sizeof(struct vcsm_user_clean_invalid2_s) + sizeof(struct vcsm_user_clean_invalid2_block_s));
|
|
+
|
|
+ b->op_count = 1,
|
|
+
|
|
+ b->s[0] = (struct vcsm_user_clean_invalid2_block_s){
|
|
+ .invalidate_mode = 3,
|
|
+ .block_count = 1,
|
|
+ .start_address = start,
|
|
+ .block_size = len,
|
|
+ .inter_block_stride = 0
|
|
+ };
|
|
+
|
|
+ vcsm_clean_invalid2(b);
|
|
+
|
|
+ free(b);
|
|
+}
|
|
+
|
|
+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs)
|
|
+{
|
|
+ switch (vlc_cs)
|
|
+ {
|
|
+ case COLOR_SPACE_BT601:
|
|
+ return MMAL_COLOR_SPACE_ITUR_BT601;
|
|
+ case COLOR_SPACE_BT709:
|
|
+ return MMAL_COLOR_SPACE_ITUR_BT709;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return MMAL_COLOR_SPACE_UNKNOWN;
|
|
+}
|
|
+
|
|
+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc)
|
|
+{
|
|
+ switch (vf_vlc->i_chroma) {
|
|
+ case VLC_CODEC_RGB32:
|
|
+ {
|
|
+ // VLC RGB32 aka RV32 means we have to look at the mask values
|
|
+ const uint32_t r = vf_vlc->i_rmask;
|
|
+ const uint32_t g = vf_vlc->i_gmask;
|
|
+ const uint32_t b = vf_vlc->i_bmask;
|
|
+ if (r == 0xff0000 && g == 0xff00 && b == 0xff)
|
|
+ return MMAL_ENCODING_BGRA;
|
|
+ if (r == 0xff && g == 0xff00 && b == 0xff0000)
|
|
+ return MMAL_ENCODING_RGBA;
|
|
+ if (r == 0xff000000 && g == 0xff0000 && b == 0xff00)
|
|
+ return MMAL_ENCODING_ABGR;
|
|
+ if (r == 0xff00 && g == 0xff0000 && b == 0xff000000)
|
|
+ return MMAL_ENCODING_ARGB;
|
|
+ break;
|
|
+ }
|
|
+ case VLC_CODEC_RGBA:
|
|
+ return MMAL_ENCODING_RGBA;
|
|
+ case VLC_CODEC_BGRA:
|
|
+ return MMAL_ENCODING_BGRA;
|
|
+ case VLC_CODEC_ARGB:
|
|
+ return MMAL_ENCODING_ARGB;
|
|
+ // VLC_CODEC_ABGR does not exist in VLC
|
|
+ case VLC_CODEC_MMAL_OPAQUE:
|
|
+ return MMAL_ENCODING_OPAQUE;
|
|
+ case VLC_CODEC_MMAL_ZC_SAND8:
|
|
+ return MMAL_ENCODING_YUVUV128;
|
|
+ case VLC_CODEC_MMAL_ZC_SAND10:
|
|
+ return MMAL_ENCODING_YUVUV64_10;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+void vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc)
|
|
+{
|
|
+ MMAL_VIDEO_FORMAT_T * const vf_mmal = &es_fmt->es->video;
|
|
+
|
|
+ vf_mmal->width = (vf_vlc->i_width + 31) & ~31;
|
|
+ vf_mmal->height = (vf_vlc->i_height + 15) & ~15;
|
|
+ vf_mmal->crop.x = vf_vlc->i_x_offset;
|
|
+ vf_mmal->crop.y = vf_vlc->i_y_offset;
|
|
+ vf_mmal->crop.width = vf_vlc->i_visible_width;
|
|
+ vf_mmal->crop.height = vf_vlc->i_visible_height;
|
|
+ if (vf_vlc->i_sar_num == 0 || vf_vlc->i_sar_den == 0) {
|
|
+ vf_mmal->par.num = 1;
|
|
+ vf_mmal->par.den = 1;
|
|
+ } else {
|
|
+ vf_mmal->par.num = vf_vlc->i_sar_num;
|
|
+ vf_mmal->par.den = vf_vlc->i_sar_den;
|
|
+ }
|
|
+ vf_mmal->frame_rate.num = vf_vlc->i_frame_rate;
|
|
+ vf_mmal->frame_rate.den = vf_vlc->i_frame_rate_base;
|
|
+ vf_mmal->color_space = vlc_to_mmal_color_space(vf_vlc->space);
|
|
+}
|
|
+
|
|
+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
|
|
+ const unsigned int headers, const uint32_t payload_size)
|
|
+{
|
|
+ hw_mmal_port_pool_ref_t * ppr = calloc(1, sizeof(hw_mmal_port_pool_ref_t));
|
|
+ if (ppr == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ if ((ppr->pool = mmal_port_pool_create(port, headers, payload_size)) == NULL)
|
|
+ goto fail;
|
|
+
|
|
+ ppr->port = port;
|
|
+ atomic_store(&ppr->refs, 1);
|
|
+ return ppr;
|
|
+
|
|
+fail:
|
|
+ free(ppr);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void do_detached(void *(*fn)(void *), void * v)
|
|
+{
|
|
+ pthread_t dothread;
|
|
+ pthread_create(&dothread, NULL, fn, v);
|
|
+ pthread_detach(dothread);
|
|
+}
|
|
+
|
|
+// Destroy a ppr - aranged s.t. it has the correct prototype for a pthread
|
|
+static void * kill_ppr(void * v)
|
|
+{
|
|
+ hw_mmal_port_pool_ref_t * const ppr = v;
|
|
+ if (ppr->port->is_enabled)
|
|
+ mmal_port_disable(ppr->port); // Avoid annoyed messages from MMAL when we kill the pool
|
|
+ mmal_port_pool_destroy(ppr->port, ppr->pool);
|
|
+ free(ppr);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb)
|
|
+{
|
|
+ if (ppr == NULL)
|
|
+ return;
|
|
+ if (atomic_fetch_sub(&ppr->refs, 1) != 1)
|
|
+ return;
|
|
+ if (in_cb)
|
|
+ do_detached(kill_ppr, ppr);
|
|
+ else
|
|
+ kill_ppr(ppr);
|
|
+}
|
|
+
|
|
+// Put buffer in port if possible - if not then release to pool
|
|
+// Returns true if sent, false if recycled
|
|
+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf)
|
|
+{
|
|
+ mmal_buffer_header_reset(buf);
|
|
+ buf->user_data = NULL;
|
|
+
|
|
+ if (mmal_port_send_buffer(ppr->port, buf) == MMAL_SUCCESS)
|
|
+ return true;
|
|
+ mmal_buffer_header_release(buf);
|
|
+ return false;
|
|
+}
|
|
+
|
|
+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr)
|
|
+{
|
|
+ MMAL_BUFFER_HEADER_T * buf;
|
|
+ MMAL_STATUS_T err = MMAL_SUCCESS;
|
|
+
|
|
+ while ((buf = mmal_queue_get(ppr->pool->queue)) != NULL) {
|
|
+ if ((err = mmal_port_send_buffer(ppr->port, buf)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ mmal_queue_put_back(ppr->pool->queue, buf);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ return err;
|
|
+}
|
|
+
|
|
+
|
|
+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
|
|
+ hw_mmal_port_pool_ref_t ** pppr,
|
|
+ MMAL_PORT_T * const port,
|
|
+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback)
|
|
+{
|
|
+ MMAL_STATUS_T status;
|
|
+
|
|
+ port->userdata = (struct MMAL_PORT_USERDATA_T *)obj;
|
|
+
|
|
+ status = port_parameter_set_uint32(port, MMAL_PARAMETER_EXTRA_BUFFERS, extra_buffers);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(obj, "Failed to set MMAL_PARAMETER_EXTRA_BUFFERS on output port (status=%"PRIx32" %s)",
|
|
+ status, mmal_status_to_string(status));
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ status = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, 1);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(obj, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
|
|
+ port->name, status, mmal_status_to_string(status));
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ port->format->encoding = MMAL_ENCODING_OPAQUE;
|
|
+ port->format->encoding_variant = 0;
|
|
+ if ((status = mmal_port_format_commit(port)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(obj, "Failed to commit format on port %s (status=%"PRIx32" %s)",
|
|
+ port->name, status, mmal_status_to_string(status));
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ port->buffer_num = 30;
|
|
+ port->buffer_size = port->buffer_size_recommended;
|
|
+
|
|
+ if ((*pppr = hw_mmal_port_pool_ref_create(port, port->buffer_num, port->buffer_size)) == NULL) {
|
|
+ msg_Err(obj, "Failed to create output pool");
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ status = mmal_port_enable(port, callback);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(obj, "Failed to enable output port %s (status=%"PRIx32" %s)",
|
|
+ port->name, status, mmal_status_to_string(status));
|
|
+ return status;
|
|
+ }
|
|
+
|
|
+ return MMAL_SUCCESS;
|
|
+}
|
|
+
|
|
+
|
|
+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn)
|
|
+{
|
|
+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i != ctx->buf_count; ++i) {
|
|
+ mmal_buffer_header_release(ctx->bufs[i]);
|
|
+ }
|
|
+ free(ctx);
|
|
+}
|
|
+
|
|
+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn)
|
|
+{
|
|
+ const pic_ctx_mmal_t * const src_ctx = (pic_ctx_mmal_t *)pic_ctx_cmn;
|
|
+ pic_ctx_mmal_t * const dst_ctx = calloc(1, sizeof(*dst_ctx));
|
|
+ unsigned int i;
|
|
+
|
|
+ if (dst_ctx == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ // Copy
|
|
+ dst_ctx->cmn = src_ctx->cmn;
|
|
+
|
|
+ dst_ctx->buf_count = src_ctx->buf_count;
|
|
+ for (i = 0; i != src_ctx->buf_count; ++i) {
|
|
+ dst_ctx->bufs[i] = src_ctx->bufs[i];
|
|
+ mmal_buffer_header_acquire(dst_ctx->bufs[i]);
|
|
+ }
|
|
+
|
|
+ return &dst_ctx->cmn;
|
|
+}
|
|
+
|
|
+static MMAL_BOOL_T
|
|
+buf_pre_release_cb(MMAL_BUFFER_HEADER_T * buf, void *userdata)
|
|
+{
|
|
+ hw_mmal_port_pool_ref_t * const ppr = userdata;
|
|
+
|
|
+ // Kill the callback - otherwise we will go in circles!
|
|
+ mmal_buffer_header_pre_release_cb_set(buf, (MMAL_BH_PRE_RELEASE_CB_T)0, NULL);
|
|
+ mmal_buffer_header_acquire(buf); // Ref it again
|
|
+
|
|
+ // As we have re-acquired the buffer we need a full release
|
|
+ // (not continue) to zap the ref count back to zero
|
|
+ // This is "safe" 'cos we have already reset the cb
|
|
+ hw_mmal_port_pool_ref_recycle(ppr, buf);
|
|
+ hw_mmal_port_pool_ref_release(ppr, true); // Assume in callback
|
|
+
|
|
+ return MMAL_TRUE;
|
|
+}
|
|
+
|
|
+// Buffer belongs to context on successful return from this fn
|
|
+// is still valid on failure
|
|
+picture_context_t *
|
|
+hw_mmal_gen_context(const MMAL_FOURCC_T fmt, MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr)
|
|
+{
|
|
+ pic_ctx_mmal_t * const ctx = calloc(1, sizeof(pic_ctx_mmal_t));
|
|
+
|
|
+ if (ctx == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ // If we have an associated ppr then ref & set appropriate callbacks
|
|
+ if (ppr != NULL) {
|
|
+ hw_mmal_port_pool_ref_acquire(ppr);
|
|
+ mmal_buffer_header_pre_release_cb_set(buf, buf_pre_release_cb, ppr);
|
|
+ buf->user_data = NULL;
|
|
+ }
|
|
+
|
|
+ ctx->cmn.copy = hw_mmal_pic_ctx_copy;
|
|
+ ctx->cmn.destroy = hw_mmal_pic_ctx_destroy;
|
|
+
|
|
+ ctx->fmt = fmt;
|
|
+ ctx->buf_count = 1;
|
|
+ ctx->bufs[0] = buf;
|
|
+
|
|
+ return &ctx->cmn;
|
|
+}
|
|
+
|
|
+int hw_mmal_get_gpu_mem(void) {
|
|
+ static int stashed_val = -2;
|
|
+ VCHI_INSTANCE_T vchi_instance;
|
|
+ VCHI_CONNECTION_T *vchi_connection = NULL;
|
|
+ char rbuf[1024] = { 0 };
|
|
+
|
|
+ if (stashed_val >= -1)
|
|
+ return stashed_val;
|
|
+
|
|
+ if (vchi_initialise(&vchi_instance) != 0)
|
|
+ goto fail0;
|
|
+
|
|
+ //create a vchi connection
|
|
+ if (vchi_connect(NULL, 0, vchi_instance) != 0)
|
|
+ goto fail0;
|
|
+
|
|
+ vc_vchi_gencmd_init(vchi_instance, &vchi_connection, 1);
|
|
+
|
|
+ //send the gencmd for the argument
|
|
+ if (vc_gencmd_send("get_mem gpu") != 0)
|
|
+ goto fail;
|
|
+
|
|
+ if (vc_gencmd_read_response(rbuf, sizeof(rbuf) - 1) != 0)
|
|
+ goto fail;
|
|
+
|
|
+ if (strncmp(rbuf, "gpu=", 4) != 0)
|
|
+ goto fail;
|
|
+
|
|
+ char *p;
|
|
+ unsigned long m = strtoul(rbuf + 4, &p, 10);
|
|
+
|
|
+ if (p[0] != 'M' || p[1] != '\0')
|
|
+ stashed_val = -1;
|
|
+ else
|
|
+ stashed_val = (int)m << 20;
|
|
+
|
|
+ vc_gencmd_stop();
|
|
+
|
|
+ //close the vchi connection
|
|
+ vchi_disconnect(vchi_instance);
|
|
+
|
|
+ return stashed_val;
|
|
+
|
|
+fail:
|
|
+ vc_gencmd_stop();
|
|
+ vchi_disconnect(vchi_instance);
|
|
+fail0:
|
|
+ stashed_val = -1;
|
|
+ return -1;
|
|
+};
|
|
+
|
|
+// ===========================================================================
|
|
+
|
|
+typedef struct pool_ent_s
|
|
+{
|
|
+ struct pool_ent_s * next;
|
|
+ struct pool_ent_s * prev;
|
|
+
|
|
+ atomic_int ref_count;
|
|
+ unsigned int seq;
|
|
+
|
|
+ size_t size;
|
|
+
|
|
+ int vcsm_hdl;
|
|
+ int vc_hdl;
|
|
+ void * buf;
|
|
+
|
|
+ unsigned int width;
|
|
+ unsigned int height;
|
|
+ MMAL_FOURCC_T enc_type;
|
|
+
|
|
+ picture_t * pic;
|
|
+} pool_ent_t;
|
|
+
|
|
+
|
|
+typedef struct ent_list_hdr_s
|
|
+{
|
|
+ pool_ent_t * ents;
|
|
+ pool_ent_t * tail;
|
|
+ unsigned int n;
|
|
+} ent_list_hdr_t;
|
|
+
|
|
+#define ENT_LIST_HDR_INIT (ent_list_hdr_t){ \
|
|
+ .ents = NULL, \
|
|
+ .tail = NULL, \
|
|
+ .n = 0 \
|
|
+}
|
|
+
|
|
+struct vzc_pool_ctl_s
|
|
+{
|
|
+ atomic_int ref_count;
|
|
+
|
|
+ ent_list_hdr_t ent_pool;
|
|
+ ent_list_hdr_t ents_cur;
|
|
+ ent_list_hdr_t ents_prev;
|
|
+
|
|
+ unsigned int max_n;
|
|
+ unsigned int seq;
|
|
+
|
|
+ vlc_mutex_t lock;
|
|
+
|
|
+ MMAL_POOL_T * buf_pool;
|
|
+};
|
|
+
|
|
+typedef struct vzc_subbuf_ent_s
|
|
+{
|
|
+ pool_ent_t * ent;
|
|
+ MMAL_RECT_T pic_rect;
|
|
+ MMAL_RECT_T orig_dest_rect;
|
|
+ MMAL_DISPLAYREGION_T dreg;
|
|
+} vzc_subbuf_ent_t;
|
|
+
|
|
+
|
|
+static pool_ent_t * ent_extract(ent_list_hdr_t * const elh, pool_ent_t * const ent)
|
|
+{
|
|
+// printf("List %p [%d]: Ext %p\n", elh, elh->n, ent);
|
|
+
|
|
+ if (ent == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ if (ent->next == NULL)
|
|
+ elh->tail = ent->prev;
|
|
+ else
|
|
+ ent->next->prev = ent->prev;
|
|
+
|
|
+ if (ent->prev == NULL)
|
|
+ elh->ents = ent->next;
|
|
+ else
|
|
+ ent->prev->next = ent->next;
|
|
+
|
|
+ ent->prev = ent->next = NULL;
|
|
+
|
|
+ --elh->n;
|
|
+
|
|
+ return ent; // For convienience
|
|
+}
|
|
+
|
|
+static inline pool_ent_t * ent_extract_tail(ent_list_hdr_t * const elh)
|
|
+{
|
|
+ return ent_extract(elh, elh->tail);
|
|
+}
|
|
+
|
|
+static void ent_add_head(ent_list_hdr_t * const elh, pool_ent_t * const ent)
|
|
+{
|
|
+// printf("List %p [%d]: Add %p\n", elh, elh->n, ent);
|
|
+
|
|
+ if ((ent->next = elh->ents) == NULL)
|
|
+ elh->tail = ent;
|
|
+ else
|
|
+ ent->next->prev = ent;
|
|
+
|
|
+ ent->prev = NULL;
|
|
+ elh->ents = ent;
|
|
+ ++elh->n;
|
|
+}
|
|
+
|
|
+static void ent_free(pool_ent_t * const ent)
|
|
+{
|
|
+// printf("Free ent: %p\n", ent);
|
|
+ if (ent != NULL) {
|
|
+ // If we still have a ref to a pic - kill it now
|
|
+ if (ent->pic != NULL)
|
|
+ picture_Release(ent->pic);
|
|
+
|
|
+ // Free contents
|
|
+ vcsm_unlock_hdl(ent->vcsm_hdl);
|
|
+
|
|
+ vcsm_free(ent->vcsm_hdl);
|
|
+
|
|
+ free(ent);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ent_free_list(ent_list_hdr_t * const elh)
|
|
+{
|
|
+ pool_ent_t * ent = elh->ents;
|
|
+
|
|
+// printf("Free list: %p [%d]\n", elh, elh->n);
|
|
+
|
|
+ *elh = ENT_LIST_HDR_INIT;
|
|
+
|
|
+ while (ent != NULL) {
|
|
+ pool_ent_t * const t = ent;
|
|
+ ent = t->next;
|
|
+ ent_free(t);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ent_list_move(ent_list_hdr_t * const dst, ent_list_hdr_t * const src)
|
|
+{
|
|
+// printf("Move %p->%p\n", src, dst);
|
|
+
|
|
+ *dst = *src;
|
|
+ *src = ENT_LIST_HDR_INIT;
|
|
+}
|
|
+
|
|
+// Scans "backwards" as that should give us the fastest match if we are
|
|
+// presented with pics in the same order each time
|
|
+static pool_ent_t * ent_list_extract_pic_ent(ent_list_hdr_t * const elh, picture_t * const pic)
|
|
+{
|
|
+ pool_ent_t *ent = elh->tail;
|
|
+
|
|
+// printf("Find list: %p [%d]; pic:%p\n", elh, elh->n, pic);
|
|
+
|
|
+ while (ent != NULL) {
|
|
+// printf("Check ent: %p, pic:%p\n", ent, ent->pic);
|
|
+
|
|
+ if (ent->pic == pic)
|
|
+ return ent_extract(elh, ent);
|
|
+ ent = ent->prev;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#define POOL_ENT_ALLOC_BLOCK 0x10000
|
|
+
|
|
+static pool_ent_t * pool_ent_alloc_new(size_t req_size)
|
|
+{
|
|
+ pool_ent_t * ent = calloc(1, sizeof(*ent));
|
|
+ const size_t alloc_size = (req_size + POOL_ENT_ALLOC_BLOCK - 1) & ~(POOL_ENT_ALLOC_BLOCK - 1);
|
|
+
|
|
+ if (ent == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ ent->next = ent->prev = NULL;
|
|
+
|
|
+ // Alloc from vcsm
|
|
+ if ((ent->vcsm_hdl = vcsm_malloc_cache(alloc_size, VCSM_CACHE_TYPE_HOST, (char *)"vlc-subpic")) == -1)
|
|
+ goto fail1;
|
|
+ if ((ent->vc_hdl = vcsm_vc_hdl_from_hdl(ent->vcsm_hdl)) == 0)
|
|
+ goto fail2;
|
|
+ if ((ent->buf = vcsm_lock(ent->vcsm_hdl)) == NULL)
|
|
+ goto fail2;
|
|
+
|
|
+ ent->size = alloc_size;
|
|
+ return ent;
|
|
+
|
|
+fail2:
|
|
+ vcsm_free(ent->vcsm_hdl);
|
|
+fail1:
|
|
+ free(ent);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static inline pool_ent_t * pool_ent_ref(pool_ent_t * const ent)
|
|
+{
|
|
+// int n = atomic_fetch_add(&ent->ref_count, 1) + 1;
|
|
+// printf("Ref: %p: %d\n", ent, n);
|
|
+ atomic_fetch_add(&ent->ref_count, 1);
|
|
+ return ent;
|
|
+}
|
|
+
|
|
+static void pool_recycle(vzc_pool_ctl_t * const pc, pool_ent_t * const ent)
|
|
+{
|
|
+ pool_ent_t * xs = NULL;
|
|
+ int n;
|
|
+
|
|
+ if (ent == NULL)
|
|
+ return;
|
|
+
|
|
+ n = atomic_fetch_sub(&ent->ref_count, 1) - 1;
|
|
+
|
|
+// printf("%s: Pool: %p: Ent: %p: %d\n", __func__, &pc->ent_pool, ent, n);
|
|
+
|
|
+ if (n != 0)
|
|
+ return;
|
|
+
|
|
+ if (ent->pic != NULL) {
|
|
+ picture_Release(ent->pic);
|
|
+ ent->pic = NULL;
|
|
+ }
|
|
+
|
|
+ vlc_mutex_lock(&pc->lock);
|
|
+
|
|
+ // If we have a full pool then extract the LRU and free it
|
|
+ // Free done outside mutex
|
|
+ if (pc->ent_pool.n >= pc->max_n)
|
|
+ xs = ent_extract_tail(&pc->ent_pool);
|
|
+
|
|
+ ent_add_head(&pc->ent_pool, ent);
|
|
+
|
|
+ vlc_mutex_unlock(&pc->lock);
|
|
+
|
|
+ ent_free(xs);
|
|
+}
|
|
+
|
|
+// * This could be made more efficient, but this is easy
|
|
+static void pool_recycle_list(vzc_pool_ctl_t * const pc, ent_list_hdr_t * const elh)
|
|
+{
|
|
+ pool_ent_t * ent;
|
|
+ while ((ent = ent_extract_tail(elh)) != NULL) {
|
|
+ pool_recycle(pc, ent);
|
|
+ }
|
|
+}
|
|
+
|
|
+static pool_ent_t * pool_best_fit(vzc_pool_ctl_t * const pc, size_t req_size)
|
|
+{
|
|
+ pool_ent_t * best = NULL;
|
|
+
|
|
+ vlc_mutex_lock(&pc->lock);
|
|
+
|
|
+ {
|
|
+ pool_ent_t * ent = pc->ent_pool.ents;
|
|
+
|
|
+ // Simple scan
|
|
+ while (ent != NULL) {
|
|
+ if (ent->size >= req_size && ent->size <= req_size * 2 + POOL_ENT_ALLOC_BLOCK &&
|
|
+ (best == NULL || best->size > ent->size))
|
|
+ best = ent;
|
|
+ ent = ent->next;
|
|
+ }
|
|
+
|
|
+ // extract best from chain if we've found it
|
|
+ ent_extract(&pc->ent_pool, best);
|
|
+ }
|
|
+
|
|
+ vlc_mutex_unlock(&pc->lock);
|
|
+
|
|
+ if (best == NULL)
|
|
+ best = pool_ent_alloc_new(req_size);
|
|
+
|
|
+ if ((best->seq = ++pc->seq) == 0)
|
|
+ best->seq = ++pc->seq; // Never allow to be zero
|
|
+
|
|
+ atomic_store(&best->ref_count, 1);
|
|
+ return best;
|
|
+}
|
|
+
|
|
+
|
|
+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH)
|
|
+{
|
|
+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
|
|
+ *pW = ent->width;
|
|
+ *pH = ent->height;
|
|
+}
|
|
+
|
|
+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt)
|
|
+{
|
|
+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
|
|
+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
|
|
+
|
|
+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
|
|
+ es_fmt->encoding = ent->enc_type;
|
|
+ es_fmt->encoding_variant = 0;
|
|
+
|
|
+ v_fmt->width = ent->width;
|
|
+ v_fmt->height = ent->height;
|
|
+ v_fmt->crop.x = 0;
|
|
+ v_fmt->crop.y = 0;
|
|
+ v_fmt->crop.width = ent->width;
|
|
+ v_fmt->crop.height = ent->height;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
|
|
+ uint32_t * const pWidth, uint32_t * const pHeight)
|
|
+{
|
|
+ const pool_ent_t *const ent = ((vzc_subbuf_ent_t *)buf->user_data)->ent;
|
|
+ *pWidth = ent->width;
|
|
+ *pHeight = ent->height;
|
|
+}
|
|
+
|
|
+
|
|
+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf)
|
|
+{
|
|
+ vzc_subbuf_ent_t * sb = buf->user_data;
|
|
+ return &sb->dreg;
|
|
+}
|
|
+
|
|
+void hw_mmal_vzc_buf_set_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const int x, const int y, const int w, const int h)
|
|
+{
|
|
+ vzc_subbuf_ent_t * sb = buf->user_data;
|
|
+ sb->orig_dest_rect.x = x;
|
|
+ sb->orig_dest_rect.y = y;
|
|
+ sb->orig_dest_rect.width = w;
|
|
+ sb->orig_dest_rect.height = h;
|
|
+}
|
|
+
|
|
+static inline int rescale_x(int x, int mul, int div)
|
|
+{
|
|
+ return div == 0 ? x * mul : (x * mul + div/2) / div;
|
|
+}
|
|
+
|
|
+static void rescale_rect(MMAL_RECT_T * const d, const MMAL_RECT_T * const s, const MMAL_RECT_T * mul_rect, const MMAL_RECT_T * div_rect)
|
|
+{
|
|
+ d->x = rescale_x(s->x, mul_rect->width, div_rect->width);
|
|
+ d->y = rescale_x(s->y, mul_rect->height, div_rect->height);
|
|
+ d->width = rescale_x(s->width, mul_rect->width, div_rect->width);
|
|
+ d->height = rescale_x(s->height, mul_rect->height, div_rect->height);
|
|
+}
|
|
+
|
|
+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect)
|
|
{
|
|
- picture_sys_t *pic_sys = picture->p_sys;
|
|
- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
|
|
+ vzc_subbuf_ent_t * sb = buf->user_data;
|
|
+ if (scale_rect == NULL) {
|
|
+ sb->dreg.dest_rect = sb->orig_dest_rect;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ rescale_rect(&sb->dreg.dest_rect, &sb->orig_dest_rect,
|
|
+ scale_rect, &sb->pic_rect);
|
|
+ }
|
|
+}
|
|
+
|
|
+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf)
|
|
+{
|
|
+ vzc_subbuf_ent_t * sb = buf->user_data;
|
|
+ return sb->ent->seq;
|
|
+}
|
|
+
|
|
+
|
|
+// The intent with the ents_cur & ents_last stuff is to remember the buffers
|
|
+// we used on the last frame and reuse them on the current one if they are the
|
|
+// same. Unfortunately detection of "is_first" is only a heuristic (there are
|
|
+// no rules governing the order in which things are blended) so we must deal
|
|
+// (fairly) gracefully with it never (or always) being set.
|
|
+
|
|
+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic, const picture_t * const dst_pic, const bool is_first)
|
|
+{
|
|
+ MMAL_BUFFER_HEADER_T * const buf = mmal_queue_get(pc->buf_pool->queue);
|
|
+ vzc_subbuf_ent_t * sb;
|
|
+
|
|
+ if (buf == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ if ((sb = calloc(1, sizeof(*sb))) == NULL)
|
|
+ goto fail1;
|
|
+
|
|
+ // If first or we've had a lot of stuff move everything to the last list
|
|
+ // (we could deal more gracefully with the "too many" case but it shouldn't
|
|
+ // really happen)
|
|
+ if (is_first || pc->ents_cur.n >= CTX_BUFS_MAX) {
|
|
+ pool_recycle_list(pc, &pc->ents_prev);
|
|
+ ent_list_move(&pc->ents_prev, &pc->ents_cur);
|
|
+ }
|
|
+
|
|
+ sb->dreg.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
|
|
+ sb->dreg.hdr.size = sizeof(sb->dreg);
|
|
+ buf->user_data = sb;
|
|
+
|
|
+ {
|
|
+ // ?? Round start offset as well as length
|
|
+ const video_format_t *const fmt = &pic->format;
|
|
+
|
|
+ const unsigned int bpp = (fmt->i_bits_per_pixel + 7) >> 3;
|
|
+ const unsigned int xl = (fmt->i_x_offset & ~15);
|
|
+ const unsigned int xr = (fmt->i_x_offset + fmt->i_visible_width + 15) & ~15;
|
|
+ const size_t dst_stride = (xr - xl) * bpp;
|
|
+ const size_t dst_lines = ((fmt->i_visible_height + 15) & ~15);
|
|
+ const size_t dst_size = dst_stride * dst_lines;
|
|
+
|
|
+ pool_ent_t * ent = ent_list_extract_pic_ent(&pc->ents_prev, pic);
|
|
+ bool needs_copy = false;
|
|
+
|
|
+ // If we didn't find ent in last then look in cur in case is_first
|
|
+ // isn't working
|
|
+ if (ent == NULL)
|
|
+ ent = ent_list_extract_pic_ent(&pc->ents_cur, pic);
|
|
+
|
|
+// printf("ent_found: %p\n", ent);
|
|
+
|
|
+ if (ent == NULL)
|
|
+ {
|
|
+ // Need a new ent
|
|
+ needs_copy = true;
|
|
+
|
|
+ if ((ent = pool_best_fit(pc, dst_size)) == NULL)
|
|
+ goto fail2;
|
|
+ if ((ent->enc_type = vlc_to_mmal_video_fourcc(&pic->format)) == 0)
|
|
+ goto fail2;
|
|
+
|
|
+ ent->pic = picture_Hold(pic);
|
|
+ }
|
|
+
|
|
+ ent_add_head(&pc->ents_cur, ent);
|
|
+
|
|
+ sb->ent = pool_ent_ref(ent);
|
|
+ hw_mmal_vzc_pool_ref(pc);
|
|
+
|
|
+ // Copy data
|
|
+ buf->next = NULL;
|
|
+ buf->cmd = 0;
|
|
+ buf->data = (uint8_t *)(ent->vc_hdl);
|
|
+ buf->alloc_size = buf->length = dst_size;
|
|
+ buf->offset = 0;
|
|
+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
|
|
+ buf->pts = buf->dts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
|
|
+ buf->type->video = (MMAL_BUFFER_HEADER_VIDEO_SPECIFIC_T){
|
|
+ .planes = 1,
|
|
+ .pitch = { dst_stride }
|
|
+ };
|
|
+
|
|
+ // Remember offsets
|
|
+ sb->dreg.set = MMAL_DISPLAY_SET_SRC_RECT;
|
|
+
|
|
+// printf("+++ bpp:%d, vis:%dx%d wxh:%dx%d, d:%dx%d\n", bpp, fmt->i_visible_width, fmt->i_visible_height, fmt->i_width, fmt->i_height, dst_stride, dst_lines);
|
|
+
|
|
+ sb->dreg.src_rect = (MMAL_RECT_T){
|
|
+ .x = (fmt->i_x_offset - xl),
|
|
+ .y = 0,
|
|
+ .width = fmt->i_visible_width,
|
|
+ .height = fmt->i_visible_height
|
|
+ };
|
|
+
|
|
+ sb->pic_rect = (MMAL_RECT_T){
|
|
+ .x = dst_pic->format.i_x_offset,
|
|
+ .y = dst_pic->format.i_y_offset,
|
|
+ .width = dst_pic->format.i_visible_width,
|
|
+ .height = dst_pic->format.i_visible_height
|
|
+ };
|
|
+
|
|
+ if (needs_copy)
|
|
+ {
|
|
+ ent->width = dst_stride / bpp;
|
|
+ ent->height = dst_lines;
|
|
+
|
|
+ // 2D copy
|
|
+ {
|
|
+ unsigned int i;
|
|
+ uint8_t *d = ent->buf;
|
|
+ const uint8_t *s = pic->p[0].p_pixels + xl * bpp + fmt->i_y_offset * pic->p[0].i_pitch;
|
|
+ for (i = 0; i != fmt->i_visible_height; ++i) {
|
|
+ memcpy(d, s, dst_stride);
|
|
+ d += dst_stride;
|
|
+ s += pic->p[0].i_pitch;
|
|
+ }
|
|
+
|
|
+ // And make sure it is actually in memory
|
|
+ flush_range(ent->buf, d - (uint8_t *)ent->buf);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return buf;
|
|
+
|
|
+fail2:
|
|
+ free(sb);
|
|
+fail1:
|
|
+ mmal_buffer_header_release(buf);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc)
|
|
+{
|
|
+ pool_recycle_list(pc, &pc->ents_prev);
|
|
+ pool_recycle_list(pc, &pc->ents_cur);
|
|
+}
|
|
|
|
- int offset = 0;
|
|
- picture->p[0].p_pixels = buffer->data;
|
|
- for (int i = 1; i < picture->i_planes; i++) {
|
|
- offset = offset + picture->p[i - 1].i_pitch * picture->p[i - 1].i_lines;
|
|
- picture->p[i].p_pixels = (ptrdiff_t)buffer->data + offset;
|
|
+static void hw_mmal_vzc_pool_delete(vzc_pool_ctl_t * const pc)
|
|
+{
|
|
+
|
|
+// printf("<<< %s\n", __func__);
|
|
+
|
|
+ hw_mmal_vzc_pool_flush(pc);
|
|
+
|
|
+ ent_free_list(&pc->ent_pool);
|
|
+
|
|
+ if (pc->buf_pool != NULL)
|
|
+ mmal_pool_destroy(pc->buf_pool);
|
|
+
|
|
+ vlc_mutex_destroy(&pc->lock);
|
|
+
|
|
+// memset(pc, 0xba, sizeof(*pc)); // Zap for (hopefully) faster crash
|
|
+
|
|
+ free (pc);
|
|
+
|
|
+ vcsm_exit();
|
|
+
|
|
+// printf(">>> %s\n", __func__);
|
|
+}
|
|
+
|
|
+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc)
|
|
+{
|
|
+ int n;
|
|
+
|
|
+ if (pc == NULL)
|
|
+ return;
|
|
+
|
|
+ n = atomic_fetch_sub(&pc->ref_count, 1) - 1;
|
|
+
|
|
+ if (n != 0)
|
|
+ return;
|
|
+
|
|
+ hw_mmal_vzc_pool_delete(pc);
|
|
+}
|
|
+
|
|
+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc)
|
|
+{
|
|
+ atomic_fetch_add(&pc->ref_count, 1);
|
|
+}
|
|
+
|
|
+static MMAL_BOOL_T vcz_pool_release_cb(MMAL_POOL_T * buf_pool, MMAL_BUFFER_HEADER_T *buf, void *userdata)
|
|
+{
|
|
+ vzc_pool_ctl_t * const pc = userdata;
|
|
+ vzc_subbuf_ent_t * const sb = buf->user_data;
|
|
+
|
|
+ VLC_UNUSED(buf_pool);
|
|
+
|
|
+// printf("<<< %s\n", __func__);
|
|
+
|
|
+ if (sb != NULL) {
|
|
+ buf->user_data = NULL;
|
|
+ pool_recycle(pc, sb->ent);
|
|
+ hw_mmal_vzc_pool_release(pc);
|
|
+ free(sb);
|
|
+ }
|
|
+
|
|
+// printf(">>> %s\n", __func__);
|
|
+
|
|
+ return MMAL_TRUE;
|
|
+}
|
|
+
|
|
+vzc_pool_ctl_t * hw_mmal_vzc_pool_new()
|
|
+{
|
|
+ vzc_pool_ctl_t * const pc = calloc(1, sizeof(*pc));
|
|
+
|
|
+ if (pc == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ vcsm_init();
|
|
+
|
|
+ pc->max_n = 8;
|
|
+ vlc_mutex_init(&pc->lock); // Must init before potential destruction
|
|
+
|
|
+ if ((pc->buf_pool = mmal_pool_create(64, 0)) == NULL)
|
|
+ {
|
|
+ hw_mmal_vzc_pool_delete(pc);
|
|
+ return NULL;
|
|
}
|
|
|
|
- pic_sys->displayed = false;
|
|
+ atomic_store(&pc->ref_count, 1);
|
|
+
|
|
+ mmal_pool_callback_set(pc->buf_pool, vcz_pool_release_cb, pc);
|
|
|
|
- return VLC_SUCCESS;
|
|
+ return pc;
|
|
}
|
|
+
|
|
+
|
|
+
|
|
--- a/modules/hw/mmal/mmal_picture.h
|
|
+++ b/modules/hw/mmal/mmal_picture.h
|
|
@@ -24,19 +24,243 @@
|
|
#ifndef VLC_MMAL_MMAL_PICTURE_H_
|
|
#define VLC_MMAL_MMAL_PICTURE_H_
|
|
|
|
+#include <stdatomic.h>
|
|
+
|
|
#include <vlc_common.h>
|
|
#include <interface/mmal/mmal.h>
|
|
|
|
/* Think twice before changing this. Incorrect values cause havoc. */
|
|
#define NUM_ACTUAL_OPAQUE_BUFFERS 30
|
|
|
|
-struct picture_sys_t {
|
|
- vlc_object_t *owner;
|
|
+#ifndef VLC_TICK_INVALID
|
|
+#define VLC_TICK_INVALID VLC_TS_INVALID
|
|
+#define VLC_VER_3 1
|
|
+#else
|
|
+#define VLC_VER_3 0
|
|
+#endif
|
|
+
|
|
+typedef struct mmal_port_pool_ref_s
|
|
+{
|
|
+ atomic_uint refs;
|
|
+ MMAL_POOL_T * pool;
|
|
+ MMAL_PORT_T * port;
|
|
+} hw_mmal_port_pool_ref_t;
|
|
+
|
|
+typedef struct pic_ctx_subpic_s {
|
|
+ picture_t * subpic;
|
|
+ int x, y;
|
|
+ int alpha;
|
|
+} pic_ctx_subpic_t;
|
|
+
|
|
+
|
|
+#define CTX_BUFS_MAX 4
|
|
|
|
- MMAL_BUFFER_HEADER_T *buffer;
|
|
- bool displayed;
|
|
-};
|
|
+typedef struct pic_ctx_mmal_s {
|
|
+ picture_context_t cmn; // PARENT: Common els at start
|
|
+
|
|
+ MMAL_FOURCC_T fmt;
|
|
+
|
|
+ unsigned int buf_count;
|
|
+ MMAL_BUFFER_HEADER_T * bufs[CTX_BUFS_MAX];
|
|
+
|
|
+#if 0
|
|
+ MMAL_BUFFER_HEADER_T * buf;
|
|
+ hw_mmal_port_pool_ref_t * ppr;
|
|
+
|
|
+ MMAL_BUFFER_HEADER_T * sub_bufs;
|
|
+ MMAL_BUFFER_HEADER_T * sub_tail;
|
|
+
|
|
+ vlc_object_t * obj;
|
|
+#endif
|
|
+} pic_ctx_mmal_t;
|
|
|
|
-int mmal_picture_lock(picture_t *picture);
|
|
+MMAL_FOURCC_T vlc_to_mmal_video_fourcc(const video_frame_format_t * const vf_vlc);
|
|
+MMAL_FOURCC_T vlc_to_mmal_color_space(const video_color_space_t vlc_cs);
|
|
+void vlc_to_mmal_video_fmt(MMAL_ES_FORMAT_T *const es_fmt, const video_frame_format_t * const vf_vlc);
|
|
+
|
|
+hw_mmal_port_pool_ref_t * hw_mmal_port_pool_ref_create(MMAL_PORT_T * const port,
|
|
+ const unsigned int headers, const uint32_t payload_size);
|
|
+void hw_mmal_port_pool_ref_release(hw_mmal_port_pool_ref_t * const ppr, const bool in_cb);
|
|
+bool hw_mmal_port_pool_ref_recycle(hw_mmal_port_pool_ref_t * const ppr, MMAL_BUFFER_HEADER_T * const buf);
|
|
+MMAL_STATUS_T hw_mmal_port_pool_ref_fill(hw_mmal_port_pool_ref_t * const ppr);
|
|
+static inline void hw_mmal_port_pool_ref_acquire(hw_mmal_port_pool_ref_t * const ppr)
|
|
+{
|
|
+ atomic_fetch_add(&ppr->refs, 1);
|
|
+}
|
|
+MMAL_STATUS_T hw_mmal_opaque_output(vlc_object_t * const obj,
|
|
+ hw_mmal_port_pool_ref_t ** pppr,
|
|
+ MMAL_PORT_T * const port,
|
|
+ const unsigned int extra_buffers, MMAL_PORT_BH_CB_T callback);
|
|
+
|
|
+static inline int hw_mmal_pic_has_sub_bufs(picture_t * const pic)
|
|
+{
|
|
+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
|
|
+ return ctx->buf_count > 1;
|
|
+}
|
|
+
|
|
+static inline void hw_mmal_pic_sub_buf_add(picture_t * const pic, MMAL_BUFFER_HEADER_T * const sub)
|
|
+{
|
|
+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
|
|
+
|
|
+ if (ctx->buf_count >= CTX_BUFS_MAX) {
|
|
+ mmal_buffer_header_release(sub);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ctx->bufs[ctx->buf_count++] = sub;
|
|
+}
|
|
+
|
|
+static inline MMAL_BUFFER_HEADER_T * hw_mmal_pic_sub_buf_get(picture_t * const pic, const unsigned int n)
|
|
+{
|
|
+ pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
|
|
+
|
|
+ return n + 1 > ctx->buf_count ? NULL : ctx->bufs[n + 1];
|
|
+}
|
|
+
|
|
+static inline bool hw_mmal_pic_is_mmal(const picture_t * const pic)
|
|
+{
|
|
+ return pic->format.i_chroma == VLC_CODEC_MMAL_OPAQUE ||
|
|
+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND8 ||
|
|
+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_SAND10 ||
|
|
+ pic->format.i_chroma == VLC_CODEC_MMAL_ZC_I420;
|
|
+}
|
|
+
|
|
+static inline MMAL_FOURCC_T hw_mmal_pic_format(const picture_t *const pic)
|
|
+{
|
|
+ const pic_ctx_mmal_t * const ctx = (pic_ctx_mmal_t *)pic->context;
|
|
+ return ctx->fmt;
|
|
+}
|
|
+
|
|
+picture_context_t * hw_mmal_pic_ctx_copy(picture_context_t * pic_ctx_cmn);
|
|
+void hw_mmal_pic_ctx_destroy(picture_context_t * pic_ctx_cmn);
|
|
+picture_context_t * hw_mmal_gen_context(const MMAL_FOURCC_T fmt,
|
|
+ MMAL_BUFFER_HEADER_T * buf, hw_mmal_port_pool_ref_t * const ppr);
|
|
+
|
|
+int hw_mmal_get_gpu_mem(void);
|
|
+
|
|
+
|
|
+static inline MMAL_STATUS_T port_parameter_set_uint32(MMAL_PORT_T * port, uint32_t id, uint32_t val)
|
|
+{
|
|
+ const MMAL_PARAMETER_UINT32_T param = {
|
|
+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_UINT32_T)},
|
|
+ .value = val
|
|
+ };
|
|
+ return mmal_port_parameter_set(port, ¶m.hdr);
|
|
+}
|
|
+
|
|
+static inline MMAL_STATUS_T port_parameter_set_bool(MMAL_PORT_T * const port, const uint32_t id, const bool val)
|
|
+{
|
|
+ const MMAL_PARAMETER_BOOLEAN_T param = {
|
|
+ .hdr = {.id = id, .size = sizeof(MMAL_PARAMETER_BOOLEAN_T)},
|
|
+ .enable = val
|
|
+ };
|
|
+ return mmal_port_parameter_set(port, ¶m.hdr);
|
|
+}
|
|
+
|
|
+static inline MMAL_STATUS_T port_send_replicated(MMAL_PORT_T * const port, MMAL_POOL_T * const rep_pool,
|
|
+ MMAL_BUFFER_HEADER_T * const src_buf,
|
|
+ const uint64_t seq)
|
|
+{
|
|
+ MMAL_STATUS_T err;
|
|
+ MMAL_BUFFER_HEADER_T *const rep_buf = mmal_queue_wait(rep_pool->queue);
|
|
+
|
|
+ if (rep_buf == NULL)
|
|
+ return MMAL_ENOSPC;
|
|
+
|
|
+ if ((err = mmal_buffer_header_replicate(rep_buf, src_buf)) != MMAL_SUCCESS)
|
|
+ return err;
|
|
+
|
|
+ rep_buf->pts = seq;
|
|
+
|
|
+ if ((err = mmal_port_send_buffer(port, rep_buf)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ mmal_buffer_header_release(rep_buf);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return MMAL_SUCCESS;
|
|
+}
|
|
+
|
|
+static inline void pic_to_buf_copy_props(MMAL_BUFFER_HEADER_T * const buf, const picture_t * const pic)
|
|
+{
|
|
+ if (!pic->b_progressive)
|
|
+ {
|
|
+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
|
|
+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
|
|
+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED;
|
|
+ }
|
|
+ if (pic->b_top_field_first)
|
|
+ {
|
|
+ buf->flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
|
|
+ buf->type->video.flags |= MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ buf->flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
|
|
+ buf->type->video.flags &= ~MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST;
|
|
+ }
|
|
+ buf->pts = pic->date != VLC_TICK_INVALID ? pic->date : MMAL_TIME_UNKNOWN;
|
|
+ buf->dts = buf->pts;
|
|
+}
|
|
+
|
|
+static inline void buf_to_pic_copy_props(picture_t * const pic, const MMAL_BUFFER_HEADER_T * const buf)
|
|
+{
|
|
+ // Contrary to docn the interlace & tff flags turn up in the header flags rather than the
|
|
+ // video specific flags (which appear to be currently unused).
|
|
+ pic->b_progressive = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_INTERLACED) == 0;
|
|
+ pic->b_top_field_first = (buf->flags & MMAL_BUFFER_HEADER_VIDEO_FLAG_TOP_FIELD_FIRST) != 0;
|
|
+
|
|
+ pic->date = buf->pts != MMAL_TIME_UNKNOWN ? buf->pts :
|
|
+ buf->dts != MMAL_TIME_UNKNOWN ? buf->dts :
|
|
+ VLC_TICK_INVALID;
|
|
+}
|
|
+
|
|
+// Retrieve buf from pic & update with pic props
|
|
+// Note that this is a weak pointer - replicate before putting in a Q
|
|
+static inline MMAL_BUFFER_HEADER_T * pic_mmal_buffer(const picture_t *const pic)
|
|
+{
|
|
+ MMAL_BUFFER_HEADER_T * const buf = ((pic_ctx_mmal_t *)pic->context)->bufs[0];
|
|
+ if (buf != NULL)
|
|
+ pic_to_buf_copy_props(buf, pic);
|
|
+
|
|
+ return buf;
|
|
+}
|
|
+
|
|
+struct vzc_pool_ctl_s;
|
|
+typedef struct vzc_pool_ctl_s vzc_pool_ctl_t;
|
|
+
|
|
+static inline bool hw_mmal_vzc_subpic_fmt_valid(const video_frame_format_t * const vf_vlc)
|
|
+{
|
|
+ const vlc_fourcc_t vfcc_src = vf_vlc->i_chroma;
|
|
+ // At the moment we cope with any mono-planar RGBA thing
|
|
+ // We could cope with many other things but they currently don't occur
|
|
+ return vfcc_src == VLC_CODEC_RGBA || vfcc_src == VLC_CODEC_BGRA || vfcc_src == VLC_CODEC_ARGB;
|
|
+}
|
|
+
|
|
+bool hw_mmal_vzc_buf_set_format(MMAL_BUFFER_HEADER_T * const buf, MMAL_ES_FORMAT_T * const es_fmt);
|
|
+MMAL_DISPLAYREGION_T * hw_mmal_vzc_buf_region(MMAL_BUFFER_HEADER_T * const buf);
|
|
+void hw_mmal_vzc_buf_set_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const int x, const int y, const int w, const int h);
|
|
+void hw_mmal_vzc_buf_scale_dest_rect(MMAL_BUFFER_HEADER_T * const buf, const MMAL_RECT_T * const scale_rect);
|
|
+void hw_mmal_vzc_buf_get_wh(MMAL_BUFFER_HEADER_T * const buf, int * const pW, int * const pH);
|
|
+unsigned int hw_mmal_vzc_buf_seq(MMAL_BUFFER_HEADER_T * const buf);
|
|
+MMAL_BUFFER_HEADER_T * hw_mmal_vzc_buf_from_pic(vzc_pool_ctl_t * const pc, picture_t * const pic, const picture_t * const dst_pic, const bool is_first);
|
|
+void hw_mmal_vzc_buf_frame_size(MMAL_BUFFER_HEADER_T * const buf,
|
|
+ uint32_t * const pWidth, uint32_t * const pHeight);
|
|
+
|
|
+void hw_mmal_vzc_pool_flush(vzc_pool_ctl_t * const pc);
|
|
+void hw_mmal_vzc_pool_release(vzc_pool_ctl_t * const pc);
|
|
+void hw_mmal_vzc_pool_ref(vzc_pool_ctl_t * const pc);
|
|
+vzc_pool_ctl_t * hw_mmal_vzc_pool_new(void);
|
|
+
|
|
+#define VOUT_DISPLAY_CHANGE_MMAL_BASE 1024
|
|
+#define VOUT_DISPLAY_CHANGE_MMAL_HIDE (VOUT_DISPLAY_CHANGE_MMAL_BASE + 0)
|
|
+
|
|
+#define MMAL_COMPONENT_DEFAULT_RESIZER "vc.ril.resize"
|
|
+#define MMAL_COMPONENT_ISP_RESIZER "vc.ril.isp"
|
|
+#define MMAL_COMPONENT_HVS "vc.ril.hvs"
|
|
|
|
#endif
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/rpi_prof.h
|
|
@@ -0,0 +1,110 @@
|
|
+#ifndef RPI_PROFILE_H
|
|
+#define RPI_PROFILE_H
|
|
+
|
|
+#include <stdint.h>
|
|
+#include <inttypes.h>
|
|
+
|
|
+#ifndef RPI_PROFILE
|
|
+#define RPI_PROFILE 0
|
|
+#endif
|
|
+
|
|
+#if RPI_PROFILE
|
|
+
|
|
+#include "v7_pmu.h"
|
|
+
|
|
+#ifdef RPI_PROC_ALLOC
|
|
+#define X volatile
|
|
+#define Z =0
|
|
+#else
|
|
+#define X extern volatile
|
|
+#define Z
|
|
+#endif
|
|
+
|
|
+X uint64_t av_rpi_prof0_cycles Z;
|
|
+X unsigned int av_rpi_prof0_cnt Z;
|
|
+#define RPI_prof0_MAX_DURATION 100000
|
|
+
|
|
+X uint64_t av_rpi_prof1_cycles Z;
|
|
+X unsigned int av_rpi_prof1_cnt Z;
|
|
+#define RPI_prof1_MAX_DURATION 100000
|
|
+
|
|
+X uint64_t av_rpi_prof2_cycles Z;
|
|
+X unsigned int av_rpi_prof2_cnt Z;
|
|
+#define RPI_prof2_MAX_DURATION 10000
|
|
+
|
|
+X uint64_t av_rpi_prof_n_cycles[128];
|
|
+X unsigned int av_rpi_prof_n_cnt[128];
|
|
+#define RPI_prof_n_MAX_DURATION 10000
|
|
+
|
|
+
|
|
+#undef X
|
|
+#undef Z
|
|
+
|
|
+#define PROFILE_INIT()\
|
|
+do {\
|
|
+ enable_pmu();\
|
|
+ enable_ccnt();\
|
|
+} while (0)
|
|
+
|
|
+#define PROFILE_START()\
|
|
+do {\
|
|
+ volatile uint32_t perf_1 = read_ccnt();\
|
|
+ volatile uint32_t perf_2
|
|
+
|
|
+
|
|
+#define PROFILE_ACC(x)\
|
|
+ perf_2 = read_ccnt();\
|
|
+ {\
|
|
+ const uint32_t duration = perf_2 - perf_1;\
|
|
+ if (duration < RPI_##x##_MAX_DURATION)\
|
|
+ {\
|
|
+ av_rpi_##x##_cycles += duration;\
|
|
+ av_rpi_##x##_cnt += 1;\
|
|
+ }\
|
|
+ }\
|
|
+} while(0)
|
|
+
|
|
+
|
|
+#define PROFILE_ACC_N(n)\
|
|
+ if ((n) >= 0) {\
|
|
+ perf_2 = read_ccnt();\
|
|
+ {\
|
|
+ const uint32_t duration = perf_2 - perf_1;\
|
|
+ if (duration < RPI_prof_n_MAX_DURATION)\
|
|
+ {\
|
|
+ av_rpi_prof_n_cycles[n] += duration;\
|
|
+ av_rpi_prof_n_cnt[n] += 1;\
|
|
+ }\
|
|
+ }\
|
|
+ }\
|
|
+} while(0)
|
|
+
|
|
+#define PROFILE_PRINTF(x)\
|
|
+ printf("%-20s cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", #x, av_rpi_##x##_cycles, av_rpi_##x##_cnt,\
|
|
+ av_rpi_##x##_cnt == 0 ? (uint64_t)0 : av_rpi_##x##_cycles / (uint64_t)av_rpi_##x##_cnt)
|
|
+
|
|
+#define PROFILE_PRINTF_N(n)\
|
|
+ printf("prof[%d] cycles=%14" PRIu64 "; cnt=%8u; avg=%5" PRIu64 "\n", (n), av_rpi_prof_n_cycles[n], av_rpi_prof_n_cnt[n],\
|
|
+ av_rpi_prof_n_cnt[n] == 0 ? (uint64_t)0 : av_rpi_prof_n_cycles[n] / (uint64_t)av_rpi_prof_n_cnt[n])
|
|
+
|
|
+#define PROFILE_CLEAR_N(n) \
|
|
+do {\
|
|
+ av_rpi_prof_n_cycles[n] = 0;\
|
|
+ av_rpi_prof_n_cnt[n] = 0;\
|
|
+} while(0)
|
|
+
|
|
+#else
|
|
+
|
|
+// No profile
|
|
+#define PROFILE_INIT()
|
|
+#define PROFILE_START()
|
|
+#define PROFILE_ACC(x)
|
|
+#define PROFILE_ACC_N(x)
|
|
+#define PROFILE_PRINTF(x)
|
|
+#define PROFILE_PRINTF_N(x)
|
|
+#define PROFILE_CLEAR_N(n)
|
|
+
|
|
+#endif
|
|
+
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/subpic.c
|
|
@@ -0,0 +1,222 @@
|
|
+/*****************************************************************************
|
|
+ * mmal.c: MMAL-based decoder plugin for Raspberry Pi
|
|
+ *****************************************************************************
|
|
+ * Authors: jc@kynesim.co.uk
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify it
|
|
+ * under the terms of the GNU Lesser General Public License as published by
|
|
+ * the Free Software Foundation; either version 2.1 of the License, or
|
|
+ * (at your option) any later version.
|
|
+ *
|
|
+ * This program is distributed in the hope that it will be useful,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
+ * GNU Lesser General Public License for more details.
|
|
+ *
|
|
+ * You should have received a copy of the GNU Lesser General Public License
|
|
+ * along with this program; if not, write to the Free Software Foundation,
|
|
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
+ *****************************************************************************/
|
|
+
|
|
+#ifdef HAVE_CONFIG_H
|
|
+#include "config.h"
|
|
+#endif
|
|
+
|
|
+#include <stdatomic.h>
|
|
+
|
|
+#include <vlc_common.h>
|
|
+#include <vlc_plugin.h>
|
|
+#include <vlc_codec.h>
|
|
+#include <vlc_filter.h>
|
|
+#include <vlc_threads.h>
|
|
+
|
|
+#include <bcm_host.h>
|
|
+#include <interface/mmal/mmal.h>
|
|
+#include <interface/mmal/util/mmal_util.h>
|
|
+#include <interface/mmal/util/mmal_default_components.h>
|
|
+
|
|
+#include "mmal_picture.h"
|
|
+#include "subpic.h"
|
|
+
|
|
+
|
|
+#define TRACE_ALL 0
|
|
+
|
|
+static inline bool cmp_rect(const MMAL_RECT_T * const a, const MMAL_RECT_T * const b)
|
|
+{
|
|
+ return a->x == b->x && a->y == b->y && a->width == b->width && a->height == b->height;
|
|
+}
|
|
+
|
|
+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const sub)
|
|
+{
|
|
+ VLC_UNUSED(p_filter);
|
|
+ if (sub->port != NULL && sub->port->is_enabled)
|
|
+ mmal_port_disable(sub->port);
|
|
+ sub->seq = 0;
|
|
+}
|
|
+
|
|
+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe)
|
|
+{
|
|
+ hw_mmal_subpic_flush(p_filter, spe);
|
|
+
|
|
+ if (spe->pool != NULL)
|
|
+ mmal_pool_destroy(spe->pool);
|
|
+
|
|
+ // Zap to avoid any accidental reuse
|
|
+ *spe = (subpic_reg_stash_t){NULL};
|
|
+}
|
|
+
|
|
+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, const unsigned int layer)
|
|
+{
|
|
+ MMAL_STATUS_T err;
|
|
+
|
|
+ // Start by zapping all to zero
|
|
+ *spe = (subpic_reg_stash_t){NULL};
|
|
+
|
|
+ if ((err = port_parameter_set_bool(port, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Failed to set sub port zero copy");
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ if ((spe->pool = mmal_pool_create(30, 0)) == NULL)
|
|
+ {
|
|
+ msg_Err(p_filter, "Failed to create sub pool");
|
|
+ return MMAL_ENOMEM;
|
|
+ }
|
|
+
|
|
+ port->userdata = (void *)p_filter;
|
|
+ spe->port = port;
|
|
+ spe->layer = layer;
|
|
+
|
|
+ return MMAL_SUCCESS;
|
|
+}
|
|
+
|
|
+static void conv_subpic_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
|
|
+{
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg((filter_t *)port->userdata, "<<< %s cmd=%d, user=%p, buf=%p, flags=%#x, len=%d/%d, pts=%lld",
|
|
+ __func__, buf->cmd, buf->user_data, buf, buf->flags, buf->length, buf->alloc_size, (long long)buf->pts);
|
|
+#else
|
|
+ VLC_UNUSED(port);
|
|
+#endif
|
|
+
|
|
+ mmal_buffer_header_release(buf); // Will extract & release pic in pool callback
|
|
+}
|
|
+
|
|
+
|
|
+int hw_mmal_subpic_update(vlc_object_t * const p_filter,
|
|
+ picture_t * const p_pic, const unsigned int sub_no,
|
|
+ subpic_reg_stash_t * const spe,
|
|
+ const MMAL_RECT_T * const scale_out,
|
|
+ const uint64_t pts)
|
|
+{
|
|
+ MMAL_STATUS_T err;
|
|
+ MMAL_BUFFER_HEADER_T * const sub_buf = hw_mmal_pic_sub_buf_get(p_pic, sub_no);
|
|
+
|
|
+ if (sub_buf == NULL)
|
|
+ {
|
|
+ if (spe->port->is_enabled && spe->seq != 0)
|
|
+ {
|
|
+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(spe->pool->queue);
|
|
+
|
|
+ if (buf == NULL) {
|
|
+ msg_Err(p_filter, "Buffer get for subpic failed");
|
|
+ return -1;
|
|
+ }
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "Remove pic for sub %d", sub_no);
|
|
+#endif
|
|
+ buf->cmd = 0;
|
|
+ buf->data = NULL;
|
|
+ buf->alloc_size = 0;
|
|
+ buf->offset = 0;
|
|
+ buf->flags = MMAL_BUFFER_HEADER_FLAG_FRAME_END;
|
|
+ buf->pts = pts;
|
|
+ buf->dts = MMAL_TIME_UNKNOWN;
|
|
+ buf->user_data = NULL;
|
|
+
|
|
+ if ((err = mmal_port_send_buffer(spe->port, buf)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Send buffer to subput failed");
|
|
+ mmal_buffer_header_release(buf);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ spe->seq = 0;
|
|
+ }
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ const unsigned int seq = hw_mmal_vzc_buf_seq(sub_buf);
|
|
+ bool needs_update = (spe->seq != seq);
|
|
+
|
|
+ hw_mmal_vzc_buf_scale_dest_rect(sub_buf, scale_out);
|
|
+
|
|
+ if (hw_mmal_vzc_buf_set_format(sub_buf, spe->port->format))
|
|
+ {
|
|
+ MMAL_DISPLAYREGION_T * const dreg = hw_mmal_vzc_buf_region(sub_buf);
|
|
+ MMAL_VIDEO_FORMAT_T *const v_fmt = &spe->port->format->es->video;
|
|
+
|
|
+ v_fmt->frame_rate.den = p_pic->format.i_frame_rate_base;
|
|
+ v_fmt->frame_rate.num = p_pic->format.i_frame_rate;
|
|
+ v_fmt->par.den = p_pic->format.i_sar_den;
|
|
+ v_fmt->par.num = p_pic->format.i_sar_num;
|
|
+ v_fmt->color_space = MMAL_COLOR_SPACE_UNKNOWN;
|
|
+
|
|
+
|
|
+ if (needs_update || dreg->alpha != spe->alpha || !cmp_rect(&dreg->dest_rect, &spe->dest_rect)) {
|
|
+
|
|
+ spe->alpha = dreg->alpha;
|
|
+ spe->dest_rect = dreg->dest_rect;
|
|
+ needs_update = true;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "Update region for sub %d", sub_no);
|
|
+#endif
|
|
+ dreg->layer = spe->layer;
|
|
+ dreg->set |= MMAL_DISPLAY_SET_LAYER;
|
|
+
|
|
+ if ((err = mmal_port_parameter_set(spe->port, &dreg->hdr)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Set display region on subput failed");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if ((err = mmal_port_format_commit(spe->port)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Dbg(p_filter, "%s: Subpic commit fail: %d", __func__, err);
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!spe->port->is_enabled)
|
|
+ {
|
|
+ spe->port->buffer_num = 30;
|
|
+ spe->port->buffer_size = spe->port->buffer_size_recommended; // Not used but shuts up the error checking
|
|
+
|
|
+ if ((err = mmal_port_enable(spe->port, conv_subpic_cb)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Dbg(p_filter, "%s: Subpic enable fail: %d", __func__, err);
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (needs_update)
|
|
+ {
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(p_filter, "Update pic for sub %d", sub_no);
|
|
+#endif
|
|
+ if ((err = port_send_replicated(spe->port, spe->pool, sub_buf, pts)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(p_filter, "Send buffer to subput failed");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ spe->seq = seq;
|
|
+ }
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/subpic.h
|
|
@@ -0,0 +1,28 @@
|
|
+#ifndef VLC_HW_MMAL_SUBPIC_H_
|
|
+#define VLC_HW_MMAL_SUBPIC_H_
|
|
+
|
|
+typedef struct subpic_reg_stash_s
|
|
+{
|
|
+ MMAL_PORT_T * port;
|
|
+ MMAL_POOL_T * pool;
|
|
+ unsigned int layer;
|
|
+ // Shadow vars so we can tell if stuff has changed
|
|
+ MMAL_RECT_T dest_rect;
|
|
+ unsigned int alpha;
|
|
+ unsigned int seq;
|
|
+} subpic_reg_stash_t;
|
|
+
|
|
+int hw_mmal_subpic_update(vlc_object_t * const p_filter,
|
|
+ picture_t * const p_pic, const unsigned int sub_no,
|
|
+ subpic_reg_stash_t * const stash,
|
|
+ const MMAL_RECT_T * const scale_out,
|
|
+ const uint64_t pts);
|
|
+
|
|
+void hw_mmal_subpic_flush(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
|
|
+
|
|
+void hw_mmal_subpic_close(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe);
|
|
+
|
|
+MMAL_STATUS_T hw_mmal_subpic_open(vlc_object_t * const p_filter, subpic_reg_stash_t * const spe, MMAL_PORT_T * const port, const unsigned int layer);
|
|
+
|
|
+#endif
|
|
+
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/v7_pmu.S
|
|
@@ -0,0 +1,263 @@
|
|
+/*------------------------------------------------------------
|
|
+Performance Monitor Block
|
|
+------------------------------------------------------------*/
|
|
+ .arm @ Make sure we are in ARM mode.
|
|
+ .text
|
|
+ .align 2
|
|
+ .global getPMN @ export this function for the linker
|
|
+
|
|
+/* Returns the number of progammable counters uint32_t getPMN(void) */
|
|
+
|
|
+getPMN:
|
|
+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC Register */
|
|
+ MOV r0, r0, LSR #11 /* Shift N field down to bit 0 */
|
|
+ AND r0, r0, #0x1F /* Mask to leave just the 5 N bits */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+
|
|
+ .global pmn_config @ export this function for the linker
|
|
+ /* Sets the event for a programmable counter to record */
|
|
+ /* void pmn_config(unsigned counter, uint32_t event) */
|
|
+ /* counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1 */
|
|
+ /* event = r1 = The event code */
|
|
+pmn_config:
|
|
+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */
|
|
+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */
|
|
+ MCR p15, 0, r1, c9, c13, 1 /* Write EVTSELx Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+
|
|
+ .global ccnt_divider @ export this function for the linker
|
|
+ /* Enables/disables the divider (1/64) on CCNT */
|
|
+ /* void ccnt_divider(int divider) */
|
|
+ /* divider = r0 = If 0 disable divider, else enable dvider */
|
|
+ccnt_divider:
|
|
+ MRC p15, 0, r1, c9, c12, 0 /* Read PMNC */
|
|
+
|
|
+ CMP r0, #0x0 /* IF (r0 == 0) */
|
|
+ BICEQ r1, r1, #0x08 /* THEN: Clear the D bit (disables the */
|
|
+ ORRNE r1, r1, #0x08 /* ELSE: Set the D bit (enables the di */
|
|
+
|
|
+ MCR p15, 0, r1, c9, c12, 0 /* Write PMNC */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+ /* --------------------------------------------------------------- */
|
|
+ /* Enable/Disable */
|
|
+ /* --------------------------------------------------------------- */
|
|
+
|
|
+ .global enable_pmu @ export this function for the linker
|
|
+ /* Global PMU enable */
|
|
+ /* void enable_pmu(void) */
|
|
+enable_pmu:
|
|
+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
|
|
+ ORR r0, r0, #0x01 /* Set E bit */
|
|
+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+
|
|
+ .global disable_pmu @ export this function for the linker
|
|
+ /* Global PMU disable */
|
|
+ /* void disable_pmu(void) */
|
|
+disable_pmu:
|
|
+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
|
|
+ BIC r0, r0, #0x01 /* Clear E bit */
|
|
+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+
|
|
+ .global enable_ccnt @ export this function for the linker
|
|
+ /* Enable the CCNT */
|
|
+ /* void enable_ccnt(void) */
|
|
+enable_ccnt:
|
|
+ MOV r0, #0x80000000 /* Set C bit */
|
|
+ MCR p15, 0, r0, c9, c12, 1 /* Write CNTENS Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+
|
|
+ .global disable_ccnt @ export this function for the linker
|
|
+ /* Disable the CCNT */
|
|
+ /* void disable_ccnt(void) */
|
|
+disable_ccnt:
|
|
+ MOV r0, #0x80000000 /* Clear C bit */
|
|
+ MCR p15, 0, r0, c9, c12, 2 /* Write CNTENC Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+
|
|
+ .global enable_pmn @ export this function for the linker
|
|
+ /* Enable PMN{n} */
|
|
+ /* void enable_pmn(uint32_t counter) */
|
|
+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
|
|
+enable_pmn: */
|
|
+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */
|
|
+ MOV r1, r1, LSL r0
|
|
+
|
|
+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+
|
|
+ .global disable_pmn @ export this function for the linker
|
|
+ /* Enable PMN{n} */
|
|
+ /* void disable_pmn(uint32_t counter) */
|
|
+ /* counter = r0 = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
|
|
+disable_pmn: */
|
|
+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */
|
|
+ MOV r1, r1, LSL r0
|
|
+
|
|
+ MCR p15, 0, r1, c9, c12, 1 /* Write CNTENS Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+
|
|
+ .global enable_pmu_user_access @ export this function for the linker
|
|
+ /* Enables User mode access to the PMU (must be called in a priviledge */
|
|
+ /* void enable_pmu_user_access(void) */
|
|
+enable_pmu_user_access:
|
|
+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */
|
|
+ ORR r0, r0, #0x01 /* Set EN bit (bit 0) */
|
|
+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+
|
|
+ .global disable_pmu_user_access @ export this function for the linke
|
|
+ /* Disables User mode access to the PMU (must be called in a priviledg */
|
|
+ /* void disable_pmu_user_access(void) */
|
|
+disable_pmu_user_access:
|
|
+ MRC p15, 0, r0, c9, c14, 0 /* Read PMUSERENR Register */
|
|
+ BIC r0, r0, #0x01 /* Clear EN bit (bit 0) */
|
|
+ MCR p15, 0, r0, c9, c14, 0 /* Write PMUSERENR Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+ /* --------------------------------------------------------------- */
|
|
+ /* Counter read registers */
|
|
+ /* --------------------------------------------------------------- */
|
|
+
|
|
+ .global read_ccnt @ export this function for the linker
|
|
+ /* Returns the value of CCNT */
|
|
+ /* uint32_t read_ccnt(void) */
|
|
+read_ccnt:
|
|
+ MRC p15, 0, r0, c9, c13, 0 /* Read CCNT Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+ .global read_pmn @ export this function for the linker
|
|
+ /* Returns the value of PMN{n} */
|
|
+ /* uint32_t read_pmn(uint32_t counter) */
|
|
+ /* counter = r0 = The counter to read (e.g. 0 for PMN0, 1 for PMN1) *
|
|
+read_pmn: */
|
|
+ AND r0, r0, #0x1F /* Mask to leave only bits 4:0 */
|
|
+ MCR p15, 0, r0, c9, c12, 5 /* Write PMNXSEL Register */
|
|
+ MRC p15, 0, r0, c9, c13, 2 /* Read current PMNx Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+ /* --------------------------------------------------------------- */
|
|
+ /* Software Increment */
|
|
+ /* --------------------------------------------------------------- */
|
|
+
|
|
+ .global pmu_software_increment @ export this function for the linker
|
|
+ /* Writes to software increment register */
|
|
+ /* void pmu_software_increment(uint32_t counter) */
|
|
+ /* counter = r0 = The counter to increment (e.g. 0 for PMN0, 1 for PMN
|
|
+pmu_software_increment: */
|
|
+ MOV r1, #0x01
|
|
+ MOV r1, r1, LSL r0
|
|
+ MCR p15, 0, r1, c9, c12, 4 /* Write SWINCR Register */
|
|
+ BX lr
|
|
+
|
|
+ /* --------------------------------------------------------------- */
|
|
+ /* Overflow & Interrupt Generation */
|
|
+ /* --------------------------------------------------------------- */
|
|
+
|
|
+ .global read_flags @ export this function for the linker
|
|
+ /* Returns the value of the overflow flags */
|
|
+ /* uint32_t read_flags(void) */
|
|
+read_flags:
|
|
+ MRC p15, 0, r0, c9, c12, 3 /* Read FLAG Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+ .global write_flags @ export this function for the linker
|
|
+ /* Writes the overflow flags */
|
|
+ /* void write_flags(uint32_t flags) */
|
|
+write_flags:
|
|
+ MCR p15, 0, r0, c9, c12, 3 /* Write FLAG Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+ .global enable_ccnt_irq @ export this function for the linker
|
|
+ /* Enables interrupt generation on overflow of the CCNT */
|
|
+ /* void enable_ccnt_irq(void) */
|
|
+enable_ccnt_irq:
|
|
+ MOV r0, #0x80000000
|
|
+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */
|
|
+ BX lr
|
|
+
|
|
+ .global disable_ccnt_irq @ export this function for the linker
|
|
+ /* Disables interrupt generation on overflow of the CCNT */
|
|
+ /* void disable_ccnt_irq(void) */
|
|
+disable_ccnt_irq:
|
|
+ MOV r0, #0x80000000
|
|
+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+ .global enable_pmn_irq @ export this function for the linker
|
|
+ /* Enables interrupt generation on overflow of PMN{x} */
|
|
+ /* void enable_pmn_irq(uint32_t counter) */
|
|
+ /* counter = r0 = The counter to enable the interrupt for (e.g. 0 for
|
|
+enable_pmn_irq: */
|
|
+ MOV r1, #0x1 /* Use arg (r0) to set which counter */
|
|
+ MOV r0, r1, LSL r0
|
|
+ MCR p15, 0, r0, c9, c14, 1 /* Write INTENS Register */
|
|
+ BX lr
|
|
+
|
|
+ .global disable_pmn_irq @ export this function for the linker
|
|
+ /* Disables interrupt generation on overflow of PMN{x} */
|
|
+ /* void disable_pmn_irq(uint32_t counter) */
|
|
+ /* counter = r0 = The counter to disable the interrupt for (e.g. 0 fo
|
|
+disable_pmn_irq: */
|
|
+ MOV r1, #0x1 /* Use arg (r0) to set which counter t */
|
|
+ MOV r0, r1, LSL r0
|
|
+ MCR p15, 0, r0, c9, c14, 2 /* Write INTENC Register */
|
|
+ BX lr
|
|
+
|
|
+ /* --------------------------------------------------------------- */
|
|
+ /* Reset Functions */
|
|
+ /* --------------------------------------------------------------- */
|
|
+
|
|
+ .global reset_pmn @ export this function for the linker
|
|
+ /* Resets the programmable counters */
|
|
+ /* void reset_pmn(void) */
|
|
+reset_pmn:
|
|
+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
|
|
+ ORR r0, r0, #0x02 /* Set P bit (Event Counter Reset) */
|
|
+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+ .global reset_ccnt @ export this function for the linker
|
|
+ /* Resets the CCNT */
|
|
+ /* void reset_ccnt(void) */
|
|
+reset_ccnt:
|
|
+ MRC p15, 0, r0, c9, c12, 0 /* Read PMNC */
|
|
+ ORR r0, r0, #0x04 /* Set C bit (Event Counter Reset) */
|
|
+ MCR p15, 0, r0, c9, c12, 0 /* Write PMNC */
|
|
+ BX lr
|
|
+
|
|
+
|
|
+ .end @end of code, this line is optional.
|
|
+/* ------------------------------------------------------------ */
|
|
+/* End of v7_pmu.s */
|
|
+/* ------------------------------------------------------------ */
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/v7_pmu.h
|
|
@@ -0,0 +1,113 @@
|
|
+// ------------------------------------------------------------
|
|
+// PMU for Cortex-A/R (v7-A/R)
|
|
+// ------------------------------------------------------------
|
|
+
|
|
+#ifndef _V7_PMU_H
|
|
+#define _V7_PMU_H
|
|
+
|
|
+// Returns the number of progammable counters
|
|
+unsigned int getPMN(void);
|
|
+
|
|
+// Sets the event for a programmable counter to record
|
|
+// counter = r0 = Which counter to program (e.g. 0 for PMN0, 1 for PMN1)
|
|
+// event = r1 = The event code (from appropiate TRM or ARM Architecture Reference Manual)
|
|
+void pmn_config(unsigned int counter, unsigned int event);
|
|
+
|
|
+// Enables/disables the divider (1/64) on CCNT
|
|
+// divider = r0 = If 0 disable divider, else enable dvider
|
|
+void ccnt_divider(int divider);
|
|
+
|
|
+//
|
|
+// Enables and disables
|
|
+//
|
|
+
|
|
+// Global PMU enable
|
|
+// On ARM11 this enables the PMU, and the counters start immediately
|
|
+// On Cortex this enables the PMU, there are individual enables for the counters
|
|
+void enable_pmu(void);
|
|
+
|
|
+// Global PMU disable
|
|
+// On Cortex, this overrides the enable state of the individual counters
|
|
+void disable_pmu(void);
|
|
+
|
|
+// Enable the CCNT
|
|
+void enable_ccnt(void);
|
|
+
|
|
+// Disable the CCNT
|
|
+void disable_ccnt(void);
|
|
+
|
|
+// Enable PMN{n}
|
|
+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
|
|
+void enable_pmn(unsigned int counter);
|
|
+
|
|
+// Enable PMN{n}
|
|
+// counter = The counter to enable (e.g. 0 for PMN0, 1 for PMN1)
|
|
+void disable_pmn(unsigned int counter);
|
|
+
|
|
+//
|
|
+// Read counter values
|
|
+//
|
|
+
|
|
+// Returns the value of CCNT
|
|
+unsigned int read_ccnt(void);
|
|
+
|
|
+// Returns the value of PMN{n}
|
|
+// counter = The counter to read (e.g. 0 for PMN0, 1 for PMN1)
|
|
+unsigned int read_pmn(unsigned int counter);
|
|
+
|
|
+//
|
|
+// Overflow and interrupts
|
|
+//
|
|
+
|
|
+// Returns the value of the overflow flags
|
|
+unsigned int read_flags(void);
|
|
+
|
|
+// Writes the overflow flags
|
|
+void write_flags(unsigned int flags);
|
|
+
|
|
+// Enables interrupt generation on overflow of the CCNT
|
|
+void enable_ccnt_irq(void);
|
|
+
|
|
+// Disables interrupt generation on overflow of the CCNT
|
|
+void disable_ccnt_irq(void);
|
|
+
|
|
+// Enables interrupt generation on overflow of PMN{x}
|
|
+// counter = The counter to enable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
|
|
+void enable_pmn_irq(unsigned int counter);
|
|
+
|
|
+// Disables interrupt generation on overflow of PMN{x}
|
|
+// counter = r0 = The counter to disable the interrupt for (e.g. 0 for PMN0, 1 for PMN1)
|
|
+void disable_pmn_irq(unsigned int counter);
|
|
+
|
|
+//
|
|
+// Counter reset functions
|
|
+//
|
|
+
|
|
+// Resets the programmable counters
|
|
+void reset_pmn(void);
|
|
+
|
|
+// Resets the CCNT
|
|
+void reset_ccnt(void);
|
|
+
|
|
+//
|
|
+// Software Increment
|
|
+
|
|
+// Writes to software increment register
|
|
+// counter = The counter to increment (e.g. 0 for PMN0, 1 for PMN1)
|
|
+void pmu_software_increment(unsigned int counter);
|
|
+
|
|
+//
|
|
+// User mode access
|
|
+//
|
|
+
|
|
+// Enables User mode access to the PMU (must be called in a priviledged mode)
|
|
+void enable_pmu_user_access(void);
|
|
+
|
|
+// Disables User mode access to the PMU (must be called in a priviledged mode)
|
|
+void disable_pmu_user_access(void);
|
|
+
|
|
+#endif
|
|
+// ------------------------------------------------------------
|
|
+// End of v7_pmu.h
|
|
+// ------------------------------------------------------------
|
|
+
|
|
--- a/modules/hw/mmal/vout.c
|
|
+++ b/modules/hw/mmal/vout.c
|
|
@@ -27,21 +27,24 @@
|
|
#endif
|
|
|
|
#include <math.h>
|
|
+#include <stdatomic.h>
|
|
|
|
#include <vlc_common.h>
|
|
-#include <vlc_atomic.h>
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_threads.h>
|
|
#include <vlc_vout_display.h>
|
|
+#include <vlc_modules.h>
|
|
|
|
#include "mmal_picture.h"
|
|
+#include "subpic.h"
|
|
|
|
#include <bcm_host.h>
|
|
#include <interface/mmal/mmal.h>
|
|
#include <interface/mmal/util/mmal_util.h>
|
|
#include <interface/mmal/util/mmal_default_components.h>
|
|
#include <interface/vmcs_host/vc_tvservice.h>
|
|
-#include <interface/vmcs_host/vc_dispmanx.h>
|
|
+
|
|
+#define TRACE_ALL 0
|
|
|
|
#define MAX_BUFFERS_IN_TRANSIT 1
|
|
#define VC_TV_MAX_MODE_IDS 127
|
|
@@ -50,11 +53,6 @@
|
|
#define MMAL_LAYER_TEXT N_("VideoCore layer where the video is displayed.")
|
|
#define MMAL_LAYER_LONGTEXT N_("VideoCore layer where the video is displayed. Subpictures are displayed directly above and a black background directly below.")
|
|
|
|
-#define MMAL_BLANK_BACKGROUND_NAME "mmal-blank-background"
|
|
-#define MMAL_BLANK_BACKGROUND_TEXT N_("Blank screen below video.")
|
|
-#define MMAL_BLANK_BACKGROUND_LONGTEXT N_("Render blank screen below video. " \
|
|
- "Increases VideoCore load.")
|
|
-
|
|
#define MMAL_ADJUST_REFRESHRATE_NAME "mmal-adjust-refreshrate"
|
|
#define MMAL_ADJUST_REFRESHRATE_TEXT N_("Adjust HDMI refresh rate to the video.")
|
|
#define MMAL_ADJUST_REFRESHRATE_LONGTEXT N_("Adjust HDMI refresh rate to the video.")
|
|
@@ -68,64 +66,30 @@
|
|
#define PHASE_OFFSET_TARGET ((double)0.25)
|
|
#define PHASE_CHECK_INTERVAL 100
|
|
|
|
-static int Open(vlc_object_t *);
|
|
-static void Close(vlc_object_t *);
|
|
+#define SUBS_MAX 4
|
|
|
|
-vlc_module_begin()
|
|
- set_shortname(N_("MMAL vout"))
|
|
- set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
|
|
- set_capability("vout display", 90)
|
|
- add_shortcut("mmal_vout")
|
|
- add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
|
|
- add_bool(MMAL_BLANK_BACKGROUND_NAME, true, MMAL_BLANK_BACKGROUND_TEXT,
|
|
- MMAL_BLANK_BACKGROUND_LONGTEXT, true);
|
|
- add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
|
|
- MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
|
|
- add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
|
|
- MMAL_NATIVE_INTERLACE_LONGTEXT, false)
|
|
- set_callbacks(Open, Close)
|
|
-vlc_module_end()
|
|
-
|
|
-struct dmx_region_t {
|
|
- struct dmx_region_t *next;
|
|
- picture_t *picture;
|
|
- VC_RECT_T bmp_rect;
|
|
- VC_RECT_T src_rect;
|
|
- VC_RECT_T dst_rect;
|
|
- VC_DISPMANX_ALPHA_T alpha;
|
|
- DISPMANX_ELEMENT_HANDLE_T element;
|
|
- DISPMANX_RESOURCE_HANDLE_T resource;
|
|
- int32_t pos_x;
|
|
- int32_t pos_y;
|
|
-};
|
|
+typedef struct vout_subpic_s {
|
|
+ MMAL_COMPONENT_T *component;
|
|
+ subpic_reg_stash_t sub;
|
|
+} vout_subpic_t;
|
|
|
|
struct vout_display_sys_t {
|
|
- vlc_cond_t buffer_cond;
|
|
- vlc_mutex_t buffer_mutex;
|
|
vlc_mutex_t manage_mutex;
|
|
|
|
- plane_t planes[3]; /* Depending on video format up to 3 planes are used */
|
|
- picture_t **pictures; /* Actual list of alloced pictures passed into picture_pool */
|
|
- picture_pool_t *picture_pool;
|
|
-
|
|
MMAL_COMPONENT_T *component;
|
|
MMAL_PORT_T *input;
|
|
MMAL_POOL_T *pool; /* mmal buffer headers, used for pushing pictures to component*/
|
|
- struct dmx_region_t *dmx_region;
|
|
int i_planes; /* Number of actually used planes, 1 for opaque, 3 for i420 */
|
|
|
|
uint32_t buffer_size; /* size of actual mmal buffers */
|
|
int buffers_in_transit; /* number of buffers currently pushed to mmal component */
|
|
unsigned num_buffers; /* number of buffers allocated at mmal port */
|
|
|
|
- DISPMANX_DISPLAY_HANDLE_T dmx_handle;
|
|
- DISPMANX_ELEMENT_HANDLE_T bkg_element;
|
|
- DISPMANX_RESOURCE_HANDLE_T bkg_resource;
|
|
unsigned display_width;
|
|
unsigned display_height;
|
|
|
|
- int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
|
|
- int i_frame_rate;
|
|
+ unsigned int i_frame_rate_base; /* cached framerate to detect changes for rate adjustment */
|
|
+ unsigned int i_frame_rate;
|
|
|
|
int next_phase_check; /* lowpass for phase check frequency */
|
|
int phase_offset; /* currently applied offset to presentation time in ns */
|
|
@@ -136,264 +100,415 @@
|
|
bool native_interlaced;
|
|
bool b_top_field_first; /* cached interlaced settings to detect changes for native mode */
|
|
bool b_progressive;
|
|
- bool opaque; /* indicated use of opaque picture format (zerocopy) */
|
|
-};
|
|
+ bool force_config;
|
|
|
|
-static const vlc_fourcc_t subpicture_chromas[] = {
|
|
- VLC_CODEC_RGBA,
|
|
- 0
|
|
+ vout_subpic_t subs[SUBS_MAX];
|
|
+
|
|
+ picture_pool_t * pic_pool;
|
|
+
|
|
+ struct vout_isp_conf_s {
|
|
+ MMAL_COMPONENT_T *component;
|
|
+ MMAL_PORT_T * input;
|
|
+ MMAL_PORT_T * output;
|
|
+ MMAL_QUEUE_T * out_q;
|
|
+ MMAL_POOL_T * in_pool;
|
|
+ MMAL_POOL_T * out_pool;
|
|
+ bool pending;
|
|
+ } isp;
|
|
};
|
|
|
|
-/* Utility functions */
|
|
-static inline uint32_t align(uint32_t x, uint32_t y);
|
|
-static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
|
|
- const video_format_t *fmt);
|
|
|
|
-/* VLC vout display callbacks */
|
|
-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count);
|
|
-static void vd_prepare(vout_display_t *vd, picture_t *picture,
|
|
- subpicture_t *subpicture);
|
|
-static void vd_display(vout_display_t *vd, picture_t *picture,
|
|
- subpicture_t *subpicture);
|
|
-static int vd_control(vout_display_t *vd, int query, va_list args);
|
|
-static void vd_manage(vout_display_t *vd);
|
|
-
|
|
-/* MMAL callbacks */
|
|
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
|
|
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer);
|
|
+// ISP setup
|
|
|
|
-/* TV service */
|
|
-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height);
|
|
-static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
|
|
- uint32_t param2);
|
|
-static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
|
|
-static int set_latency_target(vout_display_t *vd, bool enable);
|
|
+static inline bool want_isp(const vout_display_t * const vd)
|
|
+{
|
|
+ return (vd->fmt.i_chroma == VLC_CODEC_MMAL_ZC_SAND10);
|
|
+}
|
|
|
|
-/* DispManX */
|
|
-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture);
|
|
-static void close_dmx(vout_display_t *vd);
|
|
-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
|
|
- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region);
|
|
-static void dmx_region_update(struct dmx_region_t *dmx_region,
|
|
- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture);
|
|
-static void dmx_region_delete(struct dmx_region_t *dmx_region,
|
|
- DISPMANX_UPDATE_HANDLE_T update);
|
|
-static void show_background(vout_display_t *vd, bool enable);
|
|
-static void maintain_phase_sync(vout_display_t *vd);
|
|
+static MMAL_FOURCC_T vout_vlc_to_mmal_pic_fourcc(const unsigned int fcc)
|
|
+{
|
|
+ switch (fcc){
|
|
+ case VLC_CODEC_MMAL_OPAQUE:
|
|
+ return MMAL_ENCODING_OPAQUE;
|
|
+ case VLC_CODEC_MMAL_ZC_SAND8:
|
|
+ return MMAL_ENCODING_YUVUV128;
|
|
+ case VLC_CODEC_MMAL_ZC_SAND10:
|
|
+ return MMAL_ENCODING_YUVUV64_10; // It will be after we've converted it...
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
|
|
-static int Open(vlc_object_t *object)
|
|
+static void display_set_format(const vout_display_t * const vd, MMAL_ES_FORMAT_T *const es_fmt, const bool is_intermediate)
|
|
{
|
|
- vout_display_t *vd = (vout_display_t *)object;
|
|
- vout_display_sys_t *sys;
|
|
- uint32_t buffer_pitch, buffer_height;
|
|
- vout_display_place_t place;
|
|
- MMAL_DISPLAYREGION_T display_region;
|
|
- MMAL_STATUS_T status;
|
|
- int ret = VLC_SUCCESS;
|
|
- unsigned i;
|
|
+ const unsigned int w = is_intermediate ? vd->fmt.i_visible_width : vd->fmt.i_width ;
|
|
+ const unsigned int h = is_intermediate ? vd->fmt.i_visible_height : vd->fmt.i_height;
|
|
+ MMAL_VIDEO_FORMAT_T * const v_fmt = &es_fmt->es->video;
|
|
+
|
|
+ es_fmt->type = MMAL_ES_TYPE_VIDEO;
|
|
+ es_fmt->encoding = is_intermediate ? MMAL_ENCODING_I420 : vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);;
|
|
+ es_fmt->encoding_variant = 0;
|
|
+
|
|
+ v_fmt->width = (w + 31) & ~31;
|
|
+ v_fmt->height = (h + 15) & ~15;
|
|
+ v_fmt->crop.x = 0;
|
|
+ v_fmt->crop.y = 0;
|
|
+ v_fmt->crop.width = w;
|
|
+ v_fmt->crop.height = h;
|
|
+ if (vd->fmt.i_sar_num == 0 || vd->fmt.i_sar_den == 0) {
|
|
+ v_fmt->par.num = 1;
|
|
+ v_fmt->par.den = 1;
|
|
+ } else {
|
|
+ v_fmt->par.num = vd->fmt.i_sar_num;
|
|
+ v_fmt->par.den = vd->fmt.i_sar_den;
|
|
+ }
|
|
+ v_fmt->frame_rate.num = vd->fmt.i_frame_rate;
|
|
+ v_fmt->frame_rate.den = vd->fmt.i_frame_rate_base;
|
|
+ v_fmt->color_space = vlc_to_mmal_color_space(vd->fmt.space);
|
|
+}
|
|
|
|
- if (vout_display_IsWindowed(vd))
|
|
- return VLC_EGENERIC;
|
|
+static void display_src_rect(const vout_display_t * const vd, MMAL_RECT_T *const rect)
|
|
+{
|
|
+ const bool wants_isp = want_isp(vd);
|
|
+ rect->x = wants_isp ? 0 : vd->fmt.i_x_offset;
|
|
+ rect->y = wants_isp ? 0 : vd->fmt.i_y_offset;
|
|
+ rect->width = vd->fmt.i_visible_width;
|
|
+ rect->height = vd->fmt.i_visible_height;
|
|
+}
|
|
|
|
- sys = calloc(1, sizeof(struct vout_display_sys_t));
|
|
- if (!sys)
|
|
- return VLC_ENOMEM;
|
|
- vd->sys = sys;
|
|
+static void isp_input_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
|
|
+{
|
|
+#if TRACE_ALL
|
|
+ vout_display_t * const vd = (vout_display_t *)port->userdata;
|
|
+ pic_ctx_mmal_t * ctx = buf->user_data;
|
|
+ msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf,
|
|
+ buf->flags, (long long)buf->pts);
|
|
+#else
|
|
+ VLC_UNUSED(port);
|
|
+#endif
|
|
|
|
- sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
|
|
- bcm_host_init();
|
|
+ mmal_buffer_header_release(buf);
|
|
|
|
- sys->opaque = vd->fmt.i_chroma == VLC_CODEC_MMAL_OPAQUE;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, ">>> %s", __func__);
|
|
+#endif
|
|
+}
|
|
|
|
- status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
|
|
- MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
- }
|
|
+static void isp_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
+{
|
|
+ vout_display_t *vd = (vout_display_t *)port->userdata;
|
|
+ MMAL_STATUS_T status;
|
|
|
|
- sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
|
|
- status = mmal_port_enable(sys->component->control, control_port_cb);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
|
|
- sys->component->control->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
|
|
+ status = *(uint32_t *)buffer->data;
|
|
+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
|
|
}
|
|
|
|
- sys->input = sys->component->input[0];
|
|
- sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
|
|
+ mmal_buffer_header_release(buffer);
|
|
+}
|
|
|
|
- if (sys->opaque) {
|
|
- sys->input->format->encoding = MMAL_ENCODING_OPAQUE;
|
|
- sys->i_planes = 1;
|
|
- sys->buffer_size = sys->input->buffer_size_recommended;
|
|
- } else {
|
|
- sys->input->format->encoding = MMAL_ENCODING_I420;
|
|
- vd->fmt.i_chroma = VLC_CODEC_I420;
|
|
- buffer_pitch = align(vd->fmt.i_width, 32);
|
|
- buffer_height = align(vd->fmt.i_height, 16);
|
|
- sys->i_planes = 3;
|
|
- sys->buffer_size = 3 * buffer_pitch * buffer_height / 2;
|
|
- }
|
|
-
|
|
- sys->input->format->es->video.width = vd->fmt.i_width;
|
|
- sys->input->format->es->video.height = vd->fmt.i_height;
|
|
- sys->input->format->es->video.crop.x = 0;
|
|
- sys->input->format->es->video.crop.y = 0;
|
|
- sys->input->format->es->video.crop.width = vd->fmt.i_width;
|
|
- sys->input->format->es->video.crop.height = vd->fmt.i_height;
|
|
- sys->input->format->es->video.par.num = vd->source.i_sar_num;
|
|
- sys->input->format->es->video.par.den = vd->source.i_sar_den;
|
|
+static void isp_output_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
|
|
+{
|
|
+ if (buf->cmd == 0 && buf->length != 0)
|
|
+ {
|
|
+ // The filter structure etc. should always exist if we have contents
|
|
+ // but might not on later flushes as we shut down
|
|
+ vout_display_t * const vd = (vout_display_t *)port->userdata;
|
|
+ struct vout_isp_conf_s *const isp = &vd->sys->isp;
|
|
|
|
- status = mmal_port_format_commit(sys->input);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s: cmd=%d; flags=%#x, pts=%lld", __func__, buf->cmd, buf->flags, (long long) buf->pts);
|
|
+#endif
|
|
+ mmal_queue_put(isp->out_q, buf);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, ">>> %s: out Q len=%d", __func__, mmal_queue_length(isp->out_q));
|
|
+#endif
|
|
}
|
|
- sys->input->buffer_size = sys->input->buffer_size_recommended;
|
|
+ else
|
|
+ {
|
|
+ mmal_buffer_header_reset(buf);
|
|
+ mmal_buffer_header_release(buf);
|
|
+ }
|
|
+}
|
|
|
|
- vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
|
|
- display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
|
|
- display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
|
|
- display_region.fullscreen = MMAL_FALSE;
|
|
- display_region.src_rect.x = vd->fmt.i_x_offset;
|
|
- display_region.src_rect.y = vd->fmt.i_y_offset;
|
|
- display_region.src_rect.width = vd->fmt.i_visible_width;
|
|
- display_region.src_rect.height = vd->fmt.i_visible_height;
|
|
- display_region.dest_rect.x = place.x;
|
|
- display_region.dest_rect.y = place.y;
|
|
- display_region.dest_rect.width = place.width;
|
|
- display_region.dest_rect.height = place.height;
|
|
- display_region.layer = sys->layer;
|
|
- display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
|
|
- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
|
|
- status = mmal_port_parameter_set(sys->input, &display_region.hdr);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
|
|
- status, mmal_status_to_string(status));
|
|
- ret = VLC_EGENERIC;
|
|
- goto out;
|
|
+static void isp_empty_out_q(struct vout_isp_conf_s * const isp)
|
|
+{
|
|
+ MMAL_BUFFER_HEADER_T * buf;
|
|
+ // We can be called as part of error recovery so allow for missing Q
|
|
+ if (isp->out_q == NULL)
|
|
+ return;
|
|
+
|
|
+ while ((buf = mmal_queue_get(isp->out_q)) != NULL)
|
|
+ mmal_buffer_header_release(buf);
|
|
+}
|
|
+
|
|
+static void isp_flush(struct vout_isp_conf_s * const isp)
|
|
+{
|
|
+ if (!isp->input->is_enabled)
|
|
+ mmal_port_disable(isp->input);
|
|
+
|
|
+ if (isp->output->is_enabled)
|
|
+ mmal_port_disable(isp->output);
|
|
+
|
|
+ isp_empty_out_q(isp);
|
|
+ isp->pending = false;
|
|
+}
|
|
+
|
|
+static MMAL_STATUS_T isp_prepare(vout_display_t * const vd, struct vout_isp_conf_s * const isp)
|
|
+{
|
|
+ MMAL_STATUS_T err;
|
|
+ MMAL_BUFFER_HEADER_T * buf;
|
|
+
|
|
+ if (!isp->output->is_enabled) {
|
|
+ if ((err = mmal_port_enable(isp->output, isp_output_cb)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(vd, "ISP output port enable failed");
|
|
+ return err;
|
|
+ }
|
|
}
|
|
|
|
- for (i = 0; i < sys->i_planes; ++i) {
|
|
- sys->planes[i].i_lines = buffer_height;
|
|
- sys->planes[i].i_pitch = buffer_pitch;
|
|
- sys->planes[i].i_visible_lines = vd->fmt.i_visible_height;
|
|
- sys->planes[i].i_visible_pitch = vd->fmt.i_visible_width;
|
|
+ while ((buf = mmal_queue_get(isp->out_pool->queue)) != NULL) {
|
|
+ if ((err = mmal_port_send_buffer(isp->output, buf)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(vd, "ISP output port stuff failed");
|
|
+ return err;
|
|
+ }
|
|
+ }
|
|
|
|
- if (i > 0) {
|
|
- sys->planes[i].i_lines /= 2;
|
|
- sys->planes[i].i_pitch /= 2;
|
|
- sys->planes[i].i_visible_lines /= 2;
|
|
- sys->planes[i].i_visible_pitch /= 2;
|
|
+ if (!isp->input->is_enabled) {
|
|
+ if ((err = mmal_port_enable(isp->input, isp_input_cb)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(vd, "ISP input port enable failed");
|
|
+ return err;
|
|
}
|
|
}
|
|
+ return MMAL_SUCCESS;
|
|
+}
|
|
|
|
- vlc_mutex_init(&sys->buffer_mutex);
|
|
- vlc_cond_init(&sys->buffer_cond);
|
|
- vlc_mutex_init(&sys->manage_mutex);
|
|
+static void isp_close(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
|
|
+{
|
|
+ struct vout_isp_conf_s * const isp = &vd_sys->isp;
|
|
+ VLC_UNUSED(vd);
|
|
|
|
- vd->pool = vd_pool;
|
|
- vd->prepare = vd_prepare;
|
|
- vd->display = vd_display;
|
|
- vd->control = vd_control;
|
|
- vd->manage = vd_manage;
|
|
+ if (isp->component == NULL)
|
|
+ return;
|
|
|
|
- vc_tv_register_callback(tvservice_cb, vd);
|
|
+ isp_flush(isp);
|
|
|
|
- if (query_resolution(vd, &sys->display_width, &sys->display_height) >= 0) {
|
|
- vout_display_SendEventDisplaySize(vd, sys->display_width, sys->display_height);
|
|
- } else {
|
|
- sys->display_width = vd->cfg->display.width;
|
|
- sys->display_height = vd->cfg->display.height;
|
|
+ if (isp->component->control->is_enabled)
|
|
+ mmal_port_disable(isp->component->control);
|
|
+
|
|
+ if (isp->out_q != NULL) {
|
|
+ // 1st junk anything lying around
|
|
+ isp_empty_out_q(isp);
|
|
+
|
|
+ mmal_queue_destroy(isp->out_q);
|
|
+ isp->out_q = NULL;
|
|
}
|
|
|
|
- sys->dmx_handle = vc_dispmanx_display_open(0);
|
|
- vd->info.subpicture_chromas = subpicture_chromas;
|
|
+ if (isp->out_pool != NULL) {
|
|
+ mmal_port_pool_destroy(isp->output, isp->out_pool);
|
|
+ isp->out_pool = NULL;
|
|
+ }
|
|
|
|
- vout_display_DeleteWindow(vd, NULL);
|
|
+ isp->input = NULL;
|
|
+ isp->output = NULL;
|
|
|
|
-out:
|
|
- if (ret != VLC_SUCCESS)
|
|
- Close(object);
|
|
+ mmal_component_release(isp->component);
|
|
+ isp->component = NULL;
|
|
|
|
- return ret;
|
|
+ return;
|
|
}
|
|
|
|
-static void Close(vlc_object_t *object)
|
|
+// Restuff into output rather than return to pool is we can
|
|
+static MMAL_BOOL_T isp_out_pool_cb(MMAL_POOL_T *pool, MMAL_BUFFER_HEADER_T *buffer, void *userdata)
|
|
{
|
|
- vout_display_t *vd = (vout_display_t *)object;
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
- char response[20]; /* answer is hvs_update_fields=%1d */
|
|
- unsigned i;
|
|
+ struct vout_isp_conf_s * const isp = userdata;
|
|
+ VLC_UNUSED(pool);
|
|
+ if (isp->output->is_enabled) {
|
|
+ mmal_buffer_header_reset(buffer);
|
|
+ if (mmal_port_send_buffer(isp->output, buffer) == MMAL_SUCCESS)
|
|
+ return MMAL_FALSE;
|
|
+ }
|
|
+ return MMAL_TRUE;
|
|
+}
|
|
|
|
- vc_tv_unregister_callback_full(tvservice_cb, vd);
|
|
+static MMAL_STATUS_T isp_setup(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
|
|
+{
|
|
+ struct vout_isp_conf_s * const isp = &vd_sys->isp;
|
|
+ MMAL_STATUS_T err;
|
|
|
|
- if (sys->dmx_handle)
|
|
- close_dmx(vd);
|
|
+ if ((err = mmal_component_create(MMAL_COMPONENT_ISP_RESIZER, &isp->component)) != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Cannot create ISP component");
|
|
+ return err;
|
|
+ }
|
|
+ isp->input = isp->component->input[0];
|
|
+ isp->output = isp->component->output[0];
|
|
|
|
- if (sys->component && sys->component->control->is_enabled)
|
|
- mmal_port_disable(sys->component->control);
|
|
+ isp->component->control->userdata = (void *)vd;
|
|
+ if ((err = mmal_port_enable(isp->component->control, isp_control_port_cb)) != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to enable ISP control port");
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- if (sys->input && sys->input->is_enabled)
|
|
- mmal_port_disable(sys->input);
|
|
+ isp->input->userdata = (void *)vd;
|
|
+ display_set_format(vd, isp->input->format, false);
|
|
|
|
- if (sys->component && sys->component->is_enabled)
|
|
- mmal_component_disable(sys->component);
|
|
+ if ((err = port_parameter_set_bool(isp->input, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
|
|
- if (sys->pool)
|
|
- mmal_port_pool_destroy(sys->input, sys->pool);
|
|
+ if ((err = mmal_port_format_commit(isp->input)) != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to set ISP input format");
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- if (sys->component)
|
|
- mmal_component_release(sys->component);
|
|
+ isp->input->buffer_size = isp->input->buffer_size_recommended;
|
|
+ isp->input->buffer_num = 30;
|
|
|
|
- if (sys->picture_pool)
|
|
- picture_pool_Release(sys->picture_pool);
|
|
- else
|
|
- for (i = 0; i < sys->num_buffers; ++i)
|
|
- if (sys->pictures[i]) {
|
|
- mmal_buffer_header_release(sys->pictures[i]->p_sys->buffer);
|
|
- picture_Release(sys->pictures[i]);
|
|
- }
|
|
+ if ((isp->in_pool = mmal_pool_create(isp->input->buffer_num, 0)) == NULL)
|
|
+ {
|
|
+ msg_Err(vd, "Failed to create input pool");
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- vlc_mutex_destroy(&sys->buffer_mutex);
|
|
- vlc_cond_destroy(&sys->buffer_cond);
|
|
- vlc_mutex_destroy(&sys->manage_mutex);
|
|
+ if ((isp->out_q = mmal_queue_create()) == NULL)
|
|
+ {
|
|
+ err = MMAL_ENOMEM;
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- if (sys->native_interlaced) {
|
|
- if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
|
|
- response[18] != '0')
|
|
- msg_Warn(vd, "Could not reset hvs field mode");
|
|
+ display_set_format(vd, isp->output->format, true);
|
|
+
|
|
+ if ((err = port_parameter_set_bool(isp->output, MMAL_PARAMETER_ZERO_COPY, true)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+
|
|
+ if ((err = mmal_port_format_commit(isp->output)) != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to set ISP input format");
|
|
+ goto fail;
|
|
}
|
|
|
|
- free(sys->pictures);
|
|
- free(sys);
|
|
+ isp->output->buffer_size = isp->output->buffer_size_recommended;
|
|
+ isp->output->buffer_num = 2;
|
|
+ isp->output->userdata = (void *)vd;
|
|
|
|
- bcm_host_deinit();
|
|
+ if ((isp->out_pool = mmal_port_pool_create(isp->output, isp->output->buffer_num, isp->output->buffer_size)) == NULL)
|
|
+ {
|
|
+ msg_Err(vd, "Failed to make ISP port pool");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ mmal_pool_callback_set(isp->out_pool, isp_out_pool_cb, isp);
|
|
+
|
|
+ if ((err = isp_prepare(vd, isp)) != MMAL_SUCCESS)
|
|
+ goto fail;
|
|
+
|
|
+ return MMAL_SUCCESS;
|
|
+
|
|
+fail:
|
|
+ isp_close(vd, vd_sys);
|
|
+ return err;
|
|
}
|
|
|
|
-static inline uint32_t align(uint32_t x, uint32_t y) {
|
|
- uint32_t mod = x % y;
|
|
- if (mod == 0)
|
|
- return x;
|
|
+static MMAL_STATUS_T isp_check(vout_display_t * const vd, vout_display_sys_t * const vd_sys)
|
|
+{
|
|
+ struct vout_isp_conf_s *const isp = &vd_sys->isp;
|
|
+ const bool has_isp = (isp->component != NULL);
|
|
+ const bool wants_isp = want_isp(vd);
|
|
+
|
|
+ if (has_isp == wants_isp)
|
|
+ {
|
|
+ // All OK - do nothing
|
|
+ }
|
|
+ else if (has_isp)
|
|
+ {
|
|
+ // ISP active but we don't want it
|
|
+ isp_flush(isp);
|
|
+
|
|
+ // Check we have everything back and then kill it
|
|
+ if (mmal_queue_length(isp->out_pool->queue) == isp->output->buffer_num)
|
|
+ isp_close(vd, vd_sys);
|
|
+ }
|
|
else
|
|
- return x + y - mod;
|
|
+ {
|
|
+ // ISP closed but we want it
|
|
+ return isp_setup(vd, vd_sys);
|
|
+ }
|
|
+
|
|
+ return MMAL_SUCCESS;
|
|
+}
|
|
+
|
|
+/* TV service */
|
|
+static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1,
|
|
+ uint32_t param2);
|
|
+static void adjust_refresh_rate(vout_display_t *vd, const video_format_t *fmt);
|
|
+static int set_latency_target(vout_display_t *vd, bool enable);
|
|
+
|
|
+// Mmal
|
|
+static void maintain_phase_sync(vout_display_t *vd);
|
|
+
|
|
+
|
|
+
|
|
+static void vd_input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf)
|
|
+{
|
|
+#if TRACE_ALL
|
|
+ vout_display_t * const vd = (vout_display_t *)port->userdata;
|
|
+ pic_ctx_mmal_t * ctx = buf->user_data;
|
|
+ msg_Dbg(vd, "<<< %s: cmd=%d, ctx=%p, buf=%p, flags=%#x, pts=%lld", __func__, buf->cmd, ctx, buf,
|
|
+ buf->flags, (long long)buf->pts);
|
|
+#else
|
|
+ VLC_UNUSED(port);
|
|
+#endif
|
|
+
|
|
+ mmal_buffer_header_release(buf);
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, ">>> %s", __func__);
|
|
+#endif
|
|
+}
|
|
+
|
|
+static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
|
|
+{
|
|
+ TV_DISPLAY_STATE_T display_state;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (vc_tv_get_display_state(&display_state) == 0) {
|
|
+ msg_Dbg(vd, "State=%#x", display_state.state);
|
|
+ if (display_state.state & 0xFF) {
|
|
+ msg_Dbg(vd, "HDMI: %dx%d", display_state.display.hdmi.width, display_state.display.hdmi.height);
|
|
+ *width = display_state.display.hdmi.width;
|
|
+ *height = display_state.display.hdmi.height;
|
|
+ } else if (display_state.state & 0xFF00) {
|
|
+ msg_Dbg(vd, "SDTV: %dx%d", display_state.display.sdtv.width, display_state.display.sdtv.height);
|
|
+ *width = display_state.display.sdtv.width;
|
|
+ *height = display_state.display.sdtv.height;
|
|
+ } else {
|
|
+ msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
|
|
+ ret = -1;
|
|
+ }
|
|
+ } else {
|
|
+ msg_Warn(vd, "Failed to query display resolution");
|
|
+ ret = -1;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
}
|
|
|
|
static int configure_display(vout_display_t *vd, const vout_display_cfg_t *cfg,
|
|
const video_format_t *fmt)
|
|
{
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
+ vout_display_sys_t * const sys = vd->sys;
|
|
vout_display_place_t place;
|
|
MMAL_DISPLAYREGION_T display_region;
|
|
MMAL_STATUS_T status;
|
|
|
|
if (!cfg && !fmt)
|
|
+ {
|
|
+ msg_Err(vd, "%s: Missing cfg & fmt", __func__);
|
|
return -EINVAL;
|
|
+ }
|
|
+
|
|
+ isp_check(vd, sys);
|
|
|
|
if (fmt) {
|
|
sys->input->format->es->video.par.num = fmt->i_sar_num;
|
|
@@ -412,22 +527,29 @@
|
|
if (!cfg)
|
|
cfg = vd->cfg;
|
|
|
|
- vout_display_PlacePicture(&place, fmt, cfg, false);
|
|
+ {
|
|
+ // Ignore what VLC thinks might be going on with display size
|
|
+ vout_display_cfg_t tcfg = *cfg;
|
|
+ tcfg.display.width = sys->display_width;
|
|
+ tcfg.display.height = sys->display_height;
|
|
+ tcfg.is_display_filled = true;
|
|
+ vout_display_PlacePicture(&place, fmt, &tcfg, false);
|
|
+
|
|
+ msg_Dbg(vd, "%dx%d -> %dx%d @ %d,%d", tcfg.display.width, tcfg.display.height, place.width, place.height, place.x, place.y);
|
|
+ }
|
|
|
|
display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
|
|
display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
|
|
display_region.fullscreen = MMAL_FALSE;
|
|
- display_region.src_rect.x = fmt->i_x_offset;
|
|
- display_region.src_rect.y = fmt->i_y_offset;
|
|
- display_region.src_rect.width = fmt->i_visible_width;
|
|
- display_region.src_rect.height = fmt->i_visible_height;
|
|
+ display_src_rect(vd, &display_region.src_rect);
|
|
display_region.dest_rect.x = place.x;
|
|
display_region.dest_rect.y = place.y;
|
|
display_region.dest_rect.width = place.width;
|
|
display_region.dest_rect.height = place.height;
|
|
display_region.layer = sys->layer;
|
|
+ display_region.alpha = 0xff | (1 << 29);
|
|
display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
|
|
- MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
|
|
+ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_ALPHA;
|
|
status = mmal_port_parameter_set(sys->input, &display_region.hdr);
|
|
if (status != MMAL_SUCCESS) {
|
|
msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
|
|
@@ -435,7 +557,6 @@
|
|
return -EINVAL;
|
|
}
|
|
|
|
- show_background(vd, var_InheritBool(vd, MMAL_BLANK_BACKGROUND_NAME));
|
|
sys->adjust_refresh_rate = var_InheritBool(vd, MMAL_ADJUST_REFRESHRATE_NAME);
|
|
sys->native_interlaced = var_InheritBool(vd, MMAL_NATIVE_INTERLACED);
|
|
if (sys->adjust_refresh_rate) {
|
|
@@ -446,191 +567,130 @@
|
|
return 0;
|
|
}
|
|
|
|
-static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
|
|
+static void kill_pool(vout_display_sys_t * const sys)
|
|
{
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
- picture_resource_t picture_res;
|
|
- picture_pool_configuration_t picture_pool_cfg;
|
|
- video_format_t fmt = vd->fmt;
|
|
- MMAL_STATUS_T status;
|
|
- unsigned i;
|
|
-
|
|
- if (sys->picture_pool) {
|
|
- if (sys->num_buffers < count)
|
|
- msg_Warn(vd, "Picture pool with %u pictures requested, but we already have one with %u pictures",
|
|
- count, sys->num_buffers);
|
|
-
|
|
- goto out;
|
|
+ if (sys->pic_pool != NULL) {
|
|
+ picture_pool_Release(sys->pic_pool);
|
|
+ sys->pic_pool = NULL;
|
|
}
|
|
+}
|
|
|
|
- if (sys->opaque) {
|
|
- if (count <= NUM_ACTUAL_OPAQUE_BUFFERS)
|
|
- count = NUM_ACTUAL_OPAQUE_BUFFERS;
|
|
+// Actual picture pool for MMAL opaques is just a set of trivial containers
|
|
+static picture_pool_t *vd_pool(vout_display_t *vd, unsigned count)
|
|
+{
|
|
+ vout_display_sys_t * const sys = vd->sys;
|
|
|
|
- MMAL_PARAMETER_BOOLEAN_T zero_copy = {
|
|
- { MMAL_PARAMETER_ZERO_COPY, sizeof(MMAL_PARAMETER_BOOLEAN_T) },
|
|
- 1
|
|
- };
|
|
+ msg_Dbg(vd, "%s: fmt:%dx%d,sar:%d/%d; source:%dx%d", __func__,
|
|
+ vd->fmt.i_width, vd->fmt.i_height, vd->fmt.i_sar_num, vd->fmt.i_sar_den, vd->source.i_width, vd->source.i_height);
|
|
|
|
- status = mmal_port_parameter_set(sys->input, &zero_copy.hdr);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- goto out;
|
|
- }
|
|
+ if (sys->pic_pool == NULL) {
|
|
+ sys->pic_pool = picture_pool_NewFromFormat(&vd->fmt, count);
|
|
}
|
|
+ return sys->pic_pool;
|
|
+}
|
|
|
|
- if (count < sys->input->buffer_num_recommended)
|
|
- count = sys->input->buffer_num_recommended;
|
|
+static void vd_display(vout_display_t *vd, picture_t *p_pic,
|
|
+ subpicture_t *subpicture)
|
|
+{
|
|
+ vout_display_sys_t * const sys = vd->sys;
|
|
+ MMAL_STATUS_T err;
|
|
|
|
-#ifndef NDEBUG
|
|
- msg_Dbg(vd, "Creating picture pool with %u pictures", count);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s", __func__);
|
|
#endif
|
|
|
|
- sys->input->buffer_num = count;
|
|
- status = mmal_port_enable(sys->input, input_port_cb);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
|
|
- sys->input->name, status, mmal_status_to_string(status));
|
|
- goto out;
|
|
- }
|
|
-
|
|
- status = mmal_component_enable(sys->component);
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
|
|
- sys->component->name, status, mmal_status_to_string(status));
|
|
- goto out;
|
|
+ // Not expecting subpictures in the current setup
|
|
+ // Subpics should be attached to the main pic
|
|
+ if (subpicture != NULL) {
|
|
+ subpicture_Delete(subpicture);
|
|
}
|
|
|
|
- sys->num_buffers = count;
|
|
- sys->pool = mmal_port_pool_create(sys->input, sys->num_buffers,
|
|
- sys->input->buffer_size);
|
|
- if (!sys->pool) {
|
|
- msg_Err(vd, "Failed to create MMAL pool for %u buffers of size %"PRIu32,
|
|
- count, sys->input->buffer_size);
|
|
- goto out;
|
|
+ if (sys->force_config ||
|
|
+ p_pic->format.i_frame_rate != sys->i_frame_rate ||
|
|
+ p_pic->format.i_frame_rate_base != sys->i_frame_rate_base ||
|
|
+ p_pic->b_progressive != sys->b_progressive ||
|
|
+ p_pic->b_top_field_first != sys->b_top_field_first)
|
|
+ {
|
|
+ sys->force_config = false;
|
|
+ sys->b_top_field_first = p_pic->b_top_field_first;
|
|
+ sys->b_progressive = p_pic->b_progressive;
|
|
+ sys->i_frame_rate = p_pic->format.i_frame_rate;
|
|
+ sys->i_frame_rate_base = p_pic->format.i_frame_rate_base;
|
|
+ configure_display(vd, NULL, &p_pic->format);
|
|
+ }
|
|
+
|
|
+ if (!sys->input->is_enabled &&
|
|
+ (err = mmal_port_enable(sys->input, vd_input_port_cb)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(vd, "Input port enable failed");
|
|
+ goto fail;
|
|
+ }
|
|
+ // Stuff into input
|
|
+ // We assume the BH is already set up with values reflecting pic date etc.
|
|
+ if (sys->isp.pending) {
|
|
+ MMAL_BUFFER_HEADER_T *const buf = mmal_queue_wait(sys->isp.out_q);
|
|
+ sys->isp.pending = false;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "--- %s: ISP stuff", __func__);
|
|
+#endif
|
|
+ if (mmal_port_send_buffer(sys->input, buf) != MMAL_SUCCESS)
|
|
+ {
|
|
+ mmal_buffer_header_release(buf);
|
|
+ msg_Err(vd, "Send ISP buffer to render input failed");
|
|
+ goto fail;
|
|
+ }
|
|
}
|
|
-
|
|
- memset(&picture_res, 0, sizeof(picture_resource_t));
|
|
- sys->pictures = calloc(sys->num_buffers, sizeof(picture_t *));
|
|
- for (i = 0; i < sys->num_buffers; ++i) {
|
|
- picture_res.p_sys = calloc(1, sizeof(picture_sys_t));
|
|
- picture_res.p_sys->owner = (vlc_object_t *)vd;
|
|
- picture_res.p_sys->buffer = mmal_queue_get(sys->pool->queue);
|
|
-
|
|
- sys->pictures[i] = picture_NewFromResource(&fmt, &picture_res);
|
|
- if (!sys->pictures[i]) {
|
|
- msg_Err(vd, "Failed to create picture");
|
|
- free(picture_res.p_sys);
|
|
- goto out;
|
|
+ else
|
|
+ {
|
|
+ MMAL_BUFFER_HEADER_T * const pic_buf = pic_mmal_buffer(p_pic);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "--- %s: Buf stuff", __func__);
|
|
+#endif
|
|
+ if ((err = port_send_replicated(sys->input, sys->pool, pic_buf, pic_buf->pts)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(vd, "Send buffer to input failed");
|
|
+ goto fail;
|
|
}
|
|
-
|
|
- sys->pictures[i]->i_planes = sys->i_planes;
|
|
- memcpy(sys->pictures[i]->p, sys->planes, sys->i_planes * sizeof(plane_t));
|
|
}
|
|
|
|
- memset(&picture_pool_cfg, 0, sizeof(picture_pool_configuration_t));
|
|
- picture_pool_cfg.picture_count = sys->num_buffers;
|
|
- picture_pool_cfg.picture = sys->pictures;
|
|
- picture_pool_cfg.lock = mmal_picture_lock;
|
|
-
|
|
- sys->picture_pool = picture_pool_NewExtended(&picture_pool_cfg);
|
|
- if (!sys->picture_pool) {
|
|
- msg_Err(vd, "Failed to create picture pool");
|
|
- goto out;
|
|
+ if (p_pic->context == NULL) {
|
|
+ msg_Dbg(vd, "%s: No context", __func__);
|
|
}
|
|
+ else
|
|
+ {
|
|
+ unsigned int sub_no = 0;
|
|
|
|
-out:
|
|
- return sys->picture_pool;
|
|
-}
|
|
-
|
|
-static void vd_prepare(vout_display_t *vd, picture_t *picture,
|
|
- subpicture_t *subpicture)
|
|
-{
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
- picture_sys_t *pic_sys = picture->p_sys;
|
|
-
|
|
- if (!sys->adjust_refresh_rate || pic_sys->displayed)
|
|
- return;
|
|
-
|
|
- /* Apply the required phase_offset to the picture, so that vd_display()
|
|
- * will be called at the corrected time from the core */
|
|
- picture->date += sys->phase_offset;
|
|
-}
|
|
-
|
|
-static void vd_display(vout_display_t *vd, picture_t *picture,
|
|
- subpicture_t *subpicture)
|
|
-{
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
- picture_sys_t *pic_sys = picture->p_sys;
|
|
- MMAL_BUFFER_HEADER_T *buffer = pic_sys->buffer;
|
|
- MMAL_STATUS_T status;
|
|
-
|
|
- if (picture->format.i_frame_rate != sys->i_frame_rate ||
|
|
- picture->format.i_frame_rate_base != sys->i_frame_rate_base ||
|
|
- picture->b_progressive != sys->b_progressive ||
|
|
- picture->b_top_field_first != sys->b_top_field_first) {
|
|
- sys->b_top_field_first = picture->b_top_field_first;
|
|
- sys->b_progressive = picture->b_progressive;
|
|
- sys->i_frame_rate = picture->format.i_frame_rate;
|
|
- sys->i_frame_rate_base = picture->format.i_frame_rate_base;
|
|
- configure_display(vd, NULL, &picture->format);
|
|
- }
|
|
-
|
|
- if (!pic_sys->displayed || !sys->opaque) {
|
|
- buffer->cmd = 0;
|
|
- buffer->length = sys->input->buffer_size;
|
|
- buffer->user_data = picture;
|
|
-
|
|
- status = mmal_port_send_buffer(sys->input, buffer);
|
|
- if (status == MMAL_SUCCESS)
|
|
- atomic_fetch_add(&sys->buffers_in_transit, 1);
|
|
-
|
|
- if (status != MMAL_SUCCESS) {
|
|
- msg_Err(vd, "Failed to send buffer to input port. Frame dropped");
|
|
- picture_Release(picture);
|
|
+ for (sub_no = 0; sub_no != SUBS_MAX; ++sub_no) {
|
|
+ int rv;
|
|
+ if ((rv = hw_mmal_subpic_update(VLC_OBJECT(vd), p_pic, sub_no, &sys->subs[sub_no].sub,
|
|
+ &(MMAL_RECT_T){.width = sys->display_width, .height = sys->display_height},
|
|
+ p_pic->date)) == 0)
|
|
+ break;
|
|
+ else if (rv < 0)
|
|
+ goto fail;
|
|
}
|
|
-
|
|
- pic_sys->displayed = true;
|
|
- } else {
|
|
- picture_Release(picture);
|
|
}
|
|
|
|
- display_subpicture(vd, subpicture);
|
|
-
|
|
- if (subpicture)
|
|
- subpicture_Delete(subpicture);
|
|
+ picture_Release(p_pic);
|
|
|
|
if (sys->next_phase_check == 0 && sys->adjust_refresh_rate)
|
|
maintain_phase_sync(vd);
|
|
sys->next_phase_check = (sys->next_phase_check + 1) % PHASE_CHECK_INTERVAL;
|
|
|
|
- if (sys->opaque) {
|
|
- vlc_mutex_lock(&sys->buffer_mutex);
|
|
- while (atomic_load(&sys->buffers_in_transit) >= MAX_BUFFERS_IN_TRANSIT)
|
|
- vlc_cond_wait(&sys->buffer_cond, &sys->buffer_mutex);
|
|
- vlc_mutex_unlock(&sys->buffer_mutex);
|
|
- }
|
|
+fail:
|
|
+ /* NOP */;
|
|
}
|
|
|
|
static int vd_control(vout_display_t *vd, int query, va_list args)
|
|
{
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
- vout_display_cfg_t cfg;
|
|
- const vout_display_cfg_t *tmp_cfg;
|
|
+ vout_display_sys_t * const sys = vd->sys;
|
|
int ret = VLC_EGENERIC;
|
|
+ VLC_UNUSED(args);
|
|
|
|
switch (query) {
|
|
case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
|
|
- tmp_cfg = va_arg(args, const vout_display_cfg_t *);
|
|
- if (tmp_cfg->display.width == sys->display_width &&
|
|
- tmp_cfg->display.height == sys->display_height) {
|
|
- cfg = *vd->cfg;
|
|
- cfg.display.width = sys->display_width;
|
|
- cfg.display.height = sys->display_height;
|
|
- if (configure_display(vd, &cfg, NULL) >= 0)
|
|
- ret = VLC_SUCCESS;
|
|
- }
|
|
+ // Ignore this - we just use full screen anyway
|
|
+ ret = VLC_SUCCESS;
|
|
break;
|
|
|
|
case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
|
|
@@ -640,10 +700,37 @@
|
|
break;
|
|
|
|
case VOUT_DISPLAY_RESET_PICTURES:
|
|
- vlc_assert_unreachable();
|
|
+ msg_Warn(vd, "Reset Pictures");
|
|
+ kill_pool(sys);
|
|
+ vd->fmt = vd->source; // Take whatever source wants to give us
|
|
+ ret = VLC_SUCCESS;
|
|
+ break;
|
|
+
|
|
case VOUT_DISPLAY_CHANGE_ZOOM:
|
|
msg_Warn(vd, "Unsupported control query %d", query);
|
|
+ ret = VLC_SUCCESS;
|
|
+ break;
|
|
+
|
|
+ case VOUT_DISPLAY_CHANGE_MMAL_HIDE:
|
|
+ {
|
|
+ MMAL_STATUS_T err;
|
|
+ unsigned int i;
|
|
+
|
|
+ msg_Dbg(vd, "Hide display");
|
|
+
|
|
+ for (i = 0; i != SUBS_MAX; ++i)
|
|
+ hw_mmal_subpic_flush(VLC_OBJECT(vd), &sys->subs[i].sub);
|
|
+
|
|
+ if (sys->input->is_enabled &&
|
|
+ (err = mmal_port_disable(sys->input)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(vd, "Unable to disable port: err=%d", err);
|
|
+ break;
|
|
+ }
|
|
+ sys->force_config = true;
|
|
+ ret = VLC_SUCCESS;
|
|
break;
|
|
+ }
|
|
|
|
default:
|
|
msg_Warn(vd, "Unknown control query %d", query);
|
|
@@ -661,13 +748,11 @@
|
|
vlc_mutex_lock(&sys->manage_mutex);
|
|
|
|
if (sys->need_configure_display) {
|
|
- close_dmx(vd);
|
|
- sys->dmx_handle = vc_dispmanx_display_open(0);
|
|
-
|
|
if (query_resolution(vd, &width, &height) >= 0) {
|
|
sys->display_width = width;
|
|
sys->display_height = height;
|
|
- vout_display_SendEventDisplaySize(vd, width, height);
|
|
+// msg_Dbg(vd, "%s: %dx%d", __func__, width, height);
|
|
+// vout_window_ReportSize(vd->cfg->window, width, height);
|
|
}
|
|
|
|
sys->need_configure_display = false;
|
|
@@ -676,56 +761,76 @@
|
|
vlc_mutex_unlock(&sys->manage_mutex);
|
|
}
|
|
|
|
-static void control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
+static void vd_prepare(vout_display_t *vd, picture_t *p_pic,
|
|
+#if VLC_VER_3
|
|
+ subpicture_t *subpicture
|
|
+#else
|
|
+ subpicture_t *subpicture, vlc_tick_t date
|
|
+#endif
|
|
+ )
|
|
{
|
|
- vout_display_t *vd = (vout_display_t *)port->userdata;
|
|
- MMAL_STATUS_T status;
|
|
+ MMAL_STATUS_T err;
|
|
+ vout_display_sys_t * const sys = vd->sys;
|
|
|
|
- if (buffer->cmd == MMAL_EVENT_ERROR) {
|
|
- status = *(uint32_t *)buffer->data;
|
|
- msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
|
|
+ VLC_UNUSED(subpicture);
|
|
+// VLC_UNUSED(date);
|
|
+
|
|
+ vd_manage(vd);
|
|
+
|
|
+ if (isp_check(vd, sys) != MMAL_SUCCESS) {
|
|
+ return;
|
|
}
|
|
|
|
- mmal_buffer_header_release(buffer);
|
|
-}
|
|
+ if (want_isp(vd))
|
|
+ {
|
|
+ struct vout_isp_conf_s * const isp = &sys->isp;
|
|
+ MMAL_BUFFER_HEADER_T * buf;
|
|
+
|
|
+ // This should be empty - make it so if it isn't
|
|
+ isp_empty_out_q(isp);
|
|
+ isp->pending = false;
|
|
|
|
-static void input_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
-{
|
|
- vout_display_t *vd = (vout_display_t *)port->userdata;
|
|
+ // Stuff output
|
|
+ if (isp_prepare(vd, isp) != MMAL_SUCCESS)
|
|
+ return;
|
|
+
|
|
+ buf = pic_mmal_buffer(p_pic);
|
|
+ if ((err = port_send_replicated(isp->input, isp->in_pool,
|
|
+ buf, buf->pts)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Err(vd, "Send buffer to input failed");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ isp->pending = true;
|
|
+ }
|
|
+
|
|
+#if 0
|
|
+ VLC_UNUSED(date);
|
|
vout_display_sys_t *sys = vd->sys;
|
|
- picture_t *picture = (picture_t *)buffer->user_data;
|
|
+ picture_sys_t *pic_sys = picture->p_sys;
|
|
|
|
- if (picture)
|
|
- picture_Release(picture);
|
|
+ if (!sys->adjust_refresh_rate || pic_sys->displayed)
|
|
+ return;
|
|
|
|
- vlc_mutex_lock(&sys->buffer_mutex);
|
|
- atomic_fetch_sub(&sys->buffers_in_transit, 1);
|
|
- vlc_cond_signal(&sys->buffer_cond);
|
|
- vlc_mutex_unlock(&sys->buffer_mutex);
|
|
+ /* Apply the required phase_offset to the picture, so that vd_display()
|
|
+ * will be called at the corrected time from the core */
|
|
+ picture->date += sys->phase_offset;
|
|
+#endif
|
|
}
|
|
|
|
-static int query_resolution(vout_display_t *vd, unsigned *width, unsigned *height)
|
|
+
|
|
+static void vd_control_port_cb(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buffer)
|
|
{
|
|
- TV_DISPLAY_STATE_T display_state;
|
|
- int ret = 0;
|
|
+ vout_display_t *vd = (vout_display_t *)port->userdata;
|
|
+ MMAL_STATUS_T status;
|
|
|
|
- if (vc_tv_get_display_state(&display_state) == 0) {
|
|
- if (display_state.state & 0xFF) {
|
|
- *width = display_state.display.hdmi.width;
|
|
- *height = display_state.display.hdmi.height;
|
|
- } else if (display_state.state & 0xFF00) {
|
|
- *width = display_state.display.sdtv.width;
|
|
- *height = display_state.display.sdtv.height;
|
|
- } else {
|
|
- msg_Warn(vd, "Invalid display state %"PRIx32, display_state.state);
|
|
- ret = -1;
|
|
- }
|
|
- } else {
|
|
- msg_Warn(vd, "Failed to query display resolution");
|
|
- ret = -1;
|
|
+ if (buffer->cmd == MMAL_EVENT_ERROR) {
|
|
+ status = *(uint32_t *)buffer->data;
|
|
+ msg_Err(vd, "MMAL error %"PRIx32" \"%s\"", status, mmal_status_to_string(status));
|
|
}
|
|
|
|
- return ret;
|
|
+ mmal_buffer_header_release(buffer);
|
|
}
|
|
|
|
static void tvservice_cb(void *callback_data, uint32_t reason, uint32_t param1, uint32_t param2)
|
|
@@ -828,148 +933,12 @@
|
|
}
|
|
}
|
|
|
|
-static void display_subpicture(vout_display_t *vd, subpicture_t *subpicture)
|
|
-{
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
- struct dmx_region_t **dmx_region = &sys->dmx_region;
|
|
- struct dmx_region_t *unused_dmx_region;
|
|
- DISPMANX_UPDATE_HANDLE_T update = 0;
|
|
- picture_t *picture;
|
|
- video_format_t *fmt;
|
|
- struct dmx_region_t *dmx_region_next;
|
|
-
|
|
- if(subpicture) {
|
|
- subpicture_region_t *region = subpicture->p_region;
|
|
- while(region) {
|
|
- picture = region->p_picture;
|
|
- fmt = ®ion->fmt;
|
|
-
|
|
- if(!*dmx_region) {
|
|
- if(!update)
|
|
- update = vc_dispmanx_update_start(10);
|
|
- *dmx_region = dmx_region_new(vd, update, region);
|
|
- } else if(((*dmx_region)->bmp_rect.width != (int32_t)fmt->i_visible_width) ||
|
|
- ((*dmx_region)->bmp_rect.height != (int32_t)fmt->i_visible_height) ||
|
|
- ((*dmx_region)->pos_x != region->i_x) ||
|
|
- ((*dmx_region)->pos_y != region->i_y) ||
|
|
- ((*dmx_region)->alpha.opacity != (uint32_t)region->i_alpha)) {
|
|
- dmx_region_next = (*dmx_region)->next;
|
|
- if(!update)
|
|
- update = vc_dispmanx_update_start(10);
|
|
- dmx_region_delete(*dmx_region, update);
|
|
- *dmx_region = dmx_region_new(vd, update, region);
|
|
- (*dmx_region)->next = dmx_region_next;
|
|
- } else if((*dmx_region)->picture != picture) {
|
|
- if(!update)
|
|
- update = vc_dispmanx_update_start(10);
|
|
- dmx_region_update(*dmx_region, update, picture);
|
|
- }
|
|
-
|
|
- dmx_region = &(*dmx_region)->next;
|
|
- region = region->p_next;
|
|
- }
|
|
- }
|
|
-
|
|
- /* Remove remaining regions */
|
|
- unused_dmx_region = *dmx_region;
|
|
- while(unused_dmx_region) {
|
|
- dmx_region_next = unused_dmx_region->next;
|
|
- if(!update)
|
|
- update = vc_dispmanx_update_start(10);
|
|
- dmx_region_delete(unused_dmx_region, update);
|
|
- unused_dmx_region = dmx_region_next;
|
|
- }
|
|
- *dmx_region = NULL;
|
|
-
|
|
- if(update)
|
|
- vc_dispmanx_update_submit_sync(update);
|
|
-}
|
|
-
|
|
-static void close_dmx(vout_display_t *vd)
|
|
-{
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
- DISPMANX_UPDATE_HANDLE_T update = vc_dispmanx_update_start(10);
|
|
- struct dmx_region_t *dmx_region = sys->dmx_region;
|
|
- struct dmx_region_t *dmx_region_next;
|
|
-
|
|
- while(dmx_region) {
|
|
- dmx_region_next = dmx_region->next;
|
|
- dmx_region_delete(dmx_region, update);
|
|
- dmx_region = dmx_region_next;
|
|
- }
|
|
-
|
|
- vc_dispmanx_update_submit_sync(update);
|
|
- sys->dmx_region = NULL;
|
|
-
|
|
- show_background(vd, false);
|
|
-
|
|
- vc_dispmanx_display_close(sys->dmx_handle);
|
|
- sys->dmx_handle = DISPMANX_NO_HANDLE;
|
|
-}
|
|
-
|
|
-static struct dmx_region_t *dmx_region_new(vout_display_t *vd,
|
|
- DISPMANX_UPDATE_HANDLE_T update, subpicture_region_t *region)
|
|
-{
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
- video_format_t *fmt = ®ion->fmt;
|
|
- struct dmx_region_t *dmx_region = malloc(sizeof(struct dmx_region_t));
|
|
- uint32_t image_handle;
|
|
-
|
|
- dmx_region->pos_x = region->i_x;
|
|
- dmx_region->pos_y = region->i_y;
|
|
-
|
|
- vc_dispmanx_rect_set(&dmx_region->bmp_rect, 0, 0, fmt->i_visible_width,
|
|
- fmt->i_visible_height);
|
|
- vc_dispmanx_rect_set(&dmx_region->src_rect, 0, 0, fmt->i_visible_width << 16,
|
|
- fmt->i_visible_height << 16);
|
|
- vc_dispmanx_rect_set(&dmx_region->dst_rect, region->i_x, region->i_y,
|
|
- fmt->i_visible_width, fmt->i_visible_height);
|
|
-
|
|
- dmx_region->resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32,
|
|
- dmx_region->bmp_rect.width | (region->p_picture->p[0].i_pitch << 16),
|
|
- dmx_region->bmp_rect.height | (dmx_region->bmp_rect.height << 16),
|
|
- &image_handle);
|
|
- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
|
|
- region->p_picture->p[0].i_pitch,
|
|
- region->p_picture->p[0].p_pixels, &dmx_region->bmp_rect);
|
|
-
|
|
- dmx_region->alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_MIX;
|
|
- dmx_region->alpha.opacity = region->i_alpha;
|
|
- dmx_region->alpha.mask = DISPMANX_NO_HANDLE;
|
|
- dmx_region->element = vc_dispmanx_element_add(update, sys->dmx_handle,
|
|
- sys->layer + 1, &dmx_region->dst_rect, dmx_region->resource,
|
|
- &dmx_region->src_rect, DISPMANX_PROTECTION_NONE,
|
|
- &dmx_region->alpha, NULL, VC_IMAGE_ROT0);
|
|
-
|
|
- dmx_region->next = NULL;
|
|
- dmx_region->picture = region->p_picture;
|
|
-
|
|
- return dmx_region;
|
|
-}
|
|
-
|
|
-static void dmx_region_update(struct dmx_region_t *dmx_region,
|
|
- DISPMANX_UPDATE_HANDLE_T update, picture_t *picture)
|
|
-{
|
|
- vc_dispmanx_resource_write_data(dmx_region->resource, VC_IMAGE_RGBA32,
|
|
- picture->p[0].i_pitch, picture->p[0].p_pixels, &dmx_region->bmp_rect);
|
|
- vc_dispmanx_element_change_source(update, dmx_region->element, dmx_region->resource);
|
|
- dmx_region->picture = picture;
|
|
-}
|
|
-
|
|
-static void dmx_region_delete(struct dmx_region_t *dmx_region,
|
|
- DISPMANX_UPDATE_HANDLE_T update)
|
|
-{
|
|
- vc_dispmanx_element_remove(update, dmx_region->element);
|
|
- vc_dispmanx_resource_delete(dmx_region->resource);
|
|
- free(dmx_region);
|
|
-}
|
|
-
|
|
static void maintain_phase_sync(vout_display_t *vd)
|
|
{
|
|
MMAL_PARAMETER_VIDEO_RENDER_STATS_T render_stats = {
|
|
.hdr = { MMAL_PARAMETER_VIDEO_RENDER_STATS, sizeof(render_stats) },
|
|
};
|
|
- int32_t frame_duration = 1000000 /
|
|
+ int32_t frame_duration = CLOCK_FREQ /
|
|
((double)vd->sys->i_frame_rate /
|
|
vd->sys->i_frame_rate_base);
|
|
vout_display_sys_t *sys = vd->sys;
|
|
@@ -1012,32 +981,260 @@
|
|
}
|
|
}
|
|
|
|
-static void show_background(vout_display_t *vd, bool enable)
|
|
+static void CloseMmalVout(vlc_object_t *object)
|
|
{
|
|
- vout_display_sys_t *sys = vd->sys;
|
|
- uint32_t image_ptr, color = 0xFF000000;
|
|
- VC_RECT_T dst_rect, src_rect;
|
|
- DISPMANX_UPDATE_HANDLE_T update;
|
|
-
|
|
- if (enable && !sys->bkg_element) {
|
|
- sys->bkg_resource = vc_dispmanx_resource_create(VC_IMAGE_RGBA32, 1, 1,
|
|
- &image_ptr);
|
|
- vc_dispmanx_rect_set(&dst_rect, 0, 0, 1, 1);
|
|
- vc_dispmanx_resource_write_data(sys->bkg_resource, VC_IMAGE_RGBA32,
|
|
- sizeof(color), &color, &dst_rect);
|
|
- vc_dispmanx_rect_set(&src_rect, 0, 0, 1 << 16, 1 << 16);
|
|
- vc_dispmanx_rect_set(&dst_rect, 0, 0, 0, 0);
|
|
- update = vc_dispmanx_update_start(0);
|
|
- sys->bkg_element = vc_dispmanx_element_add(update, sys->dmx_handle,
|
|
- sys->layer - 1, &dst_rect, sys->bkg_resource, &src_rect,
|
|
- DISPMANX_PROTECTION_NONE, NULL, NULL, VC_IMAGE_ROT0);
|
|
- vc_dispmanx_update_submit_sync(update);
|
|
- } else if (!enable && sys->bkg_element) {
|
|
- update = vc_dispmanx_update_start(0);
|
|
- vc_dispmanx_element_remove(update, sys->bkg_element);
|
|
- vc_dispmanx_resource_delete(sys->bkg_resource);
|
|
- vc_dispmanx_update_submit_sync(update);
|
|
- sys->bkg_element = DISPMANX_NO_HANDLE;
|
|
- sys->bkg_resource = DISPMANX_NO_HANDLE;
|
|
+ vout_display_t * const vd = (vout_display_t *)object;
|
|
+ vout_display_sys_t * const sys = vd->sys;
|
|
+ char response[20]; /* answer is hvs_update_fields=%1d */
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s", __func__);
|
|
+#endif
|
|
+
|
|
+ kill_pool(sys);
|
|
+
|
|
+ vc_tv_unregister_callback_full(tvservice_cb, vd);
|
|
+
|
|
+ if (sys->component && sys->component->control->is_enabled)
|
|
+ mmal_port_disable(sys->component->control);
|
|
+
|
|
+ {
|
|
+ unsigned int i;
|
|
+ for (i = 0; i != SUBS_MAX; ++i) {
|
|
+ vout_subpic_t * const sub = sys->subs + i;
|
|
+ if (sub->component != NULL) {
|
|
+ hw_mmal_subpic_close(VLC_OBJECT(vd), &sub->sub);
|
|
+ if (sub->component->control->is_enabled)
|
|
+ mmal_port_disable(sub->component->control);
|
|
+ if (sub->component->is_enabled)
|
|
+ mmal_component_disable(sub->component);
|
|
+ mmal_component_release(sub->component);
|
|
+ sub->component = NULL;
|
|
+ }
|
|
+ }
|
|
}
|
|
+
|
|
+ if (sys->input && sys->input->is_enabled)
|
|
+ mmal_port_disable(sys->input);
|
|
+
|
|
+ if (sys->component && sys->component->is_enabled)
|
|
+ mmal_component_disable(sys->component);
|
|
+
|
|
+ if (sys->pool)
|
|
+ mmal_pool_destroy(sys->pool);
|
|
+
|
|
+ if (sys->component)
|
|
+ mmal_component_release(sys->component);
|
|
+
|
|
+ isp_close(vd, sys);
|
|
+
|
|
+ vlc_mutex_destroy(&sys->manage_mutex);
|
|
+
|
|
+ if (sys->native_interlaced) {
|
|
+ if (vc_gencmd(response, sizeof(response), "hvs_update_fields 0") < 0 ||
|
|
+ response[18] != '0')
|
|
+ msg_Warn(vd, "Could not reset hvs field mode");
|
|
+ }
|
|
+
|
|
+ free(sys);
|
|
+
|
|
+ bcm_host_deinit();
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, ">>> %s", __func__);
|
|
+#endif
|
|
}
|
|
+
|
|
+static int OpenMmalVout(vlc_object_t *object)
|
|
+{
|
|
+ vout_display_t *vd = (vout_display_t *)object;
|
|
+ vout_display_sys_t *sys;
|
|
+ vout_display_place_t place;
|
|
+ MMAL_DISPLAYREGION_T display_region;
|
|
+ MMAL_STATUS_T status;
|
|
+ int ret = VLC_EGENERIC;
|
|
+ const MMAL_FOURCC_T enc_in = vout_vlc_to_mmal_pic_fourcc(vd->fmt.i_chroma);
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s", __func__);
|
|
+#endif
|
|
+ if (enc_in == 0)
|
|
+ {
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, ">>> %s: Format not MMAL", __func__);
|
|
+#endif
|
|
+ return VLC_EGENERIC;
|
|
+ }
|
|
+
|
|
+ sys = calloc(1, sizeof(struct vout_display_sys_t));
|
|
+ if (!sys)
|
|
+ return VLC_ENOMEM;
|
|
+ vd->sys = sys;
|
|
+
|
|
+ bcm_host_init();
|
|
+
|
|
+ sys->layer = var_InheritInteger(vd, MMAL_LAYER_NAME);
|
|
+
|
|
+ status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sys->component);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to create MMAL component %s (status=%"PRIx32" %s)",
|
|
+ MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ sys->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
|
|
+ status = mmal_port_enable(sys->component->control, vd_control_port_cb);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to enable control port %s (status=%"PRIx32" %s)",
|
|
+ sys->component->control->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ sys->input = sys->component->input[0];
|
|
+ sys->input->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
|
|
+
|
|
+ sys->input->format->encoding = enc_in;
|
|
+ sys->input->format->encoding_variant = 0;
|
|
+ sys->i_planes = 1;
|
|
+ sys->buffer_size = sys->input->buffer_size_recommended;
|
|
+
|
|
+ display_set_format(vd, sys->input->format, want_isp(vd));
|
|
+
|
|
+ status = port_parameter_set_bool(sys->input, MMAL_PARAMETER_ZERO_COPY, true);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to set zero copy on port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ status = mmal_port_format_commit(sys->input);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to commit format for input port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+ sys->input->buffer_size = sys->input->buffer_size_recommended;
|
|
+ sys->input->buffer_num = 30;
|
|
+
|
|
+ vout_display_PlacePicture(&place, &vd->source, vd->cfg, false);
|
|
+ display_region.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
|
|
+ display_region.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
|
|
+ display_region.fullscreen = MMAL_FALSE;
|
|
+ display_src_rect(vd, &display_region.src_rect);
|
|
+ display_region.dest_rect.x = place.x;
|
|
+ display_region.dest_rect.y = place.y;
|
|
+ display_region.dest_rect.width = place.width;
|
|
+ display_region.dest_rect.height = place.height;
|
|
+ display_region.layer = sys->layer;
|
|
+ display_region.set = MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_SRC_RECT |
|
|
+ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_LAYER;
|
|
+ status = mmal_port_parameter_set(sys->input, &display_region.hdr);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to set display region (status=%"PRIx32" %s)",
|
|
+ status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ status = mmal_port_enable(sys->input, vd_input_port_cb);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to enable input port %s (status=%"PRIx32" %s)",
|
|
+ sys->input->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ status = mmal_component_enable(sys->component);
|
|
+ if (status != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to enable component %s (status=%"PRIx32" %s)",
|
|
+ sys->component->name, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ if ((sys->pool = mmal_pool_create(sys->input->buffer_num, 0)) == NULL)
|
|
+ {
|
|
+ msg_Err(vd, "Failed to create input pool");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ {
|
|
+ unsigned int i;
|
|
+ for (i = 0; i != SUBS_MAX; ++i) {
|
|
+ vout_subpic_t * const sub = sys->subs + i;
|
|
+ if ((status = mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &sub->component)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Dbg(vd, "Failed to create subpic component %d", i);
|
|
+ goto fail;
|
|
+ }
|
|
+ sub->component->control->userdata = (struct MMAL_PORT_USERDATA_T *)vd;
|
|
+ if ((status = mmal_port_enable(sub->component->control, vd_control_port_cb)) != MMAL_SUCCESS) {
|
|
+ msg_Err(vd, "Failed to enable control port %s on sub %d (status=%"PRIx32" %s)",
|
|
+ sys->component->control->name, i, status, mmal_status_to_string(status));
|
|
+ goto fail;
|
|
+ }
|
|
+ if ((status = hw_mmal_subpic_open(VLC_OBJECT(vd), &sub->sub, sub->component->input[0], sys->layer + i + 1)) != MMAL_SUCCESS) {
|
|
+ msg_Dbg(vd, "Failed to open subpic %d", i);
|
|
+ goto fail;
|
|
+ }
|
|
+ if ((status = mmal_component_enable(sub->component)) != MMAL_SUCCESS)
|
|
+ {
|
|
+ msg_Dbg(vd, "Failed to enable subpic component %d", i);
|
|
+ goto fail;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+
|
|
+ vlc_mutex_init(&sys->manage_mutex);
|
|
+
|
|
+ vd->info = (vout_display_info_t){
|
|
+ .is_slow = false,
|
|
+ .has_double_click = false,
|
|
+ .needs_hide_mouse = false,
|
|
+ .has_pictures_invalid = true,
|
|
+ .subpicture_chromas = NULL
|
|
+ };
|
|
+
|
|
+ vd->pool = vd_pool;
|
|
+ vd->prepare = vd_prepare;
|
|
+ vd->display = vd_display;
|
|
+ vd->control = vd_control;
|
|
+
|
|
+ vc_tv_register_callback(tvservice_cb, vd);
|
|
+
|
|
+ if (query_resolution(vd, &sys->display_width, &sys->display_height) < 0)
|
|
+ {
|
|
+ sys->display_width = vd->cfg->display.width;
|
|
+ sys->display_height = vd->cfg->display.height;
|
|
+ }
|
|
+
|
|
+ msg_Dbg(vd, ">>> %s: ok", __func__);
|
|
+ return VLC_SUCCESS;
|
|
+
|
|
+fail:
|
|
+ CloseMmalVout(object);
|
|
+
|
|
+ msg_Dbg(vd, ">>> %s: rv=%d", __func__, ret);
|
|
+
|
|
+ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
|
|
+}
|
|
+
|
|
+vlc_module_begin()
|
|
+
|
|
+ add_submodule()
|
|
+
|
|
+ set_shortname(N_("MMAL vout"))
|
|
+ set_description(N_("MMAL-based vout plugin for Raspberry Pi"))
|
|
+ set_capability("vout display", 0)
|
|
+ add_shortcut("mmal_vout")
|
|
+ set_category( CAT_VIDEO )
|
|
+ set_subcategory( SUBCAT_VIDEO_VOUT )
|
|
+
|
|
+ add_integer(MMAL_LAYER_NAME, 1, MMAL_LAYER_TEXT, MMAL_LAYER_LONGTEXT, false)
|
|
+ add_bool(MMAL_ADJUST_REFRESHRATE_NAME, false, MMAL_ADJUST_REFRESHRATE_TEXT,
|
|
+ MMAL_ADJUST_REFRESHRATE_LONGTEXT, false)
|
|
+ add_bool(MMAL_NATIVE_INTERLACED, false, MMAL_NATIVE_INTERLACE_TEXT,
|
|
+ MMAL_NATIVE_INTERLACE_LONGTEXT, false)
|
|
+ set_callbacks(OpenMmalVout, CloseMmalVout)
|
|
+
|
|
+vlc_module_end()
|
|
+
|
|
+
|
|
--- /dev/null
|
|
+++ b/modules/hw/mmal/xsplitter.c
|
|
@@ -0,0 +1,403 @@
|
|
+#ifdef HAVE_CONFIG_H
|
|
+#include "config.h"
|
|
+#endif
|
|
+
|
|
+#include <stdatomic.h>
|
|
+
|
|
+#include <vlc_common.h>
|
|
+#include <vlc_plugin.h>
|
|
+#include <vlc_threads.h>
|
|
+#include <vlc_vout_display.h>
|
|
+#include <vlc_modules.h>
|
|
+
|
|
+#include <bcm_host.h>
|
|
+#include <interface/mmal/mmal.h>
|
|
+#include <interface/mmal/util/mmal_util.h>
|
|
+#include <interface/mmal/util/mmal_default_components.h>
|
|
+
|
|
+#include "mmal_picture.h"
|
|
+
|
|
+#define TRACE_ALL 0
|
|
+
|
|
+typedef struct mmal_x11_sys_s
|
|
+{
|
|
+ bool use_mmal;
|
|
+ vout_display_t * cur_vout;
|
|
+ vout_display_t * mmal_vout;
|
|
+ vout_display_t * x_vout;
|
|
+ uint32_t changed;
|
|
+} mmal_x11_sys_t;
|
|
+
|
|
+static const char * str_fourcc(char * buf, unsigned int fcc)
|
|
+{
|
|
+ if (fcc == 0)
|
|
+ return "----";
|
|
+ buf[0] = (fcc >> 0) & 0xff;
|
|
+ buf[1] = (fcc >> 8) & 0xff;
|
|
+ buf[2] = (fcc >> 16) & 0xff;
|
|
+ buf[3] = (fcc >> 24) & 0xff;
|
|
+ buf[4] = 0;
|
|
+ return buf;
|
|
+}
|
|
+
|
|
+
|
|
+static void unload_display_module(vout_display_t * const x_vout)
|
|
+{
|
|
+ if (x_vout != NULL) {
|
|
+ if (x_vout->module != NULL) {
|
|
+ module_unneed(x_vout, x_vout->module);
|
|
+ }
|
|
+ vlc_object_release(x_vout);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void CloseMmalX11(vlc_object_t *object)
|
|
+{
|
|
+ vout_display_t * const vd = (vout_display_t *)object;
|
|
+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
|
|
+
|
|
+ msg_Dbg(vd, "<<< %s", __func__);
|
|
+
|
|
+ if (sys == NULL)
|
|
+ return;
|
|
+
|
|
+ unload_display_module(sys->x_vout);
|
|
+
|
|
+ unload_display_module(sys->mmal_vout);
|
|
+
|
|
+ free(sys);
|
|
+
|
|
+ msg_Dbg(vd, ">>> %s", __func__);
|
|
+}
|
|
+
|
|
+static void mmal_x11_event(vout_display_t * x_vd, int cmd, va_list args)
|
|
+{
|
|
+ vout_display_t * const vd = x_vd->owner.sys;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s (cmd=%d)", __func__, cmd);
|
|
+#endif
|
|
+ vd->owner.event(vd, cmd, args);
|
|
+}
|
|
+
|
|
+static vout_window_t * mmal_x11_window_new(vout_display_t * x_vd, unsigned type)
|
|
+{
|
|
+ vout_display_t * const vd = x_vd->owner.sys;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s (type=%d)", __func__, type);
|
|
+#endif
|
|
+ return vd->owner.window_new(vd, type);
|
|
+}
|
|
+
|
|
+static void mmal_x11_window_del(vout_display_t * x_vd, vout_window_t * win)
|
|
+{
|
|
+ vout_display_t * const vd = x_vd->owner.sys;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s", __func__);
|
|
+#endif
|
|
+ vd->owner.window_del(vd, win);
|
|
+}
|
|
+
|
|
+
|
|
+static vout_display_t * load_display_module(vout_display_t * const vd,
|
|
+ const char * const cap, const char * const module_name)
|
|
+{
|
|
+ vout_display_t * const x_vout = vlc_object_create(vd, sizeof(*x_vout));
|
|
+
|
|
+ if (!x_vout)
|
|
+ return NULL;
|
|
+
|
|
+ x_vout->owner.sys = vd;
|
|
+ x_vout->owner.event = mmal_x11_event;
|
|
+ x_vout->owner.window_new = mmal_x11_window_new;
|
|
+ x_vout->owner.window_del = mmal_x11_window_del;
|
|
+
|
|
+ x_vout->cfg = vd->cfg;
|
|
+ x_vout->source = vd->source;
|
|
+ x_vout->info = vd->info;
|
|
+
|
|
+ x_vout->fmt = vd->fmt;
|
|
+
|
|
+ if ((x_vout->module = module_need(x_vout, cap, module_name, true)) == NULL)
|
|
+ {
|
|
+ msg_Err(vd, "Failed to open Xsplitter:%s module", module_name);
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ msg_Dbg(vd, "R/G/B: %08x/%08x/%08x", x_vout->fmt.i_rmask, x_vout->fmt.i_gmask, x_vout->fmt.i_bmask);
|
|
+
|
|
+ return x_vout;
|
|
+
|
|
+fail:
|
|
+ vlc_object_release(x_vout);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+
|
|
+/* Return a pointer over the current picture_pool_t* (mandatory).
|
|
+ *
|
|
+ * For performance reasons, it is best to provide at least count
|
|
+ * pictures but it is not mandatory.
|
|
+ * You can return NULL when you cannot/do not want to allocate
|
|
+ * pictures.
|
|
+ * The vout display module keeps the ownership of the pool and can
|
|
+ * destroy it only when closing or on invalid pictures control.
|
|
+ */
|
|
+static picture_pool_t * mmal_x11_pool(vout_display_t * vd, unsigned count)
|
|
+{
|
|
+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
|
|
+ vout_display_t * const x_vd = sys->cur_vout;
|
|
+#if TRACE_ALL
|
|
+ char buf0[5];
|
|
+ msg_Dbg(vd, "<<< %s (count=%d) %s:%dx%d->%s:%dx%d", __func__, count,
|
|
+ str_fourcc(buf0, vd->fmt.i_chroma),
|
|
+ vd->fmt.i_width, vd->fmt.i_height,
|
|
+ str_fourcc(buf0, x_vd->fmt.i_chroma),
|
|
+ x_vd->fmt.i_width, x_vd->fmt.i_height);
|
|
+#endif
|
|
+ picture_pool_t * pool = x_vd->pool(x_vd, count);
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, ">>> %s: %p", __func__, pool);
|
|
+#endif
|
|
+ return pool;
|
|
+}
|
|
+
|
|
+/* Prepare a picture and an optional subpicture for display (optional).
|
|
+ *
|
|
+ * It is called before the next pf_display call to provide as much
|
|
+ * time as possible to prepare the given picture and the subpicture
|
|
+ * for display.
|
|
+ * You are guaranted that pf_display will always be called and using
|
|
+ * the exact same picture_t and subpicture_t.
|
|
+ * You cannot change the pixel content of the picture_t or of the
|
|
+ * subpicture_t.
|
|
+ */
|
|
+static void mmal_x11_prepare(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
|
|
+{
|
|
+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
|
|
+ vout_display_t * const x_vd = sys->cur_vout;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s", __func__);
|
|
+#endif
|
|
+ if (x_vd->prepare)
|
|
+ x_vd->prepare(x_vd, pic, sub);
|
|
+}
|
|
+
|
|
+/* Display a picture and an optional subpicture (mandatory).
|
|
+ *
|
|
+ * The picture and the optional subpicture must be displayed as soon as
|
|
+ * possible.
|
|
+ * You cannot change the pixel content of the picture_t or of the
|
|
+ * subpicture_t.
|
|
+ *
|
|
+ * This function gives away the ownership of the picture and of the
|
|
+ * subpicture, so you must release them as soon as possible.
|
|
+ */
|
|
+static void mmal_x11_display(vout_display_t * vd, picture_t * pic, subpicture_t * sub)
|
|
+{
|
|
+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
|
|
+ vout_display_t * const x_vd = sys->cur_vout;
|
|
+ const bool is_mmal_pic = hw_mmal_pic_is_mmal(pic);
|
|
+
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s: fmt: %dx%d/%dx%d, pic:%dx%d, pts=%lld, mmal=%d/%d", __func__, vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height, pic->format.i_width, pic->format.i_height, (long long)pic->date,
|
|
+ is_mmal_pic, sys->use_mmal);
|
|
+#endif
|
|
+
|
|
+ if (sys->use_mmal != is_mmal_pic) {
|
|
+ msg_Dbg(vd, "%s: Picture dropped", __func__);
|
|
+ picture_Release(pic);
|
|
+ if (sub != NULL)
|
|
+ subpicture_Delete(sub);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ x_vd->display(x_vd, pic, sub);
|
|
+}
|
|
+
|
|
+
|
|
+static int vout_display_Control(vout_display_t *vd, int query, ...)
|
|
+{
|
|
+ va_list args;
|
|
+ int result;
|
|
+
|
|
+ va_start(args, query);
|
|
+ result = vd->control(vd, query, args);
|
|
+ va_end(args);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+static bool want_mmal_vout(vout_display_t * vd, const mmal_x11_sys_t * const sys)
|
|
+{
|
|
+ return sys->mmal_vout != NULL && var_InheritBool(vd, "fullscreen");
|
|
+}
|
|
+
|
|
+/* Control on the module (mandatory) */
|
|
+static int mmal_x11_control(vout_display_t * vd, int ctl, va_list va)
|
|
+{
|
|
+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
|
|
+ vout_display_t *x_vd = sys->cur_vout;
|
|
+ int rv;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s[%d] (ctl=%d)", __func__, sys->use_mmal, ctl);
|
|
+#endif
|
|
+ // Remember what we've told this vd - unwanted ctls ignored on replay
|
|
+ if (ctl >= 0 && ctl <= 31)
|
|
+ sys->changed |= (1 << ctl);
|
|
+
|
|
+ switch (ctl) {
|
|
+ case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
|
|
+ {
|
|
+ const vout_display_cfg_t * cfg = va_arg(va, const vout_display_cfg_t *);
|
|
+ const bool want_mmal = want_mmal_vout(vd, sys);
|
|
+ vout_display_t *new_vd = want_mmal ? sys->mmal_vout : sys->x_vout;
|
|
+
|
|
+ msg_Dbg(vd, "Change size: %d, %d: mmal_vout=%p, want_mmal=%d, fs=%d",
|
|
+ cfg->display.width, cfg->display.height, sys->mmal_vout, want_mmal,
|
|
+ var_InheritBool(vd, "fullscreen"));
|
|
+
|
|
+ if (sys->use_mmal != want_mmal) {
|
|
+ if (sys->use_mmal) {
|
|
+ vout_display_Control(x_vd, VOUT_DISPLAY_CHANGE_MMAL_HIDE);
|
|
+ }
|
|
+ vout_display_SendEventPicturesInvalid(x_vd);
|
|
+ }
|
|
+
|
|
+ rv = vout_display_Control(new_vd, ctl, cfg);
|
|
+ if (rv == VLC_SUCCESS) {
|
|
+ vd->fmt = new_vd->fmt;
|
|
+ sys->cur_vout = new_vd;
|
|
+ sys->use_mmal = want_mmal;
|
|
+ }
|
|
+
|
|
+ // Repeat any control calls that we sent to the previous vd
|
|
+ if (sys->changed != 0) {
|
|
+ const uint32_t changed = sys->changed;
|
|
+ sys->changed = 0;
|
|
+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_DISPLAY_FILLED)) != 0)
|
|
+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_DISPLAY_FILLED, vd->cfg);
|
|
+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_ZOOM)) != 0)
|
|
+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg);
|
|
+ if ((changed & ((1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP) | (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT))) != 0)
|
|
+ new_vd->source = vd->source;
|
|
+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_ASPECT)) != 0)
|
|
+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_SOURCE_ASPECT);
|
|
+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_SOURCE_CROP)) != 0)
|
|
+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_SOURCE_CROP);
|
|
+ if ((changed & (1 << VOUT_DISPLAY_CHANGE_VIEWPOINT)) != 0)
|
|
+ vout_display_Control(new_vd, VOUT_DISPLAY_CHANGE_ZOOM, vd->cfg);
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ case VOUT_DISPLAY_RESET_PICTURES:
|
|
+ msg_Dbg(vd, "Reset pictures");
|
|
+// rv = x_vd->control(x_vd, ctl, va);
|
|
+ rv = sys->x_vout->control(sys->x_vout, ctl, va);
|
|
+ if (sys->mmal_vout)
|
|
+ rv = sys->mmal_vout->control(sys->mmal_vout, ctl, va);
|
|
+ msg_Dbg(vd, "<<< %s: Pic reset: fmt: %dx%d<-%dx%d, source: %dx%d/%dx%d", __func__,
|
|
+ vd->fmt.i_width, vd->fmt.i_height, x_vd->fmt.i_width, x_vd->fmt.i_height,
|
|
+ vd->source.i_width, vd->source.i_height, x_vd->source.i_width, x_vd->source.i_height);
|
|
+ vd->fmt = x_vd->fmt;
|
|
+ break;
|
|
+
|
|
+ case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
|
|
+ case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
|
|
+ x_vd->source = vd->source;
|
|
+ default:
|
|
+ rv = x_vd->control(x_vd, ctl, va);
|
|
+// vd->fmt = x_vd->fmt;
|
|
+ break;
|
|
+ }
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, ">>> %s (rv=%d)", __func__, rv);
|
|
+#endif
|
|
+ return rv;
|
|
+}
|
|
+
|
|
+#define DO_MANAGE 0
|
|
+
|
|
+#if DO_MANAGE
|
|
+/* Manage pending event (optional) */
|
|
+static void mmal_x11_manage(vout_display_t * vd)
|
|
+{
|
|
+ mmal_x11_sys_t * const sys = (mmal_x11_sys_t *)vd->sys;
|
|
+ vout_display_t * const x_vd = sys->cur_vout;
|
|
+#if TRACE_ALL
|
|
+ msg_Dbg(vd, "<<< %s", __func__);
|
|
+#endif
|
|
+ x_vd->manage(x_vd);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int OpenMmalX11(vlc_object_t *object)
|
|
+{
|
|
+ vout_display_t * const vd = (vout_display_t *)object;
|
|
+ mmal_x11_sys_t * const sys = calloc(1, sizeof(*sys));
|
|
+ int ret = VLC_SUCCESS;
|
|
+
|
|
+ if (sys == NULL) {
|
|
+ return VLC_EGENERIC;
|
|
+ }
|
|
+ vd->sys = (vout_display_sys_t *)sys;
|
|
+
|
|
+ vd->info = (vout_display_info_t){
|
|
+ .is_slow = false,
|
|
+ .has_double_click = false,
|
|
+ .needs_hide_mouse = false,
|
|
+ .has_pictures_invalid = true,
|
|
+ .subpicture_chromas = NULL
|
|
+ };
|
|
+
|
|
+ if ((sys->x_vout = load_display_module(vd, "vout display", "xcb_x11")) == NULL)
|
|
+ goto fail;
|
|
+
|
|
+ if ((sys->mmal_vout = load_display_module(vd, "vout display", "mmal_vout")) == NULL)
|
|
+ {
|
|
+ // Winge but do no more than that - route everything to X
|
|
+ char dbuf0[5], dbuf1[5];
|
|
+ msg_Info(vd, "Not a valid format for mmal vout (%s/%s)", str_fourcc(dbuf0, vd->fmt.i_chroma), str_fourcc(dbuf1, vd->source.i_chroma));
|
|
+ }
|
|
+
|
|
+ vd->pool = mmal_x11_pool;
|
|
+ vd->prepare = mmal_x11_prepare;
|
|
+ vd->display = mmal_x11_display;
|
|
+ vd->control = mmal_x11_control;
|
|
+#if DO_MANAGE
|
|
+ vd->manage = mmal_x11_manage;
|
|
+#endif
|
|
+
|
|
+ if (want_mmal_vout(vd, sys)) {
|
|
+ sys->cur_vout = sys->mmal_vout;
|
|
+ sys->use_mmal = true;
|
|
+ }
|
|
+ else {
|
|
+ sys->cur_vout = sys->x_vout;
|
|
+ sys->use_mmal = false;
|
|
+ }
|
|
+
|
|
+ vd->info = sys->cur_vout->info;
|
|
+ vd->fmt = sys->cur_vout->fmt;
|
|
+
|
|
+ return VLC_SUCCESS;
|
|
+
|
|
+fail:
|
|
+ CloseMmalX11(VLC_OBJECT(vd));
|
|
+ return ret == VLC_SUCCESS ? VLC_EGENERIC : ret;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+vlc_module_begin()
|
|
+ set_shortname(N_("MMAL x11 splitter"))
|
|
+ set_description(N_("MMAL x11 splitter for Raspberry Pi"))
|
|
+ set_capability("vout display", 900)
|
|
+ add_shortcut("mmal_x11")
|
|
+ set_category( CAT_VIDEO )
|
|
+ set_subcategory( SUBCAT_VIDEO_VOUT )
|
|
+ set_callbacks(OpenMmalX11, CloseMmalX11)
|
|
+vlc_module_end()
|
|
+
|
|
--- a/src/misc/fourcc.c
|
|
+++ b/src/misc/fourcc.c
|
|
@@ -756,6 +756,8 @@
|
|
VLC_CODEC_VDPAU_VIDEO_444, VLC_CODEC_VDPAU_OUTPUT },
|
|
FAKE_FMT() },
|
|
{ { VLC_CODEC_ANDROID_OPAQUE, VLC_CODEC_MMAL_OPAQUE,
|
|
+ VLC_CODEC_MMAL_ZC_SAND8, VLC_CODEC_MMAL_ZC_SAND10,
|
|
+ VLC_CODEC_MMAL_ZC_I420,
|
|
VLC_CODEC_D3D9_OPAQUE, VLC_CODEC_D3D11_OPAQUE },
|
|
FAKE_FMT() },
|
|
{ { VLC_CODEC_D3D11_OPAQUE_10B, VLC_CODEC_D3D9_OPAQUE_10B },
|