From ef18fc5ed586044543ff25ca64a5a37e039fbd06 Mon Sep 17 00:00:00 2001 From: graysky Date: Thu, 2 Mar 2023 04:20:34 -0500 Subject: [PATCH] alarm/ffmpeg-rpi to 4.4.3-1 --- alarm/ffmpeg-rpi/0001-vmaf-model-path.patch | 15 +- ...nc-avoid-callee-preserved-vfp-regist.patch | 287 -- ...n-rpi.patch => 0002-ffmpeg-4.4n-rpi.patch} | 3823 ++++++++++++----- ...4-fix_flags.patch => 0003-fix_flags.patch} | 15 +- alarm/ffmpeg-rpi/PKGBUILD | 19 +- 5 files changed, 2778 insertions(+), 1381 deletions(-) delete mode 100644 alarm/ffmpeg-rpi/0002-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch rename alarm/ffmpeg-rpi/{0003-ffmpeg-4.4n-rpi.patch => 0002-ffmpeg-4.4n-rpi.patch} (96%) rename alarm/ffmpeg-rpi/{0004-fix_flags.patch => 0003-fix_flags.patch} (79%) diff --git a/alarm/ffmpeg-rpi/0001-vmaf-model-path.patch b/alarm/ffmpeg-rpi/0001-vmaf-model-path.patch index abd65cdf7..ad4e45b8a 100644 --- a/alarm/ffmpeg-rpi/0001-vmaf-model-path.patch +++ b/alarm/ffmpeg-rpi/0001-vmaf-model-path.patch @@ -1,5 +1,13 @@ +From ffda9079338e1d2ef4b4c3296a2228fc5216c4f7 Mon Sep 17 00:00:00 2001 +Date: Wed, 1 Mar 2023 17:08:44 -0500 +Subject: [PATCH 1/3] vmaf-model-path + +--- + libavfilter/vf_libvmaf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + diff --git a/libavfilter/vf_libvmaf.c b/libavfilter/vf_libvmaf.c -index 249e50c720..9b791e19b1 100644 +index 4d49127..bd6f451 100644 --- a/libavfilter/vf_libvmaf.c +++ b/libavfilter/vf_libvmaf.c @@ -72,7 +72,7 @@ typedef struct LIBVMAFContext { @@ -9,5 +17,8 @@ index 249e50c720..9b791e19b1 100644 - {"model_path", "Set the model to be used for computing vmaf.", OFFSET(model_path), AV_OPT_TYPE_STRING, {.str="/usr/local/share/model/vmaf_v0.6.1.pkl"}, 0, 1, FLAGS}, + {"model_path", "Set the model to be used for computing vmaf.", OFFSET(model_path), AV_OPT_TYPE_STRING, {.str="/usr/share/model/vmaf_v0.6.1.pkl"}, 0, 1, FLAGS}, {"log_path", "Set the file path to be used to store logs.", OFFSET(log_path), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS}, - {"log_fmt", "Set the format of the log (xml or json).", OFFSET(log_fmt), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS}, + {"log_fmt", "Set the format of the log (csv, json or xml).", OFFSET(log_fmt), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 1, FLAGS}, {"enable_transform", "Enables transform for computing vmaf.", OFFSET(enable_transform), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, FLAGS}, +-- +2.39.2 + diff --git a/alarm/ffmpeg-rpi/0002-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch b/alarm/ffmpeg-rpi/0002-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch deleted file mode 100644 index 6a91596ad..000000000 --- a/alarm/ffmpeg-rpi/0002-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch +++ /dev/null @@ -1,287 +0,0 @@ -From: James Cowgill -Date: Sun, 11 Aug 2019 16:50:56 +0100 -Subject: avcodec/arm/sbcenc: avoid callee preserved vfp registers - -When compiling FFmpeg with GCC-9, some very random segfaults were -observed in code which had previously called down into the SBC encoder -NEON assembly routines. This was caused by these functions clobbering -some of the vfp callee saved registers (d8 - d15 aka q4 - q7). GCC was -using these registers to save local variables, but after these -functions returned, they would contain garbage. - -Fix by reallocating the registers in the two affected functions in -the following way: - ff_sbc_analyze_4_neon: q2-q5 => q8-q11, then q1-q4 => q8-q11 - ff_sbc_analyze_8_neon: q2-q9 => q8-q15 - -The reason for using these replacements is to keep closely related -sets of registers consecutively numbered which hopefully makes the -code more easy to follow. Since this commit only reallocates -registers, it should have no performance impact. - -Signed-off-by: James Cowgill ---- - libavcodec/arm/sbcdsp_neon.S | 220 +++++++++++++++++++++---------------------- - 1 file changed, 110 insertions(+), 110 deletions(-) - -diff --git a/libavcodec/arm/sbcdsp_neon.S b/libavcodec/arm/sbcdsp_neon.S -index d83d21d..914abfb 100644 ---- a/libavcodec/arm/sbcdsp_neon.S -+++ b/libavcodec/arm/sbcdsp_neon.S -@@ -38,49 +38,49 @@ function ff_sbc_analyze_4_neon, export=1 - /* TODO: merge even and odd cases (or even merge all four calls to this - * function) in order to have only aligned reads from 'in' array - * and reduce number of load instructions */ -- vld1.16 {d4, d5}, [r0, :64]! -- vld1.16 {d8, d9}, [r2, :128]! -+ vld1.16 {d16, d17}, [r0, :64]! -+ vld1.16 {d20, d21}, [r2, :128]! - -- vmull.s16 q0, d4, d8 -- vld1.16 {d6, d7}, [r0, :64]! -- vmull.s16 q1, d5, d9 -- vld1.16 {d10, d11}, [r2, :128]! -+ vmull.s16 q0, d16, d20 -+ vld1.16 {d18, d19}, [r0, :64]! -+ vmull.s16 q1, d17, d21 -+ vld1.16 {d22, d23}, [r2, :128]! - -- vmlal.s16 q0, d6, d10 -- vld1.16 {d4, d5}, [r0, :64]! -- vmlal.s16 q1, d7, d11 -- vld1.16 {d8, d9}, [r2, :128]! -+ vmlal.s16 q0, d18, d22 -+ vld1.16 {d16, d17}, [r0, :64]! -+ vmlal.s16 q1, d19, d23 -+ vld1.16 {d20, d21}, [r2, :128]! - -- vmlal.s16 q0, d4, d8 -- vld1.16 {d6, d7}, [r0, :64]! -- vmlal.s16 q1, d5, d9 -- vld1.16 {d10, d11}, [r2, :128]! -+ vmlal.s16 q0, d16, d20 -+ vld1.16 {d18, d19}, [r0, :64]! -+ vmlal.s16 q1, d17, d21 -+ vld1.16 {d22, d23}, [r2, :128]! - -- vmlal.s16 q0, d6, d10 -- vld1.16 {d4, d5}, [r0, :64]! -- vmlal.s16 q1, d7, d11 -- vld1.16 {d8, d9}, [r2, :128]! -+ vmlal.s16 q0, d18, d22 -+ vld1.16 {d16, d17}, [r0, :64]! -+ vmlal.s16 q1, d19, d23 -+ vld1.16 {d20, d21}, [r2, :128]! - -- vmlal.s16 q0, d4, d8 -- vmlal.s16 q1, d5, d9 -+ vmlal.s16 q0, d16, d20 -+ vmlal.s16 q1, d17, d21 - - vpadd.s32 d0, d0, d1 - vpadd.s32 d1, d2, d3 - - vrshrn.s32 d0, q0, SBC_PROTO_FIXED_SCALE - -- vld1.16 {d2, d3, d4, d5}, [r2, :128]! -+ vld1.16 {d16, d17, d18, d19}, [r2, :128]! - - vdup.i32 d1, d0[1] /* TODO: can be eliminated */ - vdup.i32 d0, d0[0] /* TODO: can be eliminated */ - -- vmull.s16 q3, d2, d0 -- vmull.s16 q4, d3, d0 -- vmlal.s16 q3, d4, d1 -- vmlal.s16 q4, d5, d1 -+ vmull.s16 q10, d16, d0 -+ vmull.s16 q11, d17, d0 -+ vmlal.s16 q10, d18, d1 -+ vmlal.s16 q11, d19, d1 - -- vpadd.s32 d0, d6, d7 /* TODO: can be eliminated */ -- vpadd.s32 d1, d8, d9 /* TODO: can be eliminated */ -+ vpadd.s32 d0, d20, d21 /* TODO: can be eliminated */ -+ vpadd.s32 d1, d22, d23 /* TODO: can be eliminated */ - - vst1.32 {d0, d1}, [r1, :128] - -@@ -91,57 +91,57 @@ function ff_sbc_analyze_8_neon, export=1 - /* TODO: merge even and odd cases (or even merge all four calls to this - * function) in order to have only aligned reads from 'in' array - * and reduce number of load instructions */ -- vld1.16 {d4, d5}, [r0, :64]! -- vld1.16 {d8, d9}, [r2, :128]! -- -- vmull.s16 q6, d4, d8 -- vld1.16 {d6, d7}, [r0, :64]! -- vmull.s16 q7, d5, d9 -- vld1.16 {d10, d11}, [r2, :128]! -- vmull.s16 q8, d6, d10 -- vld1.16 {d4, d5}, [r0, :64]! -- vmull.s16 q9, d7, d11 -- vld1.16 {d8, d9}, [r2, :128]! -- -- vmlal.s16 q6, d4, d8 -- vld1.16 {d6, d7}, [r0, :64]! -- vmlal.s16 q7, d5, d9 -- vld1.16 {d10, d11}, [r2, :128]! -- vmlal.s16 q8, d6, d10 -- vld1.16 {d4, d5}, [r0, :64]! -- vmlal.s16 q9, d7, d11 -- vld1.16 {d8, d9}, [r2, :128]! -- -- vmlal.s16 q6, d4, d8 -- vld1.16 {d6, d7}, [r0, :64]! -- vmlal.s16 q7, d5, d9 -- vld1.16 {d10, d11}, [r2, :128]! -- vmlal.s16 q8, d6, d10 -- vld1.16 {d4, d5}, [r0, :64]! -- vmlal.s16 q9, d7, d11 -- vld1.16 {d8, d9}, [r2, :128]! -- -- vmlal.s16 q6, d4, d8 -- vld1.16 {d6, d7}, [r0, :64]! -- vmlal.s16 q7, d5, d9 -- vld1.16 {d10, d11}, [r2, :128]! -- vmlal.s16 q8, d6, d10 -- vld1.16 {d4, d5}, [r0, :64]! -- vmlal.s16 q9, d7, d11 -- vld1.16 {d8, d9}, [r2, :128]! -- -- vmlal.s16 q6, d4, d8 -- vld1.16 {d6, d7}, [r0, :64]! -- vmlal.s16 q7, d5, d9 -- vld1.16 {d10, d11}, [r2, :128]! -- -- vmlal.s16 q8, d6, d10 -- vmlal.s16 q9, d7, d11 -- -- vpadd.s32 d0, d12, d13 -- vpadd.s32 d1, d14, d15 -- vpadd.s32 d2, d16, d17 -- vpadd.s32 d3, d18, d19 -+ vld1.16 {d16, d17}, [r0, :64]! -+ vld1.16 {d20, d21}, [r2, :128]! -+ -+ vmull.s16 q12, d16, d20 -+ vld1.16 {d18, d19}, [r0, :64]! -+ vmull.s16 q13, d17, d21 -+ vld1.16 {d22, d23}, [r2, :128]! -+ vmull.s16 q14, d18, d22 -+ vld1.16 {d16, d17}, [r0, :64]! -+ vmull.s16 q15, d19, d23 -+ vld1.16 {d20, d21}, [r2, :128]! -+ -+ vmlal.s16 q12, d16, d20 -+ vld1.16 {d18, d19}, [r0, :64]! -+ vmlal.s16 q13, d17, d21 -+ vld1.16 {d22, d23}, [r2, :128]! -+ vmlal.s16 q14, d18, d22 -+ vld1.16 {d16, d17}, [r0, :64]! -+ vmlal.s16 q15, d19, d23 -+ vld1.16 {d20, d21}, [r2, :128]! -+ -+ vmlal.s16 q12, d16, d20 -+ vld1.16 {d18, d19}, [r0, :64]! -+ vmlal.s16 q13, d17, d21 -+ vld1.16 {d22, d23}, [r2, :128]! -+ vmlal.s16 q14, d18, d22 -+ vld1.16 {d16, d17}, [r0, :64]! -+ vmlal.s16 q15, d19, d23 -+ vld1.16 {d20, d21}, [r2, :128]! -+ -+ vmlal.s16 q12, d16, d20 -+ vld1.16 {d18, d19}, [r0, :64]! -+ vmlal.s16 q13, d17, d21 -+ vld1.16 {d22, d23}, [r2, :128]! -+ vmlal.s16 q14, d18, d22 -+ vld1.16 {d16, d17}, [r0, :64]! -+ vmlal.s16 q15, d19, d23 -+ vld1.16 {d20, d21}, [r2, :128]! -+ -+ vmlal.s16 q12, d16, d20 -+ vld1.16 {d18, d19}, [r0, :64]! -+ vmlal.s16 q13, d17, d21 -+ vld1.16 {d22, d23}, [r2, :128]! -+ -+ vmlal.s16 q14, d18, d22 -+ vmlal.s16 q15, d19, d23 -+ -+ vpadd.s32 d0, d24, d25 -+ vpadd.s32 d1, d26, d27 -+ vpadd.s32 d2, d28, d29 -+ vpadd.s32 d3, d30, d31 - - vrshr.s32 q0, q0, SBC_PROTO_FIXED_SCALE - vrshr.s32 q1, q1, SBC_PROTO_FIXED_SCALE -@@ -153,38 +153,38 @@ function ff_sbc_analyze_8_neon, export=1 - vdup.i32 d1, d0[1] /* TODO: can be eliminated */ - vdup.i32 d0, d0[0] /* TODO: can be eliminated */ - -- vld1.16 {d4, d5}, [r2, :128]! -- vmull.s16 q6, d4, d0 -- vld1.16 {d6, d7}, [r2, :128]! -- vmull.s16 q7, d5, d0 -- vmull.s16 q8, d6, d0 -- vmull.s16 q9, d7, d0 -- -- vld1.16 {d4, d5}, [r2, :128]! -- vmlal.s16 q6, d4, d1 -- vld1.16 {d6, d7}, [r2, :128]! -- vmlal.s16 q7, d5, d1 -- vmlal.s16 q8, d6, d1 -- vmlal.s16 q9, d7, d1 -- -- vld1.16 {d4, d5}, [r2, :128]! -- vmlal.s16 q6, d4, d2 -- vld1.16 {d6, d7}, [r2, :128]! -- vmlal.s16 q7, d5, d2 -- vmlal.s16 q8, d6, d2 -- vmlal.s16 q9, d7, d2 -- -- vld1.16 {d4, d5}, [r2, :128]! -- vmlal.s16 q6, d4, d3 -- vld1.16 {d6, d7}, [r2, :128]! -- vmlal.s16 q7, d5, d3 -- vmlal.s16 q8, d6, d3 -- vmlal.s16 q9, d7, d3 -- -- vpadd.s32 d0, d12, d13 /* TODO: can be eliminated */ -- vpadd.s32 d1, d14, d15 /* TODO: can be eliminated */ -- vpadd.s32 d2, d16, d17 /* TODO: can be eliminated */ -- vpadd.s32 d3, d18, d19 /* TODO: can be eliminated */ -+ vld1.16 {d16, d17}, [r2, :128]! -+ vmull.s16 q12, d16, d0 -+ vld1.16 {d18, d19}, [r2, :128]! -+ vmull.s16 q13, d17, d0 -+ vmull.s16 q14, d18, d0 -+ vmull.s16 q15, d19, d0 -+ -+ vld1.16 {d16, d17}, [r2, :128]! -+ vmlal.s16 q12, d16, d1 -+ vld1.16 {d18, d19}, [r2, :128]! -+ vmlal.s16 q13, d17, d1 -+ vmlal.s16 q14, d18, d1 -+ vmlal.s16 q15, d19, d1 -+ -+ vld1.16 {d16, d17}, [r2, :128]! -+ vmlal.s16 q12, d16, d2 -+ vld1.16 {d18, d19}, [r2, :128]! -+ vmlal.s16 q13, d17, d2 -+ vmlal.s16 q14, d18, d2 -+ vmlal.s16 q15, d19, d2 -+ -+ vld1.16 {d16, d17}, [r2, :128]! -+ vmlal.s16 q12, d16, d3 -+ vld1.16 {d18, d19}, [r2, :128]! -+ vmlal.s16 q13, d17, d3 -+ vmlal.s16 q14, d18, d3 -+ vmlal.s16 q15, d19, d3 -+ -+ vpadd.s32 d0, d24, d25 /* TODO: can be eliminated */ -+ vpadd.s32 d1, d26, d27 /* TODO: can be eliminated */ -+ vpadd.s32 d2, d28, d29 /* TODO: can be eliminated */ -+ vpadd.s32 d3, d30, d31 /* TODO: can be eliminated */ - - vst1.32 {d0, d1, d2, d3}, [r1, :128] - diff --git a/alarm/ffmpeg-rpi/0003-ffmpeg-4.4n-rpi.patch b/alarm/ffmpeg-rpi/0002-ffmpeg-4.4n-rpi.patch similarity index 96% rename from alarm/ffmpeg-rpi/0003-ffmpeg-4.4n-rpi.patch rename to alarm/ffmpeg-rpi/0002-ffmpeg-4.4n-rpi.patch index 26005446b..13c6cfb50 100644 --- a/alarm/ffmpeg-rpi/0003-ffmpeg-4.4n-rpi.patch +++ b/alarm/ffmpeg-rpi/0002-ffmpeg-4.4n-rpi.patch @@ -1,12 +1,5 @@ -ffmpeg: jc-kynesim/dev/4.4/rpi_import_1 - -git diff n4.4 -jc-kynesim/rpi-ffmpeg/commit/009794de9bf21f801e9b5923d0bfcabed6648158 -23-Aug-2022 ---- - diff --git a/configure b/configure -index d7a3f507e8..7c2dd7161c 100755 +index 89af70d72f..c69ed44178 100755 --- a/configure +++ b/configure @@ -207,6 +207,7 @@ External library support: @@ -111,7 +104,7 @@ index d7a3f507e8..7c2dd7161c 100755 huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp" huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llvidencdsp" hymt_decoder_select="huffyuv_decoder" -@@ -2919,6 +2941,7 @@ d3d11va_deps="dxva_h ID3D11VideoDecoder ID3D11VideoContext" +@@ -2920,6 +2942,7 @@ d3d11va_deps="dxva_h ID3D11VideoDecoder ID3D11VideoContext" dxva2_deps="dxva2api_h DXVA2_ConfigPictureDecode ole32 user32" ffnvcodec_deps_any="libdl LoadLibrary" nvdec_deps="ffnvcodec" @@ -119,7 +112,7 @@ index d7a3f507e8..7c2dd7161c 100755 vaapi_x11_deps="xlib" videotoolbox_hwaccel_deps="videotoolbox pthreads" videotoolbox_hwaccel_extralibs="-framework QuartzCore" -@@ -2960,6 +2983,12 @@ hevc_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_HEVC" +@@ -2961,6 +2984,12 @@ hevc_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_HEVC" hevc_dxva2_hwaccel_select="hevc_decoder" hevc_nvdec_hwaccel_deps="nvdec" hevc_nvdec_hwaccel_select="hevc_decoder" @@ -132,7 +125,7 @@ index d7a3f507e8..7c2dd7161c 100755 hevc_vaapi_hwaccel_deps="vaapi VAPictureParameterBufferHEVC" hevc_vaapi_hwaccel_select="hevc_decoder" hevc_vdpau_hwaccel_deps="vdpau VdpPictureInfoHEVC" -@@ -3437,8 +3466,13 @@ sndio_indev_deps="sndio" +@@ -3438,8 +3467,13 @@ sndio_indev_deps="sndio" sndio_outdev_deps="sndio" v4l2_indev_deps_any="linux_videodev2_h sys_videoio_h" v4l2_indev_suggest="libv4l2" @@ -146,7 +139,7 @@ index d7a3f507e8..7c2dd7161c 100755 vfwcap_indev_deps="vfw32 vfwcap_defines" xcbgrab_indev_deps="libxcb" xcbgrab_indev_suggest="libxcb_shm libxcb_shape libxcb_xfixes" -@@ -3657,6 +3691,7 @@ tonemap_vaapi_filter_deps="vaapi VAProcFilterParameterBufferHDRToneMapping" +@@ -3658,6 +3692,7 @@ tonemap_vaapi_filter_deps="vaapi VAProcFilterParameterBufferHDRToneMapping" tonemap_opencl_filter_deps="opencl const_nan" transpose_opencl_filter_deps="opencl" transpose_vaapi_filter_deps="vaapi VAProcPipelineCaps_rotation_flags" @@ -154,7 +147,7 @@ index d7a3f507e8..7c2dd7161c 100755 unsharp_opencl_filter_deps="opencl" uspp_filter_deps="gpl avcodec" vaguedenoiser_filter_deps="gpl" -@@ -6154,6 +6189,12 @@ check_func_headers glob.h glob +@@ -6155,6 +6190,12 @@ check_func_headers glob.h glob enabled xlib && check_lib xlib "X11/Xlib.h X11/extensions/Xvlib.h" XvGetPortAttribute -lXv -lX11 -lXext @@ -167,7 +160,7 @@ index d7a3f507e8..7c2dd7161c 100755 check_headers direct.h check_headers dirent.h check_headers dxgidebug.h -@@ -6491,11 +6532,12 @@ enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt +@@ -6499,11 +6540,12 @@ enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt check_lib mbedtls mbedtls/ssl.h mbedtls_ssl_init -lmbedtls -lmbedx509 -lmbedcrypto || die "ERROR: mbedTLS not found"; } enabled mediacodec && { enabled jni || die "ERROR: mediacodec requires --enable-jni"; } @@ -182,7 +175,7 @@ index d7a3f507e8..7c2dd7161c 100755 die "ERROR: mmal not found" && check_func_headers interface/mmal/mmal.h "MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS"; } enabled openal && { { for al_extralibs in "${OPENAL_LIBS}" "-lopenal" "-lOpenAL32"; do -@@ -6536,8 +6578,16 @@ enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/r +@@ -6544,8 +6586,16 @@ enabled rkmpp && { require_pkg_config rkmpp rockchip_mpp rockchip/r { enabled libdrm || die "ERROR: rkmpp requires --enable-libdrm"; } } @@ -199,28 +192,32 @@ index d7a3f507e8..7c2dd7161c 100755 if enabled gcrypt; then GCRYPT_CONFIG="${cross_prefix}libgcrypt-config" -@@ -6617,6 +6667,16 @@ if enabled v4l2_m2m; then +@@ -6625,6 +6675,10 @@ if enabled v4l2_m2m; then check_cc vp9_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;" fi +check_func_headers "linux/media.h linux/videodev2.h" v4l2_timeval_to_ns +check_cc hevc_v4l2_request linux/videodev2.h "int i = V4L2_PIX_FMT_HEVC_SLICE;" +disable v4l2_req_hevc_vx -+if enabled hevc_v4l2request_hwaccel; then -+ enable v4l2_req_hevc_vx -+fi -+if enabled hevc_v4l2_request; then -+ disable v4l2_req_hevc_vx -+fi + check_headers sys/videoio.h test_code cc sys/videoio.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete +@@ -7112,6 +7166,9 @@ check_deps $CONFIG_LIST \ + enabled threads && ! enabled pthreads && ! enabled atomics_native && die "non pthread threading without atomics not supported, try adding --enable-pthreads or --cpu=i486 or higher if you are on x86" + enabled avresample && warn "Building with deprecated library libavresample" + ++# Sub-feature of hevc_v4l2request_hwaccel - can only be set once deps are done ++enabled hevc_v4l2request_hwaccel && disabled hevc_v4l2_request && enable v4l2_req_hevc_vx ++ + case $target_os in + haiku) + disable memalign diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c -index 46bb014de8..0502ff71b8 100644 +index dec012a299..8aa13007f9 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c -@@ -2186,8 +2186,8 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame) +@@ -2189,8 +2189,8 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame) ifilter->channel_layout != frame->channel_layout; break; case AVMEDIA_TYPE_VIDEO: @@ -231,7 +228,7 @@ index 46bb014de8..0502ff71b8 100644 break; } -@@ -2198,6 +2198,9 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame) +@@ -2201,6 +2201,9 @@ static int ifilter_send_frame(InputFilter *ifilter, AVFrame *frame) (ifilter->hw_frames_ctx && ifilter->hw_frames_ctx->data != frame->hw_frames_ctx->data)) need_reinit = 1; @@ -241,7 +238,7 @@ index 46bb014de8..0502ff71b8 100644 if (need_reinit) { ret = ifilter_parameters_from_frame(ifilter, frame); if (ret < 0) -@@ -2466,8 +2469,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_ +@@ -2469,8 +2472,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output, int64_ decoded_frame->top_field_first = ist->top_field_first; ist->frames_decoded++; @@ -251,7 +248,7 @@ index 46bb014de8..0502ff71b8 100644 err = ist->hwaccel_retrieve_data(ist->dec_ctx, decoded_frame); if (err < 0) goto fail; -@@ -2671,7 +2673,12 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo +@@ -2674,7 +2676,12 @@ static int process_input_packet(InputStream *ist, const AVPacket *pkt, int no_eo case AVMEDIA_TYPE_VIDEO: ret = decode_video (ist, repeating ? NULL : avpkt, &got_output, &duration_pts, !pkt, &decode_failed); @@ -265,7 +262,7 @@ index 46bb014de8..0502ff71b8 100644 if (pkt && pkt->duration) { duration_dts = av_rescale_q(pkt->duration, ist->st->time_base, AV_TIME_BASE_Q); } else if(ist->dec_ctx->framerate.num != 0 && ist->dec_ctx->framerate.den != 0) { -@@ -2895,6 +2902,16 @@ static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat +@@ -2898,6 +2905,16 @@ static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat } else { const HWAccel *hwaccel = NULL; int i; @@ -282,7 +279,7 @@ index 46bb014de8..0502ff71b8 100644 for (i = 0; hwaccels[i].name; i++) { if (hwaccels[i].pix_fmt == *p) { hwaccel = &hwaccels[i]; -@@ -2990,6 +3007,15 @@ static int init_input_stream(int ist_index, char *error, int error_len) +@@ -2993,6 +3010,15 @@ static int init_input_stream(int ist_index, char *error, int error_len) return ret; } @@ -391,7 +388,7 @@ index 807e783422..456d4f349b 100644 "write program-readable progress information", "url" }, { "stdin", OPT_BOOL | OPT_EXPERT, { &stdin_interaction }, diff --git a/libavcodec/Makefile b/libavcodec/Makefile -index 33a280cf69..1372d3981d 100644 +index b3d284d7d0..7a3773b9e3 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -19,6 +19,7 @@ HEADERS = ac3_parser.h \ @@ -402,7 +399,7 @@ index 33a280cf69..1372d3981d 100644 vaapi.h \ vdpau.h \ version.h \ -@@ -140,6 +141,7 @@ OBJS-$(CONFIG_QSVDEC) += qsvdec.o +@@ -139,6 +140,7 @@ OBJS-$(CONFIG_QSVDEC) += qsvdec.o OBJS-$(CONFIG_QSVENC) += qsvenc.o OBJS-$(CONFIG_RANGECODER) += rangecoder.o OBJS-$(CONFIG_RDFT) += rdft.o @@ -410,19 +407,19 @@ index 33a280cf69..1372d3981d 100644 OBJS-$(CONFIG_RV34DSP) += rv34dsp.o OBJS-$(CONFIG_SHARED) += log2_tab.o reverse.o OBJS-$(CONFIG_SINEWIN) += sinewin.o -@@ -154,7 +156,10 @@ OBJS-$(CONFIG_VIDEODSP) += videodsp.o +@@ -153,7 +155,10 @@ OBJS-$(CONFIG_VIDEODSP) += videodsp.o OBJS-$(CONFIG_VP3DSP) += vp3dsp.o OBJS-$(CONFIG_VP56DSP) += vp56dsp.o OBJS-$(CONFIG_VP8DSP) += vp8dsp.o -OBJS-$(CONFIG_V4L2_M2M) += v4l2_m2m.o v4l2_context.o v4l2_buffers.o v4l2_fmt.o +OBJS-$(CONFIG_V4L2_M2M) += v4l2_m2m.o v4l2_context.o v4l2_buffers.o v4l2_fmt.o\ -+ weak_link.o ++ weak_link.o v4l2_req_dmabufs.o +OBJS-$(CONFIG_V4L2_REQUEST) += v4l2_req_media.o v4l2_req_pollqueue.o v4l2_req_dmabufs.o\ + v4l2_req_devscan.o weak_link.o OBJS-$(CONFIG_WMA_FREQS) += wma_freqs.o OBJS-$(CONFIG_WMV2DSP) += wmv2dsp.o -@@ -403,6 +408,14 @@ OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec.o +@@ -402,6 +407,14 @@ OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec.o OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc_ps_enc.o \ hevc_data.o OBJS-$(CONFIG_HEVC_RKMPP_DECODER) += rkmppdec.o @@ -18896,10 +18893,10 @@ index 0000000000..4e35bd583d +#endif diff --git a/libavcodec/hevc-ctrls-v4.h b/libavcodec/hevc-ctrls-v4.h new file mode 100644 -index 0000000000..7829d82084 +index 0000000000..c02fdbe5a8 --- /dev/null +++ b/libavcodec/hevc-ctrls-v4.h -@@ -0,0 +1,517 @@ +@@ -0,0 +1,524 @@ +/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Video for Linux Two controls header file @@ -18955,6 +18952,13 @@ index 0000000000..7829d82084 +#include +#include + ++#ifndef V4L2_CTRL_CLASS_CODEC_STATELESS ++#define V4L2_CTRL_CLASS_CODEC_STATELESS 0x00a40000 /* Stateless codecs controls */ ++#endif ++#ifndef V4L2_CID_CODEC_STATELESS_BASE ++#define V4L2_CID_CODEC_STATELESS_BASE (V4L2_CTRL_CLASS_CODEC_STATELESS | 0x900) ++#endif ++ +#define V4L2_PIX_FMT_HEVC_SLICE v4l2_fourcc('S', '2', '6', '5') /* HEVC parsed slices */ + +#define V4L2_CID_STATELESS_HEVC_SPS (V4L2_CID_CODEC_STATELESS_BASE + 400) @@ -19501,7 +19505,7 @@ index 4f6d985ae6..eefae71275 100644 return 0; } diff --git a/libavcodec/hevcdec.c b/libavcodec/hevcdec.c -index 2231aed259..7b05b41441 100644 +index 19d6d517f3..fc5ce28718 100644 --- a/libavcodec/hevcdec.c +++ b/libavcodec/hevcdec.c @@ -333,6 +333,19 @@ static void export_stream_params(HEVCContext *s, const HEVCSPS *sps) @@ -19705,6 +19709,19 @@ index f421dc909f..f93283b893 100644 #define HWACCEL_VAAPI(codec) \ HW_CONFIG_HWACCEL(1, 1, 1, VAAPI, VAAPI, ff_ ## codec ## _vaapi_hwaccel) #define HWACCEL_VDPAU(codec) \ +diff --git a/libavcodec/mjpegenc_common.c b/libavcodec/mjpegenc_common.c +index 12dd7be2e8..0845814834 100644 +--- a/libavcodec/mjpegenc_common.c ++++ b/libavcodec/mjpegenc_common.c +@@ -247,7 +247,7 @@ void ff_mjpeg_encode_picture_header(AVCodecContext *avctx, PutBitContext *pb, + default: av_assert0(0); + } + +- put_bits(pb, 16, 17); ++ put_bits(pb, 16, 8 + 3 * components); + if (lossless && ( avctx->pix_fmt == AV_PIX_FMT_BGR0 + || avctx->pix_fmt == AV_PIX_FMT_BGRA + || avctx->pix_fmt == AV_PIX_FMT_BGR24)) diff --git a/libavcodec/mmaldec.c b/libavcodec/mmaldec.c index cb15ac072a..f6261db962 100644 --- a/libavcodec/mmaldec.c @@ -19727,11 +19744,26 @@ index cb15ac072a..f6261db962 100644 #include #include "avcodec.h" +diff --git a/libavcodec/nvenc.c b/libavcodec/nvenc.c +index 906915b5ca..0ce61041ef 100644 +--- a/libavcodec/nvenc.c ++++ b/libavcodec/nvenc.c +@@ -1025,8 +1025,9 @@ static av_cold void nvenc_setup_rate_control(AVCodecContext *avctx) + + av_log(avctx, AV_LOG_VERBOSE, "CQ(%d) mode enabled.\n", tmp_quality); + +- //CQ mode shall discard avg bitrate & honor max bitrate; ++ // CQ mode shall discard avg bitrate/vbv buffer size and honor only max bitrate + ctx->encode_config.rcParams.averageBitRate = avctx->bit_rate = 0; ++ ctx->encode_config.rcParams.vbvBufferSize = avctx->rc_buffer_size = 0; + ctx->encode_config.rcParams.maxBitRate = avctx->rc_max_rate; + } + } diff --git a/libavcodec/pthread_frame.c b/libavcodec/pthread_frame.c -index 9176027f15..0b0ff03c18 100644 +index 6f48d2c208..c4707d23d9 100644 --- a/libavcodec/pthread_frame.c +++ b/libavcodec/pthread_frame.c -@@ -209,7 +209,8 @@ FF_ENABLE_DEPRECATION_WARNINGS +@@ -215,7 +215,8 @@ FF_ENABLE_DEPRECATION_WARNINGS /* if the previous thread uses hwaccel then we take the lock to ensure * the threads don't run concurrently */ @@ -19741,7 +19773,47 @@ index 9176027f15..0b0ff03c18 100644 pthread_mutex_lock(&p->parent->hwaccel_mutex); p->hwaccel_serializing = 1; } -@@ -636,7 +637,9 @@ void ff_thread_finish_setup(AVCodecContext *avctx) { +@@ -245,7 +246,7 @@ FF_ENABLE_DEPRECATION_WARNINGS + p->hwaccel_serializing = 0; + pthread_mutex_unlock(&p->parent->hwaccel_mutex); + } +- av_assert0(!avctx->hwaccel); ++ av_assert0(!avctx->hwaccel || (avctx->hwaccel->caps_internal & HWACCEL_CAP_MT_SAFE)); + + if (p->async_serializing) { + p->async_serializing = 0; +@@ -324,6 +325,12 @@ static int update_context_from_thread(AVCodecContext *dst, AVCodecContext *src, + } + + dst->hwaccel_flags = src->hwaccel_flags; ++ if (src->hwaccel && ++ (src->hwaccel->caps_internal & HWACCEL_CAP_MT_SAFE)) { ++ dst->hwaccel = src->hwaccel; ++ dst->hwaccel_context = src->hwaccel_context; ++ dst->internal->hwaccel_priv_data = src->internal->hwaccel_priv_data; ++ } + + err = av_buffer_replace(&dst->internal->pool, src->internal->pool); + if (err < 0) +@@ -457,10 +464,13 @@ static int submit_packet(PerThreadContext *p, AVCodecContext *user_avctx, + } + + /* transfer the stashed hwaccel state, if any */ +- av_assert0(!p->avctx->hwaccel); +- FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel); +- FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context); +- FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv); ++ av_assert0(!p->avctx->hwaccel || (p->avctx->hwaccel->caps_internal & HWACCEL_CAP_MT_SAFE)); ++ if (p->avctx->hwaccel && ++ !(p->avctx->hwaccel->caps_internal & HWACCEL_CAP_MT_SAFE)) { ++ FFSWAP(const AVHWAccel*, p->avctx->hwaccel, fctx->stash_hwaccel); ++ FFSWAP(void*, p->avctx->hwaccel_context, fctx->stash_hwaccel_context); ++ FFSWAP(void*, p->avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv); ++ } + + av_packet_unref(p->avpkt); + ret = av_packet_ref(p->avpkt, avpkt); +@@ -652,7 +662,9 @@ void ff_thread_finish_setup(AVCodecContext *avctx) { if (!(avctx->active_thread_type&FF_THREAD_FRAME)) return; @@ -19752,6 +19824,69 @@ index 9176027f15..0b0ff03c18 100644 pthread_mutex_lock(&p->parent->hwaccel_mutex); p->hwaccel_serializing = 1; } +@@ -669,9 +681,12 @@ void ff_thread_finish_setup(AVCodecContext *avctx) { + * this is done here so that this worker thread can wipe its own hwaccel + * state after decoding, without requiring synchronization */ + av_assert0(!p->parent->stash_hwaccel); +- p->parent->stash_hwaccel = avctx->hwaccel; +- p->parent->stash_hwaccel_context = avctx->hwaccel_context; +- p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data; ++ if (avctx->hwaccel && ++ !(avctx->hwaccel->caps_internal & HWACCEL_CAP_MT_SAFE)) { ++ p->parent->stash_hwaccel = avctx->hwaccel; ++ p->parent->stash_hwaccel_context = avctx->hwaccel_context; ++ p->parent->stash_hwaccel_priv = avctx->internal->hwaccel_priv_data; ++ } + + pthread_mutex_lock(&p->progress_mutex); + if(atomic_load(&p->state) == STATE_SETUP_FINISHED){ +@@ -767,12 +782,23 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) + + park_frame_worker_threads(fctx, thread_count); + +- if (fctx->prev_thread && fctx->prev_thread != fctx->threads) +- if (update_context_from_thread(fctx->threads->avctx, fctx->prev_thread->avctx, 0) < 0) { +- av_log(avctx, AV_LOG_ERROR, "Final thread update failed\n"); +- fctx->prev_thread->avctx->internal->is_copy = fctx->threads->avctx->internal->is_copy; +- fctx->threads->avctx->internal->is_copy = 1; ++ if (avctx->hwaccel && (avctx->hwaccel->caps_internal & HWACCEL_CAP_MT_SAFE)) { ++ if (fctx->prev_thread && ++ avctx->internal->hwaccel_priv_data != ++ fctx->prev_thread->avctx->internal->hwaccel_priv_data) { ++ if (update_context_from_thread(avctx, fctx->prev_thread->avctx, 1) < 0) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to update user thread.\n"); ++ } + } ++ } ++ else { ++ if (fctx->prev_thread && fctx->prev_thread != fctx->threads) ++ if (update_context_from_thread(fctx->threads->avctx, fctx->prev_thread->avctx, 0) < 0) { ++ av_log(avctx, AV_LOG_ERROR, "Final thread update failed\n"); ++ fctx->prev_thread->avctx->internal->is_copy = fctx->threads->avctx->internal->is_copy; ++ fctx->threads->avctx->internal->is_copy = 1; ++ } ++ } + + for (i = 0; i < thread_count; i++) { + PerThreadContext *p = &fctx->threads[i]; +@@ -822,10 +848,13 @@ void ff_frame_thread_free(AVCodecContext *avctx, int thread_count) + + /* if we have stashed hwaccel state, move it to the user-facing context, + * so it will be freed in avcodec_close() */ +- av_assert0(!avctx->hwaccel); +- FFSWAP(const AVHWAccel*, avctx->hwaccel, fctx->stash_hwaccel); +- FFSWAP(void*, avctx->hwaccel_context, fctx->stash_hwaccel_context); +- FFSWAP(void*, avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv); ++ av_assert0(!avctx->hwaccel || (avctx->hwaccel->caps_internal & HWACCEL_CAP_MT_SAFE)); ++ if (avctx->hwaccel && ++ !(avctx->hwaccel->caps_internal & HWACCEL_CAP_MT_SAFE)) { ++ FFSWAP(const AVHWAccel*, avctx->hwaccel, fctx->stash_hwaccel); ++ FFSWAP(void*, avctx->hwaccel_context, fctx->stash_hwaccel_context); ++ FFSWAP(void*, avctx->internal->hwaccel_priv_data, fctx->stash_hwaccel_priv); ++ } + + av_freep(&avctx->internal->thread_ctx); + diff --git a/libavcodec/raw.c b/libavcodec/raw.c index 079d5c5d10..0781f28615 100644 --- a/libavcodec/raw.c @@ -50022,7 +50157,7 @@ index 0000000000..85c5b46d75 +}; + diff --git a/libavcodec/v4l2_buffers.c b/libavcodec/v4l2_buffers.c -index 4b2679eb38..9ef2f40e39 100644 +index 4b2679eb38..8d80d19788 100644 --- a/libavcodec/v4l2_buffers.c +++ b/libavcodec/v4l2_buffers.c @@ -21,6 +21,7 @@ @@ -50033,7 +50168,7 @@ index 4b2679eb38..9ef2f40e39 100644 #include #include #include -@@ -29,57 +30,82 @@ +@@ -29,57 +30,88 @@ #include #include "libavcodec/avcodec.h" #include "libavcodec/internal.h" @@ -50043,6 +50178,7 @@ index 4b2679eb38..9ef2f40e39 100644 #include "v4l2_context.h" #include "v4l2_buffers.h" #include "v4l2_m2m.h" ++#include "v4l2_req_dmabufs.h" +#include "weak_link.h" #define USEC_PER_SEC 1000000 @@ -50050,27 +50186,35 @@ index 4b2679eb38..9ef2f40e39 100644 +static const AVRational v4l2_timebase = { 1, USEC_PER_SEC }; -static inline V4L2m2mContext *buf_to_m2mctx(V4L2Buffer *buf) -+static inline V4L2m2mContext *buf_to_m2mctx(const V4L2Buffer * const buf) ++static inline V4L2m2mContext *ctx_to_m2mctx(const V4L2Context *ctx) { - return V4L2_TYPE_IS_OUTPUT(buf->context->type) ? - container_of(buf->context, V4L2m2mContext, output) : - container_of(buf->context, V4L2m2mContext, capture); +- return V4L2_TYPE_IS_OUTPUT(buf->context->type) ? +- container_of(buf->context, V4L2m2mContext, output) : +- container_of(buf->context, V4L2m2mContext, capture); ++ return V4L2_TYPE_IS_OUTPUT(ctx->type) ? ++ container_of(ctx, V4L2m2mContext, output) : ++ container_of(ctx, V4L2m2mContext, capture); } -static inline AVCodecContext *logger(V4L2Buffer *buf) -+static inline AVCodecContext *logger(const V4L2Buffer * const buf) ++static inline V4L2m2mContext *buf_to_m2mctx(const V4L2Buffer * const buf) { - return buf_to_m2mctx(buf)->avctx; +- return buf_to_m2mctx(buf)->avctx; ++ return ctx_to_m2mctx(buf->context); } -static inline AVRational v4l2_get_timebase(V4L2Buffer *avbuf) -+static inline AVRational v4l2_get_timebase(const V4L2Buffer * const avbuf) ++static inline AVCodecContext *logger(const V4L2Buffer * const buf) { - V4L2m2mContext *s = buf_to_m2mctx(avbuf); -- ++ return buf_to_m2mctx(buf)->avctx; ++} + - if (s->avctx->pkt_timebase.num) - return s->avctx->pkt_timebase; - return s->avctx->time_base; ++static inline AVRational v4l2_get_timebase(const V4L2Buffer * const avbuf) ++{ + const V4L2m2mContext *s = buf_to_m2mctx(avbuf); + const AVRational tb = s->avctx->pkt_timebase.num ? + s->avctx->pkt_timebase : @@ -50138,7 +50282,7 @@ index 4b2679eb38..9ef2f40e39 100644 } static enum AVColorPrimaries v4l2_get_color_primaries(V4L2Buffer *buf) -@@ -116,6 +142,105 @@ static enum AVColorPrimaries v4l2_get_color_primaries(V4L2Buffer *buf) +@@ -116,6 +148,105 @@ static enum AVColorPrimaries v4l2_get_color_primaries(V4L2Buffer *buf) return AVCOL_PRI_UNSPECIFIED; } @@ -50244,7 +50388,7 @@ index 4b2679eb38..9ef2f40e39 100644 static enum AVColorRange v4l2_get_color_range(V4L2Buffer *buf) { enum v4l2_quantization qt; -@@ -134,6 +259,20 @@ static enum AVColorRange v4l2_get_color_range(V4L2Buffer *buf) +@@ -134,6 +265,20 @@ static enum AVColorRange v4l2_get_color_range(V4L2Buffer *buf) return AVCOL_RANGE_UNSPECIFIED; } @@ -50265,7 +50409,7 @@ index 4b2679eb38..9ef2f40e39 100644 static enum AVColorSpace v4l2_get_color_space(V4L2Buffer *buf) { enum v4l2_ycbcr_encoding ycbcr; -@@ -210,73 +349,165 @@ static enum AVColorTransferCharacteristic v4l2_get_color_trc(V4L2Buffer *buf) +@@ -210,73 +355,178 @@ static enum AVColorTransferCharacteristic v4l2_get_color_trc(V4L2Buffer *buf) return AVCOL_TRC_UNSPECIFIED; } @@ -50300,13 +50444,13 @@ index 4b2679eb38..9ef2f40e39 100644 + buf->buf.field = !is_interlaced ? V4L2_FIELD_NONE : + is_tff ? V4L2_FIELD_INTERLACED_TB : V4L2_FIELD_INTERLACED_BT; +} -+ + +- av_buffer_unref(&avbuf->context_ref); +static uint8_t * v4l2_get_drm_frame(V4L2Buffer *avbuf) +{ + AVDRMFrameDescriptor *drm_desc = &avbuf->drm_frame; + AVDRMLayerDescriptor *layer; - -- av_buffer_unref(&avbuf->context_ref); ++ + /* fill the DRM frame descriptor */ + drm_desc->nb_objects = avbuf->num_planes; + drm_desc->nb_layers = 1; @@ -50316,7 +50460,7 @@ index 4b2679eb38..9ef2f40e39 100644 + + for (int i = 0; i < avbuf->num_planes; i++) { + layer->planes[i].object_index = i; -+ layer->planes[i].offset = 0; ++ layer->planes[i].offset = avbuf->plane_info[i].offset; + layer->planes[i].pitch = avbuf->plane_info[i].bytesperline; } + @@ -50423,47 +50567,60 @@ index 4b2679eb38..9ef2f40e39 100644 } -static int v4l2_buf_to_bufref(V4L2Buffer *in, int plane, AVBufferRef **buf) -+static int v4l2_buffer_export_drm(V4L2Buffer* avbuf) ++static inline uint32_t ff_v4l2_buf_len(const struct v4l2_buffer * b, unsigned int i) { - int ret; -+ struct v4l2_exportbuffer expbuf; -+ int i, ret; ++ return V4L2_TYPE_IS_MULTIPLANAR(b->type) ? b->m.planes[i].length : b->length; ++} - if (plane >= in->num_planes) - return AVERROR(EINVAL); -+ for (i = 0; i < avbuf->num_planes; i++) { -+ memset(&expbuf, 0, sizeof(expbuf)); ++static int v4l2_buffer_export_drm(V4L2Buffer* avbuf) ++{ ++ int i, ret; ++ const V4L2m2mContext * const s = buf_to_m2mctx(avbuf); - /* even though most encoders return 0 in data_offset encoding vp8 does require this value */ - *buf = av_buffer_create((char *)in->plane_info[plane].mm_addr + in->planes[plane].data_offset, - in->plane_info[plane].length, v4l2_free_buffer, in, 0); - if (!*buf) - return AVERROR(ENOMEM); -+ expbuf.index = avbuf->buf.index; -+ expbuf.type = avbuf->buf.type; -+ expbuf.plane = i; ++ for (i = 0; i < avbuf->num_planes; i++) { ++ int dma_fd = -1; ++ const uint32_t blen = ff_v4l2_buf_len(&avbuf->buf, i); ++ ++ if (s->db_ctl != NULL) { ++ if ((avbuf->dmabuf[i] = dmabuf_alloc(s->db_ctl, blen)) == NULL) ++ return AVERROR(ENOMEM); ++ dma_fd = dmabuf_fd(avbuf->dmabuf[i]); ++ if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->buf.type)) ++ avbuf->buf.m.planes[i].m.fd = dma_fd; ++ else ++ avbuf->buf.m.fd = dma_fd; ++ } ++ else { ++ struct v4l2_exportbuffer expbuf; ++ memset(&expbuf, 0, sizeof(expbuf)); ++ ++ expbuf.index = avbuf->buf.index; ++ expbuf.type = avbuf->buf.type; ++ expbuf.plane = i; ++ ++ ret = ioctl(s->fd, VIDIOC_EXPBUF, &expbuf); ++ if (ret < 0) ++ return AVERROR(errno); ++ dma_fd = expbuf.fd; ++ } - ret = v4l2_buf_increase_ref(in); - if (ret) - av_buffer_unref(buf); -+ ret = ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_EXPBUF, &expbuf); -+ if (ret < 0) -+ return AVERROR(errno); ++ avbuf->drm_frame.objects[i].size = blen; ++ avbuf->drm_frame.objects[i].fd = dma_fd; ++ avbuf->drm_frame.objects[i].format_modifier = DRM_FORMAT_MOD_LINEAR; ++ } - return ret; -+ if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->buf.type)) { -+ /* drm frame */ -+ avbuf->drm_frame.objects[i].size = avbuf->buf.m.planes[i].length; -+ avbuf->drm_frame.objects[i].fd = expbuf.fd; -+ avbuf->drm_frame.objects[i].format_modifier = DRM_FORMAT_MOD_LINEAR; -+ } else { -+ /* drm frame */ -+ avbuf->drm_frame.objects[0].size = avbuf->buf.length; -+ avbuf->drm_frame.objects[0].fd = expbuf.fd; -+ avbuf->drm_frame.objects[0].format_modifier = DRM_FORMAT_MOD_LINEAR; -+ } -+ } -+ + return 0; } @@ -50474,7 +50631,7 @@ index 4b2679eb38..9ef2f40e39 100644 if (plane >= out->num_planes) return AVERROR(EINVAL); -@@ -284,32 +515,57 @@ static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, i +@@ -284,32 +534,57 @@ static int v4l2_bufref_to_buf(V4L2Buffer *out, int plane, const uint8_t* data, i length = out->plane_info[plane].length; bytesused = FFMIN(size+offset, length); @@ -50529,7 +50686,7 @@ index 4b2679eb38..9ef2f40e39 100644 + frame->buf[0] = wrap_avbuf(avbuf); + if (frame->buf[0] == NULL) + return AVERROR(ENOMEM); -+ + + if (buf_to_m2mctx(avbuf)->output_drm) { + /* 1. get references to the actual data */ + frame->data[0] = (uint8_t *) v4l2_get_drm_frame(avbuf); @@ -50537,7 +50694,7 @@ index 4b2679eb38..9ef2f40e39 100644 + frame->hw_frames_ctx = av_buffer_ref(avbuf->context->frames_ref); + return 0; + } - ++ + + /* 1. get references to the actual data */ + for (i = 0; i < avbuf->num_planes; i++) { @@ -50547,7 +50704,7 @@ index 4b2679eb38..9ef2f40e39 100644 } /* fixup special cases */ -@@ -318,17 +574,17 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) +@@ -318,17 +593,17 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) case AV_PIX_FMT_NV21: if (avbuf->num_planes > 1) break; @@ -50571,7 +50728,7 @@ index 4b2679eb38..9ef2f40e39 100644 break; default: -@@ -338,68 +594,127 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) +@@ -338,68 +613,127 @@ static int v4l2_buffer_buf_to_swframe(AVFrame *frame, V4L2Buffer *avbuf) return 0; } @@ -50753,7 +50910,7 @@ index 4b2679eb38..9ef2f40e39 100644 return 0; } -@@ -409,16 +724,31 @@ static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out) +@@ -409,16 +743,31 @@ static int v4l2_buffer_swframe_to_buf(const AVFrame *frame, V4L2Buffer *out) * ******************************************************************************/ @@ -50789,7 +50946,7 @@ index 4b2679eb38..9ef2f40e39 100644 av_frame_unref(frame); -@@ -429,17 +759,32 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) +@@ -429,17 +778,32 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) /* 2. get frame information */ frame->key_frame = !!(avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME); @@ -50825,7 +50982,7 @@ index 4b2679eb38..9ef2f40e39 100644 /* 3. report errors upstream */ if (avbuf->buf.flags & V4L2_BUF_FLAG_ERROR) { -@@ -452,15 +797,15 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) +@@ -452,15 +816,15 @@ int ff_v4l2_buffer_buf_to_avframe(AVFrame *frame, V4L2Buffer *avbuf) int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *avbuf) { @@ -50847,7 +51004,7 @@ index 4b2679eb38..9ef2f40e39 100644 if (avbuf->buf.flags & V4L2_BUF_FLAG_KEYFRAME) pkt->flags |= AV_PKT_FLAG_KEY; -@@ -475,31 +820,91 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *avbuf) +@@ -475,39 +839,107 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *avbuf) return 0; } @@ -50904,9 +51061,16 @@ index 4b2679eb38..9ef2f40e39 100644 + munmap(p->mm_addr, p->length); + } + -+ for (i = 0; i != FF_ARRAY_ELEMS(avbuf->drm_frame.objects); ++i) { -+ if (avbuf->drm_frame.objects[i].fd != -1) -+ close(avbuf->drm_frame.objects[i].fd); ++ if (avbuf->dmabuf[0] == NULL) { ++ for (i = 0; i != FF_ARRAY_ELEMS(avbuf->drm_frame.objects); ++i) { ++ if (avbuf->drm_frame.objects[i].fd != -1) ++ close(avbuf->drm_frame.objects[i].fd); ++ } ++ } ++ else { ++ for (i = 0; i != FF_ARRAY_ELEMS(avbuf->dmabuf); ++i) { ++ dmabuf_free(avbuf->dmabuf[i]); ++ } + } + + av_buffer_unref(&avbuf->ref_buf); @@ -50923,7 +51087,9 @@ index 4b2679eb38..9ef2f40e39 100644 int ret, i; + V4L2Buffer * const avbuf = av_mallocz(sizeof(*avbuf)); + AVBufferRef * bufref; -+ ++ V4L2m2mContext * const s = ctx_to_m2mctx(ctx); + +- avbuf->buf.memory = V4L2_MEMORY_MMAP; + *pbufref = NULL; + if (avbuf == NULL) + return AVERROR(ENOMEM); @@ -50933,8 +51099,7 @@ index 4b2679eb38..9ef2f40e39 100644 + av_free(avbuf); + return AVERROR(ENOMEM); + } - -- avbuf->buf.memory = V4L2_MEMORY_MMAP; ++ + avbuf->context = ctx; + avbuf->buf.memory = mem; avbuf->buf.type = ctx->type; @@ -50949,16 +51114,17 @@ index 4b2679eb38..9ef2f40e39 100644 if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { avbuf->buf.length = VIDEO_MAX_PLANES; avbuf->buf.m.planes = avbuf->planes; -@@ -507,7 +912,7 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) + } - ret = ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_QUERYBUF, &avbuf->buf); +- ret = ioctl(buf_to_m2mctx(avbuf)->fd, VIDIOC_QUERYBUF, &avbuf->buf); ++ ret = ioctl(s->fd, VIDIOC_QUERYBUF, &avbuf->buf); if (ret < 0) - return AVERROR(errno); + goto fail; if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { avbuf->num_planes = 0; -@@ -520,6 +925,8 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) +@@ -520,6 +952,8 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) avbuf->num_planes = 1; for (i = 0; i < avbuf->num_planes; i++) { @@ -50967,13 +51133,14 @@ index 4b2679eb38..9ef2f40e39 100644 avbuf->plane_info[i].bytesperline = V4L2_TYPE_IS_MULTIPLANAR(ctx->type) ? ctx->format.fmt.pix_mp.plane_fmt[i].bytesperline : -@@ -527,25 +934,29 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) +@@ -527,25 +961,31 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { avbuf->plane_info[i].length = avbuf->buf.m.planes[i].length; - avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.m.planes[i].length, - PROT_READ | PROT_WRITE, MAP_SHARED, - buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.planes[i].m.mem_offset); ++ avbuf->plane_info[i].offset = avbuf->buf.m.planes[i].data_offset; + + if (want_mmap) + avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.m.planes[i].length, @@ -50984,6 +51151,7 @@ index 4b2679eb38..9ef2f40e39 100644 - avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.length, - PROT_READ | PROT_WRITE, MAP_SHARED, - buf_to_m2mctx(avbuf)->fd, avbuf->buf.m.offset); ++ avbuf->plane_info[i].offset = 0; + + if (want_mmap) + avbuf->plane_info[i].mm_addr = mmap(NULL, avbuf->buf.length, @@ -51008,16 +51176,18 @@ index 4b2679eb38..9ef2f40e39 100644 if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { avbuf->buf.m.planes = avbuf->planes; avbuf->buf.length = avbuf->num_planes; -@@ -555,20 +966,51 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) +@@ -555,20 +995,53 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index) avbuf->buf.length = avbuf->planes[0].length; } - return ff_v4l2_buffer_enqueue(avbuf); + if (!V4L2_TYPE_IS_OUTPUT(ctx->type)) { -+ if (buf_to_m2mctx(avbuf)->output_drm) { ++ if (s->output_drm) { + ret = v4l2_buffer_export_drm(avbuf); -+ if (ret) -+ goto fail; ++ if (ret) { ++ av_log(logger(avbuf), AV_LOG_ERROR, "Failed to get exported drm handles\n"); ++ goto fail; ++ } + } + } + @@ -51065,10 +51235,10 @@ index 4b2679eb38..9ef2f40e39 100644 return 0; } diff --git a/libavcodec/v4l2_buffers.h b/libavcodec/v4l2_buffers.h -index 8dbc7fc104..e64441ec9b 100644 +index 8dbc7fc104..0bda4dd06b 100644 --- a/libavcodec/v4l2_buffers.h +++ b/libavcodec/v4l2_buffers.h -@@ -27,25 +27,38 @@ +@@ -27,29 +27,44 @@ #include #include @@ -51087,6 +51257,7 @@ index 8dbc7fc104..e64441ec9b 100644 */ +struct V4L2Context; +struct ff_weak_link_client; ++struct dmabuf_h; + typedef struct V4L2Buffer { - /* each buffer needs to have a reference to its context */ @@ -51112,15 +51283,24 @@ index 8dbc7fc104..e64441ec9b 100644 /* keep track of the mmap address and mmap length */ struct V4L2Plane_info { -@@ -60,7 +73,6 @@ typedef struct V4L2Buffer { +- int bytesperline; ++ size_t bytesperline; ++ size_t offset; + void * mm_addr; + size_t length; + } plane_info[VIDEO_MAX_PLANES]; +@@ -60,9 +75,9 @@ typedef struct V4L2Buffer { struct v4l2_buffer buf; struct v4l2_plane planes[VIDEO_MAX_PLANES]; - int flags; enum V4L2Buffer_status status; ++ struct dmabuf_h * dmabuf[VIDEO_MAX_PLANES]; // If externally alloced dmabufs - stash other info here } V4L2Buffer; -@@ -98,6 +110,10 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *buf); + + /** +@@ -98,6 +113,10 @@ int ff_v4l2_buffer_buf_to_avpkt(AVPacket *pkt, V4L2Buffer *buf); */ int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out); @@ -51131,7 +51311,7 @@ index 8dbc7fc104..e64441ec9b 100644 /** * Extracts the data from an AVFrame to a V4L2Buffer * -@@ -106,7 +122,7 @@ int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out); +@@ -106,7 +125,7 @@ int ff_v4l2_buffer_avpkt_to_buf(const AVPacket *pkt, V4L2Buffer *out); * * @returns 0 in case of success, a negative AVERROR code otherwise */ @@ -51140,7 +51320,7 @@ index 8dbc7fc104..e64441ec9b 100644 /** * Initializes a V4L2Buffer -@@ -116,7 +132,7 @@ int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out); +@@ -116,7 +135,7 @@ int ff_v4l2_buffer_avframe_to_buf(const AVFrame *frame, V4L2Buffer *out); * * @returns 0 in case of success, a negative AVERROR code otherwise */ @@ -51149,7 +51329,7 @@ index 8dbc7fc104..e64441ec9b 100644 /** * Enqueues a V4L2Buffer -@@ -127,5 +143,12 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index); +@@ -127,5 +146,12 @@ int ff_v4l2_buffer_initialize(V4L2Buffer* avbuf, int index); */ int ff_v4l2_buffer_enqueue(V4L2Buffer* avbuf); @@ -51163,7 +51343,7 @@ index 8dbc7fc104..e64441ec9b 100644 #endif // AVCODEC_V4L2_BUFFERS_H diff --git a/libavcodec/v4l2_context.c b/libavcodec/v4l2_context.c -index ff1ea8e57b..0225f6ba64 100644 +index ff1ea8e57b..fcd5fdf359 100644 --- a/libavcodec/v4l2_context.c +++ b/libavcodec/v4l2_context.c @@ -27,11 +27,13 @@ @@ -51199,8 +51379,9 @@ index ff1ea8e57b..0225f6ba64 100644 { - return ctx_to_m2mctx(ctx)->avctx; + return (unsigned int)pts; -+} -+ + } + +-static inline unsigned int v4l2_get_width(struct v4l2_format *fmt) +// FFmpeg requires us to propagate a number of vars from the coded pkt into +// the decoded frame. The only thing that tracks like that in V4L2 stateful +// is timestamp. PTS maps to timestamp for this decode. FFmpeg makes no @@ -51210,7 +51391,8 @@ index ff1ea8e57b..0225f6ba64 100644 +// indexed by the tracking no. +static int64_t +xlat_pts_pkt_in(AVCodecContext *const avctx, xlat_track_t *const x, const AVPacket *const avpkt) -+{ + { +- return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.width : fmt->fmt.pix.width; + int64_t track_pts; + + // Avoid 0 @@ -51258,15 +51440,17 @@ index ff1ea8e57b..0225f6ba64 100644 + .track_pts = track_pts + }; + return track_pts; -+} -+ + } + +-static inline unsigned int v4l2_get_height(struct v4l2_format *fmt) + +// Returns -1 if we should discard the frame +static int +xlat_pts_frame_out(AVCodecContext *const avctx, + xlat_track_t * const x, + AVFrame *const frame) -+{ + { +- return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height; + unsigned int n = pts_to_track(avctx, frame->pts) % FF_V4L2_M2M_TRACK_SIZE; + V4L2m2mTrackEl *const t = x->track_els + n; + if (frame->pts == AV_NOPTS_VALUE || frame->pts != t->track_pts) @@ -51339,22 +51523,18 @@ index ff1ea8e57b..0225f6ba64 100644 + av_log(avctx, AV_LOG_TRACE, "Out pkt PTS=%" PRId64 ", track=%"PRId64", n=%d\n", + pkt->pts, t->track_pts, n); + return 0; - } - --static inline unsigned int v4l2_get_width(struct v4l2_format *fmt) ++} ++ + +static inline V4L2m2mContext *ctx_to_m2mctx(const V4L2Context *ctx) - { -- return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.width : fmt->fmt.pix.width; ++{ + return V4L2_TYPE_IS_OUTPUT(ctx->type) ? + container_of(ctx, V4L2m2mContext, output) : + container_of(ctx, V4L2m2mContext, capture); - } - --static inline unsigned int v4l2_get_height(struct v4l2_format *fmt) ++} ++ +static inline AVCodecContext *logger(const V4L2Context *ctx) - { -- return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height; ++{ + return ctx_to_m2mctx(ctx)->avctx; } @@ -51565,7 +51745,7 @@ index ff1ea8e57b..0225f6ba64 100644 return 1; } -@@ -280,171 +452,277 @@ static int v4l2_stop_encode(V4L2Context *ctx) +@@ -280,171 +452,293 @@ static int v4l2_stop_encode(V4L2Context *ctx) return 0; } @@ -51767,6 +51947,11 @@ index ff1ea8e57b..0225f6ba64 100644 + return 0; +} + ++static inline int ++dq_ok(const V4L2Context * const c) ++{ ++ return c->streamon && atomic_load(&c->q_count) != 0; ++} + +// Get a buffer +// If output then just gets the buffer in the expected way @@ -51805,7 +51990,7 @@ index ff1ea8e57b..0225f6ba64 100644 - /* 2. dequeue the buffer */ - if (pfd.revents & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM)) { + // If capture && timeout == -1 then also wait for rx buffer free -+ if (is_cap && timeout == -1 && m->output.streamon && !m->draining) ++ if (is_cap && timeout == -1 && dq_ok(&m->output) && !m->draining) + pfd.events |= poll_out; - if (!V4L2_TYPE_IS_OUTPUT(ctx->type)) { @@ -51813,9 +51998,9 @@ index ff1ea8e57b..0225f6ba64 100644 - if (pfd.revents & (POLLIN | POLLRDNORM)) - goto dequeue; + // If nothing Qed all we will get is POLLERR - avoid that -+ if ((pfd.events == poll_out && atomic_load(&m->output.q_count) == 0) || -+ (pfd.events == poll_cap && atomic_load(&m->capture.q_count) == 0) || -+ (pfd.events == (poll_cap | poll_out) && atomic_load(&m->capture.q_count) == 0 && atomic_load(&m->output.q_count) == 0)) { ++ if ((pfd.events == poll_out && !dq_ok(&m->output)) || ++ (pfd.events == poll_cap && !dq_ok(&m->capture)) || ++ (pfd.events == (poll_cap | poll_out) && !dq_ok(&m->capture) && !dq_ok(&m->output))) { + av_log(avctx, AV_LOG_TRACE, "V4L2 poll %s empty\n", ctx->name); + return AVERROR(ENOSPC); + } @@ -51874,17 +52059,17 @@ index ff1ea8e57b..0225f6ba64 100644 } - return NULL; + return AVERROR(EAGAIN); ++ } ++ ++ if ((pfd.revents & POLLERR) != 0) { ++ av_log(avctx, AV_LOG_WARNING, "V4L2 %s POLLERR\n", ctx->name); ++ return AVERROR_UNKNOWN; } - if (ctx_to_m2mctx(ctx)->draining && !V4L2_TYPE_IS_OUTPUT(ctx->type)) { - int bytesused = V4L2_TYPE_IS_MULTIPLANAR(buf.type) ? - buf.m.planes[0].bytesused : buf.bytesused; - if (bytesused == 0) { -+ if ((pfd.revents & POLLERR) != 0) { -+ av_log(avctx, AV_LOG_WARNING, "V4L2 %s POLLERR\n", ctx->name); -+ return AVERROR_UNKNOWN; -+ } -+ + if ((pfd.revents & poll_event) != 0) { + ret = get_event(m); + if (ret < 0) { @@ -51897,6 +52082,13 @@ index ff1ea8e57b..0225f6ba64 100644 - ctx->done = 1; -#endif + continue; ++ } ++ ++ if ((pfd.revents & poll_cap) != 0) { ++ ret = dq_buf(ctx, ppavbuf); ++ if (ret == AVERROR(EPIPE)) ++ continue; ++ return ret; } - avbuf = &ctx->buffers[buf.index]; @@ -51905,19 +52097,12 @@ index ff1ea8e57b..0225f6ba64 100644 - if (V4L2_TYPE_IS_MULTIPLANAR(ctx->type)) { - memcpy(avbuf->planes, planes, sizeof(planes)); - avbuf->buf.m.planes = avbuf->planes; -+ if ((pfd.revents & poll_cap) != 0) { -+ ret = dq_buf(ctx, ppavbuf); -+ if (ret == AVERROR(EPIPE)) -+ continue; -+ return ret; - } -- return avbuf; -+ + if ((pfd.revents & poll_out) != 0) { + if (is_cap) + return AVERROR(EAGAIN); + return dq_buf(ctx, ppavbuf); -+ } + } +- return avbuf; + + av_log(avctx, AV_LOG_ERROR, "V4L2 poll unexpected events=%#x, revents=%#x\n", pfd.events, pfd.revents); + return AVERROR_UNKNOWN; @@ -51939,6 +52124,21 @@ index ff1ea8e57b..0225f6ba64 100644 + buf->sequence = 0; + + return avbuf; ++} ++ ++int ++ff_v4l2_dq_all(V4L2Context *const ctx, int timeout1) ++{ ++ V4L2Buffer * avbuf; ++ if (timeout1 != 0) { ++ int rv = get_qbuf(ctx, &avbuf, timeout1); ++ if (rv != 0) ++ return rv; ++ } ++ do { ++ get_qbuf(ctx, &avbuf, 0); ++ } while (avbuf); ++ return 0; } static V4L2Buffer* v4l2_getfree_v4l2buf(V4L2Context *ctx) @@ -51947,14 +52147,12 @@ index ff1ea8e57b..0225f6ba64 100644 int i; /* get back as many output buffers as possible */ - if (V4L2_TYPE_IS_OUTPUT(ctx->type)) { +- if (V4L2_TYPE_IS_OUTPUT(ctx->type)) { - do { - } while (v4l2_dequeue_v4l2buf(ctx, timeout)); -+ V4L2Buffer * avbuf; -+ do { -+ get_qbuf(ctx, &avbuf, 0); -+ } while (avbuf); - } +- } ++ if (V4L2_TYPE_IS_OUTPUT(ctx->type)) ++ ff_v4l2_dq_all(ctx, 0); for (i = 0; i < ctx->num_buffers; i++) { - if (ctx->buffers[i].status == V4L2BUF_AVAILABLE) @@ -51965,7 +52163,7 @@ index ff1ea8e57b..0225f6ba64 100644 } return NULL; -@@ -452,25 +730,45 @@ static V4L2Buffer* v4l2_getfree_v4l2buf(V4L2Context *ctx) +@@ -452,25 +746,45 @@ static V4L2Buffer* v4l2_getfree_v4l2buf(V4L2Context *ctx) static int v4l2_release_buffers(V4L2Context* ctx) { @@ -51995,18 +52193,18 @@ index ff1ea8e57b..0225f6ba64 100644 + .type = ctx->type, + .count = 0, /* 0 -> unmap all buffers from the driver */ + }; ++ ++ while ((ret = ioctl(fd, VIDIOC_REQBUFS, &req)) == -1) { ++ if (errno == EINTR) ++ continue; ++ ++ ret = AVERROR(errno); - for (j = 0; j < buffer->num_planes; j++) { - struct V4L2Plane_info *p = &buffer->plane_info[j]; - if (p->mm_addr && p->length) - if (munmap(p->mm_addr, p->length) < 0) - av_log(logger(ctx), AV_LOG_ERROR, "%s unmap plane (%s))\n", ctx->name, av_err2str(AVERROR(errno))); -+ while ((ret = ioctl(fd, VIDIOC_REQBUFS, &req)) == -1) { -+ if (errno == EINTR) -+ continue; -+ -+ ret = AVERROR(errno); -+ + av_log(logger(ctx), AV_LOG_ERROR, "release all %s buffers (%s)\n", + ctx->name, av_err2str(AVERROR(errno))); + @@ -52025,7 +52223,7 @@ index ff1ea8e57b..0225f6ba64 100644 } static inline int v4l2_try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfmt) -@@ -499,6 +797,8 @@ static inline int v4l2_try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfm +@@ -499,6 +813,8 @@ static inline int v4l2_try_raw_format(V4L2Context* ctx, enum AVPixelFormat pixfm static int v4l2_get_raw_format(V4L2Context* ctx, enum AVPixelFormat *p) { @@ -52034,21 +52232,38 @@ index ff1ea8e57b..0225f6ba64 100644 enum AVPixelFormat pixfmt = ctx->av_pix_fmt; struct v4l2_fmtdesc fdesc; int ret; -@@ -517,6 +817,13 @@ static int v4l2_get_raw_format(V4L2Context* ctx, enum AVPixelFormat *p) +@@ -512,21 +828,22 @@ static int v4l2_get_raw_format(V4L2Context* ctx, enum AVPixelFormat *p) + return 0; + } + +- for (;;) { ++ for (;; ++fdesc.index) { + ret = ioctl(ctx_to_m2mctx(ctx)->fd, VIDIOC_ENUM_FMT, &fdesc); if (ret) return AVERROR(EINVAL); + if (priv->pix_fmt != AV_PIX_FMT_NONE) { -+ if (fdesc.pixelformat != ff_v4l2_format_avfmt_to_v4l2(priv->pix_fmt)) { -+ fdesc.index++; ++ if (fdesc.pixelformat != ff_v4l2_format_avfmt_to_v4l2(priv->pix_fmt)) + continue; -+ } + } + pixfmt = ff_v4l2_format_v4l2_to_avfmt(fdesc.pixelformat, AV_CODEC_ID_RAWVIDEO); ret = v4l2_try_raw_format(ctx, pixfmt); - if (ret){ -@@ -569,30 +876,99 @@ static int v4l2_get_coded_format(V4L2Context* ctx, uint32_t *p) +- if (ret){ +- fdesc.index++; +- continue; ++ if (ret == 0) { ++ *p = pixfmt; ++ return 0; + } +- +- *p = pixfmt; +- +- return 0; + } + + return AVERROR(EINVAL); +@@ -569,30 +886,99 @@ static int v4l2_get_coded_format(V4L2Context* ctx, uint32_t *p) * *****************************************************************************/ @@ -52156,7 +52371,7 @@ index ff1ea8e57b..0225f6ba64 100644 s->draining= 1; return 0; } -@@ -601,23 +977,29 @@ int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* frame) +@@ -601,23 +987,29 @@ int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* frame) if (!avbuf) return AVERROR(EAGAIN); @@ -52189,7 +52404,7 @@ index ff1ea8e57b..0225f6ba64 100644 s->draining = 1; return 0; } -@@ -626,8 +1008,13 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) +@@ -626,8 +1018,13 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) if (!avbuf) return AVERROR(EAGAIN); @@ -52205,7 +52420,7 @@ index ff1ea8e57b..0225f6ba64 100644 return ret; return ff_v4l2_buffer_enqueue(avbuf); -@@ -635,42 +1022,36 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) +@@ -635,42 +1032,36 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt) int ff_v4l2_context_dequeue_frame(V4L2Context* ctx, AVFrame* frame, int timeout) { @@ -52223,6 +52438,9 @@ index ff1ea8e57b..0225f6ba64 100644 - if (!avbuf) { - if (ctx->done) - return AVERROR_EOF; +- +- return AVERROR(EAGAIN); +- } + do { + if ((rv = get_qbuf(ctx, &avbuf, timeout)) != 0) + return rv; @@ -52230,14 +52448,12 @@ index ff1ea8e57b..0225f6ba64 100644 + return rv; + } while (xlat_pts_frame_out(avctx, &s->xlat, frame) != 0); -- return AVERROR(EAGAIN); -- } -- - return ff_v4l2_buffer_buf_to_avframe(frame, avbuf); + return 0; } - int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt) +-int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt) ++int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt, int timeout) { + V4L2m2mContext *s = ctx_to_m2mctx(ctx); + AVCodecContext *const avctx = s->avctx; @@ -52253,22 +52469,22 @@ index ff1ea8e57b..0225f6ba64 100644 - if (!avbuf) { - if (ctx->done) - return AVERROR_EOF; -- -- return AVERROR(EAGAIN); -- } + do { -+ if ((rv = get_qbuf(ctx, &avbuf, -1)) != 0) ++ if ((rv = get_qbuf(ctx, &avbuf, timeout)) != 0) + return rv == AVERROR(ENOSPC) ? AVERROR(EAGAIN) : rv; // Caller not currently expecting ENOSPC + if ((rv = ff_v4l2_buffer_buf_to_avpkt(pkt, avbuf)) != 0) + return rv; + } while (xlat_pts_pkt_out(avctx, &s->xlat, pkt) != 0); +- return AVERROR(EAGAIN); +- } +- - return ff_v4l2_buffer_buf_to_avpkt(pkt, avbuf); + return 0; } int ff_v4l2_context_get_format(V4L2Context* ctx, int probe) -@@ -702,78 +1083,160 @@ int ff_v4l2_context_get_format(V4L2Context* ctx, int probe) +@@ -702,78 +1093,179 @@ int ff_v4l2_context_get_format(V4L2Context* ctx, int probe) int ff_v4l2_context_set_format(V4L2Context* ctx) { @@ -52326,14 +52542,14 @@ index ff1ea8e57b..0225f6ba64 100644 + V4L2m2mContext * const s = ctx_to_m2mctx(ctx); struct v4l2_requestbuffers req; - int ret, i; -+ int ret; -+ int i; - +- - if (!v4l2_type_supported(ctx)) { - av_log(logger(ctx), AV_LOG_ERROR, "type %i not supported\n", ctx->type); - return AVERROR_PATCHWELCOME; - } -- ++ int ret; ++ int i; + - ret = ioctl(s->fd, VIDIOC_G_FMT, &ctx->format); - if (ret) - av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_G_FMT failed\n", ctx->name); @@ -52404,10 +52620,10 @@ index ff1ea8e57b..0225f6ba64 100644 + av_freep(&ctx->bufrefs); + return ret; +} - -- av_freep(&ctx->buffers); ++ +int ff_v4l2_context_init(V4L2Context* ctx) +{ ++ struct v4l2_queryctrl qctrl; + V4L2m2mContext * const s = ctx_to_m2mctx(ctx); + int ret; + @@ -52418,7 +52634,8 @@ index ff1ea8e57b..0225f6ba64 100644 + av_log(logger(ctx), AV_LOG_ERROR, "type %i not supported\n", ctx->type); + return AVERROR_PATCHWELCOME; + } -+ + +- av_freep(&ctx->buffers); + ff_mutex_init(&ctx->lock, NULL); + pthread_cond_init(&ctx->cond, NULL); + atomic_init(&ctx->q_count, 0); @@ -52449,6 +52666,24 @@ index ff1ea8e57b..0225f6ba64 100644 + goto fail_unref_hwframes; + } + ++ memset(&qctrl, 0, sizeof(qctrl)); ++ qctrl.id = V4L2_CID_MIN_BUFFERS_FOR_OUTPUT; ++ if (ioctl(s->fd, VIDIOC_QUERYCTRL, &qctrl) != 0) { ++ ret = AVERROR(errno); ++ if (ret != AVERROR(EINVAL)) { ++ av_log(logger(ctx), AV_LOG_ERROR, "%s VIDIOC_QUERCTRL failed: %s\n", ctx->name, av_err2str(ret)); ++ goto fail_unref_hwframes; ++ } ++ // Control unsupported - set default if wanted ++ if (ctx->num_buffers < 2) ++ ctx->num_buffers = 4; ++ } ++ else { ++ if (ctx->num_buffers < 2) ++ ctx->num_buffers = qctrl.minimum + 2; ++ ctx->num_buffers = av_clip(ctx->num_buffers, qctrl.minimum, qctrl.maximum); ++ } ++ + ret = create_buffers(ctx, ctx->num_buffers, ctx->buf_mem); + if (ret < 0) + goto fail_unref_hwframes; @@ -52462,7 +52697,7 @@ index ff1ea8e57b..0225f6ba64 100644 return ret; } diff --git a/libavcodec/v4l2_context.h b/libavcodec/v4l2_context.h -index 22a9532444..267a629925 100644 +index 22a9532444..108fc05a6f 100644 --- a/libavcodec/v4l2_context.h +++ b/libavcodec/v4l2_context.h @@ -31,6 +31,7 @@ @@ -52533,6 +52768,15 @@ index 22a9532444..267a629925 100644 } V4L2Context; /** +@@ -147,7 +177,7 @@ int ff_v4l2_context_set_status(V4L2Context* ctx, uint32_t cmd); + * @param[inout] pkt The AVPacket to dequeue to. + * @return 0 in case of success, AVERROR(EAGAIN) if no buffer was ready, another negative error in case of error. + */ +-int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt); ++int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt, int timeout); + + /** + * Dequeues a buffer from a V4L2Context to an AVFrame. @@ -156,7 +186,10 @@ int ff_v4l2_context_dequeue_packet(V4L2Context* ctx, AVPacket* pkt); * @param[in] ctx The V4L2Context to dequeue from. * @param[inout] f The AVFrame to dequeue to. @@ -52553,14 +52797,45 @@ index 22a9532444..267a629925 100644 /** * Enqueues a buffer to a V4L2Context from an AVFrame +@@ -183,4 +216,28 @@ int ff_v4l2_context_enqueue_packet(V4L2Context* ctx, const AVPacket* pkt); + */ + int ff_v4l2_context_enqueue_frame(V4L2Context* ctx, const AVFrame* f); + ++/** ++ * Dequeue all buffers on this queue ++ * ++ * Used to recycle output buffers ++ * ++ * @param[in] ctx The V4L2Context to dequeue from. ++ * @param[in] timeout1 A timeout on dequeuing the 1st buffer, ++ * all others have a timeout of zero ++ * @return AVERROR(EAGAIN) if timeout1 non-zero then the return ++ * of the first dequeue operation, 0 otherwise. ++ */ ++int ff_v4l2_dq_all(V4L2Context *const ctx, int timeout1); ++ ++/** ++ * Returns the number of buffers currently queued ++ * ++ * @param[in] ctx The V4L2Context to evaluate ++ */ ++static inline int ++ff_v4l2_context_q_count(const V4L2Context* const ctx) ++{ ++ return atomic_load(&ctx->q_count); ++} ++ + #endif // AVCODEC_V4L2_CONTEXT_H diff --git a/libavcodec/v4l2_m2m.c b/libavcodec/v4l2_m2m.c -index cdfd579810..77fe5fc4e3 100644 +index cdfd579810..a919bdc030 100644 --- a/libavcodec/v4l2_m2m.c +++ b/libavcodec/v4l2_m2m.c -@@ -36,6 +36,14 @@ +@@ -35,6 +35,15 @@ + #include "v4l2_context.h" #include "v4l2_fmt.h" #include "v4l2_m2m.h" - ++#include "v4l2_req_dmabufs.h" ++ +static void +xlat_init(xlat_track_t * const x) +{ @@ -52568,21 +52843,35 @@ index cdfd579810..77fe5fc4e3 100644 + x->last_pts = AV_NOPTS_VALUE; +} + -+ + static inline int v4l2_splane_video(struct v4l2_capability *cap) { - if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) && -@@ -68,7 +76,9 @@ static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe) +@@ -68,7 +77,9 @@ static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe) s->capture.done = s->output.done = 0; s->capture.name = "capture"; -+ s->capture.buf_mem = V4L2_MEMORY_MMAP; ++ s->capture.buf_mem = s->db_ctl != NULL ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP; s->output.name = "output"; + s->output.buf_mem = s->input_drm ? V4L2_MEMORY_DMABUF : V4L2_MEMORY_MMAP; atomic_init(&s->refcount, 0); sem_init(&s->refsync, 0, 0); -@@ -215,13 +225,7 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) +@@ -85,12 +96,14 @@ static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe) + if (v4l2_mplane_video(&cap)) { + s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; ++ s->output.format.type = s->output.type; + return 0; + } + + if (v4l2_splane_video(&cap)) { + s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; ++ s->output.format.type = s->output.type; + return 0; + } + +@@ -215,13 +228,7 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n"); /* 2. unmap the capture buffers (v4l2 and ffmpeg): @@ -52596,7 +52885,7 @@ index cdfd579810..77fe5fc4e3 100644 ff_v4l2_context_release(&s->capture); /* 3. get the new capture format */ -@@ -240,7 +244,6 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) +@@ -240,7 +247,6 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) /* 5. complete reinit */ s->draining = 0; @@ -52604,7 +52893,7 @@ index cdfd579810..77fe5fc4e3 100644 return 0; } -@@ -274,7 +277,6 @@ int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s) +@@ -274,7 +280,6 @@ int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s) /* start again now that we know the stream dimensions */ s->draining = 0; @@ -52612,7 +52901,7 @@ index cdfd579810..77fe5fc4e3 100644 ret = ff_v4l2_context_get_format(&s->output, 0); if (ret) { -@@ -328,10 +330,14 @@ static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context) +@@ -328,10 +333,14 @@ static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context) ff_v4l2_context_release(&s->capture); sem_destroy(&s->refsync); @@ -52628,22 +52917,23 @@ index cdfd579810..77fe5fc4e3 100644 av_free(s); } -@@ -344,6 +350,11 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) +@@ -344,6 +353,11 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) if (!s) return 0; + av_log(s->avctx, AV_LOG_DEBUG, "V4L2 Codec end\n"); + -+ if (av_codec_is_decoder(s->avctx->codec)) ++ if (s->avctx && av_codec_is_decoder(s->avctx->codec)) + av_packet_unref(&s->buf_pkt); + if (s->fd >= 0) { ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF); if (ret) -@@ -356,7 +367,14 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) +@@ -356,7 +370,15 @@ int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) ff_v4l2_context_release(&s->output); ++ dmabufs_ctl_unref(&s->db_ctl); + close(s->fd); + s->fd = -1; + @@ -52655,7 +52945,7 @@ index cdfd579810..77fe5fc4e3 100644 av_buffer_unref(&priv->context_ref); return 0; -@@ -400,35 +418,38 @@ int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv) +@@ -400,35 +422,38 @@ int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv) return v4l2_configure_contexts(s); } @@ -52707,7 +52997,7 @@ index cdfd579810..77fe5fc4e3 100644 return 0; } diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h -index b67b216331..ee72beb052 100644 +index b67b216331..ded1478a49 100644 --- a/libavcodec/v4l2_m2m.h +++ b/libavcodec/v4l2_m2m.h @@ -30,6 +30,7 @@ @@ -52718,7 +53008,7 @@ index b67b216331..ee72beb052 100644 #include "v4l2_context.h" #define container_of(ptr, type, member) ({ \ -@@ -38,7 +39,37 @@ +@@ -38,7 +39,39 @@ #define V4L_M2M_DEFAULT_OPTS \ { "num_output_buffers", "Number of buffers in the output context",\ @@ -52750,14 +53040,16 @@ index b67b216331..ee72beb052 100644 + +typedef struct xlat_track_s { + unsigned int track_no; -+ int64_t last_pts; ++ int64_t last_pts; // Last valid PTS decoded + int64_t last_opaque; + V4L2m2mTrackEl track_els[FF_V4L2_M2M_TRACK_SIZE]; +} xlat_track_t; ++ ++struct dmabufs_ctl; typedef struct V4L2m2mContext { char devname[PATH_MAX]; -@@ -52,7 +83,6 @@ typedef struct V4L2m2mContext { +@@ -52,10 +85,10 @@ typedef struct V4L2m2mContext { AVCodecContext *avctx; sem_t refsync; atomic_uint refcount; @@ -52765,7 +53057,11 @@ index b67b216331..ee72beb052 100644 /* null frame/packet received */ int draining; -@@ -66,6 +96,36 @@ typedef struct V4L2m2mContext { ++ int running; + AVPacket buf_pkt; + + /* Reference to a frame. Only used during encoding */ +@@ -66,6 +99,35 @@ typedef struct V4L2m2mContext { /* reference back to V4L2m2mPriv */ void *priv; @@ -52780,8 +53076,6 @@ index b67b216331..ee72beb052 100644 + + /* Frame tracking */ + xlat_track_t xlat; -+ int pending_hw; -+ int pending_n; + + pts_stats_t pts_stat; + @@ -52791,7 +53085,7 @@ index b67b216331..ee72beb052 100644 + /* Ext data sent */ + int extdata_sent; + /* Ext data sent in packet - overrides ctx */ -+ uint8_t * extdata_data; ++ void * extdata_data; + size_t extdata_size; + +#define FF_V4L2_QUIRK_REINIT_ALWAYS 1 @@ -52799,18 +53093,20 @@ index b67b216331..ee72beb052 100644 + /* Quirks */ + unsigned int quirks; + ++ struct dmabufs_ctl * db_ctl; } V4L2m2mContext; typedef struct V4L2m2mPriv { -@@ -76,6 +136,7 @@ typedef struct V4L2m2mPriv { +@@ -76,6 +138,8 @@ typedef struct V4L2m2mPriv { int num_output_buffers; int num_capture_buffers; ++ const char * dmabuf_alloc; + enum AVPixelFormat pix_fmt; } V4L2m2mPriv; /** -@@ -129,4 +190,26 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *ctx); +@@ -129,4 +193,26 @@ int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *ctx); */ int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *ctx); @@ -52838,7 +53134,7 @@ index b67b216331..ee72beb052 100644 + #endif /* AVCODEC_V4L2_M2M_H */ diff --git a/libavcodec/v4l2_m2m_dec.c b/libavcodec/v4l2_m2m_dec.c -index ab07c0a24a..9312afdf56 100644 +index ab07c0a24a..2bd113facb 100644 --- a/libavcodec/v4l2_m2m_dec.c +++ b/libavcodec/v4l2_m2m_dec.c @@ -23,6 +23,10 @@ @@ -52852,7 +53148,7 @@ index ab07c0a24a..9312afdf56 100644 #include "libavutil/pixfmt.h" #include "libavutil/pixdesc.h" #include "libavutil/opt.h" -@@ -30,75 +34,111 @@ +@@ -30,75 +34,267 @@ #include "libavcodec/decode.h" #include "libavcodec/internal.h" @@ -52863,17 +53159,30 @@ index ab07c0a24a..9312afdf56 100644 #include "v4l2_context.h" #include "v4l2_m2m.h" #include "v4l2_fmt.h" ++#include "v4l2_req_dmabufs.h" -static int v4l2_try_start(AVCodecContext *avctx) --{ ++// Pick 64 for max last count - that is >1sec at 60fps ++#define STATS_LAST_COUNT_MAX 64 ++#define STATS_INTERVAL_MAX (1 << 30) ++ ++#ifndef FF_API_BUFFER_SIZE_T ++#define FF_API_BUFFER_SIZE_T 1 ++#endif ++ ++#define DUMP_FAILED_EXTRADATA 0 ++ ++#if DUMP_FAILED_EXTRADATA ++static inline char hex1(unsigned int x) + { - V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; - V4L2Context *const capture = &s->capture; - V4L2Context *const output = &s->output; - struct v4l2_selection selection = { 0 }; - int ret; -+// Pick 64 for max last count - that is >1sec at 60fps -+#define STATS_LAST_COUNT_MAX 64 -+#define STATS_INTERVAL_MAX (1 << 30) ++ x &= 0xf; ++ return x <= 9 ? '0' + x : 'a' + x - 10; ++} - /* 1. start the output process */ - if (!output->streamon) { @@ -52882,21 +53191,59 @@ index ab07c0a24a..9312afdf56 100644 - av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON on output context\n"); - return ret; - } -- } -+#ifndef FF_API_BUFFER_SIZE_T -+#define FF_API_BUFFER_SIZE_T 1 -+#endif ++static inline char * hex2(char * s, unsigned int x) ++{ ++ *s++ = hex1(x >> 4); ++ *s++ = hex1(x); ++ return s; ++} ++ ++static inline char * hex4(char * s, unsigned int x) ++{ ++ s = hex2(s, x >> 8); ++ s = hex2(s, x); ++ return s; ++} ++ ++static inline char * dash2(char * s) ++{ ++ *s++ = '-'; ++ *s++ = '-'; ++ return s; ++} ++ ++static void ++data16(char * s, const unsigned int offset, const uint8_t * m, const size_t len) ++{ ++ size_t i; ++ s = hex4(s, offset); ++ m += offset; ++ for (i = 0; i != 8; ++i) { ++ *s++ = ' '; ++ s = len > i + offset ? hex2(s, *m++) : dash2(s); + } ++ *s++ = ' '; ++ *s++ = ':'; ++ for (; i != 16; ++i) { ++ *s++ = ' '; ++ s = len > i + offset ? hex2(s, *m++) : dash2(s); ++ } ++ *s++ = 0; ++} - if (capture->streamon) - return 0; -+static int64_t pts_stats_guess(const pts_stats_t * const stats) ++static void ++log_dump(void * logctx, int lvl, const void * const data, const size_t len) +{ -+ if (stats->last_pts == AV_NOPTS_VALUE || -+ stats->last_interval == 0 || -+ stats->last_count >= STATS_LAST_COUNT_MAX) -+ return AV_NOPTS_VALUE; -+ return stats->last_pts + (int64_t)(stats->last_count - 1) * (int64_t)stats->last_interval; ++ size_t i; ++ for (i = 0; i < len; i += 16) { ++ char buf[80]; ++ data16(buf, i, data, len); ++ av_log(logctx, lvl, "%s\n", buf); ++ } +} ++#endif - /* 2. get the capture format */ - capture->format.type = capture->type; @@ -52904,6 +53251,17 @@ index ab07c0a24a..9312afdf56 100644 - if (ret) { - av_log(avctx, AV_LOG_WARNING, "VIDIOC_G_FMT ioctl\n"); - return ret; ++static int64_t pts_stats_guess(const pts_stats_t * const stats) ++{ ++ if (stats->last_count <= 1) ++ return stats->last_pts; ++ if (stats->last_pts == AV_NOPTS_VALUE || ++ stats->last_interval == 0 || ++ stats->last_count >= STATS_LAST_COUNT_MAX) ++ return AV_NOPTS_VALUE; ++ return stats->last_pts + (int64_t)(stats->last_count - 1) * (int64_t)stats->last_interval; ++} ++ +static void pts_stats_add(pts_stats_t * const stats, int64_t pts) +{ + if (pts == AV_NOPTS_VALUE || pts == stats->last_pts) { @@ -52938,17 +53296,10 @@ index ab07c0a24a..9312afdf56 100644 + av_log(stats->logctx, AV_LOG_DEBUG, "%s: %s: Bad interval: %" PRId64 "/%d\n", + __func__, stats->name, interval, stats->last_count); + stats->last_interval = 0; - } -- } ++ } + else { + const int64_t frame_time = interval / (int64_t)stats->last_count; - -- /* 4. init the capture context now that we have the capture format */ -- if (!capture->buffers) { -- ret = ff_v4l2_context_init(capture); -- if (ret) { -- av_log(avctx, AV_LOG_ERROR, "can't request capture buffers\n"); -- return AVERROR(ENOMEM); ++ + if (frame_time != stats->last_interval) + av_log(stats->logctx, AV_LOG_DEBUG, "%s: %s: New interval: %u->%" PRId64 "/%d=%" PRId64 "\n", + __func__, stats->name, stats->last_interval, interval, stats->last_count, frame_time); @@ -52956,10 +53307,12 @@ index ab07c0a24a..9312afdf56 100644 } } -- /* 5. start the capture process */ -- ret = ff_v4l2_context_set_status(capture, VIDIOC_STREAMON); -- if (ret) { -- av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON, on capture context\n"); +- /* 4. init the capture context now that we have the capture format */ +- if (!capture->buffers) { +- ret = ff_v4l2_context_init(capture); +- if (ret) { +- av_log(avctx, AV_LOG_ERROR, "can't request capture buffers\n"); +- return AVERROR(ENOMEM); + stats->last_pts = pts; + stats->last_count = 1; +} @@ -52975,6 +53328,102 @@ index ab07c0a24a..9312afdf56 100644 + }; +} + ++// If abdata == NULL then this just counts space required ++// Unpacks avcC if detected ++static int ++h264_xd_copy(const uint8_t * const extradata, const int extrasize, uint8_t * abdata) ++{ ++ const uint8_t * const xdend = extradata + extrasize; ++ const uint8_t * p = extradata; ++ uint8_t * d = abdata; ++ unsigned int n; ++ unsigned int len; ++ const unsigned int hdrlen = 4; ++ unsigned int need_pps = 1; ++ ++ if (extrasize < 8) ++ return AVERROR(EINVAL); ++ ++ if (p[0] == 0 && p[1] == 0) { ++ // Assume a couple of leading zeros are good enough to indicate NAL ++ if (abdata) ++ memcpy(d, p, extrasize); ++ return extrasize; ++ } ++ ++ // avcC starts with a 1 ++ if (p[0] != 1) ++ return AVERROR(EINVAL); ++ ++ p += 5; ++ n = *p++ & 0x1f; ++ ++doxps: ++ while (n--) { ++ if (xdend - p < 2) ++ return AVERROR(EINVAL); ++ len = (p[0] << 8) | p[1]; ++ p += 2; ++ if (xdend - p < (ptrdiff_t)len) ++ return AVERROR(EINVAL); ++ if (abdata) { ++ d[0] = 0; ++ d[1] = 0; ++ d[2] = 0; ++ d[3] = 1; ++ memcpy(d + 4, p, len); + } ++ d += len + hdrlen; ++ p += len; ++ } ++ if (need_pps) { ++ need_pps = 0; ++ if (p >= xdend) ++ return AVERROR(EINVAL); ++ n = *p++; ++ goto doxps; + } + +- /* 5. start the capture process */ +- ret = ff_v4l2_context_set_status(capture, VIDIOC_STREAMON); +- if (ret) { +- av_log(avctx, AV_LOG_DEBUG, "VIDIOC_STREAMON, on capture context\n"); ++ return d - abdata; ++} ++ ++static int ++copy_extradata(AVCodecContext * const avctx, ++ const void * const src_data, const int src_len, ++ void ** const pdst_data, size_t * const pdst_len) ++{ ++ int len; ++ ++ *pdst_len = 0; ++ av_freep(pdst_data); ++ ++ if (avctx->codec_id == AV_CODEC_ID_H264) ++ len = h264_xd_copy(src_data, src_len, NULL); ++ else ++ len = src_len < 0 ? AVERROR(EINVAL) : src_len; ++ ++ // Zero length is OK but we want to stop - -ve is error val ++ if (len <= 0) ++ return len; ++ ++ if ((*pdst_data = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE)) == NULL) ++ return AVERROR(ENOMEM); ++ ++ if (avctx->codec_id == AV_CODEC_ID_H264) ++ h264_xd_copy(src_data, src_len, *pdst_data); ++ else ++ memcpy(*pdst_data, src_data, len); ++ *pdst_len = len; ++ ++ return 0; ++} ++ ++ ++ +static int check_output_streamon(AVCodecContext *const avctx, V4L2m2mContext *const s) +{ + int ret; @@ -53015,7 +53464,7 @@ index ab07c0a24a..9312afdf56 100644 return 0; } -@@ -133,58 +173,555 @@ static int v4l2_prepare_decoder(V4L2m2mContext *s) +@@ -133,58 +329,548 @@ static int v4l2_prepare_decoder(V4L2m2mContext *s) return 0; } @@ -53048,41 +53497,54 @@ index ab07c0a24a..9312afdf56 100644 +xlat_flush(xlat_track_t * const x) +{ + unsigned int i; ++ // Do not reset track_no - this ensures that any frames left in the decoder ++ // that turn up later get discarded. ++ ++ x->last_pts = AV_NOPTS_VALUE; ++ x->last_opaque = 0; + for (i = 0; i != FF_V4L2_M2M_TRACK_SIZE; ++i) { + x->track_els[i].pending = 0; + x->track_els[i].discard = 1; + } -+ x->last_pts = AV_NOPTS_VALUE; ++} ++ ++static void ++xlat_init(xlat_track_t * const x) ++{ ++ memset(x, 0, sizeof(*x)); ++ xlat_flush(x); +} + +static int +xlat_pending(const xlat_track_t * const x) +{ + unsigned int n = x->track_no % FF_V4L2_M2M_TRACK_SIZE; -+ unsigned int i; -+ int r = 0; -+ int64_t now = AV_NOPTS_VALUE; ++ int i; ++ const int64_t now = x->last_pts; + -+ for (i = 0; i < 32; ++i, n = (n - 1) % FF_V4L2_M2M_TRACK_SIZE) { ++ for (i = 0; i < FF_V4L2_M2M_TRACK_SIZE; ++i, n = (n - 1) & (FF_V4L2_M2M_TRACK_SIZE - 1)) { + const V4L2m2mTrackEl * const t = x->track_els + n; + ++ // Discard only set on never-set or flushed entries ++ // So if we get here we've never successfully decoded a frame so allow ++ // more frames into the buffer before stalling ++ if (t->discard) ++ return i - 16; ++ ++ // If we've got this frame out then everything before this point ++ // must have entered the decoder + if (!t->pending) ++ break; ++ ++ // If we've never seen a pts all we can do is count frames ++ if (now == AV_NOPTS_VALUE) + continue; + -+ if (now == AV_NOPTS_VALUE) -+ now = t->dts; -+ -+ if (t->pts == AV_NOPTS_VALUE || -+ ((now == AV_NOPTS_VALUE || t->pts <= now) && -+ (x->last_pts == AV_NOPTS_VALUE || t->pts > x->last_pts))) -+ ++r; ++ if (t->dts != AV_NOPTS_VALUE && now >= t->dts) ++ break; + } + -+ // If we never get any ideas about PTS vs DTS allow a lot more buffer -+ if (now == AV_NOPTS_VALUE) -+ r -= 16; -+ -+ return r; ++ return i; +} + +static inline int stream_started(const V4L2m2mContext * const s) { @@ -53142,13 +53604,8 @@ index ab07c0a24a..9312afdf56 100644 + side_data = av_packet_get_side_data(&s->buf_pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); + if (side_data) { + av_log(avctx, AV_LOG_DEBUG, "New extradata\n"); -+ av_freep(&s->extdata_data); -+ if ((s->extdata_data = av_malloc(side_size ? side_size : 1)) == NULL) { -+ av_log(avctx, AV_LOG_ERROR, "Failed to alloc %d bytes of extra data\n", (int)side_size); -+ return AVERROR(ENOMEM); -+ } -+ memcpy(s->extdata_data, side_data, side_size); -+ s->extdata_size = side_size; ++ if ((ret = copy_extradata(avctx, side_data, (int)side_size, &s->extdata_data, &s->extdata_size)) < 0) ++ av_log(avctx, AV_LOG_WARNING, "Failed to copy new extra data: %s\n", av_err2str(ret)); + s->extdata_sent = 0; + } + @@ -53215,25 +53672,23 @@ index ab07c0a24a..9312afdf56 100644 + } + return NQ_DRAINING; + } -+ -+ if (!s->buf_pkt.size) -+ return NQ_NONE; -+ -+ if ((ret = check_output_streamon(avctx, s)) != 0) -+ return ret; - ret = ff_v4l2_context_enqueue_packet(output, &s->buf_pkt); - if (ret < 0 && ret != AVERROR(EAGAIN)) - goto fail; -+ if (s->extdata_sent) -+ ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, NULL, 0); -+ else if (s->extdata_data) -+ ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, s->extdata_data, s->extdata_size); -+ else -+ ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, avctx->extradata, avctx->extradata_size); ++ if (!s->buf_pkt.size) ++ return NQ_NONE; - /* if EAGAIN don't unref packet and try to enqueue in the next iteration */ - if (ret != AVERROR(EAGAIN)) ++ if ((ret = check_output_streamon(avctx, s)) != 0) ++ return ret; ++ ++ if (s->extdata_sent) ++ ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, NULL, 0); ++ else ++ ret = ff_v4l2_context_enqueue_packet(&s->output, &s->buf_pkt, s->extdata_data, s->extdata_size); ++ + if (ret == AVERROR(EAGAIN)) { + // Out of input buffers - keep packet + ret = NQ_Q_FULL; @@ -53252,6 +53707,15 @@ index ab07c0a24a..9312afdf56 100644 - goto fail; + av_log(avctx, AV_LOG_ERROR, "Packet enqueue failure: err=%d\n", ret); + return ret; ++ } ++ } ++ ++ // Start if we haven't ++ { ++ const int ret2 = v4l2_try_start(avctx); ++ if (ret2) { ++ av_log(avctx, AV_LOG_DEBUG, "Start failure: err=%d\n", ret2); ++ ret = (ret2 == AVERROR(ENOMEM)) ? ret2 : NQ_DEAD; } } @@ -53259,18 +53723,9 @@ index ab07c0a24a..9312afdf56 100644 - return ff_v4l2_context_dequeue_frame(capture, frame, -1); -fail: - av_packet_unref(&s->buf_pkt); -+ // Start if we haven't -+ { -+ const int ret2 = v4l2_try_start(avctx); -+ if (ret2) { -+ av_log(avctx, AV_LOG_DEBUG, "Start failure: err=%d\n", ret2); -+ ret = (ret2 == AVERROR(ENOMEM)) ? ret2 : NQ_DEAD; -+ } -+ } -+ -+ return ret; -+} -+ + return ret; + } + +static int qbuf_wait(AVCodecContext * const avctx, V4L2Context * const ctx) +{ + int rv = 0; @@ -53289,18 +53744,6 @@ index ab07c0a24a..9312afdf56 100644 + return rv; +} + -+// Number of frames over what xlat_pending returns that we keep *16 -+// This is a min value - if it appears to be too small the threshold should -+// adjust dynamically. -+#define PENDING_HW_MIN (3 * 16) -+// Offset to use when setting dynamically -+// Set to %16 == 15 to avoid the threshold changing immediately as we relax -+#define PENDING_HW_OFFSET (PENDING_HW_MIN - 1) -+// Number of consecutive times we've failed to get a frame when we prefer it -+// before we increase the prefer threshold (5ms * N = max expected decode -+// time) -+#define PENDING_N_THRESHOLD 6 -+ +static int v4l2_receive_frame(AVCodecContext *avctx, AVFrame *frame) +{ + V4L2m2mContext *const s = ((V4L2m2mPriv*)avctx->priv_data)->context; @@ -53310,9 +53753,11 @@ index ab07c0a24a..9312afdf56 100644 + + do { + const int pending = xlat_pending(&s->xlat); -+ const int prefer_dq = (pending > s->pending_hw / 16); ++ const int prefer_dq = (pending > 4); + const int last_src_rv = src_rv; + ++ av_log(avctx, AV_LOG_TRACE, "Pending=%d, src_rv=%d, req_pkt=%d\n", pending, src_rv, s->req_pkt); ++ + // Enqueue another pkt for decode if + // (a) We don't have a lot of stuff in the buffer already OR + // (b) ... we (think we) do but we've failed to get a frame already OR @@ -53337,10 +53782,14 @@ index ab07c0a24a..9312afdf56 100644 + // (b) enqueue returned a status indicating that decode should be attempted + if (dst_rv != 0 && TRY_DQ(src_rv)) { + // Pick a timeout depending on state ++ // The pending count isn't completely reliable so it is good enough ++ // hint that we want a frame but not good enough to require it in ++ // all cases; however if it has got > 31 that exceeds its margin of ++ // error so require a frame to prevent ridiculous levels of latency + const int t = ++ src_rv == NQ_Q_FULL ? -1 : + src_rv == NQ_DRAINING ? 300 : -+ prefer_dq ? 5 : -+ src_rv == NQ_Q_FULL ? -1 : 0; ++ prefer_dq ? (s->running && pending > 31 ? 100 : 5) : 0; + + // Dequeue frame will unref any previous contents of frame + // if it returns success so we don't need an explicit unref @@ -53357,18 +53806,11 @@ index ab07c0a24a..9312afdf56 100644 + } + } + -+ // Adjust dynamic pending threshold + if (dst_rv == 0) { -+ if (--s->pending_hw < PENDING_HW_MIN) -+ s->pending_hw = PENDING_HW_MIN; -+ s->pending_n = 0; -+ + set_best_effort_pts(avctx, &s->pts_stat, frame); -+ } -+ else if (dst_rv == AVERROR(EAGAIN)) { -+ if (prefer_dq && ++s->pending_n > PENDING_N_THRESHOLD) { -+ s->pending_hw = pending * 16 + PENDING_HW_OFFSET; -+ s->pending_n = 0; ++ if (!s->running) { ++ s->running = 1; ++ av_log(avctx, AV_LOG_VERBOSE, "Decode running\n"); + } + } + @@ -53440,8 +53882,8 @@ index ab07c0a24a..9312afdf56 100644 + ret = v4l2_receive_frame2(avctx, frame); + done = us_time(); + av_log(avctx, AV_LOG_TRACE, ">>> %s: rx time=%" PRId64 ", rv=%d\n", __func__, done - now, ret); - return ret; - } ++ return ret; ++} +#endif + +static int @@ -53563,7 +54005,7 @@ index ab07c0a24a..9312afdf56 100644 + // with small WxH + return size + (1 << 16); +} - ++ static av_cold int v4l2_decode_init(AVCodecContext *avctx) { V4L2Context *capture, *output; @@ -53589,13 +54031,13 @@ index ab07c0a24a..9312afdf56 100644 if (ret < 0) return ret; ++ xlat_init(&s->xlat); + pts_stats_init(&s->pts_stat, avctx, "decoder"); -+ s->pending_hw = PENDING_HW_MIN; + capture = &s->capture; output = &s->output; -@@ -192,14 +729,51 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) +@@ -192,14 +878,65 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) * by the v4l2 driver; this event will trigger a full pipeline reconfig and * the proper values will be retrieved from the kernel driver. */ @@ -53637,6 +54079,20 @@ index ab07c0a24a..9312afdf56 100644 + s->output_drm = 0; + } + ++ s->db_ctl = NULL; ++ if (priv->dmabuf_alloc != NULL && strcmp(priv->dmabuf_alloc, "v4l2") != 0) { ++ if (strcmp(priv->dmabuf_alloc, "cma") == 0) ++ s->db_ctl = dmabufs_ctl_new(); ++ else { ++ av_log(avctx, AV_LOG_ERROR, "Unknown dmabuf alloc method: '%s'\n", priv->dmabuf_alloc); ++ return AVERROR(EINVAL); ++ } ++ if (!s->db_ctl) { ++ av_log(avctx, AV_LOG_ERROR, "Can't open dmabuf provider '%s'\n", priv->dmabuf_alloc); ++ return AVERROR(ENOMEM); ++ } ++ } ++ + s->device_ref = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_DRM); + if (!s->device_ref) { + ret = AVERROR(ENOMEM); @@ -53649,11 +54105,20 @@ index ab07c0a24a..9312afdf56 100644 s->avctx = avctx; ret = ff_v4l2_m2m_codec_init(priv); -@@ -208,12 +782,74 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) +@@ -208,12 +945,84 @@ static av_cold int v4l2_decode_init(AVCodecContext *avctx) return ret; } - return v4l2_prepare_decoder(s); ++ if (avctx->extradata && ++ (ret = copy_extradata(avctx, avctx->extradata, avctx->extradata_size, &s->extdata_data, &s->extdata_size)) != 0) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to copy extradata from context: %s\n", av_err2str(ret)); ++#if DUMP_FAILED_EXTRADATA ++ log_dump(avctx, AV_LOG_INFO, avctx->extradata, avctx->extradata_size); ++#endif ++ return ret; ++ } ++ + if ((ret = v4l2_prepare_decoder(s)) < 0) + return ret; + @@ -53716,7 +54181,8 @@ index ab07c0a24a..9312afdf56 100644 + + // resend extradata + s->extdata_sent = 0; -+ // clear EOS status vars ++ // clear status vars ++ s->running = 0; + s->draining = 0; + output->done = 0; + capture->done = 0; @@ -53726,13 +54192,14 @@ index ab07c0a24a..9312afdf56 100644 } #define OFFSET(x) offsetof(V4L2m2mPriv, x) -@@ -222,10 +858,16 @@ static av_cold int v4l2_decode_close(AVCodecContext *avctx) +@@ -222,10 +1031,17 @@ static av_cold int v4l2_decode_close(AVCodecContext *avctx) static const AVOption options[] = { V4L_M2M_DEFAULT_OPTS, { "num_capture_buffers", "Number of buffers in the capture context", - OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 20}, 20, INT_MAX, FLAGS }, + OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 20}, 2, INT_MAX, FLAGS }, + { "pixel_format", "Pixel format to be used by the decoder", OFFSET(pix_fmt), AV_OPT_TYPE_PIXEL_FMT, {.i64 = AV_PIX_FMT_NONE}, AV_PIX_FMT_NONE, AV_PIX_FMT_NB, FLAGS }, ++ { "dmabuf_alloc", "Dmabuf alloc method", OFFSET(dmabuf_alloc), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, FLAGS }, { NULL}, }; @@ -53744,7 +54211,7 @@ index ab07c0a24a..9312afdf56 100644 #define M2MDEC_CLASS(NAME) \ static const AVClass v4l2_m2m_ ## NAME ## _dec_class = { \ .class_name = #NAME "_v4l2m2m_decoder", \ -@@ -246,9 +888,15 @@ static const AVOption options[] = { +@@ -246,9 +1062,15 @@ static const AVOption options[] = { .init = v4l2_decode_init, \ .receive_frame = v4l2_receive_frame, \ .close = v4l2_decode_close, \ @@ -53761,7 +54228,7 @@ index ab07c0a24a..9312afdf56 100644 } diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c -index f644b50133..3195ec729b 100644 +index f644b50133..6472b56030 100644 --- a/libavcodec/v4l2_m2m_enc.c +++ b/libavcodec/v4l2_m2m_enc.c @@ -24,6 +24,8 @@ @@ -53826,7 +54293,7 @@ index f644b50133..3195ec729b 100644 return AVERROR_PATCHWELCOME; } -@@ -271,13 +300,184 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) +@@ -271,17 +300,208 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s) return 0; } @@ -53945,19 +54412,34 @@ index f644b50133..3195ec729b 100644 + return 1; +} + ++static inline int q_full(const V4L2Context *const output) ++{ ++ return ff_v4l2_context_q_count(output) == output->num_buffers; ++} + static int v4l2_send_frame(AVCodecContext *avctx, const AVFrame *frame) { V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context; V4L2Context *const output = &s->output; - -+ // Signal EOF if needed ++ int rv; ++ const int needs_slot = q_full(output); ++ ++ av_log(avctx, AV_LOG_TRACE, "<<< %s; needs_slot=%d\n", __func__, needs_slot); ++ ++ // Signal EOF if needed (doesn't need q slot) + if (!frame) { ++ av_log(avctx, AV_LOG_TRACE, "--- %s: EOS\n", __func__); + return ff_v4l2_context_enqueue_frame(output, frame); + } + ++ if ((rv = ff_v4l2_dq_all(output, needs_slot? 500 : 0)) != 0) { ++ // We should be able to return AVERROR(EAGAIN) to indicate buffer ++ // exhaustion, but ffmpeg currently treats that as fatal. ++ av_log(avctx, AV_LOG_WARNING, "Failed to get buffer for src frame: %s\n", av_err2str(rv)); ++ return rv; ++ } ++ + if (s->input_drm && !output->streamon) { -+ int rv; + struct v4l2_format req_format = {.type = output->format.type}; + + // Set format when we first get a buffer @@ -54005,20 +54487,56 @@ index f644b50133..3195ec729b 100644 + selection.r.width, selection.r.height, selection.r.left, selection.r.top); + } + } -+ + #ifdef V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME - if (frame && frame->pict_type == AV_PICTURE_TYPE_I) + if (frame->pict_type == AV_PICTURE_TYPE_I) v4l2_set_ext_ctrl(s, MPEG_CID(FORCE_KEY_FRAME), 0, "force key frame", 1); #endif -@@ -328,7 +528,70 @@ static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) +- return ff_v4l2_context_enqueue_frame(output, frame); ++ rv = ff_v4l2_context_enqueue_frame(output, frame); ++ if (rv) { ++ av_log(avctx, AV_LOG_ERROR, "Enqueue frame failed: %s\n", av_err2str(rv)); ++ } ++ ++ return rv; + } + + static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) +@@ -292,6 +512,11 @@ static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) + AVFrame *frame = s->frame; + int ret; + ++ av_log(avctx, AV_LOG_TRACE, "<<< %s: qlen out %d cap %d\n", __func__, ++ ff_v4l2_context_q_count(output), ff_v4l2_context_q_count(capture)); ++ ++ ff_v4l2_dq_all(output, 0); ++ + if (s->draining) + goto dequeue; + +@@ -328,7 +553,115 @@ static int v4l2_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) } dequeue: - return ff_v4l2_context_dequeue_packet(capture, avpkt); -+ if ((ret = ff_v4l2_context_dequeue_packet(capture, avpkt)) != 0) -+ return ret; ++ // Dequeue a frame ++ for (;;) { ++ int t = q_full(output) ? -1 : s->draining ? 300 : 0; ++ int rv2; ++ ++ // If output is full wait for either a packet or output to become not full ++ ret = ff_v4l2_context_dequeue_packet(capture, avpkt, t); ++ ++ // If output was full retry packet dequeue ++ t = (ret != AVERROR(EAGAIN) || t != -1) ? 0 : 300; ++ rv2 = ff_v4l2_dq_all(output, t); ++ if (t == 0 || rv2 != 0) ++ break; ++ } ++ if (ret) ++ return (s->draining && ret == AVERROR(EAGAIN)) ? AVERROR_EOF : ret; + + if (capture->first_buf == 1) { + uint8_t * data; @@ -54033,14 +54551,12 @@ index f644b50133..3195ec729b 100644 + av_freep(&avctx->extradata); + avctx->extradata_size = 0; + -+ if ((data = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE)) != NULL) -+ memcpy(data, avpkt->data, len); ++ if ((data = av_malloc(len + AV_INPUT_BUFFER_PADDING_SIZE)) == NULL) ++ goto fail_no_mem; + ++ memcpy(data, avpkt->data, len); + av_packet_unref(avpkt); + -+ if (data == NULL) -+ return AVERROR(ENOMEM); -+ + // We need to copy the header, but keep local if not global + if ((avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) != 0) { + avctx->extradata = data; @@ -54051,23 +54567,35 @@ index f644b50133..3195ec729b 100644 + s->extdata_size = len; + } + -+ if ((ret = ff_v4l2_context_dequeue_packet(capture, avpkt)) != 0) ++ ret = ff_v4l2_context_dequeue_packet(capture, avpkt, 0); ++ ff_v4l2_dq_all(output, 0); ++ if (ret) + return ret; + } + + // First frame must be key so mark as such even if encoder forgot -+ if (capture->first_buf == 2) ++ if (capture->first_buf == 2) { + avpkt->flags |= AV_PKT_FLAG_KEY; + ++ // Add any extradata to the 1st packet we emit as we cannot create it at init ++ if (avctx->extradata_size > 0 && avctx->extradata) { ++ void * const side = av_packet_new_side_data(avpkt, ++ AV_PKT_DATA_NEW_EXTRADATA, ++ avctx->extradata_size); ++ if (!side) ++ goto fail_no_mem; ++ ++ memcpy(side, avctx->extradata, avctx->extradata_size); ++ } ++ } ++ + // Add SPS/PPS to the start of every key frame if non-global headers + if ((avpkt->flags & AV_PKT_FLAG_KEY) != 0 && s->extdata_size != 0) { + const size_t newlen = s->extdata_size + avpkt->size; + AVBufferRef * const buf = av_buffer_alloc(newlen + AV_INPUT_BUFFER_PADDING_SIZE); + -+ if (buf == NULL) { -+ av_packet_unref(avpkt); -+ return AVERROR(ENOMEM); -+ } ++ if (buf == NULL) ++ goto fail_no_mem; + + memcpy(buf->data, s->extdata_data, s->extdata_size); + memcpy(buf->data + s->extdata_size, avpkt->data, avpkt->size); @@ -54077,14 +54605,35 @@ index f644b50133..3195ec729b 100644 + avpkt->data = buf->data; + avpkt->size = newlen; + } ++ else if (ff_v4l2_context_q_count(capture) < 2) { ++ // Avoid running out of capture buffers ++ // In most cases the buffers will be returned quickly in which case ++ // we don't copy and can use the v4l2 buffers directly but sometimes ++ // ffmpeg seems to hold onto all of them for a long time (.mkv ++ // creation?) so avoid deadlock in those cases. ++ AVBufferRef * const buf = av_buffer_alloc(avpkt->size + AV_INPUT_BUFFER_PADDING_SIZE); ++ if (buf == NULL) ++ goto fail_no_mem; ++ ++ memcpy(buf->data, avpkt->data, avpkt->size); ++ av_buffer_unref(&avpkt->buf); // Will recycle the V4L2 buffer ++ ++ avpkt->buf = buf; ++ avpkt->data = buf->data; ++ } + -+// av_log(avctx, AV_LOG_INFO, "%s: PTS out=%"PRId64", size=%d, ret=%d\n", __func__, avpkt->pts, avpkt->size, ret); + capture->first_buf = 0; + return 0; ++ ++fail_no_mem: ++ av_log(avctx, AV_LOG_ERROR, "Rx pkt failed: No memory\n"); ++ ret = AVERROR(ENOMEM); ++ av_packet_unref(avpkt); ++ return ret; } static av_cold int v4l2_encode_init(AVCodecContext *avctx) -@@ -340,6 +603,8 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) +@@ -340,6 +673,8 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) uint32_t v4l2_fmt_output; int ret; @@ -54093,7 +54642,7 @@ index f644b50133..3195ec729b 100644 ret = ff_v4l2_m2m_create_context(priv, &s); if (ret < 0) return ret; -@@ -347,13 +612,17 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) +@@ -347,13 +682,17 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) capture = &s->capture; output = &s->output; @@ -54112,7 +54661,7 @@ index f644b50133..3195ec729b 100644 /* capture context */ capture->av_codec_id = avctx->codec_id; -@@ -372,7 +641,7 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) +@@ -372,7 +711,7 @@ static av_cold int v4l2_encode_init(AVCodecContext *avctx) v4l2_fmt_output = output->format.fmt.pix.pixelformat; pix_fmt_output = ff_v4l2_format_v4l2_to_avfmt(v4l2_fmt_output, AV_CODEC_ID_RAWVIDEO); @@ -54121,6 +54670,19 @@ index f644b50133..3195ec729b 100644 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt_output); av_log(avctx, AV_LOG_ERROR, "Encoder requires %s pixel format.\n", desc->name); return AVERROR(EINVAL); +@@ -390,9 +729,10 @@ static av_cold int v4l2_encode_close(AVCodecContext *avctx) + #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM + + #define V4L_M2M_CAPTURE_OPTS \ +- V4L_M2M_DEFAULT_OPTS,\ ++ { "num_output_buffers", "Number of buffers in the output context",\ ++ OFFSET(num_output_buffers), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS },\ + { "num_capture_buffers", "Number of buffers in the capture context", \ +- OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 4 }, 4, INT_MAX, FLAGS } ++ OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 8 }, 8, INT_MAX, FLAGS } + + static const AVOption mpeg4_options[] = { + V4L_M2M_CAPTURE_OPTS, diff --git a/libavcodec/v4l2_req_decode_q.c b/libavcodec/v4l2_req_decode_q.c new file mode 100644 index 0000000000..5b3fb958fa @@ -54699,13 +55261,15 @@ index 0000000000..cfa94d55c4 + diff --git a/libavcodec/v4l2_req_devscan.h b/libavcodec/v4l2_req_devscan.h new file mode 100644 -index 0000000000..0baef36535 +index 0000000000..956d9234f1 --- /dev/null +++ b/libavcodec/v4l2_req_devscan.h -@@ -0,0 +1,21 @@ +@@ -0,0 +1,23 @@ +#ifndef _DEVSCAN_H_ +#define _DEVSCAN_H_ + ++#include ++ +struct devscan; +struct decdev; +enum v4l2_buf_type; @@ -54726,10 +55290,11 @@ index 0000000000..0baef36535 +#endif diff --git a/libavcodec/v4l2_req_dmabufs.c b/libavcodec/v4l2_req_dmabufs.c new file mode 100644 -index 0000000000..c4bbed18c6 +index 0000000000..acc0366e76 --- /dev/null +++ b/libavcodec/v4l2_req_dmabufs.c -@@ -0,0 +1,288 @@ +@@ -0,0 +1,369 @@ ++#include +#include +#include +#include @@ -54751,9 +55316,22 @@ index 0000000000..c4bbed18c6 + +#define TRACE_ALLOC 0 + ++struct dmabufs_ctl; ++struct dmabuf_h; ++ ++struct dmabuf_fns { ++ int (*buf_alloc)(struct dmabufs_ctl * dbsc, struct dmabuf_h * dh, size_t size); ++ void (*buf_free)(struct dmabuf_h * dh); ++ int (*ctl_new)(struct dmabufs_ctl * dbsc); ++ void (*ctl_free)(struct dmabufs_ctl * dbsc); ++}; ++ +struct dmabufs_ctl { ++ atomic_int ref_count; + int fd; + size_t page_size; ++ void * v; ++ const struct dmabuf_fns * fns; +}; + +struct dmabuf_h { @@ -54761,6 +55339,8 @@ index 0000000000..c4bbed18c6 + size_t size; + size_t len; + void * mapptr; ++ void * v; ++ const struct dmabuf_fns * fns; +}; + +#if TRACE_ALLOC @@ -54820,15 +55400,8 @@ index 0000000000..c4bbed18c6 +struct dmabuf_h * dmabuf_realloc(struct dmabufs_ctl * dbsc, struct dmabuf_h * old, size_t size) +{ + struct dmabuf_h * dh; -+ struct dma_heap_allocation_data data = { -+ .len = (size + dbsc->page_size - 1) & ~(dbsc->page_size - 1), -+ .fd = 0, -+ .fd_flags = O_RDWR, -+ .heap_flags = 0 -+ }; -+ + if (old != NULL) { -+ if (old->size == data.len) { ++ if (old->size >= size) { + return old; + } + dmabuf_free(old); @@ -54838,24 +55411,16 @@ index 0000000000..c4bbed18c6 + (dh = malloc(sizeof(*dh))) == NULL) + return NULL; + -+ while (ioctl(dbsc->fd, DMA_HEAP_IOCTL_ALLOC, &data)) { -+ int err = errno; -+ request_log("Failed to alloc %" PRIu64 " from dma-heap(fd=%d): %d (%s)\n", -+ (uint64_t)data.len, -+ dbsc->fd, -+ err, -+ strerror(err)); -+ if (err == EINTR) -+ continue; -+ goto fail; -+ } -+ + *dh = (struct dmabuf_h){ -+ .fd = data.fd, -+ .size = (size_t)data.len, -+ .mapptr = MAP_FAILED ++ .fd = -1, ++ .mapptr = MAP_FAILED, ++ .fns = dbsc->fns + }; + ++ if (dh->fns->buf_alloc(dbsc, dh, size) != 0) ++ goto fail; ++ ++ +#if TRACE_ALLOC + ++total_bufs; + total_size += dh->size; @@ -54952,8 +55517,6 @@ index 0000000000..c4bbed18c6 + dh->len = len; +} + -+ -+ +void dmabuf_free(struct dmabuf_h * dh) +{ + if (!dh) @@ -54965,20 +55528,72 @@ index 0000000000..c4bbed18c6 + request_log("%s: Free: %zd, total=%zd, bufs=%d\n", __func__, dh->size, total_size, total_bufs); +#endif + -+ if (dh->mapptr != MAP_FAILED) ++ dh->fns->buf_free(dh); ++ ++ if (dh->mapptr != MAP_FAILED && dh->mapptr != NULL) + munmap(dh->mapptr, dh->size); -+ while (close(dh->fd) == -1 && errno == EINTR) -+ /* loop */; ++ if (dh->fd != -1) ++ while (close(dh->fd) == -1 && errno == EINTR) ++ /* loop */; + free(dh); +} + -+struct dmabufs_ctl * dmabufs_ctl_new(void) ++static struct dmabufs_ctl * dmabufs_ctl_new2(const struct dmabuf_fns * const fns) +{ -+ struct dmabufs_ctl * dbsc = malloc(sizeof(*dbsc)); ++ struct dmabufs_ctl * dbsc = calloc(1, sizeof(*dbsc)); + + if (!dbsc) + return NULL; + ++ dbsc->fd = -1; ++ dbsc->fns = fns; ++ dbsc->page_size = (size_t)sysconf(_SC_PAGE_SIZE); ++ ++ if (fns->ctl_new(dbsc) != 0) ++ goto fail; ++ ++ return dbsc; ++ ++fail: ++ free(dbsc); ++ return NULL; ++} ++ ++static void dmabufs_ctl_free(struct dmabufs_ctl * const dbsc) ++{ ++ request_debug(NULL, "Free dmabuf ctl\n"); ++ ++ dbsc->fns->ctl_free(dbsc); ++ ++ free(dbsc); ++} ++ ++void dmabufs_ctl_unref(struct dmabufs_ctl ** const pDbsc) ++{ ++ struct dmabufs_ctl * const dbsc = *pDbsc; ++ ++ if (!dbsc) ++ return; ++ *pDbsc = NULL; ++ ++ if (atomic_fetch_sub(&dbsc->ref_count, 1) != 0) ++ return; ++ ++ dmabufs_ctl_free(dbsc); ++} ++ ++struct dmabufs_ctl * dmabufs_ctl_ref(struct dmabufs_ctl * const dbsc) ++{ ++ atomic_fetch_add(&dbsc->ref_count, 1); ++ return dbsc; ++} ++ ++//----------------------------------------------------------------------------- ++// ++// Alloc dmabuf via CMA ++ ++static int ctl_cma_new(struct dmabufs_ctl * dbsc) ++{ + while ((dbsc->fd = open(DMABUF_NAME1, O_RDWR)) == -1 && + errno == EINTR) + /* Loop */; @@ -54990,48 +55605,81 @@ index 0000000000..c4bbed18c6 + if (dbsc->fd == -1) { + request_log("Unable to open either %s or %s\n", + DMABUF_NAME1, DMABUF_NAME2); -+ goto fail; ++ return -1; + } + } -+ -+ dbsc->page_size = (size_t)sysconf(_SC_PAGE_SIZE); -+ -+ return dbsc; -+ -+fail: -+ free(dbsc); -+ return NULL; ++ return 0; +} + -+void dmabufs_ctl_delete(struct dmabufs_ctl ** const pDbsc) ++static void ctl_cma_free(struct dmabufs_ctl * dbsc) +{ -+ struct dmabufs_ctl * const dbsc = *pDbsc; ++ if (dbsc->fd != -1) ++ while (close(dbsc->fd) == -1 && errno == EINTR) ++ /* loop */; + -+ if (!dbsc) -+ return; -+ *pDbsc = NULL; -+ -+ while (close(dbsc->fd) == -1 && errno == EINTR) -+ /* loop */; -+ -+ free(dbsc); +} + ++static int buf_cma_alloc(struct dmabufs_ctl * const dbsc, struct dmabuf_h * dh, size_t size) ++{ ++ struct dma_heap_allocation_data data = { ++ .len = (size + dbsc->page_size - 1) & ~(dbsc->page_size - 1), ++ .fd = 0, ++ .fd_flags = O_RDWR, ++ .heap_flags = 0 ++ }; ++ ++ while (ioctl(dbsc->fd, DMA_HEAP_IOCTL_ALLOC, &data)) { ++ int err = errno; ++ request_log("Failed to alloc %" PRIu64 " from dma-heap(fd=%d): %d (%s)\n", ++ (uint64_t)data.len, ++ dbsc->fd, ++ err, ++ strerror(err)); ++ if (err == EINTR) ++ continue; ++ return -err; ++ } ++ ++ dh->fd = data.fd; ++ dh->size = (size_t)data.len; ++ return 0; ++} ++ ++static void buf_cma_free(struct dmabuf_h * dh) ++{ ++ // Nothing needed ++} ++ ++static const struct dmabuf_fns dmabuf_cma_fns = { ++ .buf_alloc = buf_cma_alloc, ++ .buf_free = buf_cma_free, ++ .ctl_new = ctl_cma_new, ++ .ctl_free = ctl_cma_free, ++}; ++ ++struct dmabufs_ctl * dmabufs_ctl_new(void) ++{ ++ request_debug(NULL, "Dmabufs using CMA\n");; ++ return dmabufs_ctl_new2(&dmabuf_cma_fns); ++} + diff --git a/libavcodec/v4l2_req_dmabufs.h b/libavcodec/v4l2_req_dmabufs.h new file mode 100644 -index 0000000000..e36aa0a712 +index 0000000000..381ba2708d --- /dev/null +++ b/libavcodec/v4l2_req_dmabufs.h -@@ -0,0 +1,41 @@ +@@ -0,0 +1,44 @@ +#ifndef DMABUFS_H +#define DMABUFS_H + ++#include ++ +struct dmabufs_ctl; +struct dmabuf_h; + +struct dmabufs_ctl * dmabufs_ctl_new(void); -+void dmabufs_ctl_delete(struct dmabufs_ctl ** const pdbsc); ++void dmabufs_ctl_unref(struct dmabufs_ctl ** const pdbsc); ++struct dmabufs_ctl * dmabufs_ctl_ref(struct dmabufs_ctl * const dbsc); + +// Need not preserve old contents +// On NULL return old buffer is freed @@ -58845,10 +59493,14 @@ index 0000000000..e1182cb2fc +#endif /* POLLQUEUE_H_ */ diff --git a/libavcodec/v4l2_req_utils.h b/libavcodec/v4l2_req_utils.h new file mode 100644 -index 0000000000..cb4bd164b4 +index 0000000000..a31cc1f4ec --- /dev/null +++ b/libavcodec/v4l2_req_utils.h -@@ -0,0 +1,22 @@ +@@ -0,0 +1,27 @@ ++#ifndef AVCODEC_V4L2_REQ_UTILS_H ++#define AVCODEC_V4L2_REQ_UTILS_H ++ ++#include +#include "libavutil/log.h" + +#define request_log(...) av_log(NULL, AV_LOG_INFO, __VA_ARGS__) @@ -58871,12 +59523,13 @@ index 0000000000..cb4bd164b4 + return tbuf; +} + ++#endif diff --git a/libavcodec/v4l2_request_hevc.c b/libavcodec/v4l2_request_hevc.c new file mode 100644 -index 0000000000..ebeb7bc6f6 +index 0000000000..fbec16a93e --- /dev/null +++ b/libavcodec/v4l2_request_hevc.c -@@ -0,0 +1,345 @@ +@@ -0,0 +1,347 @@ +/* + * This file is part of FFmpeg. + * @@ -58904,6 +59557,7 @@ index 0000000000..ebeb7bc6f6 +#include "v4l2_request_hevc.h" + +#include "libavutil/hwcontext_drm.h" ++#include "libavutil/pixdesc.h" + +#include "v4l2_req_devscan.h" +#include "v4l2_req_dmabufs.h" @@ -58982,7 +59636,7 @@ index 0000000000..ebeb7bc6f6 + mediabufs_ctl_unref(&ctx->mbufs); + media_pool_delete(&ctx->mpool); + pollqueue_unref(&ctx->pq); -+ dmabufs_ctl_delete(&ctx->dbufs); ++ dmabufs_ctl_unref(&ctx->dbufs); + devscan_delete(&ctx->devscan); + + decode_q_uninit(&ctx->decode_q); @@ -59184,10 +59838,11 @@ index 0000000000..ebeb7bc6f6 + // Set our s/w format + avctx->sw_pix_fmt = ((AVHWFramesContext *)avctx->hw_frames_ctx->data)->sw_format; + -+ av_log(avctx, AV_LOG_INFO, "Hwaccel %s; devices: %s,%s; buffers: src %s, dst %s\n", ++ av_log(avctx, AV_LOG_INFO, "Hwaccel %s; devices: %s,%s; buffers: src %s, dst %s; swfmt=%s\n", + ctx->fns->name, + decdev_media_path(decdev), decdev_video_path(decdev), -+ mediabufs_memory_name(src_memtype), mediabufs_memory_name(dst_memtype)); ++ mediabufs_memory_name(src_memtype), mediabufs_memory_name(dst_memtype), ++ av_get_pix_fmt_name(avctx->sw_pix_fmt)); + + return 0; + @@ -59200,7 +59855,7 @@ index 0000000000..ebeb7bc6f6 +fail2: + pollqueue_unref(&ctx->pq); +fail1: -+ dmabufs_ctl_delete(&ctx->dbufs); ++ dmabufs_ctl_unref(&ctx->dbufs); +fail0: + devscan_delete(&ctx->devscan); + return ret; @@ -59224,13 +59879,14 @@ index 0000000000..ebeb7bc6f6 +}; diff --git a/libavcodec/v4l2_request_hevc.h b/libavcodec/v4l2_request_hevc.h new file mode 100644 -index 0000000000..830444bf92 +index 0000000000..99c90064ea --- /dev/null +++ b/libavcodec/v4l2_request_hevc.h -@@ -0,0 +1,101 @@ +@@ -0,0 +1,102 @@ +#ifndef AVCODEC_V4L2_REQUEST_HEVC_H +#define AVCODEC_V4L2_REQUEST_HEVC_H + ++#include +#include +#include "v4l2_req_decode_q.h" + @@ -59330,7 +59986,7 @@ index 0000000000..830444bf92 + +#endif diff --git a/libavcodec/vc1dec.c b/libavcodec/vc1dec.c -index ea93e11588..a9e0c6323e 100644 +index d4ceb60791..fb7f839c5e 100644 --- a/libavcodec/vc1dec.c +++ b/libavcodec/vc1dec.c @@ -486,7 +486,7 @@ static av_cold int vc1_decode_init(AVCodecContext *avctx) @@ -59604,10 +60260,10 @@ index 92b27a1d14..19d2a9de55 100644 diff --git a/libavdevice/drm_vout.c b/libavdevice/drm_vout.c new file mode 100644 -index 0000000000..4b25ec4344 +index 0000000000..c7b90e6dd8 --- /dev/null +++ b/libavdevice/drm_vout.c -@@ -0,0 +1,643 @@ +@@ -0,0 +1,680 @@ +/* + * Copyright (c) 2020 John Cox for Raspberry Pi Trading + * @@ -59644,6 +60300,7 @@ index 0000000000..4b25ec4344 + +#include +#include ++#include + +#define TRACE_ALL 0 + @@ -59679,7 +60336,9 @@ index 0000000000..4b25ec4344 + uint32_t con_id; + struct drm_setup setup; + enum AVPixelFormat avfmt; ++ + int show_all; ++ const char * drm_module; + + unsigned int ano; + drm_aux_t aux[AUX_SIZE]; @@ -59725,9 +60384,11 @@ index 0000000000..4b25ec4344 +{ + drmModePlaneResPtr planes; + drmModePlanePtr plane; ++ drmModeObjectPropertiesPtr props = NULL; ++ drmModePropertyPtr prop = NULL; + unsigned int i; + unsigned int j; -+ int ret = 0; ++ int ret = -1; + + planes = drmModeGetPlaneResources(drmfd); + if (!planes) @@ -59764,11 +60425,37 @@ index 0000000000..4b25ec4344 + break; + } + -+ if (i == planes->count_planes) -+ ret = -1; ++ if (i == planes->count_planes) { ++ ret = -1; ++ goto fail; ++ } + -+ drmModeFreePlaneResources(planes); -+ return ret; ++ props = drmModeObjectGetProperties(drmfd, *pplane_id, DRM_MODE_OBJECT_PLANE); ++ if (!props) ++ goto fail; ++ for (i = 0; i != props->count_props; ++i) { ++ if (prop) ++ drmModeFreeProperty(prop); ++ prop = drmModeGetProperty(drmfd, props->props[i]); ++ if (!prop) ++ goto fail; ++ if (strcmp("zpos", prop->name) == 0) { ++ if (drmModeObjectSetProperty(drmfd, *pplane_id, DRM_MODE_OBJECT_PLANE, props->props[i], prop->values[1]) == 0) ++ av_log(avctx, AV_LOG_DEBUG, "ZPOS set to %d\n", (int)prop->values[1]); ++ else ++ av_log(avctx, AV_LOG_WARNING, "Failed to set ZPOS on DRM plane\n"); ++ break; ++ } ++ } ++ ++ ret = 0; ++fail: ++ if (props) ++ drmModeFreeObjectProperties(props); ++ if (prop) ++ drmModeFreeProperty(prop); ++ drmModeFreePlaneResources(planes); ++ return ret; +} + +static void da_uninit(drm_display_env_t * const de, drm_aux_t * da) @@ -59831,6 +60518,7 @@ index 0000000000..4b25ec4344 + uint32_t offsets[4] = {0}; + uint64_t modifiers[4] = {0}; + uint32_t bo_handles[4] = {0}; ++ int has_mods = 0; + int i, j, n; + + da->frame = frame; @@ -59840,6 +60528,9 @@ index 0000000000..4b25ec4344 + av_log(s, AV_LOG_WARNING, "drmPrimeFDToHandle[%d](%d) failed: %s\n", i, desc->objects[i].fd, ERRSTR); + return -1; + } ++ if (desc->objects[i].format_modifier != DRM_FORMAT_MOD_LINEAR && ++ desc->objects[i].format_modifier != DRM_FORMAT_MOD_INVALID) ++ has_mods = 1; + } + + n = 0; @@ -59881,11 +60572,13 @@ index 0000000000..4b25ec4344 +#endif + + if (drmModeAddFB2WithModifiers(de->drm_fd, -+ av_frame_cropped_width(frame), -+ av_frame_cropped_height(frame), -+ desc->layers[0].format, bo_handles, -+ pitches, offsets, modifiers, -+ &da->fb_handle, DRM_MODE_FB_MODIFIERS /** 0 if no mods */) != 0) { ++ av_frame_cropped_width(frame), ++ av_frame_cropped_height(frame), ++ desc->layers[0].format, bo_handles, ++ pitches, offsets, ++ has_mods ? modifiers : NULL, ++ &da->fb_handle, ++ has_mods ? DRM_MODE_FB_MODIFIERS : 0) != 0) { + av_log(s, AV_LOG_WARNING, "drmModeAddFB2WithModifiers failed: %s\n", ERRSTR); + return -1; + } @@ -60151,7 +60844,6 @@ index 0000000000..4b25ec4344 +{ + drm_display_env_t * const de = s->priv_data; + int rv; -+ const char * drm_module = DRM_MODULE; + + av_log(s, AV_LOG_DEBUG, "<<< %s\n", __func__); + @@ -60160,10 +60852,10 @@ index 0000000000..4b25ec4344 + de->setup = (struct drm_setup){0}; + de->q_terminate = 0; + -+ if ((de->drm_fd = drmOpen(drm_module, NULL)) < 0) ++ if ((de->drm_fd = drmOpen(de->drm_module, NULL)) < 0) + { + rv = AVERROR(errno); -+ av_log(s, AV_LOG_ERROR, "Failed to drmOpen %s: %s\n", drm_module, av_err2str(rv)); ++ av_log(s, AV_LOG_ERROR, "Failed to drmOpen %s: %s\n", de->drm_module, av_err2str(rv)); + return rv; + } + @@ -60178,7 +60870,7 @@ index 0000000000..4b25ec4344 + sem_init(&de->q_sem_out, 0, 0); + if (pthread_create(&de->q_thread, NULL, display_thread, s)) { + rv = AVERROR(errno); -+ av_log(s, AV_LOG_ERROR, "Failed to creatye display thread: %s\n", av_err2str(rv)); ++ av_log(s, AV_LOG_ERROR, "Failed to create display thread: %s\n", av_err2str(rv)); + goto fail_close; + } + @@ -60223,6 +60915,7 @@ index 0000000000..4b25ec4344 +#define OFFSET(x) offsetof(drm_display_env_t, x) +static const AVOption options[] = { + { "show_all", "show all frames", OFFSET(show_all), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "drm_module", "drm_module name to use, default=" DRM_MODULE, OFFSET(drm_module), AV_OPT_TYPE_STRING, { .str = DRM_MODULE }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } +}; + @@ -60253,10 +60946,10 @@ index 0000000000..4b25ec4344 + diff --git a/libavdevice/egl_vout.c b/libavdevice/egl_vout.c new file mode 100644 -index 0000000000..f666adc8e9 +index 0000000000..cc6e310551 --- /dev/null +++ b/libavdevice/egl_vout.c -@@ -0,0 +1,816 @@ +@@ -0,0 +1,788 @@ +/* + * Copyright (c) 2020 John Cox for Raspberry Pi Trading + * @@ -60307,20 +61000,20 @@ index 0000000000..f666adc8e9 +#define TRACE_ALL 0 + +struct egl_setup { -+ int conId; ++ int conId; + -+ Display *dpy; -+ EGLDisplay egl_dpy; -+ EGLContext ctx; -+ EGLSurface surf; -+ Window win; ++ Display *dpy; ++ EGLDisplay egl_dpy; ++ EGLContext ctx; ++ EGLSurface surf; ++ Window win; + -+ uint32_t crtcId; -+ int crtcIdx; -+ uint32_t planeId; -+ struct { -+ int x, y, width, height; -+ } compose; ++ uint32_t crtcId; ++ int crtcIdx; ++ uint32_t planeId; ++ struct { ++ int x, y, width, height; ++ } compose; +}; + +typedef struct egl_aux_s { @@ -60329,8 +61022,7 @@ index 0000000000..f666adc8e9 + +} egl_aux_t; + -+typedef struct egl_display_env_s -+{ ++typedef struct egl_display_env_s { + AVClass *class; + + struct egl_setup setup; @@ -60348,8 +61040,8 @@ index 0000000000..f666adc8e9 + sem_t display_start_sem; + sem_t q_sem; + int q_terminate; -+ AVFrame * q_this; -+ AVFrame * q_next; ++ AVFrame *q_this; ++ AVFrame *q_next; + +} egl_display_env_t; + @@ -60358,45 +61050,44 @@ index 0000000000..f666adc8e9 + * Remove window border/decorations. + */ +static void -+no_border( Display *dpy, Window w) ++no_border(Display *dpy, Window w) +{ -+ static const unsigned MWM_HINTS_DECORATIONS = (1 << 1); -+ static const int PROP_MOTIF_WM_HINTS_ELEMENTS = 5; ++ static const unsigned MWM_HINTS_DECORATIONS = (1 << 1); ++ static const int PROP_MOTIF_WM_HINTS_ELEMENTS = 5; + -+ typedef struct -+ { -+ unsigned long flags; -+ unsigned long functions; -+ unsigned long decorations; -+ long inputMode; -+ unsigned long status; -+ } PropMotifWmHints; ++ typedef struct { ++ unsigned long flags; ++ unsigned long functions; ++ unsigned long decorations; ++ long inputMode; ++ unsigned long status; ++ } PropMotifWmHints; + -+ PropMotifWmHints motif_hints; -+ Atom prop, proptype; -+ unsigned long flags = 0; ++ PropMotifWmHints motif_hints; ++ Atom prop, proptype; ++ unsigned long flags = 0; + -+ /* setup the property */ -+ motif_hints.flags = MWM_HINTS_DECORATIONS; -+ motif_hints.decorations = flags; ++ /* setup the property */ ++ motif_hints.flags = MWM_HINTS_DECORATIONS; ++ motif_hints.decorations = flags; + -+ /* get the atom for the property */ -+ prop = XInternAtom( dpy, "_MOTIF_WM_HINTS", True ); -+ if (!prop) { -+ /* something went wrong! */ -+ return; -+ } ++ /* get the atom for the property */ ++ prop = XInternAtom(dpy, "_MOTIF_WM_HINTS", True); ++ if (!prop) { ++ /* something went wrong! */ ++ return; ++ } + -+ /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */ -+ proptype = prop; ++ /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */ ++ proptype = prop; + -+ XChangeProperty( dpy, w, /* display, window */ ++ XChangeProperty(dpy, w, /* display, window */ + prop, proptype, /* property, type */ + 32, /* format: 32-bit datums */ + PropModeReplace, /* mode */ -+ (unsigned char *) &motif_hints, /* data */ ++ (unsigned char *)&motif_hints, /* data */ + PROP_MOTIF_WM_HINTS_ELEMENTS /* nelements */ -+ ); ++ ); +} + + @@ -60405,247 +61096,247 @@ index 0000000000..f666adc8e9 + * Return the window and context handles. + */ +static int -+make_window(struct AVFormatContext * const s, -+ egl_display_env_t * const de, ++make_window(struct AVFormatContext *const s, ++ egl_display_env_t *const de, + Display *dpy, EGLDisplay egl_dpy, const char *name, + Window *winRet, EGLContext *ctxRet, EGLSurface *surfRet) +{ -+ int scrnum = DefaultScreen( dpy ); -+ XSetWindowAttributes attr; -+ unsigned long mask; -+ Window root = RootWindow( dpy, scrnum ); -+ Window win; -+ EGLContext ctx; -+ const int fullscreen = de->fullscreen; -+ EGLConfig config; -+ int x = de->window_x; -+ int y = de->window_y; -+ int width = de->window_width ? de->window_width : 1280; -+ int height = de->window_height ? de->window_height : 720; ++ int scrnum = DefaultScreen(dpy); ++ XSetWindowAttributes attr; ++ unsigned long mask; ++ Window root = RootWindow(dpy, scrnum); ++ Window win; ++ EGLContext ctx; ++ const int fullscreen = de->fullscreen; ++ EGLConfig config; ++ int x = de->window_x; ++ int y = de->window_y; ++ int width = de->window_width ? de->window_width : 1280; ++ int height = de->window_height ? de->window_height : 720; + + -+ if (fullscreen) { -+ int scrnum = DefaultScreen(dpy); ++ if (fullscreen) { ++ int scrnum = DefaultScreen(dpy); + -+ x = 0; y = 0; -+ width = DisplayWidth(dpy, scrnum); -+ height = DisplayHeight(dpy, scrnum); -+ } ++ x = 0; y = 0; ++ width = DisplayWidth(dpy, scrnum); ++ height = DisplayHeight(dpy, scrnum); ++ } + -+ { -+ EGLint num_configs; -+ static const EGLint attribs[] = { -+ EGL_RED_SIZE, 1, -+ EGL_GREEN_SIZE, 1, -+ EGL_BLUE_SIZE, 1, -+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, -+ EGL_NONE -+ }; ++ { ++ EGLint num_configs; ++ static const EGLint attribs[] = { ++ EGL_RED_SIZE, 1, ++ EGL_GREEN_SIZE, 1, ++ EGL_BLUE_SIZE, 1, ++ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, ++ EGL_NONE ++ }; + -+ if (!eglChooseConfig(egl_dpy, attribs, &config, 1, &num_configs)) { -+ av_log(s, AV_LOG_ERROR, "Error: couldn't get an EGL visual config\n"); -+ return -1; -+ } -+ } ++ if (!eglChooseConfig(egl_dpy, attribs, &config, 1, &num_configs)) { ++ av_log(s, AV_LOG_ERROR, "Error: couldn't get an EGL visual config\n"); ++ return -1; ++ } ++ } + -+ { -+ EGLint vid; -+ if (!eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) { -+ av_log(s, AV_LOG_ERROR, "Error: eglGetConfigAttrib() failed\n"); -+ return -1; -+ } ++ { ++ EGLint vid; ++ if (!eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) { ++ av_log(s, AV_LOG_ERROR, "Error: eglGetConfigAttrib() failed\n"); ++ return -1; ++ } + -+ { -+ XVisualInfo visTemplate = { -+ .visualid = vid, -+ }; -+ int num_visuals; -+ XVisualInfo *visinfo = XGetVisualInfo(dpy, VisualIDMask, -+ &visTemplate, &num_visuals); ++ { ++ XVisualInfo visTemplate = { ++ .visualid = vid, ++ }; ++ int num_visuals; ++ XVisualInfo *visinfo = XGetVisualInfo(dpy, VisualIDMask, ++ &visTemplate, &num_visuals); + -+ /* window attributes */ -+ attr.background_pixel = 0; -+ attr.border_pixel = 0; -+ attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); -+ attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; -+ /* XXX this is a bad way to get a borderless window! */ -+ mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; ++ /* window attributes */ ++ attr.background_pixel = 0; ++ attr.border_pixel = 0; ++ attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone); ++ attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; ++ /* XXX this is a bad way to get a borderless window! */ ++ mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; + -+ win = XCreateWindow( dpy, root, x, y, width, height, -+ 0, visinfo->depth, InputOutput, -+ visinfo->visual, mask, &attr ); -+ XFree(visinfo); -+ } -+ } ++ win = XCreateWindow(dpy, root, x, y, width, height, ++ 0, visinfo->depth, InputOutput, ++ visinfo->visual, mask, &attr); ++ XFree(visinfo); ++ } ++ } + -+ if (fullscreen) -+ no_border(dpy, win); ++ if (fullscreen) ++ no_border(dpy, win); + -+ /* set hints and properties */ -+ { -+ XSizeHints sizehints; -+ sizehints.x = x; -+ sizehints.y = y; -+ sizehints.width = width; -+ sizehints.height = height; -+ sizehints.flags = USSize | USPosition; -+ XSetNormalHints(dpy, win, &sizehints); -+ XSetStandardProperties(dpy, win, name, name, -+ None, (char **)NULL, 0, &sizehints); -+ } ++ /* set hints and properties */ ++ { ++ XSizeHints sizehints; ++ sizehints.x = x; ++ sizehints.y = y; ++ sizehints.width = width; ++ sizehints.height = height; ++ sizehints.flags = USSize | USPosition; ++ XSetNormalHints(dpy, win, &sizehints); ++ XSetStandardProperties(dpy, win, name, name, ++ None, (char **)NULL, 0, &sizehints); ++ } + -+ eglBindAPI(EGL_OPENGL_ES_API); ++ eglBindAPI(EGL_OPENGL_ES_API); + -+ { -+ static const EGLint ctx_attribs[] = { -+ EGL_CONTEXT_CLIENT_VERSION, 2, -+ EGL_NONE -+ }; -+ ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs ); -+ if (!ctx) { -+ av_log(s, AV_LOG_ERROR, "Error: eglCreateContext failed\n"); -+ return -1; -+ } -+ } ++ { ++ static const EGLint ctx_attribs[] = { ++ EGL_CONTEXT_CLIENT_VERSION, 2, ++ EGL_NONE ++ }; ++ ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs); ++ if (!ctx) { ++ av_log(s, AV_LOG_ERROR, "Error: eglCreateContext failed\n"); ++ return -1; ++ } ++ } + + -+ XMapWindow(dpy, win); ++ XMapWindow(dpy, win); + -+ { -+ EGLSurface surf = eglCreateWindowSurface(egl_dpy, config, (EGLNativeWindowType)win, NULL); -+ if (!surf) { -+ av_log(s, AV_LOG_ERROR, "Error: eglCreateWindowSurface failed\n"); -+ return -1; -+ } ++ { ++ EGLSurface surf = eglCreateWindowSurface(egl_dpy, config, (EGLNativeWindowType)win, NULL); ++ if (!surf) { ++ av_log(s, AV_LOG_ERROR, "Error: eglCreateWindowSurface failed\n"); ++ return -1; ++ } + -+ if (!eglMakeCurrent(egl_dpy, surf, surf, ctx)) { -+ av_log(s, AV_LOG_ERROR, "Error: eglCreateContext failed\n"); -+ return -1; -+ } ++ if (!eglMakeCurrent(egl_dpy, surf, surf, ctx)) { ++ av_log(s, AV_LOG_ERROR, "Error: eglCreateContext failed\n"); ++ return -1; ++ } + -+ *winRet = win; -+ *ctxRet = ctx; -+ *surfRet = surf; -+ } ++ *winRet = win; ++ *ctxRet = ctx; ++ *surfRet = surf; ++ } + -+ return 0; ++ return 0; +} + +static GLint -+compile_shader(struct AVFormatContext * const avctx, GLenum target, const char *source) ++compile_shader(struct AVFormatContext *const avctx, GLenum target, const char *source) +{ -+ GLuint s = glCreateShader(target); ++ GLuint s = glCreateShader(target); + -+ if (s == 0) { -+ av_log(avctx, AV_LOG_ERROR, "Failed to create shader\n"); -+ return 0; -+ } ++ if (s == 0) { ++ av_log(avctx, AV_LOG_ERROR, "Failed to create shader\n"); ++ return 0; ++ } + -+ glShaderSource(s, 1, (const GLchar **) &source, NULL); -+ glCompileShader(s); ++ glShaderSource(s, 1, (const GLchar **)&source, NULL); ++ glCompileShader(s); + -+ { -+ GLint ok; -+ glGetShaderiv(s, GL_COMPILE_STATUS, &ok); ++ { ++ GLint ok; ++ glGetShaderiv(s, GL_COMPILE_STATUS, &ok); + -+ if (!ok) { -+ GLchar *info; -+ GLint size; ++ if (!ok) { ++ GLchar *info; ++ GLint size; + -+ glGetShaderiv(s, GL_INFO_LOG_LENGTH, &size); -+ info = malloc(size); ++ glGetShaderiv(s, GL_INFO_LOG_LENGTH, &size); ++ info = malloc(size); + -+ glGetShaderInfoLog(s, size, NULL, info); -+ av_log(avctx, AV_LOG_ERROR, "Failed to compile shader: %ssource:\n%s\n", info, source); ++ glGetShaderInfoLog(s, size, NULL, info); ++ av_log(avctx, AV_LOG_ERROR, "Failed to compile shader: %ssource:\n%s\n", info, source); + -+ return 0; -+ } -+ } ++ return 0; ++ } ++ } + -+ return s; ++ return s; +} + -+static GLuint link_program(struct AVFormatContext * const s, GLint vs, GLint fs) ++static GLuint link_program(struct AVFormatContext *const s, GLint vs, GLint fs) +{ -+ GLuint prog = glCreateProgram(); ++ GLuint prog = glCreateProgram(); + -+ if (prog == 0) { -+ av_log(s, AV_LOG_ERROR, "Failed to create program\n"); -+ return 0; -+ } ++ if (prog == 0) { ++ av_log(s, AV_LOG_ERROR, "Failed to create program\n"); ++ return 0; ++ } + -+ glAttachShader(prog, vs); -+ glAttachShader(prog, fs); -+ glLinkProgram(prog); ++ glAttachShader(prog, vs); ++ glAttachShader(prog, fs); ++ glLinkProgram(prog); + -+ { -+ GLint ok; -+ glGetProgramiv(prog, GL_LINK_STATUS, &ok); -+ if (!ok) { -+ /* Some drivers return a size of 1 for an empty log. This is the size -+ * of a log that contains only a terminating NUL character. -+ */ -+ GLint size; -+ GLchar *info = NULL; -+ glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size); -+ if (size > 1) { -+ info = malloc(size); -+ glGetProgramInfoLog(prog, size, NULL, info); -+ } ++ { ++ GLint ok; ++ glGetProgramiv(prog, GL_LINK_STATUS, &ok); ++ if (!ok) { ++ /* Some drivers return a size of 1 for an empty log. This is the size ++ * of a log that contains only a terminating NUL character. ++ */ ++ GLint size; ++ GLchar *info = NULL; ++ glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size); ++ if (size > 1) { ++ info = malloc(size); ++ glGetProgramInfoLog(prog, size, NULL, info); ++ } + -+ av_log(s, AV_LOG_ERROR, "Failed to link: %s\n", -+ (info != NULL) ? info : ""); -+ return 0; -+ } -+ } ++ av_log(s, AV_LOG_ERROR, "Failed to link: %s\n", ++ (info != NULL) ? info : ""); ++ return 0; ++ } ++ } + -+ return prog; ++ return prog; +} + +static int -+gl_setup(struct AVFormatContext * const s) ++gl_setup(struct AVFormatContext *const s) +{ -+ const char *vs = -+ "attribute vec4 pos;\n" -+ "varying vec2 texcoord;\n" -+ "\n" -+ "void main() {\n" -+ " gl_Position = pos;\n" -+ " texcoord.x = (pos.x + 1.0) / 2.0;\n" -+ " texcoord.y = (-pos.y + 1.0) / 2.0;\n" -+ "}\n"; -+ const char *fs = -+ "#extension GL_OES_EGL_image_external : enable\n" -+ "precision mediump float;\n" -+ "uniform samplerExternalOES s;\n" -+ "varying vec2 texcoord;\n" -+ "void main() {\n" -+ " gl_FragColor = texture2D(s, texcoord);\n" -+ "}\n"; ++ const char *vs = ++ "attribute vec4 pos;\n" ++ "varying vec2 texcoord;\n" ++ "\n" ++ "void main() {\n" ++ " gl_Position = pos;\n" ++ " texcoord.x = (pos.x + 1.0) / 2.0;\n" ++ " texcoord.y = (-pos.y + 1.0) / 2.0;\n" ++ "}\n"; ++ const char *fs = ++ "#extension GL_OES_EGL_image_external : enable\n" ++ "precision mediump float;\n" ++ "uniform samplerExternalOES s;\n" ++ "varying vec2 texcoord;\n" ++ "void main() {\n" ++ " gl_FragColor = texture2D(s, texcoord);\n" ++ "}\n"; + -+ GLuint vs_s; -+ GLuint fs_s; -+ GLuint prog; ++ GLuint vs_s; ++ GLuint fs_s; ++ GLuint prog; + -+ if (!(vs_s = compile_shader(s, GL_VERTEX_SHADER, vs)) || -+ !(fs_s = compile_shader(s, GL_FRAGMENT_SHADER, fs)) || -+ !(prog = link_program(s, vs_s, fs_s))) -+ return -1; ++ if (!(vs_s = compile_shader(s, GL_VERTEX_SHADER, vs)) || ++ !(fs_s = compile_shader(s, GL_FRAGMENT_SHADER, fs)) || ++ !(prog = link_program(s, vs_s, fs_s))) ++ return -1; + -+ glUseProgram(prog); ++ glUseProgram(prog); + -+ { -+ static const float verts[] = { -+ -1, -1, -+ 1, -1, -+ 1, 1, -+ -1, 1, -+ }; -+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); -+ } ++ { ++ static const float verts[] = { ++ -1, -1, ++ 1, -1, ++ 1, 1, ++ -1, 1, ++ }; ++ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); ++ } + -+ glEnableVertexAttribArray(0); -+ return 0; ++ glEnableVertexAttribArray(0); ++ return 0; +} + +static int egl_vout_write_trailer(AVFormatContext *s) @@ -60659,12 +61350,12 @@ index 0000000000..f666adc8e9 + +static int egl_vout_write_header(AVFormatContext *s) +{ -+ const AVCodecParameters * const par = s->streams[0]->codecpar; ++ const AVCodecParameters *const par = s->streams[0]->codecpar; + +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "%s\n", __func__); +#endif -+ if ( s->nb_streams > 1 ++ if (s->nb_streams > 1 + || par->codec_type != AVMEDIA_TYPE_VIDEO + || par->codec_id != AV_CODEC_ID_WRAPPED_AVFRAME) { + av_log(s, AV_LOG_ERROR, "Only supports one wrapped avframe stream\n"); @@ -60675,10 +61366,10 @@ index 0000000000..f666adc8e9 +} + + -+static int do_display(AVFormatContext * const s, egl_display_env_t * const de, AVFrame * const frame) ++static int do_display(AVFormatContext *const s, egl_display_env_t *const de, AVFrame *const frame) +{ -+ const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor*)frame->data[0]; -+ egl_aux_t * da = NULL; ++ const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)frame->data[0]; ++ egl_aux_t *da = NULL; + unsigned int i; + +#if TRACE_ALL @@ -60699,26 +61390,26 @@ index 0000000000..f666adc8e9 + + if (da->texture == 0) { + EGLint attribs[50]; -+ EGLint * a = attribs; ++ EGLint *a = attribs; + int i, j; + static const EGLint anames[] = { -+ EGL_DMA_BUF_PLANE0_FD_EXT, -+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, -+ EGL_DMA_BUF_PLANE0_PITCH_EXT, -+ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, -+ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, -+ EGL_DMA_BUF_PLANE1_FD_EXT, -+ EGL_DMA_BUF_PLANE1_OFFSET_EXT, -+ EGL_DMA_BUF_PLANE1_PITCH_EXT, -+ EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, -+ EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, -+ EGL_DMA_BUF_PLANE2_FD_EXT, -+ EGL_DMA_BUF_PLANE2_OFFSET_EXT, -+ EGL_DMA_BUF_PLANE2_PITCH_EXT, -+ EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, -+ EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, ++ EGL_DMA_BUF_PLANE0_FD_EXT, ++ EGL_DMA_BUF_PLANE0_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE0_PITCH_EXT, ++ EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, ++ EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, ++ EGL_DMA_BUF_PLANE1_FD_EXT, ++ EGL_DMA_BUF_PLANE1_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE1_PITCH_EXT, ++ EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, ++ EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, ++ EGL_DMA_BUF_PLANE2_FD_EXT, ++ EGL_DMA_BUF_PLANE2_OFFSET_EXT, ++ EGL_DMA_BUF_PLANE2_PITCH_EXT, ++ EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, ++ EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, + }; -+ const EGLint * b = anames; ++ const EGLint *b = anames; + + *a++ = EGL_WIDTH; + *a++ = av_frame_cropped_width(frame); @@ -60729,8 +61420,8 @@ index 0000000000..f666adc8e9 + + for (i = 0; i < desc->nb_layers; ++i) { + for (j = 0; j < desc->layers[i].nb_planes; ++j) { -+ const AVDRMPlaneDescriptor * const p = desc->layers[i].planes + j; -+ const AVDRMObjectDescriptor * const obj = desc->objects + p->object_index; ++ const AVDRMPlaneDescriptor *const p = desc->layers[i].planes + j; ++ const AVDRMObjectDescriptor *const obj = desc->objects + p->object_index; + *a++ = *b++; + *a++ = obj->fd; + *a++ = *b++; @@ -60738,13 +61429,13 @@ index 0000000000..f666adc8e9 + *a++ = *b++; + *a++ = p->pitch; + if (obj->format_modifier == 0) { -+ b += 2; ++ b += 2; + } + else { -+ *a++ = *b++; -+ *a++ = (EGLint)(obj->format_modifier & 0xFFFFFFFF); -+ *a++ = *b++; -+ *a++ = (EGLint)(obj->format_modifier >> 32); ++ *a++ = *b++; ++ *a++ = (EGLint)(obj->format_modifier & 0xFFFFFFFF); ++ *a++ = *b++; ++ *a++ = (EGLint)(obj->format_modifier >> 32); + } + } + } @@ -60753,54 +61444,29 @@ index 0000000000..f666adc8e9 + +#if TRACE_ALL + for (a = attribs, i = 0; *a != EGL_NONE; a += 2, ++i) { -+ av_log(s, AV_LOG_INFO, "[%2d] %4x: %d\n", i, a[0], a[1]); ++ av_log(s, AV_LOG_INFO, "[%2d] %4x: %d\n", i, a[0], a[1]); + } +#endif + { -+ const EGLImage image = eglCreateImageKHR(de->setup.egl_dpy, -+ EGL_NO_CONTEXT, -+ EGL_LINUX_DMA_BUF_EXT, -+ NULL, attribs); -+ if (!image) { -+ av_log(s, AV_LOG_ERROR, "Failed to import fd %d\n", desc->objects[0].fd); -+ return -1; -+ } ++ const EGLImage image = eglCreateImageKHR(de->setup.egl_dpy, ++ EGL_NO_CONTEXT, ++ EGL_LINUX_DMA_BUF_EXT, ++ NULL, attribs); ++ if (!image) { ++ av_log(s, AV_LOG_ERROR, "Failed to import fd %d\n", desc->objects[0].fd); ++ return -1; ++ } + -+ glGenTextures(1, &da->texture); -+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, da->texture); -+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -+ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); ++ glGenTextures(1, &da->texture); ++ glBindTexture(GL_TEXTURE_EXTERNAL_OES, da->texture); ++ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); ++ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ++ glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image); + -+ eglDestroyImageKHR(de->setup.egl_dpy, image); ++ eglDestroyImageKHR(de->setup.egl_dpy, image); + } + + da->fd = desc->objects[0].fd; -+ -+#if 0 -+ av_log(s, AV_LOG_INFO, "%dx%d, fmt: %x, boh=%d,%d,%d,%d, pitch=%d,%d,%d,%d," -+ " offset=%d,%d,%d,%d, mod=%llx,%llx,%llx,%llx\n", -+ av_frame_cropped_width(frame), -+ av_frame_cropped_height(frame), -+ desc->layers[0].format, -+ bo_plane_handles[0], -+ bo_plane_handles[1], -+ bo_plane_handles[2], -+ bo_plane_handles[3], -+ pitches[0], -+ pitches[1], -+ pitches[2], -+ pitches[3], -+ offsets[0], -+ offsets[1], -+ offsets[2], -+ offsets[3], -+ (long long)modifiers[0], -+ (long long)modifiers[1], -+ (long long)modifiers[2], -+ (long long)modifiers[3] -+ ); -+#endif + } + + glClearColor(0.5, 0.5, 0.5, 0.5); @@ -60817,55 +61483,55 @@ index 0000000000..f666adc8e9 + return 0; +} + -+static void * display_thread(void * v) ++static void* display_thread(void *v) +{ -+ AVFormatContext * const s = v; -+ egl_display_env_t * const de = s->priv_data; ++ AVFormatContext *const s = v; ++ egl_display_env_t *const de = s->priv_data; + +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "<<< %s\n", __func__); +#endif + { -+ EGLint egl_major, egl_minor; ++ EGLint egl_major, egl_minor; + -+ de->setup.dpy = XOpenDisplay(NULL); -+ if (!de->setup.dpy) { -+ av_log(s, AV_LOG_ERROR, "Couldn't open X display\n"); -+ goto fail; -+ } ++ de->setup.dpy = XOpenDisplay(NULL); ++ if (!de->setup.dpy) { ++ av_log(s, AV_LOG_ERROR, "Couldn't open X display\n"); ++ goto fail; ++ } + -+ de->setup.egl_dpy = eglGetDisplay(de->setup.dpy); -+ if (!de->setup.egl_dpy) { -+ av_log(s, AV_LOG_ERROR, "eglGetDisplay() failed\n"); -+ goto fail; -+ } ++ de->setup.egl_dpy = eglGetDisplay(de->setup.dpy); ++ if (!de->setup.egl_dpy) { ++ av_log(s, AV_LOG_ERROR, "eglGetDisplay() failed\n"); ++ goto fail; ++ } + -+ if (!eglInitialize(de->setup.egl_dpy, &egl_major, &egl_minor)) { -+ av_log(s, AV_LOG_ERROR, "Error: eglInitialize() failed\n"); -+ goto fail; -+ } ++ if (!eglInitialize(de->setup.egl_dpy, &egl_major, &egl_minor)) { ++ av_log(s, AV_LOG_ERROR, "Error: eglInitialize() failed\n"); ++ goto fail; ++ } + -+ av_log(s, AV_LOG_INFO, "EGL version %d.%d\n", egl_major, egl_minor); ++ av_log(s, AV_LOG_INFO, "EGL version %d.%d\n", egl_major, egl_minor); + -+ if (!epoxy_has_egl_extension(de->setup.egl_dpy, "EGL_KHR_image_base")) { -+ av_log(s, AV_LOG_ERROR, "Missing EGL KHR image extension\n"); -+ goto fail; -+ } ++ if (!epoxy_has_egl_extension(de->setup.egl_dpy, "EGL_KHR_image_base")) { ++ av_log(s, AV_LOG_ERROR, "Missing EGL KHR image extension\n"); ++ goto fail; ++ } + } + + if (!de->window_width || !de->window_height) { -+ de->window_width = 1280; -+ de->window_height = 720; ++ de->window_width = 1280; ++ de->window_height = 720; + } + if (make_window(s, de, de->setup.dpy, de->setup.egl_dpy, "ffmpeg-vout", + &de->setup.win, &de->setup.ctx, &de->setup.surf)) { -+ av_log(s, AV_LOG_ERROR, "%s: make_window failed\n", __func__); -+ goto fail; ++ av_log(s, AV_LOG_ERROR, "%s: make_window failed\n", __func__); ++ goto fail; + } + + if (gl_setup(s)) { -+ av_log(s, AV_LOG_ERROR, "%s: gl_setup failed\n", __func__); -+ goto fail; ++ av_log(s, AV_LOG_ERROR, "%s: gl_setup failed\n", __func__); ++ goto fail; + } + +#if TRACE_ALL @@ -60874,7 +61540,7 @@ index 0000000000..f666adc8e9 + sem_post(&de->display_start_sem); + + for (;;) { -+ AVFrame * frame; ++ AVFrame *frame; + + while (sem_wait(&de->q_sem) != 0) { + av_assert0(errno == EINTR); @@ -60912,9 +61578,9 @@ index 0000000000..f666adc8e9 + +static int egl_vout_write_packet(AVFormatContext *s, AVPacket *pkt) +{ -+ const AVFrame * const src_frame = (AVFrame *)pkt->data; -+ AVFrame * frame; -+ egl_display_env_t * const de = s->priv_data; ++ const AVFrame *const src_frame = (AVFrame *)pkt->data; ++ AVFrame *frame; ++ egl_display_env_t *const de = s->priv_data; + +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "%s\n", __func__); @@ -60927,8 +61593,7 @@ index 0000000000..f666adc8e9 + else if (src_frame->format == AV_PIX_FMT_VAAPI) { + frame = av_frame_alloc(); + frame->format = AV_PIX_FMT_DRM_PRIME; -+ if (av_hwframe_map(frame, src_frame, 0) != 0) -+ { ++ if (av_hwframe_map(frame, src_frame, 0) != 0) { + av_log(s, AV_LOG_WARNING, "Failed to map frame (format=%d) to DRM_PRiME\n", src_frame->format); + av_frame_free(&frame); + return AVERROR(EINVAL); @@ -60941,12 +61606,12 @@ index 0000000000..f666adc8e9 + + // Really hacky sync + while (de->show_all && de->q_next) { -+ usleep(3000); ++ usleep(3000); + } + + pthread_mutex_lock(&de->q_lock); + { -+ AVFrame * const t = de->q_next; ++ AVFrame *const t = de->q_next; + de->q_next = frame; + frame = t; + } @@ -60961,7 +61626,7 @@ index 0000000000..f666adc8e9 +} + +static int egl_vout_write_frame(AVFormatContext *s, int stream_index, AVFrame **ppframe, -+ unsigned flags) ++ unsigned flags) +{ +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "%s: idx=%d, flags=%#x\n", __func__, stream_index, flags); @@ -60979,7 +61644,7 @@ index 0000000000..f666adc8e9 +#if TRACE_ALL + av_log(s, AV_LOG_INFO, "%s: %d\n", __func__, type); +#endif -+ switch(type) { ++ switch (type) { + case AV_APP_TO_DEV_WINDOW_REPAINT: + return 0; + default: @@ -60989,14 +61654,14 @@ index 0000000000..f666adc8e9 +} + +// deinit is called if init fails so no need to clean up explicity here -+static int egl_vout_init(struct AVFormatContext * s) ++static int egl_vout_init(struct AVFormatContext *s) +{ -+ egl_display_env_t * const de = s->priv_data; ++ egl_display_env_t *const de = s->priv_data; + unsigned int i; + + av_log(s, AV_LOG_DEBUG, "<<< %s\n", __func__); + -+ de->setup = (struct egl_setup){0}; ++ de->setup = (struct egl_setup) { 0 }; + + for (i = 0; i != 32; ++i) { + de->aux[i].fd = -1; @@ -61010,8 +61675,8 @@ index 0000000000..f666adc8e9 + + sem_wait(&de->display_start_sem); + if (de->q_terminate) { -+ av_log(s, AV_LOG_ERROR, "%s: Display startup failure\n", __func__); -+ return -1; ++ av_log(s, AV_LOG_ERROR, "%s: Display startup failure\n", __func__); ++ return -1; + } + + av_log(s, AV_LOG_DEBUG, ">>> %s\n", __func__); @@ -61019,9 +61684,9 @@ index 0000000000..f666adc8e9 + return 0; +} + -+static void egl_vout_deinit(struct AVFormatContext * s) ++static void egl_vout_deinit(struct AVFormatContext *s) +{ -+ egl_display_env_t * const de = s->priv_data; ++ egl_display_env_t *const de = s->priv_data; + + av_log(s, AV_LOG_DEBUG, "<<< %s\n", __func__); + @@ -61039,11 +61704,11 @@ index 0000000000..f666adc8e9 + +#define OFFSET(x) offsetof(egl_display_env_t, x) +static const AVOption options[] = { -+ { "show_all", "show all frames", OFFSET(show_all), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "window_size", "set window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, {.str = NULL}, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "window_x", "set window x offset", OFFSET(window_x), AV_OPT_TYPE_INT, {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "window_y", "set window y offset", OFFSET(window_y), AV_OPT_TYPE_INT, {.i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, -+ { "fullscreen", "set fullscreen display", OFFSET(fullscreen), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "show_all", "show all frames", OFFSET(show_all), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "window_size", "set window forced size", OFFSET(window_width), AV_OPT_TYPE_IMAGE_SIZE, { .str = NULL }, 0, 0, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "window_x", "set window x offset", OFFSET(window_x), AV_OPT_TYPE_INT, { .i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "window_y", "set window y offset", OFFSET(window_y), AV_OPT_TYPE_INT, { .i64 = 0 }, -INT_MAX, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM }, ++ { "fullscreen", "set fullscreen display", OFFSET(fullscreen), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, + { NULL } + +}; @@ -61634,7 +62299,7 @@ index b2c254ea67..144fbda652 100644 OBJS-$(CONFIG_UNSHARP_OPENCL_FILTER) += vf_unsharp_opencl.o opencl.o \ opencl/unsharp.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c -index 0872c6e0f2..8b23df9323 100644 +index 0872c6e0f2..1dd05e4d75 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -218,6 +218,7 @@ extern AVFilter ff_vf_dedot; @@ -61645,7 +62310,15 @@ index 0872c6e0f2..8b23df9323 100644 extern AVFilter ff_vf_deinterlace_vaapi; extern AVFilter ff_vf_dejudder; extern AVFilter ff_vf_delogo; -@@ -438,6 +439,7 @@ extern AVFilter ff_vf_transpose_opencl; +@@ -377,6 +378,7 @@ extern AVFilter ff_vf_scale; + extern AVFilter ff_vf_scale_cuda; + extern AVFilter ff_vf_scale_npp; + extern AVFilter ff_vf_scale_qsv; ++extern AVFilter ff_vf_scale_v4l2m2m; + extern AVFilter ff_vf_scale_vaapi; + extern AVFilter ff_vf_scale_vulkan; + extern AVFilter ff_vf_scale2ref; +@@ -438,6 +440,7 @@ extern AVFilter ff_vf_transpose_opencl; extern AVFilter ff_vf_transpose_vaapi; extern AVFilter ff_vf_trim; extern AVFilter ff_vf_unpremultiply; @@ -61798,10 +62471,10 @@ index da1cf9941e..c588ed23cb 100644 case AVMEDIA_TYPE_AUDIO: diff --git a/libavfilter/vf_deinterlace_v4l2m2m.c b/libavfilter/vf_deinterlace_v4l2m2m.c new file mode 100644 -index 0000000000..49fab3158d +index 0000000000..d4c11cfc51 --- /dev/null +++ b/libavfilter/vf_deinterlace_v4l2m2m.c -@@ -0,0 +1,1336 @@ +@@ -0,0 +1,2115 @@ +/* + * This file is part of FFmpeg. + * @@ -61839,6 +62512,8 @@ index 0000000000..49fab3158d +#include +#include + ++#include "config.h" ++ +#include "libavutil/avassert.h" +#include "libavutil/avstring.h" +#include "libavutil/common.h" @@ -61856,33 +62531,50 @@ index 0000000000..49fab3158d +#include "avfilter.h" +#include "formats.h" +#include "internal.h" ++#include "scale_eval.h" +#include "video.h" + ++#ifndef DRM_FORMAT_P030 ++#define DRM_FORMAT_P030 fourcc_code('P', '0', '3', '0') /* 2x2 subsampled Cr:Cb plane 10 bits per channel packed */ ++#endif ++ ++// V4L2_PIX_FMT_NV12_10_COL128 and V4L2_PIX_FMT_NV12_COL128 should be defined ++// in drm_fourcc.h hopefully will be sometime in the future but until then... ++#ifndef V4L2_PIX_FMT_NV12_10_COL128 ++#define V4L2_PIX_FMT_NV12_10_COL128 v4l2_fourcc('N', 'C', '3', '0') ++#endif ++ ++#ifndef V4L2_PIX_FMT_NV12_COL128 ++#define V4L2_PIX_FMT_NV12_COL128 v4l2_fourcc('N', 'C', '1', '2') /* 12 Y/CbCr 4:2:0 128 pixel wide column */ ++#endif ++ +typedef struct V4L2Queue V4L2Queue; +typedef struct DeintV4L2M2MContextShared DeintV4L2M2MContextShared; + -+typedef struct V4L2PlaneInfo { -+ int bytesperline; -+ size_t length; -+} V4L2PlaneInfo; ++typedef enum filter_type_v4l2_e ++{ ++ FILTER_V4L2_DEINTERLACE = 1, ++ FILTER_V4L2_SCALE, ++} filter_type_v4l2_t; + +typedef struct V4L2Buffer { + int enqueued; + int reenqueue; -+ int fd; + struct v4l2_buffer buffer; + AVFrame frame; + struct v4l2_plane planes[VIDEO_MAX_PLANES]; + int num_planes; -+ V4L2PlaneInfo plane_info[VIDEO_MAX_PLANES]; + AVDRMFrameDescriptor drm_frame; + V4L2Queue *q; +} V4L2Buffer; + +typedef struct V4L2Queue { + struct v4l2_format format; ++ struct v4l2_selection sel; ++ int eos; + int num_buffers; + V4L2Buffer *buffers; ++ const char * name; + DeintV4L2M2MContextShared *ctx; +} V4L2Queue; + @@ -61913,13 +62605,41 @@ index 0000000000..49fab3158d + pts_track_el_t a[PTS_TRACK_SIZE]; +} pts_track_t; + ++typedef enum drain_state_e ++{ ++ DRAIN_NONE = 0, // Not draining ++ DRAIN_TIMEOUT, // Drain until normal timeout setup yields no frame ++ DRAIN_LAST, // Drain with long timeout last_frame in received on output expected ++ DRAIN_EOS, // Drain with long timeout EOS expected ++ DRAIN_DONE // Drained ++} drain_state_t; ++ +typedef struct DeintV4L2M2MContextShared { + void * logctx; // For logging - will be NULL when done ++ filter_type_v4l2_t filter_type; + + int fd; -+ int done; ++ int done; // fd closed - awating all refs dropped + int width; + int height; ++ ++ int drain; // EOS received (inlink status) ++ drain_state_t drain_state; ++ int64_t drain_pts; // PTS associated with inline status ++ ++ unsigned int frames_rx; ++ unsigned int frames_tx; ++ ++ // from options ++ int output_width; ++ int output_height; ++ enum AVPixelFormat output_format; ++ ++ int has_enc_stop; ++ // We expect to get exactly the same number of frames out as we put in ++ // We can drain by matching input to output ++ int one_to_one; ++ + int orig_width; + int orig_height; + atomic_uint refcount; @@ -61938,8 +62658,70 @@ index 0000000000..49fab3158d + const AVClass *class; + + DeintV4L2M2MContextShared *shared; ++ ++ char * w_expr; ++ char * h_expr; ++ char * output_format_string;; ++ ++ int force_original_aspect_ratio; ++ int force_divisible_by; ++ ++ char *colour_primaries_string; ++ char *colour_transfer_string; ++ char *colour_matrix_string; ++ int colour_range; ++ char *chroma_location_string; ++ ++ enum AVColorPrimaries colour_primaries; ++ enum AVColorTransferCharacteristic colour_transfer; ++ enum AVColorSpace colour_matrix; ++ enum AVChromaLocation chroma_location; +} DeintV4L2M2MContext; + ++ ++static inline int drain_frame_expected(const drain_state_t d) ++{ ++ return d == DRAIN_EOS || d == DRAIN_LAST; ++} ++ ++// These just list the ones we know we can cope with ++static uint32_t ++fmt_av_to_v4l2(const enum AVPixelFormat avfmt) ++{ ++ switch (avfmt) { ++ case AV_PIX_FMT_YUV420P: ++ return V4L2_PIX_FMT_YUV420; ++ case AV_PIX_FMT_NV12: ++ return V4L2_PIX_FMT_NV12; ++#if CONFIG_SAND ++ case AV_PIX_FMT_RPI4_8: ++ case AV_PIX_FMT_SAND128: ++ return V4L2_PIX_FMT_NV12_COL128; ++#endif ++ default: ++ break; ++ } ++ return 0; ++} ++ ++static enum AVPixelFormat ++fmt_v4l2_to_av(const uint32_t pixfmt) ++{ ++ switch (pixfmt) { ++ case V4L2_PIX_FMT_YUV420: ++ return AV_PIX_FMT_YUV420P; ++ case V4L2_PIX_FMT_NV12: ++ return AV_PIX_FMT_NV12; ++#if CONFIG_SAND ++ case V4L2_PIX_FMT_NV12_COL128: ++ return AV_PIX_FMT_RPI4_8; ++#endif ++ default: ++ break; ++ } ++ return AV_PIX_FMT_NONE; ++} ++ +static unsigned int pts_stats_interval(const pts_stats_t * const stats) +{ + return stats->last_interval; @@ -62057,6 +62839,13 @@ index 0000000000..49fab3158d + return 0; +} + ++// We are only ever expecting in-order frames so nothing more clever is required ++static unsigned int ++pts_track_count(const pts_track_t * const trk) ++{ ++ return (trk->n - trk->last_n) & (PTS_TRACK_SIZE - 1); ++} ++ +static struct timeval pts_track_add_frame(pts_track_t * const trk, const AVFrame * const src) +{ + const uint32_t n = pts_track_next_n(trk); @@ -62105,6 +62894,45 @@ index 0000000000..49fab3158d + return 0; +} + ++static inline uint32_t ++fmt_bpl(const struct v4l2_format * const fmt, const unsigned int plane_n) ++{ ++ return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.plane_fmt[plane_n].bytesperline : fmt->fmt.pix.bytesperline; ++} ++ ++static inline uint32_t ++fmt_height(const struct v4l2_format * const fmt) ++{ ++ return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.height : fmt->fmt.pix.height; ++} ++ ++static inline uint32_t ++fmt_width(const struct v4l2_format * const fmt) ++{ ++ return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.width : fmt->fmt.pix.width; ++} ++ ++static inline uint32_t ++fmt_pixelformat(const struct v4l2_format * const fmt) ++{ ++ return V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? fmt->fmt.pix_mp.pixelformat : fmt->fmt.pix.pixelformat; ++} ++ ++static inline uint32_t ++buf_bytesused0(const struct v4l2_buffer * const buf) ++{ ++ return V4L2_TYPE_IS_MULTIPLANAR(buf->type) ? buf->m.planes[0].bytesused : buf->bytesused; ++} ++ ++static void ++init_format(V4L2Queue * const q, const uint32_t format_type) ++{ ++ memset(&q->format, 0, sizeof(q->format)); ++ memset(&q->sel, 0, sizeof(q->sel)); ++ q->format.type = format_type; ++ q->sel.type = format_type; ++} ++ +static int deint_v4l2m2m_prepare_context(DeintV4L2M2MContextShared *ctx) +{ + struct v4l2_capability cap; @@ -62115,80 +62943,99 @@ index 0000000000..49fab3158d + if (ret < 0) + return ret; + -+ if (!(cap.capabilities & V4L2_CAP_STREAMING)) ++ if (ctx->filter_type == FILTER_V4L2_SCALE && ++ strcmp("bcm2835-codec-isp", cap.card) != 0) ++ { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Not ISP\n"); + return AVERROR(EINVAL); ++ } + -+ if (cap.capabilities & V4L2_CAP_VIDEO_M2M) { -+ ctx->capture.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; -+ ctx->output.format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; -+ -+ return 0; ++ if (!(cap.capabilities & V4L2_CAP_STREAMING)) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "No streaming\n"); ++ return AVERROR(EINVAL); + } + + if (cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) { -+ ctx->capture.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; -+ ctx->output.format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; -+ -+ return 0; ++ init_format(&ctx->capture, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); ++ init_format(&ctx->output, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); ++ } ++ else if (cap.capabilities & V4L2_CAP_VIDEO_M2M) { ++ init_format(&ctx->capture, V4L2_BUF_TYPE_VIDEO_CAPTURE); ++ init_format(&ctx->output, V4L2_BUF_TYPE_VIDEO_OUTPUT); ++ } ++ else { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Not M2M\n"); ++ return AVERROR(EINVAL); + } + -+ return AVERROR(EINVAL); ++ return 0; +} + -+static int deint_v4l2m2m_try_format(V4L2Queue *queue) ++// Just use for probe - doesn't modify q format ++static int deint_v4l2m2m_try_format(V4L2Queue *queue, const uint32_t width, const uint32_t height, const enum AVPixelFormat avfmt) +{ -+ struct v4l2_format *fmt = &queue->format; ++ struct v4l2_format fmt = {.type = queue->format.type}; + DeintV4L2M2MContextShared *ctx = queue->ctx; + int ret, field; ++ // Pick YUV to test with if not otherwise specified ++ uint32_t pixelformat = avfmt == AV_PIX_FMT_NONE ? V4L2_PIX_FMT_YUV420 : fmt_av_to_v4l2(avfmt); ++ enum AVPixelFormat r_avfmt; + -+ ret = ioctl(ctx->fd, VIDIOC_G_FMT, fmt); ++ ++ ret = ioctl(ctx->fd, VIDIOC_G_FMT, &fmt); + if (ret) + av_log(ctx->logctx, AV_LOG_ERROR, "VIDIOC_G_FMT failed: %d\n", ret); + -+ if (V4L2_TYPE_IS_OUTPUT(fmt->type)) ++ if (ctx->filter_type == FILTER_V4L2_DEINTERLACE && V4L2_TYPE_IS_OUTPUT(fmt.type)) + field = V4L2_FIELD_INTERLACED_TB; + else + field = V4L2_FIELD_NONE; + -+ if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { -+ fmt->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420; -+ fmt->fmt.pix_mp.field = field; -+ fmt->fmt.pix_mp.width = ctx->width; -+ fmt->fmt.pix_mp.height = ctx->height; ++ if (V4L2_TYPE_IS_MULTIPLANAR(fmt.type)) { ++ fmt.fmt.pix_mp.pixelformat = pixelformat; ++ fmt.fmt.pix_mp.field = field; ++ fmt.fmt.pix_mp.width = width; ++ fmt.fmt.pix_mp.height = height; + } else { -+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; -+ fmt->fmt.pix.field = field; -+ fmt->fmt.pix.width = ctx->width; -+ fmt->fmt.pix.height = ctx->height; ++ fmt.fmt.pix.pixelformat = pixelformat; ++ fmt.fmt.pix.field = field; ++ fmt.fmt.pix.width = width; ++ fmt.fmt.pix.height = height; + } + -+ av_log(ctx->logctx, AV_LOG_DEBUG, "%s: Trying format for type %d, wxh: %dx%d, fmt: %08x, size %u bpl %u pre\n", __func__, -+ fmt->type, fmt->fmt.pix_mp.width, fmt->fmt.pix_mp.height, -+ fmt->fmt.pix_mp.pixelformat, -+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage, fmt->fmt.pix_mp.plane_fmt[0].bytesperline); ++ av_log(ctx->logctx, AV_LOG_TRACE, "%s: Trying format for type %d, wxh: %dx%d, fmt: %08x, size %u bpl %u pre\n", __func__, ++ fmt.type, fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height, ++ fmt.fmt.pix_mp.pixelformat, ++ fmt.fmt.pix_mp.plane_fmt[0].sizeimage, fmt.fmt.pix_mp.plane_fmt[0].bytesperline); + -+ ret = ioctl(ctx->fd, VIDIOC_TRY_FMT, fmt); ++ ret = ioctl(ctx->fd, VIDIOC_TRY_FMT, &fmt); + if (ret) + return AVERROR(EINVAL); + -+ av_log(ctx->logctx, AV_LOG_DEBUG, "%s: Trying format for type %d, wxh: %dx%d, fmt: %08x, size %u bpl %u post\n", __func__, -+ fmt->type, fmt->fmt.pix_mp.width, fmt->fmt.pix_mp.height, -+ fmt->fmt.pix_mp.pixelformat, -+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage, fmt->fmt.pix_mp.plane_fmt[0].bytesperline); ++ av_log(ctx->logctx, AV_LOG_TRACE, "%s: Trying format for type %d, wxh: %dx%d, fmt: %08x, size %u bpl %u post\n", __func__, ++ fmt.type, fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height, ++ fmt.fmt.pix_mp.pixelformat, ++ fmt.fmt.pix_mp.plane_fmt[0].sizeimage, fmt.fmt.pix_mp.plane_fmt[0].bytesperline); + -+ if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { -+ if ((fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_YUV420 && -+ fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) || -+ fmt->fmt.pix_mp.field != field) { -+ av_log(ctx->logctx, AV_LOG_DEBUG, "format not supported for type %d\n", fmt->type); ++ r_avfmt = fmt_v4l2_to_av(fmt_pixelformat(&fmt)); ++ if (r_avfmt != avfmt && avfmt != AV_PIX_FMT_NONE) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Unable to set format %s on %s port\n", av_get_pix_fmt_name(avfmt), V4L2_TYPE_IS_CAPTURE(fmt.type) ? "dest" : "src"); ++ return AVERROR(EINVAL); ++ } ++ if (r_avfmt == AV_PIX_FMT_NONE) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "No supported format on %s port\n", V4L2_TYPE_IS_CAPTURE(fmt.type) ? "dest" : "src"); ++ return AVERROR(EINVAL); ++ } ++ ++ if (V4L2_TYPE_IS_MULTIPLANAR(fmt.type)) { ++ if (fmt.fmt.pix_mp.field != field) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "format not supported for type %d\n", fmt.type); + + return AVERROR(EINVAL); + } + } else { -+ if ((fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_YUV420 && -+ fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_NV12) || -+ fmt->fmt.pix.field != field) { -+ av_log(ctx->logctx, AV_LOG_DEBUG, "format not supported for type %d\n", fmt->type); ++ if (fmt.fmt.pix.field != field) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "format not supported for type %d\n", fmt.type); + + return AVERROR(EINVAL); + } @@ -62197,68 +63044,414 @@ index 0000000000..49fab3158d + return 0; +} + -+static int deint_v4l2m2m_set_format(V4L2Queue *queue, uint32_t pixelformat, uint32_t field, int width, int height, int pitch, int ysize) ++static int ++do_s_fmt(V4L2Queue * const q) +{ -+ struct v4l2_format *fmt = &queue->format; -+ DeintV4L2M2MContextShared *ctx = queue->ctx; ++ DeintV4L2M2MContextShared * const ctx = q->ctx; ++ const uint32_t pixelformat = fmt_pixelformat(&q->format); + int ret; + -+ struct v4l2_selection sel = { -+ .type = fmt->type, -+ .target = V4L2_TYPE_IS_OUTPUT(fmt->type) ? V4L2_SEL_TGT_CROP_BOUNDS : V4L2_SEL_TGT_COMPOSE_BOUNDS, -+ }; -+ -+ // This works for most single object 4:2:0 types -+ if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { -+ fmt->fmt.pix_mp.pixelformat = pixelformat; -+ fmt->fmt.pix_mp.field = field; -+ fmt->fmt.pix_mp.width = width; -+ fmt->fmt.pix_mp.height = ysize / pitch; -+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline = pitch; -+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage = ysize + (ysize >> 1); -+ } else { -+ fmt->fmt.pix.pixelformat = pixelformat; -+ fmt->fmt.pix.field = field; -+ fmt->fmt.pix.width = width; -+ fmt->fmt.pix.height = height; -+ fmt->fmt.pix.sizeimage = 0; -+ fmt->fmt.pix.bytesperline = 0; -+ } -+ -+ ret = ioctl(ctx->fd, VIDIOC_S_FMT, fmt); ++ ret = ioctl(ctx->fd, VIDIOC_S_FMT, &q->format); + if (ret) { + ret = AVERROR(errno); -+ av_log(ctx->logctx, AV_LOG_ERROR, "VIDIOC_S_FMT failed: %d\n", ret); ++ av_log(ctx->logctx, AV_LOG_ERROR, "VIDIOC_S_FMT failed: %s\n", av_err2str(ret)); + return ret; + } + -+ if (pixelformat != fmt->fmt.pix.pixelformat) { -+ av_log(ctx->logctx, AV_LOG_ERROR, "Format not supported: %s; S_FMT returned %s\n", av_fourcc2str(pixelformat), av_fourcc2str(fmt->fmt.pix.pixelformat)); ++ if (pixelformat != fmt_pixelformat(&q->format)) { ++ av_log(ctx->logctx, AV_LOG_ERROR, "Format not supported: %s; S_FMT returned %s\n", av_fourcc2str(pixelformat), av_fourcc2str(fmt_pixelformat(&q->format))); + return AVERROR(EINVAL); + } + -+ ret = ioctl(ctx->fd, VIDIOC_G_SELECTION, &sel); ++ q->sel.target = V4L2_TYPE_IS_OUTPUT(q->sel.type) ? V4L2_SEL_TGT_CROP : V4L2_SEL_TGT_COMPOSE, ++ q->sel.flags = V4L2_TYPE_IS_OUTPUT(q->sel.type) ? V4L2_SEL_FLAG_LE : V4L2_SEL_FLAG_GE; ++ ++ ret = ioctl(ctx->fd, VIDIOC_S_SELECTION, &q->sel); + if (ret) { + ret = AVERROR(errno); -+ av_log(ctx->logctx, AV_LOG_WARNING, "VIDIOC_G_SELECTION failed: %d\n", ret); -+ } -+ -+ sel.r.width = width; -+ sel.r.height = height; -+ sel.r.left = 0; -+ sel.r.top = 0; -+ sel.target = V4L2_TYPE_IS_OUTPUT(fmt->type) ? V4L2_SEL_TGT_CROP : V4L2_SEL_TGT_COMPOSE, -+ sel.flags = V4L2_SEL_FLAG_LE; -+ -+ ret = ioctl(ctx->fd, VIDIOC_S_SELECTION, &sel); -+ if (ret) { -+ ret = AVERROR(errno); -+ av_log(ctx->logctx, AV_LOG_WARNING, "VIDIOC_S_SELECTION failed: %d\n", ret); ++ av_log(ctx->logctx, AV_LOG_WARNING, "VIDIOC_S_SELECTION failed: %s\n", av_err2str(ret)); + } + + return 0; +} + ++static void ++set_fmt_color(struct v4l2_format *const fmt, ++ const enum AVColorPrimaries avcp, ++ const enum AVColorSpace avcs, ++ const enum AVColorTransferCharacteristic avxc) ++{ ++ enum v4l2_ycbcr_encoding ycbcr = V4L2_YCBCR_ENC_DEFAULT; ++ enum v4l2_colorspace cs = V4L2_COLORSPACE_DEFAULT; ++ enum v4l2_xfer_func xfer = V4L2_XFER_FUNC_DEFAULT; ++ ++ switch (avcp) { ++ case AVCOL_PRI_BT709: ++ cs = V4L2_COLORSPACE_REC709; ++ ycbcr = V4L2_YCBCR_ENC_709; ++ break; ++ case AVCOL_PRI_BT470M: ++ cs = V4L2_COLORSPACE_470_SYSTEM_M; ++ ycbcr = V4L2_YCBCR_ENC_601; ++ break; ++ case AVCOL_PRI_BT470BG: ++ cs = V4L2_COLORSPACE_470_SYSTEM_BG; ++ break; ++ case AVCOL_PRI_SMPTE170M: ++ cs = V4L2_COLORSPACE_SMPTE170M; ++ break; ++ case AVCOL_PRI_SMPTE240M: ++ cs = V4L2_COLORSPACE_SMPTE240M; ++ break; ++ case AVCOL_PRI_BT2020: ++ cs = V4L2_COLORSPACE_BT2020; ++ break; ++ case AVCOL_PRI_SMPTE428: ++ case AVCOL_PRI_SMPTE431: ++ case AVCOL_PRI_SMPTE432: ++ case AVCOL_PRI_EBU3213: ++ case AVCOL_PRI_RESERVED: ++ case AVCOL_PRI_FILM: ++ case AVCOL_PRI_UNSPECIFIED: ++ default: ++ break; ++ } ++ ++ switch (avcs) { ++ case AVCOL_SPC_RGB: ++ cs = V4L2_COLORSPACE_SRGB; ++ break; ++ case AVCOL_SPC_BT709: ++ cs = V4L2_COLORSPACE_REC709; ++ break; ++ case AVCOL_SPC_FCC: ++ cs = V4L2_COLORSPACE_470_SYSTEM_M; ++ break; ++ case AVCOL_SPC_BT470BG: ++ cs = V4L2_COLORSPACE_470_SYSTEM_BG; ++ break; ++ case AVCOL_SPC_SMPTE170M: ++ cs = V4L2_COLORSPACE_SMPTE170M; ++ break; ++ case AVCOL_SPC_SMPTE240M: ++ cs = V4L2_COLORSPACE_SMPTE240M; ++ break; ++ case AVCOL_SPC_BT2020_CL: ++ cs = V4L2_COLORSPACE_BT2020; ++ ycbcr = V4L2_YCBCR_ENC_BT2020_CONST_LUM; ++ break; ++ case AVCOL_SPC_BT2020_NCL: ++ cs = V4L2_COLORSPACE_BT2020; ++ break; ++ default: ++ break; ++ } ++ ++ switch (xfer) { ++ case AVCOL_TRC_BT709: ++ xfer = V4L2_XFER_FUNC_709; ++ break; ++ case AVCOL_TRC_IEC61966_2_1: ++ xfer = V4L2_XFER_FUNC_SRGB; ++ break; ++ case AVCOL_TRC_SMPTE240M: ++ xfer = V4L2_XFER_FUNC_SMPTE240M; ++ break; ++ case AVCOL_TRC_SMPTE2084: ++ xfer = V4L2_XFER_FUNC_SMPTE2084; ++ break; ++ default: ++ break; ++ } ++ ++ if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { ++ fmt->fmt.pix_mp.colorspace = cs; ++ fmt->fmt.pix_mp.ycbcr_enc = ycbcr; ++ fmt->fmt.pix_mp.xfer_func = xfer; ++ } else { ++ fmt->fmt.pix.colorspace = cs; ++ fmt->fmt.pix.ycbcr_enc = ycbcr; ++ fmt->fmt.pix.xfer_func = xfer; ++ } ++} ++ ++static void ++set_fmt_color_range(struct v4l2_format *const fmt, const enum AVColorRange avcr) ++{ ++ const enum v4l2_quantization q = ++ avcr == AVCOL_RANGE_MPEG ? V4L2_QUANTIZATION_LIM_RANGE : ++ avcr == AVCOL_RANGE_JPEG ? V4L2_QUANTIZATION_FULL_RANGE : ++ V4L2_QUANTIZATION_DEFAULT; ++ ++ if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { ++ fmt->fmt.pix_mp.quantization = q; ++ } else { ++ fmt->fmt.pix.quantization = q; ++ } ++} ++ ++static enum AVColorPrimaries get_color_primaries(const struct v4l2_format *const fmt) ++{ ++ enum v4l2_ycbcr_encoding ycbcr; ++ enum v4l2_colorspace cs; ++ ++ cs = V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? ++ fmt->fmt.pix_mp.colorspace : ++ fmt->fmt.pix.colorspace; ++ ++ ycbcr = V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? ++ fmt->fmt.pix_mp.ycbcr_enc: ++ fmt->fmt.pix.ycbcr_enc; ++ ++ switch(ycbcr) { ++ case V4L2_YCBCR_ENC_XV709: ++ case V4L2_YCBCR_ENC_709: return AVCOL_PRI_BT709; ++ case V4L2_YCBCR_ENC_XV601: ++ case V4L2_YCBCR_ENC_601:return AVCOL_PRI_BT470M; ++ default: ++ break; ++ } ++ ++ switch(cs) { ++ case V4L2_COLORSPACE_470_SYSTEM_BG: return AVCOL_PRI_BT470BG; ++ case V4L2_COLORSPACE_SMPTE170M: return AVCOL_PRI_SMPTE170M; ++ case V4L2_COLORSPACE_SMPTE240M: return AVCOL_PRI_SMPTE240M; ++ case V4L2_COLORSPACE_BT2020: return AVCOL_PRI_BT2020; ++ default: ++ break; ++ } ++ ++ return AVCOL_PRI_UNSPECIFIED; ++} ++ ++static enum AVColorSpace get_color_space(const struct v4l2_format *const fmt) ++{ ++ enum v4l2_ycbcr_encoding ycbcr; ++ enum v4l2_colorspace cs; ++ ++ cs = V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? ++ fmt->fmt.pix_mp.colorspace : ++ fmt->fmt.pix.colorspace; ++ ++ ycbcr = V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? ++ fmt->fmt.pix_mp.ycbcr_enc: ++ fmt->fmt.pix.ycbcr_enc; ++ ++ switch(cs) { ++ case V4L2_COLORSPACE_SRGB: return AVCOL_SPC_RGB; ++ case V4L2_COLORSPACE_REC709: return AVCOL_SPC_BT709; ++ case V4L2_COLORSPACE_470_SYSTEM_M: return AVCOL_SPC_FCC; ++ case V4L2_COLORSPACE_470_SYSTEM_BG: return AVCOL_SPC_BT470BG; ++ case V4L2_COLORSPACE_SMPTE170M: return AVCOL_SPC_SMPTE170M; ++ case V4L2_COLORSPACE_SMPTE240M: return AVCOL_SPC_SMPTE240M; ++ case V4L2_COLORSPACE_BT2020: ++ if (ycbcr == V4L2_YCBCR_ENC_BT2020_CONST_LUM) ++ return AVCOL_SPC_BT2020_CL; ++ else ++ return AVCOL_SPC_BT2020_NCL; ++ default: ++ break; ++ } ++ ++ return AVCOL_SPC_UNSPECIFIED; ++} ++ ++static enum AVColorTransferCharacteristic get_color_trc(const struct v4l2_format *const fmt) ++{ ++ enum v4l2_ycbcr_encoding ycbcr; ++ enum v4l2_xfer_func xfer; ++ enum v4l2_colorspace cs; ++ ++ cs = V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? ++ fmt->fmt.pix_mp.colorspace : ++ fmt->fmt.pix.colorspace; ++ ++ ycbcr = V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? ++ fmt->fmt.pix_mp.ycbcr_enc: ++ fmt->fmt.pix.ycbcr_enc; ++ ++ xfer = V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? ++ fmt->fmt.pix_mp.xfer_func: ++ fmt->fmt.pix.xfer_func; ++ ++ switch (xfer) { ++ case V4L2_XFER_FUNC_709: return AVCOL_TRC_BT709; ++ case V4L2_XFER_FUNC_SRGB: return AVCOL_TRC_IEC61966_2_1; ++ default: ++ break; ++ } ++ ++ switch (cs) { ++ case V4L2_COLORSPACE_470_SYSTEM_M: return AVCOL_TRC_GAMMA22; ++ case V4L2_COLORSPACE_470_SYSTEM_BG: return AVCOL_TRC_GAMMA28; ++ case V4L2_COLORSPACE_SMPTE170M: return AVCOL_TRC_SMPTE170M; ++ case V4L2_COLORSPACE_SMPTE240M: return AVCOL_TRC_SMPTE240M; ++ default: ++ break; ++ } ++ ++ switch (ycbcr) { ++ case V4L2_YCBCR_ENC_XV709: ++ case V4L2_YCBCR_ENC_XV601: return AVCOL_TRC_BT1361_ECG; ++ default: ++ break; ++ } ++ ++ return AVCOL_TRC_UNSPECIFIED; ++} ++ ++static enum AVColorRange get_color_range(const struct v4l2_format *const fmt) ++{ ++ enum v4l2_quantization qt; ++ ++ qt = V4L2_TYPE_IS_MULTIPLANAR(fmt->type) ? ++ fmt->fmt.pix_mp.quantization : ++ fmt->fmt.pix.quantization; ++ ++ switch (qt) { ++ case V4L2_QUANTIZATION_LIM_RANGE: return AVCOL_RANGE_MPEG; ++ case V4L2_QUANTIZATION_FULL_RANGE: return AVCOL_RANGE_JPEG; ++ default: ++ break; ++ } ++ ++ return AVCOL_RANGE_UNSPECIFIED; ++} ++ ++static int set_src_fmt(V4L2Queue * const q, const AVFrame * const frame) ++{ ++ struct v4l2_format *const format = &q->format; ++ const AVDRMFrameDescriptor *const src = (const AVDRMFrameDescriptor *)frame->data[0]; ++ ++ const uint32_t drm_fmt = src->layers[0].format; ++ // Treat INVALID as LINEAR ++ const uint64_t mod = src->objects[0].format_modifier == DRM_FORMAT_MOD_INVALID ? ++ DRM_FORMAT_MOD_LINEAR : src->objects[0].format_modifier; ++ uint32_t pix_fmt = 0; ++ uint32_t w = 0; ++ uint32_t h = 0; ++ uint32_t bpl = src->layers[0].planes[0].pitch; ++ ++ // We really don't expect multiple layers ++ // All formats that we currently cope with are single object ++ ++ if (src->nb_layers != 1 || src->nb_objects != 1) ++ return AVERROR(EINVAL); ++ ++ switch (drm_fmt) { ++ case DRM_FORMAT_YUV420: ++ if (mod == DRM_FORMAT_MOD_LINEAR) { ++ if (src->layers[0].nb_planes != 3) ++ break; ++ pix_fmt = V4L2_PIX_FMT_YUV420; ++ h = src->layers[0].planes[1].offset / bpl; ++ w = bpl; ++ } ++ break; ++ ++ case DRM_FORMAT_NV12: ++ if (mod == DRM_FORMAT_MOD_LINEAR) { ++ if (src->layers[0].nb_planes != 2) ++ break; ++ pix_fmt = V4L2_PIX_FMT_NV12; ++ h = src->layers[0].planes[1].offset / bpl; ++ w = bpl; ++ } ++#if CONFIG_SAND ++ else if (fourcc_mod_broadcom_mod(mod) == DRM_FORMAT_MOD_BROADCOM_SAND128) { ++ if (src->layers[0].nb_planes != 2) ++ break; ++ pix_fmt = V4L2_PIX_FMT_NV12_COL128; ++ w = bpl; ++ h = src->layers[0].planes[1].offset / 128; ++ bpl = fourcc_mod_broadcom_param(mod); ++ } ++#endif ++ break; ++ ++ case DRM_FORMAT_P030: ++#if CONFIG_SAND ++ if (fourcc_mod_broadcom_mod(mod) == DRM_FORMAT_MOD_BROADCOM_SAND128) { ++ if (src->layers[0].nb_planes != 2) ++ break; ++ pix_fmt = V4L2_PIX_FMT_NV12_10_COL128; ++ w = bpl / 2; // Matching lie to how we construct this ++ h = src->layers[0].planes[1].offset / 128; ++ bpl = fourcc_mod_broadcom_param(mod); ++ } ++#endif ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (!pix_fmt) ++ return AVERROR(EINVAL); ++ ++ if (V4L2_TYPE_IS_MULTIPLANAR(format->type)) { ++ struct v4l2_pix_format_mplane *const pix = &format->fmt.pix_mp; ++ ++ pix->width = w; ++ pix->height = h; ++ pix->pixelformat = pix_fmt; ++ pix->plane_fmt[0].bytesperline = bpl; ++ pix->num_planes = 1; ++ } ++ else { ++ struct v4l2_pix_format *const pix = &format->fmt.pix; ++ ++ pix->width = w; ++ pix->height = h; ++ pix->pixelformat = pix_fmt; ++ pix->bytesperline = bpl; ++ } ++ ++ set_fmt_color(format, frame->color_primaries, frame->colorspace, frame->color_trc); ++ set_fmt_color_range(format, frame->color_range); ++ ++ q->sel.r.width = frame->width - (frame->crop_left + frame->crop_right); ++ q->sel.r.height = frame->height - (frame->crop_top + frame->crop_bottom); ++ q->sel.r.left = frame->crop_left; ++ q->sel.r.top = frame->crop_top; ++ ++ return 0; ++} ++ ++ ++static int set_dst_format(DeintV4L2M2MContext * const priv, V4L2Queue *queue, uint32_t pixelformat, uint32_t field, int width, int height) ++{ ++ struct v4l2_format * const fmt = &queue->format; ++ struct v4l2_selection *const sel = &queue->sel; ++ ++ memset(&fmt->fmt, 0, sizeof(fmt->fmt)); ++ ++ // Align w/h to 16 here in case there are alignment requirements at the next ++ // stage of the filter chain (also RPi deinterlace setup is bust and this ++ // fixes it) ++ if (V4L2_TYPE_IS_MULTIPLANAR(fmt->type)) { ++ fmt->fmt.pix_mp.pixelformat = pixelformat; ++ fmt->fmt.pix_mp.field = field; ++ fmt->fmt.pix_mp.width = FFALIGN(width, 16); ++ fmt->fmt.pix_mp.height = FFALIGN(height, 16); ++ } else { ++ fmt->fmt.pix.pixelformat = pixelformat; ++ fmt->fmt.pix.field = field; ++ fmt->fmt.pix.width = FFALIGN(width, 16); ++ fmt->fmt.pix.height = FFALIGN(height, 16); ++ } ++ ++ set_fmt_color(fmt, priv->colour_primaries, priv->colour_matrix, priv->colour_transfer); ++ set_fmt_color_range(fmt, priv->colour_range); ++ ++ sel->r.width = width; ++ sel->r.height = height; ++ sel->r.left = 0; ++ sel->r.top = 0; ++ ++ return do_s_fmt(queue); ++} ++ +static int deint_v4l2m2m_probe_device(DeintV4L2M2MContextShared *ctx, char *node) +{ + int ret; @@ -62268,16 +63461,22 @@ index 0000000000..49fab3158d + return AVERROR(errno); + + ret = deint_v4l2m2m_prepare_context(ctx); -+ if (ret) ++ if (ret) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Failed to prepare context\n"); + goto fail; ++ } + -+ ret = deint_v4l2m2m_try_format(&ctx->capture); -+ if (ret) ++ ret = deint_v4l2m2m_try_format(&ctx->capture, ctx->output_width, ctx->output_height, ctx->output_format); ++ if (ret) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Failed to try dst format\n"); + goto fail; ++ } + -+ ret = deint_v4l2m2m_try_format(&ctx->output); -+ if (ret) ++ ret = deint_v4l2m2m_try_format(&ctx->output, ctx->width, ctx->height, AV_PIX_FMT_NONE); ++ if (ret) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Failed to try src format\n"); + goto fail; ++ } + + return 0; + @@ -62338,26 +63537,118 @@ index 0000000000..49fab3158d + return 0; +} + -+static int v4l2_buffer_export_drm(V4L2Buffer* avbuf, const uint32_t pixelformat) ++static void ++drm_frame_init(AVDRMFrameDescriptor * const d) ++{ ++ unsigned int i; ++ for (i = 0; i != AV_DRM_MAX_PLANES; ++i) { ++ d->objects[i].fd = -1; ++ } ++} ++ ++static void ++drm_frame_uninit(AVDRMFrameDescriptor * const d) ++{ ++ unsigned int i; ++ for (i = 0; i != d->nb_objects; ++i) { ++ if (d->objects[i].fd != -1) { ++ close(d->objects[i].fd); ++ d->objects[i].fd = -1; ++ } ++ } ++} ++ ++static void ++avbufs_delete(V4L2Buffer** ppavbufs, const unsigned int n) ++{ ++ unsigned int i; ++ V4L2Buffer* const avbufs = *ppavbufs; ++ ++ if (avbufs == NULL) ++ return; ++ *ppavbufs = NULL; ++ ++ for (i = 0; i != n; ++i) { ++ V4L2Buffer* const avbuf = avbufs + i; ++ drm_frame_uninit(&avbuf->drm_frame); ++ } ++ ++ av_free(avbufs); ++} ++ ++static int v4l2_buffer_export_drm(V4L2Queue * const q, V4L2Buffer * const avbuf) +{ + struct v4l2_exportbuffer expbuf; + int i, ret; + uint64_t mod = DRM_FORMAT_MOD_LINEAR; -+ uint32_t fmt = 0; + -+ switch (pixelformat) { -+ case V4L2_PIX_FMT_NV12: -+ fmt = DRM_FORMAT_NV12; -+ break; -+ case V4L2_PIX_FMT_YUV420: -+ fmt = DRM_FORMAT_YUV420; -+ break; -+ default: -+ return AVERROR(EINVAL); ++ AVDRMFrameDescriptor * const drm_desc = &avbuf->drm_frame; ++ AVDRMLayerDescriptor * const layer = &drm_desc->layers[0]; ++ const struct v4l2_format *const fmt = &q->format; ++ const uint32_t height = fmt_height(fmt); ++ ptrdiff_t bpl0; ++ ++ /* fill the DRM frame descriptor */ ++ drm_desc->nb_layers = 1; ++ layer->nb_planes = avbuf->num_planes; ++ ++ for (int i = 0; i < avbuf->num_planes; i++) { ++ layer->planes[i].object_index = i; ++ layer->planes[i].offset = 0; ++ layer->planes[i].pitch = fmt_bpl(fmt, i); ++ } ++ bpl0 = layer->planes[0].pitch; ++ ++ switch (fmt_pixelformat(fmt)) { ++#if CONFIG_SAND ++ case V4L2_PIX_FMT_NV12_COL128: ++ mod = DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(bpl0); ++ layer->format = V4L2_PIX_FMT_NV12; ++ ++ if (avbuf->num_planes > 1) ++ break; ++ ++ layer->nb_planes = 2; ++ layer->planes[1].object_index = 0; ++ layer->planes[1].offset = height * 128; ++ layer->planes[0].pitch = fmt_width(fmt); ++ layer->planes[1].pitch = layer->planes[0].pitch; ++ break; ++#endif ++ ++ case DRM_FORMAT_NV12: ++ layer->format = V4L2_PIX_FMT_NV12; ++ ++ if (avbuf->num_planes > 1) ++ break; ++ ++ layer->nb_planes = 2; ++ layer->planes[1].object_index = 0; ++ layer->planes[1].offset = bpl0 * height; ++ layer->planes[1].pitch = bpl0; ++ break; ++ ++ case V4L2_PIX_FMT_YUV420: ++ layer->format = DRM_FORMAT_YUV420; ++ ++ if (avbuf->num_planes > 1) ++ break; ++ ++ layer->nb_planes = 3; ++ layer->planes[1].object_index = 0; ++ layer->planes[1].offset = bpl0 * height; ++ layer->planes[1].pitch = bpl0 / 2; ++ layer->planes[2].object_index = 0; ++ layer->planes[2].offset = layer->planes[1].offset + ((bpl0 * height) / 4); ++ layer->planes[2].pitch = bpl0 / 2; ++ break; ++ ++ default: ++ drm_desc->nb_layers = 0; ++ return AVERROR(EINVAL); + } + -+ avbuf->drm_frame.layers[0].format = fmt; -+ ++ drm_desc->nb_objects = 0; + for (i = 0; i < avbuf->num_planes; i++) { + memset(&expbuf, 0, sizeof(expbuf)); + @@ -62369,19 +63660,11 @@ index 0000000000..49fab3158d + if (ret < 0) + return AVERROR(errno); + -+ avbuf->fd = expbuf.fd; -+ -+ if (V4L2_TYPE_IS_MULTIPLANAR(avbuf->buffer.type)) { -+ /* drm frame */ -+ avbuf->drm_frame.objects[i].size = avbuf->buffer.m.planes[i].length; -+ avbuf->drm_frame.objects[i].fd = expbuf.fd; -+ avbuf->drm_frame.objects[i].format_modifier = mod; -+ } else { -+ /* drm frame */ -+ avbuf->drm_frame.objects[0].size = avbuf->buffer.length; -+ avbuf->drm_frame.objects[0].fd = expbuf.fd; -+ avbuf->drm_frame.objects[0].format_modifier = mod; -+ } ++ drm_desc->objects[i].size = V4L2_TYPE_IS_MULTIPLANAR(avbuf->buffer.type) ? ++ avbuf->buffer.m.planes[i].length : avbuf->buffer.length; ++ drm_desc->objects[i].fd = expbuf.fd; ++ drm_desc->objects[i].format_modifier = mod; ++ drm_desc->nb_objects = i + 1; + } + + return 0; @@ -62392,7 +63675,7 @@ index 0000000000..49fab3158d + struct v4l2_format *fmt = &queue->format; + DeintV4L2M2MContextShared *ctx = queue->ctx; + struct v4l2_requestbuffers req; -+ int ret, i, j, multiplanar; ++ int ret, i, multiplanar; + uint32_t memory; + + memory = V4L2_TYPE_IS_OUTPUT(fmt->type) ? @@ -62421,10 +63704,9 @@ index 0000000000..49fab3158d + } + + for (i = 0; i < queue->num_buffers; i++) { -+ V4L2Buffer *buf = &queue->buffers[i]; ++ V4L2Buffer * const buf = &queue->buffers[i]; + + buf->enqueued = 0; -+ buf->fd = -1; + buf->q = queue; + + buf->buffer.type = fmt->type; @@ -62436,6 +63718,12 @@ index 0000000000..49fab3158d + buf->buffer.m.planes = buf->planes; + } + ++ drm_frame_init(&buf->drm_frame); ++ } ++ ++ for (i = 0; i < queue->num_buffers; i++) { ++ V4L2Buffer * const buf = &queue->buffers[i]; ++ + ret = ioctl(ctx->fd, VIDIOC_QUERYBUF, &buf->buffer); + if (ret < 0) { + ret = AVERROR(errno); @@ -62443,29 +63731,14 @@ index 0000000000..49fab3158d + goto fail; + } + -+ if (multiplanar) -+ buf->num_planes = buf->buffer.length; -+ else -+ buf->num_planes = 1; -+ -+ for (j = 0; j < buf->num_planes; j++) { -+ V4L2PlaneInfo *info = &buf->plane_info[j]; -+ -+ if (multiplanar) { -+ info->bytesperline = fmt->fmt.pix_mp.plane_fmt[j].bytesperline; -+ info->length = buf->buffer.m.planes[j].length; -+ } else { -+ info->bytesperline = fmt->fmt.pix.bytesperline; -+ info->length = buf->buffer.length; -+ } -+ } ++ buf->num_planes = multiplanar ? buf->buffer.length : 1; + + if (!V4L2_TYPE_IS_OUTPUT(fmt->type)) { + ret = deint_v4l2m2m_enqueue_buffer(buf); + if (ret) + goto fail; + -+ ret = v4l2_buffer_export_drm(buf, multiplanar ? fmt->fmt.pix_mp.pixelformat : fmt->fmt.pix.pixelformat); ++ ret = v4l2_buffer_export_drm(queue, buf); + if (ret) + goto fail; + } @@ -62474,12 +63747,8 @@ index 0000000000..49fab3158d + return 0; + +fail: -+ for (i = 0; i < queue->num_buffers; i++) -+ if (queue->buffers[i].fd >= 0) -+ close(queue->buffers[i].fd); -+ av_free(queue->buffers); -+ queue->buffers = NULL; -+ ++ avbufs_delete(&queue->buffers, queue->num_buffers); ++ queue->num_buffers = 0; + return ret; +} + @@ -62666,7 +63935,6 @@ index 0000000000..49fab3158d + if (atomic_fetch_sub(&ctx->refcount, 1) == 1) { + V4L2Queue *capture = &ctx->capture; + V4L2Queue *output = &ctx->output; -+ int i; + + av_log(NULL, AV_LOG_DEBUG, "%s - destroying context\n", __func__); + @@ -62675,12 +63943,7 @@ index 0000000000..49fab3158d + deint_v4l2m2m_streamoff(output); + } + -+ if (capture->buffers) -+ for (i = 0; i < capture->num_buffers; i++) { -+ capture->buffers[i].q = NULL; -+ if (capture->buffers[i].fd >= 0) -+ close(capture->buffers[i].fd); -+ } ++ avbufs_delete(&capture->buffers, capture->num_buffers); + + deint_v4l2m2m_unref_queued(output); + @@ -62712,85 +63975,37 @@ index 0000000000..49fab3158d + deint_v4l2m2m_destroy_context(ctx); +} + -+static uint8_t * v4l2_get_drm_frame(V4L2Buffer *avbuf, int height) -+{ -+ AVDRMFrameDescriptor *drm_desc = &avbuf->drm_frame; -+ AVDRMLayerDescriptor *layer; -+ -+ /* fill the DRM frame descriptor */ -+ drm_desc->nb_objects = avbuf->num_planes; -+ drm_desc->nb_layers = 1; -+ -+ layer = &drm_desc->layers[0]; -+ layer->nb_planes = avbuf->num_planes; -+ -+ for (int i = 0; i < avbuf->num_planes; i++) { -+ layer->planes[i].object_index = i; -+ layer->planes[i].offset = 0; -+ layer->planes[i].pitch = avbuf->plane_info[i].bytesperline; -+ } -+ -+ switch (layer->format) { -+ case DRM_FORMAT_YUYV: -+ layer->nb_planes = 1; -+ break; -+ -+ case DRM_FORMAT_NV12: -+ case DRM_FORMAT_NV21: -+ if (avbuf->num_planes > 1) -+ break; -+ -+ layer->nb_planes = 2; -+ -+ layer->planes[1].object_index = 0; -+ layer->planes[1].offset = avbuf->plane_info[0].bytesperline * -+ height; -+ layer->planes[1].pitch = avbuf->plane_info[0].bytesperline; -+ break; -+ -+ case DRM_FORMAT_YUV420: -+ if (avbuf->num_planes > 1) -+ break; -+ -+ layer->nb_planes = 3; -+ -+ layer->planes[1].object_index = 0; -+ layer->planes[1].offset = avbuf->plane_info[0].bytesperline * -+ height; -+ layer->planes[1].pitch = avbuf->plane_info[0].bytesperline >> 1; -+ -+ layer->planes[2].object_index = 0; -+ layer->planes[2].offset = layer->planes[1].offset + -+ ((avbuf->plane_info[0].bytesperline * -+ height) >> 2); -+ layer->planes[2].pitch = avbuf->plane_info[0].bytesperline >> 1; -+ break; -+ -+ default: -+ drm_desc->nb_layers = 0; -+ break; -+ } -+ -+ return (uint8_t *) drm_desc; -+} -+ +// timeout in ms +static int deint_v4l2m2m_dequeue_frame(V4L2Queue *queue, AVFrame* frame, int timeout) +{ + DeintV4L2M2MContextShared *ctx = queue->ctx; + V4L2Buffer* avbuf; ++ enum AVColorPrimaries color_primaries; ++ enum AVColorSpace colorspace; ++ enum AVColorTransferCharacteristic color_trc; ++ enum AVColorRange color_range; + + av_log(ctx->logctx, AV_LOG_TRACE, "<<< %s\n", __func__); + ++ if (queue->eos) { ++ av_log(ctx->logctx, AV_LOG_TRACE, ">>> %s: EOS\n", __func__); ++ return AVERROR_EOF; ++ } ++ + avbuf = deint_v4l2m2m_dequeue_buffer(queue, timeout); + if (!avbuf) { + av_log(ctx->logctx, AV_LOG_DEBUG, "%s: No buffer to dequeue (timeout=%d)\n", __func__, timeout); + return AVERROR(EAGAIN); + } + ++ if (V4L2_TYPE_IS_CAPTURE(avbuf->buffer.type)) { ++ if ((avbuf->buffer.flags & V4L2_BUF_FLAG_LAST) != 0) ++ queue->eos = 1; ++ if (buf_bytesused0(&avbuf->buffer) == 0) ++ return queue->eos ? AVERROR_EOF : AVERROR(EINVAL); ++ } ++ + // Fill in PTS and anciliary info from src frame -+ // we will want to overwrite some fields as only the pts/dts -+ // fields are updated with new timing in this fn + pts_track_get_frame(&ctx->track, avbuf->buffer.timestamp, frame); + + frame->buf[0] = av_buffer_create((uint8_t *) &avbuf->drm_frame, @@ -62803,18 +64018,36 @@ index 0000000000..49fab3158d + + atomic_fetch_add(&ctx->refcount, 1); + -+ frame->data[0] = (uint8_t *)v4l2_get_drm_frame(avbuf, ctx->orig_height); ++ frame->data[0] = (uint8_t *)&avbuf->drm_frame; + frame->format = AV_PIX_FMT_DRM_PRIME; + if (ctx->hw_frames_ctx) + frame->hw_frames_ctx = av_buffer_ref(ctx->hw_frames_ctx); -+ frame->height = ctx->height; -+ frame->width = ctx->width; ++ frame->height = ctx->output_height; ++ frame->width = ctx->output_width; + -+ // Not interlaced now -+ frame->interlaced_frame = 0; -+ frame->top_field_first = 0; -+ // Pkt duration halved -+ frame->pkt_duration /= 2; ++ color_primaries = get_color_primaries(&ctx->capture.format); ++ colorspace = get_color_space(&ctx->capture.format); ++ color_trc = get_color_trc(&ctx->capture.format); ++ color_range = get_color_range(&ctx->capture.format); ++ ++ // If the color parameters are unspecified by V4L2 then leave alone as they ++ // will have been copied from src ++ if (color_primaries != AVCOL_PRI_UNSPECIFIED) ++ frame->color_primaries = color_primaries; ++ if (colorspace != AVCOL_SPC_UNSPECIFIED) ++ frame->colorspace = colorspace; ++ if (color_trc != AVCOL_TRC_UNSPECIFIED) ++ frame->color_trc = color_trc; ++ if (color_range != AVCOL_RANGE_UNSPECIFIED) ++ frame->color_range = color_range; ++ ++ if (ctx->filter_type == FILTER_V4L2_DEINTERLACE) { ++ // Not interlaced now ++ frame->interlaced_frame = 0; // *** Fill in from dst buffer? ++ frame->top_field_first = 0; ++ // Pkt duration halved ++ frame->pkt_duration /= 2; ++ } + + if (avbuf->buffer.flags & V4L2_BUF_FLAG_ERROR) { + av_log(ctx->logctx, AV_LOG_ERROR, "driver decode error\n"); @@ -62836,14 +64069,36 @@ index 0000000000..49fab3158d + ctx->height = avctx->inputs[0]->h; + ctx->width = avctx->inputs[0]->w; + -+ av_log(priv, AV_LOG_DEBUG, "%s: %dx%d\n", __func__, ctx->width, ctx->height); ++ if (ctx->filter_type == FILTER_V4L2_SCALE) { ++ if ((ret = ff_scale_eval_dimensions(priv, ++ priv->w_expr, priv->h_expr, ++ inlink, outlink, ++ &ctx->output_width, &ctx->output_height)) < 0) ++ return ret; ++ ++ ff_scale_adjust_dimensions(inlink, &ctx->output_width, &ctx->output_height, ++ priv->force_original_aspect_ratio, priv->force_divisible_by); ++ } ++ else { ++ ctx->output_width = ctx->width; ++ ctx->output_height = ctx->height; ++ } ++ ++ av_log(priv, AV_LOG_DEBUG, "%s: %dx%d->%dx%d FR: %d/%d->%d/%d\n", __func__, ++ ctx->width, ctx->height, ctx->output_width, ctx->output_height, ++ inlink->frame_rate.num, inlink->frame_rate.den, outlink->frame_rate.num, outlink->frame_rate.den); + + outlink->time_base = inlink->time_base; -+ outlink->w = inlink->w; -+ outlink->h = inlink->h; -+ outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; ++ outlink->w = ctx->output_width; ++ outlink->h = ctx->output_height; + outlink->format = inlink->format; -+ outlink->frame_rate = (AVRational) {1, 0}; // Deny knowledge of frame rate ++ if (ctx->filter_type == FILTER_V4L2_DEINTERLACE && inlink->frame_rate.den != 0) ++ outlink->frame_rate = (AVRational){inlink->frame_rate.num * 2, inlink->frame_rate.den}; ++ ++ if (inlink->sample_aspect_ratio.num) ++ outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h * inlink->w, outlink->w * inlink->h}, inlink->sample_aspect_ratio); ++ else ++ outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; + + ret = deint_v4l2m2m_find_device(ctx); + if (ret) @@ -62861,7 +64116,7 @@ index 0000000000..49fab3158d +{ + static const enum AVPixelFormat pixel_formats[] = { + AV_PIX_FMT_DRM_PRIME, -+ AV_PIX_FMT_YUV420P, ++// AV_PIX_FMT_YUV420P, + AV_PIX_FMT_NONE, + }; + @@ -62870,18 +64125,22 @@ index 0000000000..49fab3158d + +static uint32_t desc_pixelformat(const AVDRMFrameDescriptor * const drm_desc) +{ -+ const int is_linear = (drm_desc->objects[0].format_modifier == DRM_FORMAT_MOD_LINEAR || -+ drm_desc->objects[0].format_modifier == DRM_FORMAT_MOD_INVALID); ++ const uint64_t mod = drm_desc->objects[0].format_modifier; ++ const int is_linear = (mod == DRM_FORMAT_MOD_LINEAR || mod == DRM_FORMAT_MOD_INVALID); ++ ++ // Only currently support single object things ++ if (drm_desc->nb_objects != 1) ++ return 0; + + switch (drm_desc->layers[0].format) { + case DRM_FORMAT_YUV420: -+ if (is_linear) -+ return drm_desc->nb_objects == 1 ? V4L2_PIX_FMT_YUV420 : 0; -+ break; ++ return is_linear ? V4L2_PIX_FMT_YUV420 : 0; + case DRM_FORMAT_NV12: -+ if (is_linear) -+ return drm_desc->nb_objects == 1 ? V4L2_PIX_FMT_NV12 : 0; -+ break; ++ return is_linear ? V4L2_PIX_FMT_NV12 : ++#if CONFIG_SAND ++ fourcc_mod_broadcom_mod(mod) == DRM_FORMAT_MOD_BROADCOM_SAND128 ? V4L2_PIX_FMT_NV12_COL128 : ++#endif ++ 0; + default: + break; + } @@ -62897,14 +64156,14 @@ index 0000000000..49fab3158d + V4L2Queue *output = &ctx->output; + int ret; + -+ av_log(priv, AV_LOG_DEBUG, "<<< %s: input pts: %"PRId64" (%"PRId64") field :%d interlaced: %d aspect:%d/%d\n", -+ __func__, in->pts, AV_NOPTS_VALUE, in->top_field_first, in->interlaced_frame, in->sample_aspect_ratio.num, in->sample_aspect_ratio.den); ++ av_log(priv, AV_LOG_DEBUG, "<<< %s: input pts: %"PRId64" dts: %"PRId64" field :%d interlaced: %d aspect:%d/%d\n", ++ __func__, in->pts, in->pkt_dts, in->top_field_first, in->interlaced_frame, in->sample_aspect_ratio.num, in->sample_aspect_ratio.den); + av_log(priv, AV_LOG_DEBUG, "--- %s: in status in %d/ot %d; out status in %d/out %d\n", __func__, + avctx->inputs[0]->status_in, avctx->inputs[0]->status_out, avctx->outputs[0]->status_in, avctx->outputs[0]->status_out); + + if (ctx->field_order == V4L2_FIELD_ANY) { + const AVDRMFrameDescriptor * const drm_desc = (AVDRMFrameDescriptor *)in->data[0]; -+ const uint32_t pixelformat = desc_pixelformat(drm_desc); ++ uint32_t pixelformat = desc_pixelformat(drm_desc); + + if (pixelformat == 0) { + av_log(avctx, AV_LOG_ERROR, "Unsupported DRM format %s in %d objects, modifier %#" PRIx64 "\n", @@ -62919,35 +64178,69 @@ index 0000000000..49fab3158d + av_log(priv, AV_LOG_DEBUG, "%s: %dx%d (%td,%td)\n", __func__, ctx->width, ctx->height, + drm_desc->layers[0].planes[0].pitch, drm_desc->layers[0].planes[1].offset); + -+ ret = deint_v4l2m2m_set_format(output, pixelformat, ctx->field_order, ctx->width, ctx->height, ctx->orig_width, drm_desc->layers[0].planes[1].offset); -+ if (ret) ++ if ((ret = set_src_fmt(output, in)) != 0) { ++ av_log(avctx, AV_LOG_WARNING, "Unknown input DRM format: %s mod: %#" PRIx64 "\n", ++ av_fourcc2str(drm_desc->layers[0].format), drm_desc->objects[0].format_modifier); + return ret; ++ } + -+ ret = deint_v4l2m2m_set_format(capture, pixelformat, V4L2_FIELD_NONE, ctx->width, ctx->height, ctx->orig_width, drm_desc->layers[0].planes[1].offset); -+ if (ret) ++ ret = do_s_fmt(output); ++ if (ret) { ++ av_log(avctx, AV_LOG_WARNING, "Failed to set source format\n"); + return ret; ++ } ++ ++ if (ctx->output_format != AV_PIX_FMT_NONE) ++ pixelformat = fmt_av_to_v4l2(ctx->output_format); ++ ret = set_dst_format(priv, capture, pixelformat, V4L2_FIELD_NONE, ctx->output_width, ctx->output_height); ++ if (ret) { ++ av_log(avctx, AV_LOG_WARNING, "Failed to set destination format\n"); ++ return ret; ++ } + + ret = deint_v4l2m2m_allocate_buffers(capture); -+ if (ret) ++ if (ret) { ++ av_log(avctx, AV_LOG_WARNING, "Failed to allocate destination buffers\n"); + return ret; ++ } + + ret = deint_v4l2m2m_streamon(capture); -+ if (ret) ++ if (ret) { ++ av_log(avctx, AV_LOG_WARNING, "Failed set destination streamon: %s\n", av_err2str(ret)); + return ret; ++ } + + ret = deint_v4l2m2m_allocate_buffers(output); -+ if (ret) ++ if (ret) { ++ av_log(avctx, AV_LOG_WARNING, "Failed to allocate src buffers\n"); + return ret; ++ } + + ret = deint_v4l2m2m_streamon(output); -+ if (ret) ++ if (ret) { ++ av_log(avctx, AV_LOG_WARNING, "Failed set src streamon: %s\n", av_err2str(ret)); + return ret; ++ } + + if (in->top_field_first) + ctx->field_order = V4L2_FIELD_INTERLACED_TB; + else + ctx->field_order = V4L2_FIELD_INTERLACED_BT; + ++ { ++ struct v4l2_encoder_cmd ecmd = { ++ .cmd = V4L2_ENC_CMD_STOP ++ }; ++ ctx->has_enc_stop = 0; ++ if (ioctl(ctx->fd, VIDIOC_TRY_ENCODER_CMD, &ecmd) == 0) { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Test encode stop succeeded\n"); ++ ctx->has_enc_stop = 1; ++ } ++ else { ++ av_log(ctx->logctx, AV_LOG_DEBUG, "Test encode stop fail: %s\n", av_err2str(AVERROR(errno))); ++ } ++ ++ } + } + + ret = deint_v4l2m2m_enqueue_frame(output, in); @@ -62956,6 +64249,41 @@ index 0000000000..49fab3158d + return ret; +} + ++static int ++ack_inlink(AVFilterContext * const avctx, DeintV4L2M2MContextShared *const s, ++ AVFilterLink * const inlink) ++{ ++ int instatus; ++ int64_t inpts; ++ ++ if (ff_inlink_acknowledge_status(inlink, &instatus, &inpts) <= 0) ++ return 0; ++ ++ s->drain = instatus; ++ s->drain_pts = inpts; ++ s->drain_state = DRAIN_TIMEOUT; ++ ++ if (s->field_order == V4L2_FIELD_ANY) { // Not yet started ++ s->drain_state = DRAIN_DONE; ++ } ++ else if (s->one_to_one) { ++ s->drain_state = DRAIN_LAST; ++ } ++ else if (s->has_enc_stop) { ++ struct v4l2_encoder_cmd ecmd = { ++ .cmd = V4L2_ENC_CMD_STOP ++ }; ++ if (ioctl(s->fd, VIDIOC_ENCODER_CMD, &ecmd) == 0) { ++ av_log(avctx->priv, AV_LOG_DEBUG, "Do Encode stop\n"); ++ s->drain_state = DRAIN_EOS; ++ } ++ else { ++ av_log(avctx->priv, AV_LOG_WARNING, "Encode stop fail: %s\n", av_err2str(AVERROR(errno))); ++ } ++ } ++ return 1; ++} ++ +static int deint_v4l2m2m_activate(AVFilterContext *avctx) +{ + DeintV4L2M2MContext * const priv = avctx->priv; @@ -62964,25 +64292,19 @@ index 0000000000..49fab3158d + AVFilterLink * const inlink = avctx->inputs[0]; + int n = 0; + int cn = 99; -+ int instatus = 0; -+ int64_t inpts = 0; + int did_something = 0; + + av_log(priv, AV_LOG_TRACE, "<<< %s\n", __func__); + + FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, avctx); + -+ ff_inlink_acknowledge_status(inlink, &instatus, &inpts); ++ ack_inlink(avctx, s, inlink); + -+ if (!ff_outlink_frame_wanted(outlink)) { -+ av_log(priv, AV_LOG_TRACE, "%s: Not wanted out\n", __func__); -+ } -+ else if (s->field_order != V4L2_FIELD_ANY) // Can't DQ if no setup! ++ if (s->field_order != V4L2_FIELD_ANY) // Can't DQ if no setup! + { + AVFrame * frame = av_frame_alloc(); + int rv; + -+again: + recycle_q(&s->output); + n = count_enqueued(&s->output); + @@ -62991,10 +64313,21 @@ index 0000000000..49fab3158d + return AVERROR(ENOMEM); + } + -+ rv = deint_v4l2m2m_dequeue_frame(&s->capture, frame, n > 4 ? 300 : 0); ++ rv = deint_v4l2m2m_dequeue_frame(&s->capture, frame, ++ drain_frame_expected(s->drain_state) || n > 4 ? 300 : 0); + if (rv != 0) { + av_frame_free(&frame); -+ if (rv != AVERROR(EAGAIN)) { ++ if (rv == AVERROR_EOF) { ++ av_log(priv, AV_LOG_DEBUG, "%s: --- DQ EOF\n", __func__); ++ s->drain_state = DRAIN_DONE; ++ } ++ else if (rv == AVERROR(EAGAIN)) { ++ if (s->drain_state != DRAIN_NONE) { ++ av_log(priv, AV_LOG_DEBUG, "%s: --- DQ empty - drain done\n", __func__); ++ s->drain_state = DRAIN_DONE; ++ } ++ } ++ else { + av_log(priv, AV_LOG_ERROR, ">>> %s: DQ fail: %s\n", __func__, av_err2str(rv)); + return rv; + } @@ -63004,29 +64337,30 @@ index 0000000000..49fab3158d + // frame is always consumed by filter_frame - even on error despite + // a somewhat confusing comment in the header + rv = ff_filter_frame(outlink, frame); -+ -+ if (instatus != 0) { -+ av_log(priv, AV_LOG_TRACE, "%s: eof loop\n", __func__); -+ goto again; -+ } ++ ++s->frames_tx; + + av_log(priv, AV_LOG_TRACE, "%s: Filtered: %s\n", __func__, av_err2str(rv)); + did_something = 1; ++ ++ if (s->drain_state != DRAIN_NONE && pts_track_count(&s->track) == 0) { ++ av_log(priv, AV_LOG_DEBUG, "%s: --- DQ last - drain done\n", __func__); ++ s->drain_state = DRAIN_DONE; ++ } + } + + cn = count_enqueued(&s->capture); + } + -+ if (instatus != 0) { -+ ff_outlink_set_status(outlink, instatus, inpts); -+ av_log(priv, AV_LOG_TRACE, ">>> %s: Status done: %s\n", __func__, av_err2str(instatus)); ++ if (s->drain_state == DRAIN_DONE) { ++ ff_outlink_set_status(outlink, s->drain, s->drain_pts); ++ av_log(priv, AV_LOG_TRACE, ">>> %s: Status done: %s\n", __func__, av_err2str(s->drain)); + return 0; + } + + recycle_q(&s->output); + n = count_enqueued(&s->output); + -+ while (n < 6) { ++ while (n < 6 && !s->drain) { + AVFrame * frame; + int rv; + @@ -63037,8 +64371,13 @@ index 0000000000..49fab3158d + + if (frame == NULL) { + av_log(priv, AV_LOG_TRACE, "%s: No frame\n", __func__); ++ if (!ack_inlink(avctx, s, inlink)) { ++ ff_inlink_request_frame(inlink); ++ av_log(priv, AV_LOG_TRACE, "%s: req frame\n", __func__); ++ } + break; + } ++ ++s->frames_rx; + + rv = deint_v4l2m2m_filter_frame(inlink, frame); + av_frame_free(&frame); @@ -63047,16 +64386,11 @@ index 0000000000..49fab3158d + return rv; + + av_log(priv, AV_LOG_TRACE, "%s: Q frame\n", __func__); ++ did_something = 1; + ++n; + } + -+ if (n < 6) { -+ ff_inlink_request_frame(inlink); -+ did_something = 1; -+ av_log(priv, AV_LOG_TRACE, "%s: req frame\n", __func__); -+ } -+ -+ if (n > 4 && ff_outlink_frame_wanted(outlink)) { ++ if ((n > 4 || s->drain) && ff_outlink_frame_wanted(outlink)) { + ff_filter_set_ready(avctx, 1); + did_something = 1; + av_log(priv, AV_LOG_TRACE, "%s: ready\n", __func__); @@ -63066,7 +64400,7 @@ index 0000000000..49fab3158d + return did_something ? 0 : FFERROR_NOT_READY; +} + -+static av_cold int deint_v4l2m2m_init(AVFilterContext *avctx) ++static av_cold int common_v4l2m2m_init(AVFilterContext * const avctx, const filter_type_v4l2_t filter_type) +{ + DeintV4L2M2MContext * const priv = avctx->priv; + DeintV4L2M2MContextShared * const ctx = av_mallocz(sizeof(DeintV4L2M2MContextShared)); @@ -63077,11 +64411,14 @@ index 0000000000..49fab3158d + } + priv->shared = ctx; + ctx->logctx = priv; ++ ctx->filter_type = filter_type; + ctx->fd = -1; + ctx->output.ctx = ctx; + ctx->output.num_buffers = 8; ++ ctx->output.name = "OUTPUT"; + ctx->capture.ctx = ctx; + ctx->capture.num_buffers = 12; ++ ctx->capture.name = "CAPTURE"; + ctx->done = 0; + ctx->field_order = V4L2_FIELD_ANY; + @@ -63089,6 +64426,60 @@ index 0000000000..49fab3158d + + atomic_init(&ctx->refcount, 1); + ++ if (priv->output_format_string) { ++ ctx->output_format = av_get_pix_fmt(priv->output_format_string); ++ if (ctx->output_format == AV_PIX_FMT_NONE) { ++ av_log(avctx, AV_LOG_ERROR, "Invalid ffmpeg output format '%s'.\n", priv->output_format_string); ++ return AVERROR(EINVAL); ++ } ++ if (fmt_av_to_v4l2(ctx->output_format) == 0) { ++ av_log(avctx, AV_LOG_ERROR, "Unsupported output format for V4L2: %s.\n", av_get_pix_fmt_name(ctx->output_format)); ++ return AVERROR(EINVAL); ++ } ++ } else { ++ // Use the input format once that is configured. ++ ctx->output_format = AV_PIX_FMT_NONE; ++ } ++ ++#define STRING_OPTION(var_name, func_name, default_value) do { \ ++ if (priv->var_name ## _string) { \ ++ int var = av_ ## func_name ## _from_name(priv->var_name ## _string); \ ++ if (var < 0) { \ ++ av_log(avctx, AV_LOG_ERROR, "Invalid %s.\n", #var_name); \ ++ return AVERROR(EINVAL); \ ++ } \ ++ priv->var_name = var; \ ++ } else { \ ++ priv->var_name = default_value; \ ++ } \ ++ } while (0) ++ ++ STRING_OPTION(colour_primaries, color_primaries, AVCOL_PRI_UNSPECIFIED); ++ STRING_OPTION(colour_transfer, color_transfer, AVCOL_TRC_UNSPECIFIED); ++ STRING_OPTION(colour_matrix, color_space, AVCOL_SPC_UNSPECIFIED); ++ STRING_OPTION(chroma_location, chroma_location, AVCHROMA_LOC_UNSPECIFIED); ++ ++ return 0; ++} ++ ++static av_cold int deint_v4l2m2m_init(AVFilterContext *avctx) ++{ ++ return common_v4l2m2m_init(avctx, FILTER_V4L2_DEINTERLACE); ++} ++ ++static av_cold int scale_v4l2m2m_init(AVFilterContext *avctx) ++{ ++ int rv; ++ DeintV4L2M2MContext * priv; ++ DeintV4L2M2MContextShared * ctx; ++ ++ if ((rv = common_v4l2m2m_init(avctx, FILTER_V4L2_SCALE)) != 0) ++ return rv; ++ ++ priv = avctx->priv; ++ ctx = priv->shared; ++ ++ ctx->one_to_one = 1; + return 0; +} + @@ -63097,6 +64488,8 @@ index 0000000000..49fab3158d + DeintV4L2M2MContext *priv = avctx->priv; + DeintV4L2M2MContextShared *ctx = priv->shared; + ++ av_log(priv, AV_LOG_VERBOSE, "Frames Rx: %u, Frames Tx: %u\n", ++ ctx->frames_rx, ctx->frames_tx); + ctx->done = 1; + ctx->logctx = NULL; // Log to NULL works, log to missing crashes + pts_track_uninit(&ctx->track); @@ -63109,6 +64502,51 @@ index 0000000000..49fab3158d + +AVFILTER_DEFINE_CLASS(deinterlace_v4l2m2m); + ++#define OFFSET(x) offsetof(DeintV4L2M2MContext, x) ++#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM) ++ ++static const AVOption scale_v4l2m2m_options[] = { ++ { "w", "Output video width", ++ OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS }, ++ { "h", "Output video height", ++ OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS }, ++ { "format", "Output video format (software format of hardware frames)", ++ OFFSET(output_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS }, ++ // These colour properties match the ones of the same name in vf_scale. ++ { "out_color_matrix", "Output colour matrix coefficient set", ++ OFFSET(colour_matrix_string), AV_OPT_TYPE_STRING, { .str = NULL }, .flags = FLAGS }, ++ { "out_range", "Output colour range", ++ OFFSET(colour_range), AV_OPT_TYPE_INT, { .i64 = AVCOL_RANGE_UNSPECIFIED }, ++ AVCOL_RANGE_UNSPECIFIED, AVCOL_RANGE_JPEG, FLAGS, "range" }, ++ { "full", "Full range", ++ 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, ++ { "limited", "Limited range", ++ 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, ++ { "jpeg", "Full range", ++ 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, ++ { "mpeg", "Limited range", ++ 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, ++ { "tv", "Limited range", ++ 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_MPEG }, 0, 0, FLAGS, "range" }, ++ { "pc", "Full range", ++ 0, AV_OPT_TYPE_CONST, { .i64 = AVCOL_RANGE_JPEG }, 0, 0, FLAGS, "range" }, ++ // These colour properties match the ones in the VAAPI scaler ++ { "out_color_primaries", "Output colour primaries", ++ OFFSET(colour_primaries_string), AV_OPT_TYPE_STRING, ++ { .str = NULL }, .flags = FLAGS }, ++ { "out_color_transfer", "Output colour transfer characteristics", ++ OFFSET(colour_transfer_string), AV_OPT_TYPE_STRING, ++ { .str = NULL }, .flags = FLAGS }, ++ { "out_chroma_location", "Output chroma sample location", ++ OFFSET(chroma_location_string), AV_OPT_TYPE_STRING, ++ { .str = NULL }, .flags = FLAGS }, ++ { "force_original_aspect_ratio", "decrease or increase w/h if necessary to keep the original AR", OFFSET(force_original_aspect_ratio), AV_OPT_TYPE_INT, { .i64 = 0}, 0, 2, FLAGS, "force_oar" }, ++ { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1}, 1, 256, FLAGS }, ++ { NULL }, ++}; ++ ++AVFILTER_DEFINE_CLASS(scale_v4l2m2m); ++ +static const AVFilterPad deint_v4l2m2m_inputs[] = { + { + .name = "default", @@ -63138,6 +64576,20 @@ index 0000000000..49fab3158d + .priv_class = &deinterlace_v4l2m2m_class, + .activate = deint_v4l2m2m_activate, +}; ++ ++AVFilter ff_vf_scale_v4l2m2m = { ++ .name = "scale_v4l2m2m", ++ .description = NULL_IF_CONFIG_SMALL("V4L2 M2M scaler"), ++ .priv_size = sizeof(DeintV4L2M2MContext), ++ .init = &scale_v4l2m2m_init, ++ .uninit = &deint_v4l2m2m_uninit, ++ .query_formats = &deint_v4l2m2m_query_formats, ++ .inputs = deint_v4l2m2m_inputs, ++ .outputs = deint_v4l2m2m_outputs, ++ .priv_class = &scale_v4l2m2m_class, ++ .activate = deint_v4l2m2m_activate, ++}; ++ diff --git a/libavfilter/vf_unsand.c b/libavfilter/vf_unsand.c new file mode 100644 index 0000000000..61c03a385c @@ -63373,11 +64825,102 @@ index 0000000000..61c03a385c + .outputs = avfilter_vf_unsand_outputs, +}; + +diff --git a/libavformat/matroskaenc.c b/libavformat/matroskaenc.c +index b4284a8778..692265593c 100644 +--- a/libavformat/matroskaenc.c ++++ b/libavformat/matroskaenc.c +@@ -58,6 +58,9 @@ + * Info, Tracks, Chapters, Attachments, Tags (potentially twice) and Cues */ + #define MAX_SEEKHEAD_ENTRIES 7 + ++/* Reserved size for H264 headers if not extant at init time */ ++#define MAX_H264_HEADER_SIZE 1024 ++ + #define IS_SEEKABLE(pb, mkv) (((pb)->seekable & AVIO_SEEKABLE_NORMAL) && \ + !(mkv)->is_live) + +@@ -721,8 +724,12 @@ static int mkv_write_native_codecprivate(AVFormatContext *s, AVIOContext *pb, + case AV_CODEC_ID_WAVPACK: + return put_wv_codecpriv(dyn_cp, par); + case AV_CODEC_ID_H264: +- return ff_isom_write_avcc(dyn_cp, par->extradata, +- par->extradata_size); ++ if (par->extradata_size) ++ return ff_isom_write_avcc(dyn_cp, par->extradata, ++ par->extradata_size); ++ else ++ put_ebml_void(pb, MAX_H264_HEADER_SIZE); ++ break; + case AV_CODEC_ID_HEVC: + return ff_isom_write_hvcc(dyn_cp, par->extradata, + par->extradata_size, 0); +@@ -2259,7 +2266,9 @@ static int mkv_check_new_extra_data(AVFormatContext *s, const AVPacket *pkt) + break; + // FIXME: Remove the following once libaom starts propagating extradata during init() + // See https://bugs.chromium.org/p/aomedia/issues/detail?id=2012 ++ // H264 V4L2 has a similar issue + case AV_CODEC_ID_AV1: ++ case AV_CODEC_ID_H264: + if (side_data_size && mkv->track.bc && !par->extradata_size) { + AVIOContext *dyn_cp; + uint8_t *codecpriv; +@@ -2267,7 +2276,10 @@ static int mkv_check_new_extra_data(AVFormatContext *s, const AVPacket *pkt) + ret = avio_open_dyn_buf(&dyn_cp); + if (ret < 0) + return ret; +- ff_isom_write_av1c(dyn_cp, side_data, side_data_size); ++ if (par->codec_id == AV_CODEC_ID_H264) ++ ff_isom_write_avcc(dyn_cp, side_data, side_data_size); ++ else ++ ff_isom_write_av1c(dyn_cp, side_data, side_data_size); + codecpriv_size = avio_get_dyn_buf(dyn_cp, &codecpriv); + if ((ret = dyn_cp->error) < 0 || + !codecpriv_size && (ret = AVERROR_INVALIDDATA)) { +@@ -2275,8 +2287,25 @@ static int mkv_check_new_extra_data(AVFormatContext *s, const AVPacket *pkt) + return ret; + } + avio_seek(mkv->track.bc, track->codecpriv_offset, SEEK_SET); +- // Do not write the OBUs as we don't have space saved for them +- put_ebml_binary(mkv->track.bc, MATROSKA_ID_CODECPRIVATE, codecpriv, 4); ++ if (par->codec_id == AV_CODEC_ID_H264) { ++ int filler; ++ // Up to 6 bytes for header and the filler must be at least 2 ++ if (codecpriv_size > MAX_H264_HEADER_SIZE - 8) { ++ av_log(s, AV_LOG_ERROR, "H264 header size %d > %d bytes\n", codecpriv_size, MAX_H264_HEADER_SIZE - 8); ++ return AVERROR_INVALIDDATA; ++ } ++ put_ebml_binary(mkv->track.bc, MATROSKA_ID_CODECPRIVATE, codecpriv, codecpriv_size); ++ filler = MAX_H264_HEADER_SIZE - (avio_tell(mkv->track.bc) - track->codecpriv_offset); ++ if (filler < 2) { ++ av_log(s, AV_LOG_ERROR, "Unexpected SPS/PPS filler length: %d\n", filler); ++ return AVERROR_BUG; ++ } ++ put_ebml_void(mkv->track.bc, filler); ++ } ++ else { ++ // Do not write the OBUs as we don't have space saved for them ++ put_ebml_binary(mkv->track.bc, MATROSKA_ID_CODECPRIVATE, codecpriv, 4); ++ } + ffio_free_dyn_buf(&dyn_cp); + ret = ff_alloc_extradata(par, side_data_size); + if (ret < 0) +diff --git a/libavformat/movenc.c b/libavformat/movenc.c +index 8a06de2fd2..9f83c4da2a 100644 +--- a/libavformat/movenc.c ++++ b/libavformat/movenc.c +@@ -5926,6 +5926,7 @@ static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt) + if (trk->par->codec_id == AV_CODEC_ID_MP4ALS || + trk->par->codec_id == AV_CODEC_ID_AAC || + trk->par->codec_id == AV_CODEC_ID_AV1 || ++ trk->par->codec_id == AV_CODEC_ID_H264 || + trk->par->codec_id == AV_CODEC_ID_FLAC) { + buffer_size_t side_size; + uint8_t *side = av_packet_get_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); diff --git a/libavformat/utils.c b/libavformat/utils.c -index 1384b56771..27479e3c40 100644 +index b2d011a0db..9bc842aa75 100644 --- a/libavformat/utils.c +++ b/libavformat/utils.c -@@ -3011,6 +3011,40 @@ static int has_codec_parameters(AVStream *st, const char **errmsg_ptr) +@@ -3013,6 +3013,40 @@ static int has_codec_parameters(AVStream *st, const char **errmsg_ptr) return 1; } @@ -63418,7 +64961,7 @@ index 1384b56771..27479e3c40 100644 /* returns 1 or 0 if or if not decoded data was returned, or a negative error */ static int try_decode_frame(AVFormatContext *s, AVStream *st, const AVPacket *avpkt, AVDictionary **options) -@@ -3049,7 +3083,11 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, +@@ -3051,7 +3085,11 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, av_dict_set(options ? options : &thread_opt, "lowres", "0", 0); if (s->codec_whitelist) av_dict_set(options ? options : &thread_opt, "codec_whitelist", s->codec_whitelist, 0); @@ -63431,7 +64974,7 @@ index 1384b56771..27479e3c40 100644 if (!options) av_dict_free(&thread_opt); if (ret < 0) { -@@ -3080,6 +3118,14 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, +@@ -3082,6 +3120,14 @@ static int try_decode_frame(AVFormatContext *s, AVStream *st, if (avctx->codec_type == AVMEDIA_TYPE_VIDEO || avctx->codec_type == AVMEDIA_TYPE_AUDIO) { ret = avcodec_send_packet(avctx, &pkt); @@ -63446,7 +64989,7 @@ index 1384b56771..27479e3c40 100644 if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) break; if (ret >= 0) -@@ -3708,9 +3754,20 @@ FF_ENABLE_DEPRECATION_WARNINGS +@@ -3710,9 +3756,20 @@ FF_ENABLE_DEPRECATION_WARNINGS // Try to just open decoders, in case this is enough to get parameters. if (!has_codec_parameters(st, NULL) && st->internal->request_probe <= 0) { if (codec && !avctx->codec) @@ -65722,14 +67265,16 @@ index 18c7a0efc8..bab13a4d50 100644 #if FF_API_PLUS1_MINUS1 FF_ENABLE_DEPRECATION_WARNINGS diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h -index 46ef211add..84b7c9dd88 100644 +index 46ef211add..9195ead15f 100644 --- a/libavutil/pixfmt.h +++ b/libavutil/pixfmt.h -@@ -357,6 +357,12 @@ enum AVPixelFormat { +@@ -357,6 +357,14 @@ enum AVPixelFormat { AV_PIX_FMT_Y210BE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, big-endian AV_PIX_FMT_Y210LE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, little-endian +// RPI - not on ifdef so can be got at by calling progs ++// #define so code that uses this can know it is there ++#define AVUTIL_HAVE_PIX_FMT_SAND 1 + AV_PIX_FMT_SAND128, ///< 4:2:0 8-bit 128x*Y stripe, 64x*UV stripe, then next x stripe, mysterious padding + AV_PIX_FMT_SAND64_10, ///< 4:2:0 10-bit 64x*Y stripe, 32x*UV stripe, then next x stripe, mysterious padding + AV_PIX_FMT_SAND64_16, ///< 4:2:0 16-bit 64x*Y stripe, 32x*UV stripe, then next x stripe, mysterious padding @@ -66616,6 +68161,32 @@ index 0000000000..462ccb8abd + +#endif + +diff --git a/libswscale/aarch64/yuv2rgb_neon.S b/libswscale/aarch64/yuv2rgb_neon.S +index f4b220fb60..f341268c5d 100644 +--- a/libswscale/aarch64/yuv2rgb_neon.S ++++ b/libswscale/aarch64/yuv2rgb_neon.S +@@ -118,8 +118,8 @@ + .endm + + .macro increment_yuv422p +- add x6, x6, w7, UXTW // srcU += incU +- add x13, x13, w14, UXTW // srcV += incV ++ add x6, x6, w7, SXTW // srcU += incU ++ add x13, x13, w14, SXTW // srcV += incV + .endm + + .macro compute_rgba r1 g1 b1 a1 r2 g2 b2 a2 +@@ -189,8 +189,8 @@ function ff_\ifmt\()_to_\ofmt\()_neon, export=1 + st4 {v16.8B,v17.8B,v18.8B,v19.8B}, [x2], #32 + subs w8, w8, #16 // width -= 16 + b.gt 2b +- add x2, x2, w3, UXTW // dst += padding +- add x4, x4, w5, UXTW // srcY += paddingY ++ add x2, x2, w3, SXTW // dst += padding ++ add x4, x4, w5, SXTW // srcY += paddingY + increment_\ifmt + subs w1, w1, #1 // height -= 1 + b.gt 1b diff --git a/pi-util/BUILD.txt b/pi-util/BUILD.txt new file mode 100644 index 0000000000..b050971f63 @@ -67433,10 +69004,10 @@ index 0000000000..fc14f2a3c2 +1,WPP_F_ericsson_MAIN_2,WPP_F_ericsson_MAIN_2.bit,WPP_F_ericsson_MAIN_2_yuv.md5 diff --git a/pi-util/conf_native.sh b/pi-util/conf_native.sh new file mode 100755 -index 0000000000..37cea71756 +index 0000000000..a9e053801c --- /dev/null +++ b/pi-util/conf_native.sh -@@ -0,0 +1,106 @@ +@@ -0,0 +1,107 @@ +echo "Configure for native build" + +FFSRC=`pwd` @@ -67532,6 +69103,7 @@ index 0000000000..37cea71756 + --enable-libdrm\ + --enable-vout-egl\ + --enable-vout-drm\ ++ --enable-gpl\ + $SHARED_LIBS\ + $RPIOPTS\ + --extra-cflags="-ggdb $RPI_KEEPS $RPI_DEFINES $RPI_INCLUDES"\ @@ -68116,6 +69688,95 @@ index 0000000000..a4dbb6eacd +$QASM -mc_c:$DST_BASE,$DST_BASE,$DST_BASE $SRC_FILE > $TARGET_DIR/$DST_BASE.c +$QASM -mc_h:$DST_BASE,$DST_BASE,$DST_BASE $SRC_FILE > $TARGET_DIR/$DST_BASE.h + +diff --git a/pi-util/testfilt.py b/pi-util/testfilt.py +new file mode 100755 +index 0000000000..b322dac0c2 +--- /dev/null ++++ b/pi-util/testfilt.py +@@ -0,0 +1,83 @@ ++#!/usr/bin/env python3 ++ ++import string ++import os ++import subprocess ++import re ++import argparse ++import sys ++import csv ++from stat import * ++ ++class validator: ++ def __init__(self): ++ self.ok = False ++ ++ def isok(self): ++ return self.ok ++ ++ def setok(self): ++ self.ok = True ++ ++class valid_regex(validator): ++ def __init__(self, regex): ++ super().__init__() ++ self.regex = re.compile(regex) ++ ++ def scanline(self, line): ++ if self.isok() or self.regex.search(line): ++ self.setok() ++ ++ ++def validate(validators, flog): ++ for line in flog: ++ for v in validators: ++ v.scanline(line) ++ ++ ok = True ++ for v in validators: ++ if not v.isok(): ++ ok = False ++ # complain ++ print("Test failed") ++ ++ if ok: ++ print("OK") ++ return ok ++ ++def runtest(name, ffmpeg, args, suffix, validators): ++ log_root = os.path.join("/tmp", "testfilt", name) ++ ofilename = os.path.join(log_root, name + suffix) ++ ++ if not os.path.exists(log_root): ++ os.makedirs(log_root) ++ ++ try: ++ os.remove(ofilename) ++ except: ++ pass ++ ++ flog = open(os.path.join(log_root, name + ".log"), "wb") ++ ffargs = [ffmpeg] + args + [ofilename] ++ ++ subprocess.call(ffargs, stdout=flog, stderr=subprocess.STDOUT, text=False) ++ flog.close ++ ++ flog = open(os.path.join(log_root, name + ".log"), "rt") ++ return validate(validators, flog) ++ ++def sayok(log_root, flog): ++ print("Woohoo") ++ return True ++ ++if __name__ == '__main__': ++ ++ argp = argparse.ArgumentParser(description="FFmpeg filter tester") ++ argp.add_argument("--ffmpeg", default="./ffmpeg", help="ffmpeg exec name") ++ args = argp.parse_args() ++ ++ runtest("ATest", args.ffmpeg, ["-v", "verbose", "-no_cvt_hw", "-an", "-c:v", "h264_v4l2m2m", "-i", ++ "/home/johncox/server/TestMedia/Sony/jellyfish-10-mbps-hd-h264.mkv", ++# "/home/jc/rpi/streams/jellyfish-3-mbps-hd-h264.mkv", ++ "-c:v", "h264_v4l2m2m", "-b:v", "2M"], ".mkv", ++ [valid_regex(r'Output stream #0:0 \(video\): 900 frames encoded; 900 packets muxed')]) diff --git a/pi-util/v3dusage.py b/pi-util/v3dusage.py new file mode 100755 index 0000000000..5935a11ca5 diff --git a/alarm/ffmpeg-rpi/0004-fix_flags.patch b/alarm/ffmpeg-rpi/0003-fix_flags.patch similarity index 79% rename from alarm/ffmpeg-rpi/0004-fix_flags.patch rename to alarm/ffmpeg-rpi/0003-fix_flags.patch index 6175d5be6..5b3366dd9 100644 --- a/alarm/ffmpeg-rpi/0004-fix_flags.patch +++ b/alarm/ffmpeg-rpi/0003-fix_flags.patch @@ -1,6 +1,16 @@ +From e132ddbd8321f069f025f1fa42260bf5a854e385 Mon Sep 17 00:00:00 2001 +Date: Wed, 1 Mar 2023 17:10:10 -0500 +Subject: [PATCH 3/3] fix flags + +--- + configure | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/configure b/configure +index 83383b0..d71e6b9 100755 --- a/configure +++ b/configure -@@ -6471,11 +6471,9 @@ enabled mbedtls && { check_pkg +@@ -6533,11 +6533,9 @@ enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt die "ERROR: mbedTLS not found"; } enabled mediacodec && { enabled jni || die "ERROR: mediacodec requires --enable-jni"; } ( enabled rpi || @@ -15,3 +25,6 @@ die "ERROR: mmal not found" && check_func_headers interface/mmal/mmal.h "MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS"; } enabled openal && { { for al_extralibs in "${OPENAL_LIBS}" "-lopenal" "-lOpenAL32"; do +-- +2.39.2 + diff --git a/alarm/ffmpeg-rpi/PKGBUILD b/alarm/ffmpeg-rpi/PKGBUILD index 098203309..c988d7664 100644 --- a/alarm/ffmpeg-rpi/PKGBUILD +++ b/alarm/ffmpeg-rpi/PKGBUILD @@ -6,8 +6,8 @@ pkgbase=ffmpeg-rpi pkgname=($pkgbase $pkgbase-bin) -pkgver=4.4.2 -pkgrel=4 +pkgver=4.4.3 +pkgrel=1 arch=(aarch64) url=https://ffmpeg.org/ license=(GPL3) @@ -15,6 +15,7 @@ makedepends=( amf-headers avisynthplus clang + debugedit ffnvcodec-headers git ladspa @@ -76,16 +77,14 @@ depends=( options=(debug) source=(https://ffmpeg.org/releases/${pkgname/-rpi}-$pkgver.tar.xz{,.asc} 0001-vmaf-model-path.patch - 0002-avcodec-arm-sbcenc-avoid-callee-preserved-vfp-regist.patch - 0003-ffmpeg-4.4n-rpi.patch - 0004-fix_flags.patch + 0002-ffmpeg-4.4n-rpi.patch + 0003-fix_flags.patch ) -sha256sums=('af419a7f88adbc56c758ab19b4c708afbcae15ef09606b82b855291f6a6faa93' +sha256sums=('6c5b6c195e61534766a0b5fe16acc919170c883362612816d0a1c7f4f947006e' 'SKIP' - '8dff51f84a5f7460f8893f0514812f5d2bd668c3276ef7ab7713c99b71d7bd8d' - '06bd27ca2316cec738ff53ec827b2795bd9fbcba9f78d88af49c329f8569db5c' - '590d2e90ddbc4130f9a01f11f8c2688a8b138dce1503dc7f5bc2a5b42c6ad42e' - '656d1d57e6163801d8345c8937e88842661135197c1ab71dd1b97d013f0065ba') + '2e8d885de789b461ddf63c10646cdb16ad5519b671efd1624bf5a8e7da43dbf3' + '5048895674c27876fc252b1a5e2af8036616ec31fc27cc7f57ec2500da9d4151' + '42f57e7a55f250811515571c870372d6ed0ed504f823b341d26f383c082ce0a0') validpgpkeys=('FCF986EA15E6E293A5644F10B4322F04D67658D8') prepare() {