From ca49f640863e788e9131c53f58b8fdc13dfbd91e Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Thu, 10 Sep 2026 20:41:51 -0500 Subject: [PATCH] Stateless video decoding added. --- CHANGELOG | 11 + CMakeLists.txt | 9 + cmake/zig/arm64Packages.cmake | 2 + src/videoPlayer.c | 24 + thirdparty/ffmpeg/configure | 34 +- thirdparty/ffmpeg/configure.orig | 8998 +++++++++++++++++ thirdparty/ffmpeg/libavcodec/Makefile | 8 + thirdparty/ffmpeg/libavcodec/Makefile.orig | 1438 +++ thirdparty/ffmpeg/libavcodec/av1dec.c | 10 + thirdparty/ffmpeg/libavcodec/av1dec.c.orig | 1573 +++ thirdparty/ffmpeg/libavcodec/h264_slice.c | 13 +- .../ffmpeg/libavcodec/h264_slice.c.orig | 2850 ++++++ thirdparty/ffmpeg/libavcodec/h264dec.c | 3 + thirdparty/ffmpeg/libavcodec/h264dec.c.orig | 1158 +++ thirdparty/ffmpeg/libavcodec/h264dec.h | 2 + thirdparty/ffmpeg/libavcodec/hevc/hevcdec.c | 10 + .../ffmpeg/libavcodec/hevc/hevcdec.c.orig | 4279 ++++++++ thirdparty/ffmpeg/libavcodec/hwaccels.h | 6 + thirdparty/ffmpeg/libavcodec/hwaccels.h.orig | 102 + thirdparty/ffmpeg/libavcodec/hwconfig.h | 2 + thirdparty/ffmpeg/libavcodec/mpeg12dec.c | 6 + thirdparty/ffmpeg/libavcodec/mpeg12dec.c.orig | 2885 ++++++ thirdparty/ffmpeg/libavcodec/v4l2_request.c | 784 ++ thirdparty/ffmpeg/libavcodec/v4l2_request.h | 105 + .../ffmpeg/libavcodec/v4l2_request_av1.c | 636 ++ .../ffmpeg/libavcodec/v4l2_request_h264.c | 532 + .../ffmpeg/libavcodec/v4l2_request_hevc.c | 752 ++ .../ffmpeg/libavcodec/v4l2_request_mpeg2.c | 184 + .../ffmpeg/libavcodec/v4l2_request_vp8.c | 242 + .../ffmpeg/libavcodec/v4l2_request_vp9.c | 530 + thirdparty/ffmpeg/libavcodec/vp8.c | 6 + thirdparty/ffmpeg/libavcodec/vp8.c.orig | 2983 ++++++ thirdparty/ffmpeg/libavcodec/vp9.c | 7 + thirdparty/ffmpeg/libavcodec/vp9.c.orig | 1964 ++++ thirdparty/ffmpeg/libavutil/Makefile | 4 + thirdparty/ffmpeg/libavutil/Makefile.orig | 333 + thirdparty/ffmpeg/libavutil/hwcontext.c | 4 + thirdparty/ffmpeg/libavutil/hwcontext.h | 1 + .../ffmpeg/libavutil/hwcontext_internal.h | 1 + .../ffmpeg/libavutil/hwcontext_v4l2request.c | 1177 +++ .../ffmpeg/libavutil/hwcontext_v4l2request.h | 70 + .../hwcontext_v4l2request_internal.h | 67 + 42 files changed, 33803 insertions(+), 2 deletions(-) create mode 100755 thirdparty/ffmpeg/configure.orig create mode 100644 thirdparty/ffmpeg/libavcodec/Makefile.orig create mode 100644 thirdparty/ffmpeg/libavcodec/av1dec.c.orig create mode 100644 thirdparty/ffmpeg/libavcodec/h264_slice.c.orig create mode 100644 thirdparty/ffmpeg/libavcodec/h264dec.c.orig create mode 100644 thirdparty/ffmpeg/libavcodec/hevc/hevcdec.c.orig create mode 100644 thirdparty/ffmpeg/libavcodec/hwaccels.h.orig create mode 100644 thirdparty/ffmpeg/libavcodec/mpeg12dec.c.orig create mode 100644 thirdparty/ffmpeg/libavcodec/v4l2_request.c create mode 100644 thirdparty/ffmpeg/libavcodec/v4l2_request.h create mode 100644 thirdparty/ffmpeg/libavcodec/v4l2_request_av1.c create mode 100644 thirdparty/ffmpeg/libavcodec/v4l2_request_h264.c create mode 100644 thirdparty/ffmpeg/libavcodec/v4l2_request_hevc.c create mode 100644 thirdparty/ffmpeg/libavcodec/v4l2_request_mpeg2.c create mode 100644 thirdparty/ffmpeg/libavcodec/v4l2_request_vp8.c create mode 100644 thirdparty/ffmpeg/libavcodec/v4l2_request_vp9.c create mode 100644 thirdparty/ffmpeg/libavcodec/vp8.c.orig create mode 100644 thirdparty/ffmpeg/libavcodec/vp9.c.orig create mode 100644 thirdparty/ffmpeg/libavutil/Makefile.orig create mode 100644 thirdparty/ffmpeg/libavutil/hwcontext_v4l2request.c create mode 100644 thirdparty/ffmpeg/libavutil/hwcontext_v4l2request.h create mode 100644 thirdparty/ffmpeg/libavutil/hwcontext_v4l2request_internal.h diff --git a/CHANGELOG b/CHANGELOG index 9a30f26cc..0c367a398 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -660,6 +660,17 @@ API Changes written against it behaves the same here; note that library names its sumhexa hexsum. +- The ARM64 build carries the V4L2 stateless decoders: AV1, H.264, + HEVC, MPEG-2, VP8 and VP9. These reach the hardware on the boards + whose kernel drivers use the V4L2 Request API rather than the older + memory-to-memory one -- Allwinner's Cedrus, Rockchip's rkvdec and + hantro, the Pi 5's rpivid, i.MX8's hantro -- none of which the engine + could use before. Singe tries them first on ARM, then Rockchip's MPP, + then stateful V4L2, and decodes in software when a board offers none. + Desktop platforms are untouched. The support is Jonas Karlman's + V4L2 Request patchset for FFmpeg, carried in thirdparty/ffmpeg; it + brings a runtime dependency on libudev, which these boards all have. + - The Raspberry Pi build target is now linux-aarch64 and its binary is Singe-v3.00-Linux-aarch64. Nothing in it was ever Pi specific: the V4L2 decoder it builds talks to any memory-to-memory device, so the diff --git a/CMakeLists.txt b/CMakeLists.txt index ce01991c4..07e2e2ea2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -576,6 +576,15 @@ if(KANGAROO_OS STREQUAL "linux") # An ARM board decodes video through its V4L2 memory-to-memory device (videoPlayer.c). # Nothing here is particular to a Raspberry Pi: any board whose kernel offers one is served. target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE SINGE_V4L2_DECODE) + # The stateless decoders are a separate interface again, and FFmpeg builds them only when its + # own dependencies were met, so the engine follows what FFmpeg actually did. + if(EXISTS ${BUILD_DIR}/build/ffmpeg/config.h) + file(READ ${BUILD_DIR}/build/ffmpeg/config.h ffmpegConfig) + if(ffmpegConfig MATCHES "#define CONFIG_V4L2_REQUEST 1") + target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE SINGE_V4L2_REQUEST_DECODE) + message(STATUS "FFmpeg carries the V4L2 stateless decoders; enabling them in the player") + endif() + endif() else() # ffmpeg's vdpau hardware context is always compiled in on X11 hosts. set(SYSTEM_LIBS -lX11 -lvdpau -ldl -rdynamic) # -rdynamic gives the crash backtrace function names diff --git a/cmake/zig/arm64Packages.cmake b/cmake/zig/arm64Packages.cmake index 5261f9976..e314973f5 100644 --- a/cmake/zig/arm64Packages.cmake +++ b/cmake/zig/arm64Packages.cmake @@ -6,6 +6,8 @@ set(arm64PackagesSnapshot "https://snapshot.debian.org/archive/debian/20250901T000000Z") set(arm64Packages + # Kernel headers, for the V4L2 stateless decoders: linux/media.h, videodev2.h, v4l2-controls.h. + "${arm64PackagesSnapshot}/pool/main/l/linux/linux-libc-dev_6.1.148-1_arm64.deb|55baaddd21bcaa7fed8894c89e4d65b684e85abcb48acf443519438185e1153f" "${arm64PackagesSnapshot}/pool/main/libd/libdrm/libdrm-dev_2.4.114-1+b1_arm64.deb|26d7d0dcbbda8ed023b57989e0fca7f0ff8372daaf654daf8b3a362557be5e21" "${arm64PackagesSnapshot}/pool/main/libd/libdrm/libdrm2_2.4.114-1+b1_arm64.deb|f5f15a46d02cf5d9fa52d4f1c54b8cf80c398711ad771a9938b12399b8d8090c" "${arm64PackagesSnapshot}/pool/main/libd/libdrm/libdrm-amdgpu1_2.4.114-1+b1_arm64.deb|24692e933b98b2b8b80d87c45728efa2bc7176120ce9efcb8793407e508b5a32" diff --git a/src/videoPlayer.c b/src/videoPlayer.c index 87ebaba48..b84883d33 100644 --- a/src/videoPlayer.c +++ b/src/videoPlayer.c @@ -108,6 +108,12 @@ typedef struct iso639_lang_t iso639_lang_t; #define RKMPP_DECODE false #endif +#if defined(SINGE_V4L2_REQUEST_DECODE) +#define V4L2_REQUEST_DECODE true +#else +#define V4L2_REQUEST_DECODE false +#endif + #if defined(SINGE_V4L2_DECODE) #define V4L2_DECODE true #else @@ -1198,6 +1204,12 @@ static void _openHardware(VideoPlayerT *v, const AVCodec *decoder) { #elif defined(__APPLE__) AV_HWDEVICE_TYPE_VIDEOTOOLBOX, #else + // Stateless V4L2 comes first on the ARM boards that have it: Allwinner's Cedrus, Rockchip's + // rkvdec and hantro, the Pi 5's rpivid, i.MX8's hantro. A desktop has no such device and + // falls straight through to VAAPI or VDPAU, so the order costs it nothing. +#if defined(SINGE_V4L2_REQUEST_DECODE) + AV_HWDEVICE_TYPE_V4L2REQUEST, +#endif AV_HWDEVICE_TYPE_VAAPI, AV_HWDEVICE_TYPE_VDPAU, #endif @@ -1718,6 +1730,18 @@ const char *videoGetDecoderDescription(void) { return "software only (--softwarevideo)"; } if (V4L2_DECODE) { + // An ARM build may carry three: the stateless V4L2 decoders, Rockchip's own, and the + // stateful V4L2 ones. Which of them a board actually has is a question for the board. + if (RKMPP_DECODE && V4L2_REQUEST_DECODE) { + return "hardware v4l2 stateless, rkmpp or v4l2m2m where the board allows, software otherwise"; + } + if (V4L2_REQUEST_DECODE) { + return "hardware v4l2 stateless or v4l2m2m where the board allows, software otherwise"; + } + if (RKMPP_DECODE) { + return "hardware rkmpp or v4l2m2m where the board allows, software otherwise"; + } + return "hardware v4l2m2m where the codec allows, software otherwise"; } #if defined(_WIN32) diff --git a/thirdparty/ffmpeg/configure b/thirdparty/ffmpeg/configure index 374bd5c15..f8fe8f92d 100755 --- a/thirdparty/ffmpeg/configure +++ b/thirdparty/ffmpeg/configure @@ -364,6 +364,7 @@ External library support: --disable-nvenc disable Nvidia video encoding code [autodetect] --enable-rkmpp enable Rockchip Media Process Platform code [no] --disable-v4l2-m2m disable V4L2 mem2mem code [autodetect] + --disable-v4l2-request disable V4L2 Request API code [autodetect] --disable-vaapi disable Video Acceleration API (mainly Unix/Intel) code [autodetect] --disable-vdpau disable Nvidia Video Decode and Presentation API for Unix code [autodetect] --disable-videotoolbox disable VideoToolbox code [autodetect] @@ -2136,6 +2137,7 @@ EXTERNAL_LIBRARY_LIST=" libtorch libtwolame libuavs3d + libudev libv4l2 libvmaf libvorbis @@ -2180,6 +2182,7 @@ HWACCEL_AUTODETECT_LIBRARY_LIST=" videotoolbox vulkan v4l2_m2m + v4l2_request " # catchall list of things that require external libs to link @@ -2693,6 +2696,7 @@ TYPES_LIST=" struct_sockaddr_sa_len struct_sockaddr_storage struct_stat_st_mtim_tv_nsec + struct_v4l2_ctrl_hevc_decode_params_num_delta_pocs_of_ref_rps_idx struct_v4l2_frmivalenum_discrete struct_mfxConfigInterface " @@ -3398,6 +3402,8 @@ dxva2_deps="dxva2api_h DXVA2_ConfigPictureDecode ole32 user32" ffnvcodec_deps_any="libdl LoadLibrary" mediacodec_deps="android mediandk pthreads" nvdec_deps="ffnvcodec" +v4l2_request_deps="linux_media_h v4l2_timeval_to_ns v4l2_m2m_hold_capture_buf libdrm libudev" +v4l2_request_suggest="libdrm libudev" vaapi_x11_deps="xlib_x11" videotoolbox_hwaccel_deps="videotoolbox pthreads" videotoolbox_hwaccel_extralibs="-framework QuartzCore" @@ -3416,6 +3422,8 @@ av1_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_AV1" av1_dxva2_hwaccel_select="av1_decoder" av1_nvdec_hwaccel_deps="nvdec CUVIDAV1PICPARAMS" av1_nvdec_hwaccel_select="av1_decoder" +av1_v4l2request_hwaccel_deps="v4l2_request av1_v4l2_request" +av1_v4l2request_hwaccel_select="av1_decoder" av1_vaapi_hwaccel_deps="vaapi VADecPictureParameterBufferAV1_bit_depth_idx" av1_vaapi_hwaccel_select="av1_decoder" av1_vdpau_hwaccel_deps="vdpau VdpPictureInfoAV1" @@ -3442,6 +3450,8 @@ h264_dxva2_hwaccel_deps="dxva2" h264_dxva2_hwaccel_select="h264_decoder" h264_nvdec_hwaccel_deps="nvdec" h264_nvdec_hwaccel_select="h264_decoder" +h264_v4l2request_hwaccel_deps="v4l2_request h264_v4l2_request" +h264_v4l2request_hwaccel_select="h264_decoder" h264_vaapi_hwaccel_deps="vaapi" h264_vaapi_hwaccel_select="h264_decoder" h264_vdpau_hwaccel_deps="vdpau" @@ -3460,6 +3470,8 @@ 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" +hevc_v4l2request_hwaccel_deps="v4l2_request hevc_v4l2_request" +hevc_v4l2request_hwaccel_select="hevc_decoder" hevc_vaapi_hwaccel_deps="vaapi VAPictureParameterBufferHEVC" hevc_vaapi_hwaccel_select="hevc_decoder" hevc_vdpau_hwaccel_deps="vdpau VdpPictureInfoHEVC" @@ -3488,6 +3500,8 @@ mpeg2_dxva2_hwaccel_deps="dxva2" mpeg2_dxva2_hwaccel_select="mpeg2video_decoder" mpeg2_nvdec_hwaccel_deps="nvdec" mpeg2_nvdec_hwaccel_select="mpeg2video_decoder" +mpeg2_v4l2request_hwaccel_deps="v4l2_request mpeg2_v4l2_request" +mpeg2_v4l2request_hwaccel_select="mpeg2video_decoder" mpeg2_vaapi_hwaccel_deps="vaapi" mpeg2_vaapi_hwaccel_select="mpeg2video_decoder" mpeg2_vdpau_hwaccel_deps="vdpau" @@ -3526,6 +3540,8 @@ vc1_vdpau_hwaccel_deps="vdpau" vc1_vdpau_hwaccel_select="vc1_decoder" vp8_nvdec_hwaccel_deps="nvdec" vp8_nvdec_hwaccel_select="vp8_decoder" +vp8_v4l2request_hwaccel_deps="v4l2_request vp8_v4l2_request" +vp8_v4l2request_hwaccel_select="vp8_decoder" vp8_vaapi_hwaccel_deps="vaapi" vp8_vaapi_hwaccel_select="vp8_decoder" vp9_d3d11va_hwaccel_deps="d3d11va DXVA_PicParams_VP9" @@ -3538,6 +3554,8 @@ vp9_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_VP9" vp9_dxva2_hwaccel_select="vp9_decoder" vp9_nvdec_hwaccel_deps="nvdec" vp9_nvdec_hwaccel_select="vp9_decoder" +vp9_v4l2request_hwaccel_deps="v4l2_request vp9_v4l2_request" +vp9_v4l2request_hwaccel_select="vp9_decoder" vp9_vaapi_hwaccel_deps="vaapi VADecPictureParameterBufferVP9_bit_depth" vp9_vaapi_hwaccel_select="vp9_decoder" vp9_vdpau_hwaccel_deps="vdpau VdpPictureInfoVP9" @@ -4371,7 +4389,7 @@ shader_compression_suggest="zlib" avcodec_extralibs="pthreads_extralibs iconv_extralibs dxva2_extralibs liblcevc_dec_extralibs lcms2_extralibs" avfilter_extralibs="pthreads_extralibs" -avutil_extralibs="d3d11va_extralibs d3d12va_extralibs mediacodec_extralibs nanosleep_extralibs pthreads_extralibs vaapi_drm_extralibs vaapi_x11_extralibs vaapi_win32_extralibs vdpau_x11_extralibs" +avutil_extralibs="d3d11va_extralibs d3d12va_extralibs mediacodec_extralibs nanosleep_extralibs pthreads_extralibs v4l2_request_extralibs vaapi_drm_extralibs vaapi_x11_extralibs vaapi_win32_extralibs vdpau_x11_extralibs" # programs ffmpeg_deps="avcodec avfilter avformat threads" @@ -7651,6 +7669,20 @@ if enabled v4l2_m2m; then check_cc vp9_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;" fi +if enabled v4l2_request; then + check_cc av1_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_AV1_SEQUENCE" + check_cc h264_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_H264_DECODE_MODE" + check_cc hevc_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_HEVC_SPS" + check_cc mpeg2_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_MPEG2_SEQUENCE" + check_cc v4l2_m2m_hold_capture_buf linux/videodev2.h "int i = V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF" + check_cc vp8_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_VP8_FRAME" + check_cc vp9_v4l2_request linux/videodev2.h "int i = V4L2_CID_STATELESS_VP9_FRAME" + check_func_headers "linux/media.h linux/videodev2.h" v4l2_timeval_to_ns + check_pkg_config libudev libudev libudev.h udev_new + check_struct linux/videodev2.h "struct v4l2_ctrl_hevc_decode_params" num_delta_pocs_of_ref_rps_idx + v4l2_request_extralibs="$libudev_extralibs" +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 diff --git a/thirdparty/ffmpeg/configure.orig b/thirdparty/ffmpeg/configure.orig new file mode 100755 index 000000000..374bd5c15 --- /dev/null +++ b/thirdparty/ffmpeg/configure.orig @@ -0,0 +1,8998 @@ +#!/bin/sh +# +# FFmpeg configure script +# +# Copyright (c) 2000-2002 Fabrice Bellard +# Copyright (c) 2005-2008 Diego Biurrun +# Copyright (c) 2005-2008 Mans Rullgard +# + +# Prevent locale nonsense from breaking basic text processing. +LC_ALL=C +export LC_ALL + +# make sure we are running under a compatible shell +# try to make this part work with most shells + +try_exec(){ + echo "Trying shell $1" + type "$1" > /dev/null 2>&1 && exec "$@" +} + +unset foo +(: ${foo%%bar}) 2> /dev/null +E1="$?" + +(: ${foo?}) 2> /dev/null +E2="$?" + +if test "$E1" != 0 || test "$E2" = 0; then + echo "Broken shell detected. Trying alternatives." + export FF_CONF_EXEC + if test "0$FF_CONF_EXEC" -lt 1; then + FF_CONF_EXEC=1 + try_exec bash "$0" "$@" + fi + if test "0$FF_CONF_EXEC" -lt 2; then + FF_CONF_EXEC=2 + try_exec ksh "$0" "$@" + fi + if test "0$FF_CONF_EXEC" -lt 3; then + FF_CONF_EXEC=3 + try_exec /usr/xpg4/bin/sh "$0" "$@" + fi + echo "No compatible shell script interpreter found." + echo "This configure script requires a POSIX-compatible shell" + echo "such as bash or ksh." + echo "THIS IS NOT A BUG IN FFMPEG, DO NOT REPORT IT AS SUCH." + echo "Instead, install a working POSIX-compatible shell." + echo "Disabling this configure test will create a broken FFmpeg." + if test "$BASH_VERSION" = '2.04.0(1)-release'; then + echo "This bash version ($BASH_VERSION) is broken on your platform." + echo "Upgrade to a later version if available." + fi + exit 1 +fi + +test -d /usr/xpg4/bin && PATH=/usr/xpg4/bin:$PATH + +show_help(){ + cat <[-sanitizer[-...]], e.g. clang-asan-ubsan + tools: gcc, clang, llvm, msvc, icl, gcov, llvm-cov, + valgrind-memcheck, valgrind-massif, hardened + sanitizers: asan, fuzz, lsan, msan, tsan, ubsan) + --nm=NM use nm tool NM [$nm_default] + --ar=AR use archive tool AR [$ar_default] + --as=AS use assembler AS [$as_default] + --ln_s=LN_S use symbolic link tool LN_S [$ln_s_default] + --strip=STRIP use strip tool STRIP [$strip_default] + --windres=WINDRES use windows resource compiler WINDRES [$windres_default] + --x86asmexe=EXE use nasm-compatible assembler EXE [$x86asmexe_default] + --cc=CC use C compiler CC [$cc_default] + --stdc=STDC use C standard STDC [$stdc_default] + --cxx=CXX use C compiler CXX [$cxx_default] + --stdcxx=STDCXX use C standard STDCXX [$stdcxx_default] + --objcc=OCC use ObjC compiler OCC [$cc_default] + --dep-cc=DEPCC use dependency generator DEPCC [$cc_default] + --glslc=GLSLC use GLSL compiler GLSLC [$glslc_default] + --nvcc=NVCC use Nvidia CUDA compiler NVCC or clang [$nvcc_default] + --ld=LD use linker LD [$ld_default] + --metalcc=METALCC use metal compiler METALCC [$metalcc_default] + --metallib=METALLIB use metal linker METALLIB [$metallib_default] + --pkg-config=PKGCONFIG use pkg-config tool PKGCONFIG [$pkg_config_default] + --pkg-config-flags=FLAGS pass additional flags to pkgconf [] + --ranlib=RANLIB use ranlib RANLIB [$ranlib_default] + --doxygen=DOXYGEN use DOXYGEN to generate API doc [$doxygen_default] + --makeinfo=MAKEINFO use MAKEINFO to generate documentation [$makeinfo_default] + --host-cc=HOSTCC use host C compiler HOSTCC + --host-cflags=HCFLAGS use HCFLAGS when compiling for host + --host-cppflags=HCPPFLAGS use HCPPFLAGS when compiling for host + --host-ld=HOSTLD use host linker HOSTLD + --host-ldflags=HLDFLAGS use HLDFLAGS when linking for host + --host-extralibs=HLIBS use libs HLIBS when linking for host + --host-os=OS compiler host OS [$target_os] + --extra-cflags=ECFLAGS add ECFLAGS to CFLAGS [$CFLAGS] + --extra-cxxflags=ECFLAGS add ECFLAGS to CXXFLAGS [$CXXFLAGS] + --extra-objcflags=FLAGS add FLAGS to OBJCFLAGS [$OBJCFLAGS] + --extra-ldflags=ELDFLAGS add ELDFLAGS to LDFLAGS [$LDFLAGS] + --extra-ldexeflags=ELDFLAGS add ELDFLAGS to LDEXEFLAGS [$LDEXEFLAGS] + --extra-ldsoflags=ELDFLAGS add ELDFLAGS to LDSOFLAGS [$LDSOFLAGS] + --extra-libs=ELIBS add ELIBS [$ELIBS] + --extra-version=STRING version string suffix [] + --optflags=OPTFLAGS override optimization-related compiler flags + --glslcflags=GLSLCFLAGS extra glslc flags [$glslcflags_default] + --nvccflags=NVCCFLAGS override nvcc flags [$nvccflags_default] + --build-suffix=SUFFIX library name suffix [] + --enable-pic build position-independent code + --enable-thumb compile for Thumb instruction set + --enable-lto[=arg] use link-time optimization + --env="ENV=override" override the environment variables + --disable-response-files Don't pass the list of objects to linker in a file [autodetect] + +Advanced options (experts only): + --malloc-prefix=PREFIX prefix malloc and related names with PREFIX + --custom-allocator=NAME use a supported custom allocator + --disable-symver disable symbol versioning + --enable-hardcoded-tables use hardcoded tables instead of runtime generation + --disable-safe-bitstream-reader + disable buffer boundary checking in bitreaders + (This disables some security checks and can cause undefined behavior, + crashes and arbitrary code execution, it may be faster, but + should only be used with trusted input) + --sws-max-filter-size=N the max filter size swscale uses [$sws_max_filter_size_default] + +Optimization options (experts only): + --disable-asm disable all assembly optimizations + --disable-altivec disable AltiVec optimizations + --disable-vsx disable VSX optimizations + --disable-power8 disable POWER8 optimizations + --disable-mmx disable MMX optimizations + --disable-mmxext disable MMXEXT optimizations + --disable-sse disable SSE optimizations + --disable-sse2 disable SSE2 optimizations + --disable-sse3 disable SSE3 optimizations + --disable-ssse3 disable SSSE3 optimizations + --disable-sse4 disable SSE4 optimizations + --disable-sse42 disable SSE4.2 optimizations + --disable-avx disable AVX optimizations + --disable-xop disable XOP optimizations + --disable-fma3 disable FMA3 optimizations + --disable-fma4 disable FMA4 optimizations + --disable-avx2 disable AVX2 optimizations + --disable-avx512 disable AVX-512 optimizations + --disable-avx512icl disable AVX-512ICL optimizations + --disable-aesni disable AESNI optimizations + --disable-clmul disable CLMUL optimizations + --disable-armv5te disable armv5te optimizations + --disable-armv6 disable armv6 optimizations + --disable-armv6t2 disable armv6t2 optimizations + --disable-vfp disable VFP optimizations + --disable-neon disable NEON optimizations + --disable-arm-crc disable ARM/AArch64 CRC optimizations + --disable-dotprod disable DOTPROD optimizations + --disable-i8mm disable I8MM optimizations + --disable-pmull disable PMULL optimizations + --disable-eor3 disable EOR3 optimizations + --disable-sve disable SVE optimizations + --disable-sve2 disable SVE2 optimizations + --disable-sme disable SME optimizations + --disable-sme-i16i64 disable SME-I16I64 optimizations + --disable-sme2 disable SME2 optimizations + --disable-inline-asm disable use of inline assembly + --disable-x86asm disable use of standalone x86 assembly + --disable-mipsdsp disable MIPS DSP ASE R1 optimizations + --disable-mipsdspr2 disable MIPS DSP ASE R2 optimizations + --disable-msa disable MSA optimizations + --disable-mipsfpu disable floating point MIPS optimizations + --disable-mmi disable Loongson MMI optimizations + --disable-lsx disable Loongson LSX optimizations + --disable-lasx disable Loongson LASX optimizations + --disable-rvv disable RISC-V Vector optimizations + --disable-fast-unaligned consider unaligned accesses slow + --disable-simd128 disable WebAssembly simd128 optimizations + +Developer options (useful when working on FFmpeg itself): + --disable-debug disable debugging symbols + --enable-debug=LEVEL set the debug level [$debuglevel] + --disable-optimizations disable compiler optimizations + --enable-extra-warnings enable more compiler warnings + --disable-stripping disable stripping of executables and shared libraries + --assert-level=level 0(default), 1 or 2, amount of assertion testing, + 2 causes a slowdown at runtime. + --enable-memory-poisoning fill heap uninitialized allocated space with arbitrary data + --valgrind=VALGRIND run "make fate" tests through valgrind to detect memory + leaks and errors, using the specified valgrind binary. + Cannot be combined with --target-exec + --enable-ftrapv Trap arithmetic overflows + --samples=PATH location of test samples for FATE, if not set use + \$FATE_SAMPLES at make invocation time. + --enable-neon-clobber-test check NEON registers for clobbering (should be + used only for debugging purposes) + --enable-xmm-clobber-test check XMM registers for clobbering (Win64-only; + should be used only for debugging purposes) + --enable-random randomly enable/disable components + --disable-random + --enable-random=LIST randomly enable/disable specific components or + --disable-random=LIST component groups. LIST is a comma-separated list + of NAME[:PROB] entries where NAME is a component + (group) and PROB the probability associated with + NAME (default 0.5). + --random-seed=VALUE seed value for --enable/disable-random + --disable-valgrind-backtrace do not print a backtrace under Valgrind + (only applies to --disable-optimizations builds) + --enable-ossfuzz Enable building fuzzer tool + --libfuzzer=PATH path to libfuzzer + --ignore-tests=TESTS comma-separated list (without "fate-" prefix + in the name) of tests whose result is ignored + --enable-linux-perf enable Linux Performance Monitor API + --enable-macos-kperf enable macOS kperf (private) API + --disable-large-tests disable tests that use a large amount of memory + --disable-shader-compression don't compress shader code even when possible + --disable-resource-compression don't compress resources even when possible + --disable-version-tracking don't include the git/release version in the build + +NOTE: Object files are built at the place where configure is launched. +EOF + exit 0 +} + +if test -t 1 && which tput >/dev/null 2>&1; then + ncolors=$(tput colors) + if test -n "$ncolors" && test $ncolors -ge 8; then + bold_color=$(tput bold) + warn_color=$(tput setaf 3) + error_color=$(tput setaf 1) + reset_color=$(tput sgr0) + fi + # 72 used instead of 80 since that's the default of pr + ncols=$(tput cols) +fi +: ${ncols:=72} + +log(){ + echo "$@" >> $logfile +} + +log_file(){ + log BEGIN "$1" + log_file_i=1 + while IFS= read -r log_file_line; do + printf '%5d\t%s\n' "$log_file_i" "$log_file_line" + log_file_i=$(($log_file_i+1)) + done < "$1" >> "$logfile" + log END "$1" +} + +warn(){ + log "WARNING: $*" + WARNINGS="${WARNINGS}WARNING: $*\n" +} + +die(){ + log "$@" + echo "$error_color$bold_color$@$reset_color" + cat <>file; + } else if (file ~ /\\.asm\$/) { + printf(\"%%define %s %d\\n\", c, v) >>file; + } else if (file ~ /\\.mak\$/) { + n = -v ? \"\" : \"!\"; + printf(\"%s%s=yes\\n\", n, c) >>file; + } else if (file ~ /\\.texi\$/) { + pre = -v ? \"\" : \"@c \"; + yesno = \$2; + c2 = tolower(c); + gsub(/_/, \"-\", c2); + printf(\"%s@set %s %s\\n\", pre, c2, yesno) >>file; + } + } + }" +} + +print_enabled(){ + suf=$1 + shift + for v; do + enabled $v && printf "%s\n" ${v%$suf} + done +} + +append(){ + var=$1 + shift + eval "$var=\"\$$var $*\"" +} + +prepend(){ + var=$1 + shift + eval "$var=\"$* \$$var\"" +} + +reverse () { + eval ' + reverse_out= + for v in $'$1'; do + reverse_out="$v $reverse_out" + done + '$1'=$reverse_out + ' +} + +# keeps the last occurrence of each non-unique item +unique(){ + unique_out= + eval unique_in=\$$1 + reverse unique_in + for v in $unique_in; do + # " $unique_out" +space such that every item is surrounded with spaces + case " $unique_out" in *" $v "*) continue; esac # already in list + unique_out="$unique_out$v " + done + reverse unique_out + eval $1=\$unique_out +} + +resolve(){ + resolve_out= + eval resolve_in=\$$1 + for v in $resolve_in; do + eval 'resolve_out="$resolve_out$'$v' "' + done + eval $1=\$resolve_out +} + +add_cppflags(){ + append CPPFLAGS "$@" +} + +add_cflags(){ + append CFLAGS $($cflags_filter "$@") +} + +add_cflags_headers(){ + append CFLAGS_HEADERS $($cflags_filter "$@") +} + +add_cxxflags(){ + append CXXFLAGS $($cflags_filter "$@") +} + +add_objcflags(){ + append OBJCFLAGS $($objcflags_filter "$@") +} + +add_allcflags(){ + add_cflags "$@" + add_cxxflags "$@" + add_objcflags "$@" +} + +add_asflags(){ + append ASFLAGS $($asflags_filter "$@") +} + +add_ldflags(){ + append LDFLAGS $($ldflags_filter "$@") +} + +add_ldexeflags(){ + append LDEXEFLAGS $($ldflags_filter "$@") +} + +add_ldsoflags(){ + append LDSOFLAGS $($ldflags_filter "$@") +} + +add_extralibs(){ + prepend extralibs $($ldflags_filter "$@") +} + +add_stripflags(){ + append ASMSTRIPFLAGS "$@" +} + +add_host_cppflags(){ + append host_cppflags "$@" +} + +add_host_cflags(){ + append host_cflags $($host_cflags_filter "$@") +} + +add_host_ldflags(){ + append host_ldflags $($host_ldflags_filter "$@") +} + +add_compat(){ + append compat_objs $1 + shift + map 'add_cppflags -D$v' "$@" +} + +test_cmd(){ + log "$@" + "$@" >> $logfile 2>&1 +} + +test_stat(){ + log test_stat "$@" + stat "$1" >> $logfile 2>&1 +} + +cc_e(){ + eval printf '%s\\n' $CC_E +} + +cc_o(){ + eval printf '%s\\n' $CC_O +} + +cxx_e(){ + eval printf '%s\\n' $CXX_E +} + +cxx_o(){ + eval printf '%s\\n' $CXX_O +} + +as_o(){ + eval printf '%s\\n' $AS_O +} + +x86asm_o(){ + eval printf '%s\\n' $X86ASM_O +} + +ld_o(){ + eval printf '%s\\n' $LD_O +} + +hostcc_e(){ + eval printf '%s\\n' $HOSTCC_E +} + +hostcc_o(){ + eval printf '%s\\n' $HOSTCC_O +} + +hostld_o(){ + eval printf '%s\\n' $HOSTLD_O +} + +glslc_o(){ + eval printf '%s\\n' $GLSLC_O +} + +nvcc_o(){ + eval printf '%s\\n' $NVCC_O +} + +test_cc(){ + log test_cc "$@" + cat > $TMPC + log_file $TMPC + test_cmd $cc $CPPFLAGS $CFLAGS "$@" $CC_C $(cc_o $TMPO) $TMPC +} + +test_cxx(){ + log test_cxx "$@" + cat > $TMPCPP + log_file $TMPCPP + test_cmd $cxx $CPPFLAGS $CXXFLAGS "$@" $CXX_C $(cxx_o $TMPO) $TMPCPP +} + +test_objcc(){ + log test_objcc "$@" + cat > $TMPM + log_file $TMPM + test_cmd $objcc -Werror=missing-prototypes $CPPFLAGS $CFLAGS $OBJCFLAGS "$@" $OBJCC_C $(cc_o $TMPO) $TMPM +} + +test_glslc(){ + log test_glslc "$@" + cat > $TMPGLSL + log_file $TMPGLSL + test_cmd $glslc $GLSLCFLAGS $glslcflags "$@" $(glslc_o $TMPO) $TMPGLSL +} + +check_glslc(){ + log check_glslc "$@" + name=$1 + shift 1 + disable $name + test_glslc "$@" < $TMPCU + log_file $TMPCU + tmpcu_=$TMPCU + tmpo_=$TMPO + [ -x "$(command -v cygpath)" ] && tmpcu_=$(cygpath -m $tmpcu_) && tmpo_=$(cygpath -m $tmpo_) + test_cmd $nvcc $nvccflags "$@" $NVCC_C $(nvcc_o $tmpo_) $tmpcu_ +} + +check_nvcc() { + log check_nvcc "$@" + name=$1 + shift 1 + disabled $name && return + disable $name + test_nvcc "$@" < $TMPC + log_file $TMPC + test_cmd $cc $CPPFLAGS $CFLAGS "$@" $(cc_e $TMPO) $TMPC +} + +test_as(){ + log test_as "$@" + cat > $TMPS + log_file $TMPS + test_cmd $as $CPPFLAGS $ASFLAGS "$@" $AS_C $(as_o $TMPO) $TMPS +} + +test_x86asm(){ + log test_x86asm "$@" + echo "$1" > $TMPASM + log_file $TMPASM + shift + test_cmd $x86asmexe $X86ASMFLAGS -Werror "$@" $(x86asm_o $TMPO) $TMPASM +} + +check_cmd(){ + log check_cmd "$@" + cmd=$1 + disabled $cmd && return + disable $cmd + test_cmd $@ && enable $cmd +} + +check_as(){ + log check_as "$@" + name=$1 + code=$2 + shift 2 + disable $name + test_as $@ < $TMPC <&1) + ret=$? + echo "$output" >> $logfile + # link.exe and lld-link exit 0 even for unrecognized options, emitting + # only a warning (LNK4044 / "ignoring unknown argument"). Treat such + # output as failure so check_ldflags rejects those flags correctly. + test $ret -eq 0 && ! echo "$output" | grep -qE 'LNK4044|lld-link: warning: ignoring unknown argument' +} + +check_ld(){ + log check_ld "$@" + type=$1 + name=$2 + shift 2 + disable $name + test_ld $type $@ && enable $name +} + +print_include(){ + hdr=$1 + test "${hdr%.h}" = "${hdr}" && + echo "#include $hdr" || + echo "#include <$hdr>" +} + +test_code(){ + log test_code "$@" + check=$1 + headers=$2 + code=$3 + shift 3 + { + for hdr in $headers; do + print_include $hdr + done + echo "int main(void) { $code; return 0; }" + } | test_$check "$@" +} + +check_cppflags(){ + log check_cppflags "$@" + test_cpp "$@" < +EOF +} + +test_cflags(){ + log test_cflags "$@" + set -- $($cflags_filter "$@") + test_cc "$@" <" + echo "int main(void) { return 0; }" + } | test_objcc && test_stat "$TMPO" && enable_sanitized $header +} + +check_apple_framework(){ + log check_apple_framework "$@" + framework="$1" + name="$(tolower $framework)" + header="${framework}/${framework}.h" + disable $name + check_header_objcc $header && + enable $name && eval ${name}_extralibs='"-framework $framework"' +} + +check_func(){ + log check_func "$@" + func=$1 + shift + disable $func + test_ld "cc" "$@" < +float foo(float f, float g) { return $func($args); } +int main(void){ return (int) foo; } +EOF +} + +check_func_headers(){ + log check_func_headers "$@" + headers=$1 + funcs=$2 + shift 2 + { + for hdr in $headers; do + print_include $hdr + done + echo "#include " + for func in $funcs; do + echo "long check_$func(void) { return (long) $func; }" + done + echo "int main(void) { int ret = 0;" + # LTO could optimize out the test functions without this + for func in $funcs; do + echo " ret |= ((intptr_t)check_$func) & 0xFFFF;" + done + echo "return ret; }" + } | test_ld "cc" "$@" && enable $funcs && enable_sanitized $headers +} + +check_class_headers_cxx(){ + log check_class_headers_cxx "$@" + headers=$1 + classes=$2 + shift 2 + { + for hdr in $headers; do + echo "#include <$hdr>" + done + echo "int main(void) { " + i=1 + for class in $classes; do + echo "$class *obj$i;" + i=$(expr $i + 1) + done + echo "return 0; }" + } | test_ld "cxx" "$@" && enable $funcs && enable_sanitized $headers +} + +test_cpp_condition(){ + log test_cpp_condition "$@" + header=$1 + condition=$2 + shift 2 + test_cpp "$@" < +#if !($condition) +#error "unsatisfied condition: $condition" +#endif +EOF +} + +check_cpp_condition(){ + log check_cpp_condition "$@" + name=$1 + shift 1 + disable $name + test_cpp_condition "$@" && enable $name +} + +test_cflags_cc(){ + log test_cflags_cc "$@" + flags=$1 + header=$2 + condition=$3 + shift 3 + set -- $($cflags_filter "$flags") + test_cc "$@" < +#if !($condition) +#error "unsatisfied condition: $condition" +#endif +EOF +} + +check_cflags_cc(){ + log check_cflags_cc "$@" + flags=$1 + test_cflags_cc "$@" && add_cflags $flags +} + +test_cxxflags_cc(){ + log test_cxxflags_cc "$@" + flags=$1 + header=$2 + condition=$3 + shift 3 + set -- $($cflags_filter "$flags") + test_cxx "$@" < +#if !($condition) +#error "unsatisfied condition: $condition" +#endif +EOF +} + +check_cxxflags_cc(){ + log check_cxxflags_cc "$@" + flags=$1 + test_cxxflags_cc "$@" && add_cxxflags $flags +} + +check_lib(){ + log check_lib "$@" + name="$1" + headers="$2" + funcs="$3" + shift 3 + disable $name + check_func_headers "$headers" "$funcs" "$@" && + enable $name && eval ${name}_extralibs="\$@" +} + +check_lib_cxx(){ + log check_lib_cxx "$@" + name="$1" + headers="$2" + classes="$3" + shift 3 + disable $name + check_class_headers_cxx "$headers" "$classes" "$@" && + enable $name && eval ${name}_extralibs="\$@" +} + +test_pkg_config(){ + log test_pkg_config "$@" + name="$1" + pkg_version="$2" + pkg="${2%% *}" + headers="$3" + funcs="$4" + shift 4 + disable $name + test_cmd $pkg_config --exists --print-errors "$pkg_version" || return + pkg_cflags=$($pkg_config --cflags $pkg_config_flags $pkg) + pkg_libs=$($pkg_config --libs $pkg_config_flags $pkg) + pkg_incdir=$($pkg_config --variable=includedir $pkg_config_flags $pkg) + check_func_headers "$headers" "$funcs" $pkg_cflags $pkg_libs "$@" && + enable $name && + set_sanitized "${name}_cflags" $pkg_cflags && + set_sanitized "${name}_incdir" $pkg_incdir && + set_sanitized "${name}_extralibs" $pkg_libs +} + +test_pkg_config_cpp(){ + log test_pkg_config_cpp "$@" + name="$1" + pkg_version="$2" + pkg="${2%% *}" + headers="$3" + cond="$4" + shift 4 + disable $name + test_cmd $pkg_config --exists --print-errors "$pkg_version" || return + pkg_cflags=$($pkg_config --cflags $pkg_config_flags $pkg) + pkg_incdir=$($pkg_config --variable=includedir $pkg_config_flags $pkg) + pkg_incflags=$($pkg_config --cflags-only-I $pkg_config_flags $pkg) + test_cpp_condition "$pkg_incdir/$headers" "$cond" $pkg_cflags "$@" && + enable $name && + set_sanitized "${name}_cflags" $pkg_cflags && + set_sanitized "${name}_incdir" $pkg_incdir && + set_sanitized "${name}_incflags" $pkg_incflags +} + +test_pkg_config_cxx(){ + log test_pkg_config_cxx "$@" + name="$1" + pkg_version="$2" + pkg="${2%% *}" + headers="$3" + classes="$4" + shift 4 + disable $name + test_cmd $pkg_config --exists --print-errors $pkg_version || return + pkg_cflags=$($pkg_config --cflags $pkg_config_flags $pkg) + pkg_libs=$($pkg_config --libs $pkg_config_flags $pkg) + pkg_incdir=$($pkg_config --variable=includedir $pkg_config_flags $pkg) + check_class_headers_cxx "$headers" "$classes" $pkg_cflags $pkg_libs "-lstdc++" "$@" && + enable $name && + set_sanitized "${name}_cflags" $pkg_cflags && + set_sanitized "${name}_incdir" $pkg_incdir && + set_sanitized "${name}_extralibs" $pkg_libs "-lstdc++" +} + +check_pkg_config(){ + log check_pkg_config "$@" + name="$1" + test_pkg_config "$@" && + eval add_cflags \$${name}_cflags +} + +check_pkg_config_cpp(){ + log check_pkg_config_cpp "$@" + name="$1" + test_pkg_config_cpp "$@" && + eval add_cflags \$${name}_cflags +} + +check_pkg_config_cxx(){ + log check_pkg_config_cxx "$@" + name="$1" + test_pkg_config_cxx "$@" && + eval add_cxxflags \$${name}_cflags +} + +check_pkg_config_header_only(){ + log check_pkg_config_cpp "$@" + name="$1" + test_pkg_config_cpp "$@" && + eval add_cflags \$${name}_incflags +} + +test_exec(){ + test_ld "cc" "$@" && { enabled cross_compile || $TMPE >> $logfile 2>&1; } +} + +check_exec_crash(){ + log check_exec_crash "$@" + code=$(cat) + + # exit() is not async signal safe. _Exit (C99) and _exit (POSIX) + # are safe but may not be available everywhere. Thus we use + # raise(SIGTERM) instead. The check is run in a subshell so we + # can redirect the "Terminated" message from the shell. SIGBUS + # is not defined by standard C so it is used conditionally. + + (test_exec "$@") >> $logfile 2>&1 < +static void sighandler(int sig){ + raise(SIGTERM); +} +int foo(void){ + $code +} +int (*func_ptr)(void) = foo; +int main(void){ + signal(SIGILL, sighandler); + signal(SIGFPE, sighandler); + signal(SIGSEGV, sighandler); +#ifdef SIGBUS + signal(SIGBUS, sighandler); +#endif + return func_ptr(); +} +EOF +} + +check_type(){ + log check_type "$@" + headers=$1 + type=$2 + shift 2 + disable_sanitized "$type" + test_code cc "$headers" "$type v" "$@" && enable_sanitized "$type" +} + +check_objc_class(){ + log check_objc_class "$@" + headers=$1 + type=$2 + shift 2 + disable_sanitized "$type" + test_code objcc "$headers" "$type* v" "$@" && enable_sanitized "$type" +} + +check_struct(){ + log check_struct "$@" + headers=$1 + struct=$2 + member=$3 + shift 3 + disable_sanitized "${struct}_${member}" + test_code cc "$headers" "const void *p = &(($struct *)0)->$member" "$@" && + enable_sanitized "${struct}_${member}" +} + +check_builtin(){ + log check_builtin "$@" + name=$1 + headers=$2 + builtin=$3 + shift 3 + disable "$name" + test_code ld "$headers" "$builtin" "cc" "$@" && enable "$name" +} + +check_compile_assert(){ + log check_compile_assert "$@" + name=$1 + headers=$2 + condition=$3 + shift 3 + disable "$name" + test_code cc "$headers" "char c[2 * !!($condition) - 1]" "$@" && enable "$name" +} + +check_cc(){ + log check_cc "$@" + name=$1 + shift + disable "$name" + test_code cc "$@" && enable "$name" +} + +require(){ + log require "$@" + name_version="$1" + name="${1%% *}" + shift + check_lib $name "$@" || die "ERROR: $name_version not found" +} + +require_cc(){ + log require_cc "$@" + name="$1" + check_cc "$@" || die "ERROR: $name failed" +} + +require_cxx(){ + log require_cxx "$@" + name_version="$1" + name="${1%% *}" + shift + check_lib_cxx "$name" "$@" || die "ERROR: $name_version not found" +} + +require_headers(){ + log require_headers "$@" + headers="$1" + check_headers "$@" || die "ERROR: $headers not found" +} + +require_cpp_condition(){ + log require_cpp_condition "$@" + condition="$3" + check_cpp_condition "$@" || die "ERROR: $condition not satisfied" +} + +require_pkg_config(){ + log require_pkg_config "$@" + pkg_version="$2" + check_pkg_config "$@" || die "ERROR: $pkg_version not found using pkg-config$pkg_config_fail_message" +} + +require_pkg_config_cpp(){ + log require_pkg_config_cpp "$@" + pkg_version="$2" + check_pkg_config_cpp "$@" || die "ERROR: $pkg_version not found using pkg-config$pkg_config_fail_message" +} + +require_pkg_config_cxx(){ + log require_pkg_config_cxx "$@" + pkg_version="$2" + check_pkg_config_cxx "$@" || die "ERROR: $pkg_version not found using pkg-config$pkg_config_fail_message" +} + +test_host_cc(){ + log test_host_cc "$@" + cat > $TMPC + log_file $TMPC + test_cmd $host_cc $host_cflags "$@" $HOSTCC_C $(hostcc_o $TMPO) $TMPC +} + +test_host_ld(){ + log test_host_ld "$@" + flags=$(filter_out '-l*|*.so' $@) + libs=$(filter '-l*|*.so' $@) + test_host_cc $($host_cflags_filter $flags) || return + flags=$($host_ldflags_filter $flags) + libs=$($host_ldflags_filter $libs) + test_cmd $host_ld $host_ldflags $flags $(hostld_o $TMPE) $TMPO $libs $host_extralibs +} + +test_host_cpp(){ + log test_host_cpp "$@" + cat > $TMPC + log_file $TMPC + test_cmd $host_cc $host_cppflags $host_cflags "$@" $(hostcc_e $TMPO) $TMPC +} + +check_host_cppflags(){ + log check_host_cppflags "$@" + test_host_cpp "$@" < +EOF +} + +check_host_cflags(){ + log check_host_cflags "$@" + set -- $($host_cflags_filter "$@") + test_host_cc "$@" < +#if !($condition) +#error "unsatisfied condition: $condition" +#endif +EOF +} + +check_host_cflags_cc(){ + log check_host_cflags_cc "$@" + flags=$1 + test_host_cflags_cc "$@" && add_host_cflags $flags +} + +test_host_cpp_condition(){ + log test_host_cpp_condition "$@" + header=$1 + condition=$2 + shift 2 + test_host_cpp "$@" < +#if !($condition) +#error "unsatisfied condition: $condition" +#endif +EOF +} + +check_host_cpp_condition(){ + log check_host_cpp_condition "$@" + name=$1 + shift 1 + disable $name + test_host_cpp_condition "$@" && enable $name +} + +check_host_lib(){ + log check_host_lib "$@" + headers="$1" + funcs="$2" + shift 2 + { + for hdr in $headers; do + print_include $hdr + done + echo "#include " + for func in $funcs; do + echo "long check_$func(void) { return (long) $func; }" + done + echo "int main(void) { int ret = 0;" + for func in $funcs; do + echo " ret |= ((intptr_t)check_$func) & 0xFFFF;" + done + echo "return ret; }" + } | test_host_ld "$@" && append host_extralibs "$@" +} + +cp_if_changed(){ + cmp -s "$1" "$2" && { test "$quiet" != "yes" && echo "$2 is unchanged"; } && return + mkdir -p "$(dirname $2)" + cp -f "$1" "$2" +} + +# CONFIG_LIST contains configurable options, while HAVE_LIST is for +# system-dependent things. + +AVCODEC_COMPONENTS=" + bsfs + decoders + encoders + hwaccels + parsers +" + +AVDEVICE_COMPONENTS=" + indevs + outdevs +" + +AVFILTER_COMPONENTS=" + filters +" + +AVFORMAT_COMPONENTS=" + demuxers + muxers + protocols +" + +COMPONENT_LIST=" + $AVCODEC_COMPONENTS + $AVDEVICE_COMPONENTS + $AVFILTER_COMPONENTS + $AVFORMAT_COMPONENTS +" + +EXAMPLE_LIST=" + avio_http_serve_files_example + avio_list_dir_example + avio_read_callback_example + decode_audio_example + decode_filter_audio_example + decode_filter_video_example + decode_video_example + demux_decode_example + encode_audio_example + encode_video_example + extract_mvs_example + filter_audio_example + hw_decode_example + mux_example + qsv_decode_example + remux_example + resample_audio_example + scale_video_example + show_metadata_example + transcode_aac_example + transcode_example + vaapi_encode_example + vaapi_transcode_example + qsv_transcode_example +" + +EXTERNAL_AUTODETECT_LIBRARY_LIST=" + alsa + appkit + avfoundation + bzlib + coreimage + iconv + libxcb + libxcb_shm + libxcb_shape + libxcb_xfixes + lzma + mediafoundation + metal + schannel + sdl2 + securetransport + sndio + xlib + zlib +" + +EXTERNAL_LIBRARY_GPL_LIST=" + avisynth + frei0r + libcdio + libdavs2 + libdvdnav + libdvdread + librubberband + libvidstab + libx264 + libx265 + libxavs + libxavs2 + libxvid +" + +EXTERNAL_LIBRARY_NONFREE_LIST=" + decklink + libfdk_aac + libmpeghdec +" + +EXTERNAL_LIBRARY_VERSION3_LIST=" + gmp + libaribb24 + liblensfun + libopencore_amrnb + libopencore_amrwb + libvo_amrwbenc + mbedtls + rkmpp +" + +EXTERNAL_LIBRARY_GPLV3_LIST=" + libsmbclient +" + +EXTERNAL_LIBRARY_LIST=" + $EXTERNAL_LIBRARY_GPL_LIST + $EXTERNAL_LIBRARY_NONFREE_LIST + $EXTERNAL_LIBRARY_VERSION3_LIST + $EXTERNAL_LIBRARY_GPLV3_LIST + cairo + chromaprint + gcrypt + gnutls + jni + ladspa + lcms2 + libaom + libaribcaption + libass + libbluray + libbs2b + libcaca + libcodec2 + libdav1d + libdc1394 + libflite + libfontconfig + libfreetype + libfribidi + libharfbuzz + libgme + libgsm + libiec61883 + libilbc + libjack + libjxl + libklvanc + libkvazaar + liblc3 + liblcevc_dec + libmodplug + libmp3lame + libmysofa + liboapv + libonnxruntime + libopencv + libopencolorio + libopenh264 + libopenjpeg + libopenmpt + libopenvino + libopus + libplacebo + libpulse + libqrencode + libquirc + librabbitmq + librav1e + librist + librsvg + librtmp + libshine + libsmbclient + libsnappy + libsoxr + libspeex + libsrt + libssh + libsvtav1 + libsvtjpegxs + libtensorflow + libtesseract + libtheora + libtls + libtorch + libtwolame + libuavs3d + libv4l2 + libvmaf + libvorbis + libvpx + libvvenc + libwebp + libxevd + libxevdb + libxeve + libxeveb + libxml2 + libzimg + libzmq + libzvbi + lv2 + mediacodec + ohcodec + openal + opengl + openssl + pocketsphinx + vapoursynth + vulkan_static + whisper +" + +HWACCEL_AUTODETECT_LIBRARY_LIST=" + amf + audiotoolbox + cuda + cuda_llvm + cuvid + d3d11va + d3d12va + dxva2 + ffnvcodec + libdrm + nvdec + nvenc + vaapi + vdpau + videotoolbox + vulkan + v4l2_m2m +" + +# catchall list of things that require external libs to link +EXTRALIBS_LIST=" + checkasm + cpu_init + cws2fws +" + +HWACCEL_LIBRARY_NONFREE_LIST=" + cuda_nvcc + cuda_sdk +" + +HWACCEL_LIBRARY_LIST=" + $HWACCEL_LIBRARY_NONFREE_LIST + libmfx + libvpl + mmal + opencl +" + +DOCUMENT_LIST=" + doc + htmlpages + manpages + podpages + txtpages +" + +FEATURE_LIST=" + checkasm + ftrapv + gray + hardcoded_tables + runtime_cpudetect + safe_bitstream_reader + shared + small + static + swscale_alpha + unstable +" + +# this list should be kept in linking order +LIBRARY_LIST=" + avdevice + avfilter + swscale + avformat + avcodec + swresample + avutil +" + +LICENSE_LIST=" + gpl + nonfree + version3 +" + +PROGRAM_LIST=" + ffplay + ffprobe + ffmpeg +" + +SUBSYSTEM_LIST=" + dwt + error_resilience + faan + fast_unaligned + iamf + lsp + pixelutils + network +" + +# COMPONENT_LIST needs to come last to ensure correct dependency checking +CONFIG_LIST=" + $DOCUMENT_LIST + $EXAMPLE_LIST + $EXTERNAL_LIBRARY_LIST + $EXTERNAL_AUTODETECT_LIBRARY_LIST + $HWACCEL_LIBRARY_LIST + $HWACCEL_AUTODETECT_LIBRARY_LIST + $FEATURE_LIST + $LICENSE_LIST + $LIBRARY_LIST + $PROGRAM_LIST + $SUBSYSTEM_LIST + autodetect + fontconfig + large_tests + linux_perf + macos_kperf + memory_poisoning + neon_clobber_test + ossfuzz + pic + shader_compression + resource_compression + thumb + valgrind_backtrace + xmm_clobber_test + $COMPONENT_LIST +" + +THREADS_LIST=" + pthreads + os2threads + w32threads +" + +ATOMICS_LIST=" + atomics_win32 +" + +AUTODETECT_LIBS=" + $EXTERNAL_AUTODETECT_LIBRARY_LIST + $HWACCEL_AUTODETECT_LIBRARY_LIST + $THREADS_LIST +" + +ARCH_LIST=" + aarch64 + arm + ia64 + loongarch + loongarch32 + loongarch64 + m68k + mips + mips64 + parisc + ppc + ppc64 + riscv + s390 + sparc + sparc64 + tilegx + tilepro + wasm + x86 + x86_32 + x86_64 +" + +ARCH_EXT_LIST_ARM=" + armv5te + armv6 + armv6t2 + armv8 + arm_crc + dotprod + i8mm + pmull + eor3 + neon + vfp + vfpv3 + setend + sve + sve2 + sme + sme_i16i64 + sme2 +" + +ARCH_EXT_LIST_MIPS=" + mipsfpu + mips32r2 + mips32r5 + mips64r2 + mips32r6 + mips64r6 + mipsdsp + mipsdspr2 + msa +" + +ARCH_EXT_LIST_LOONGSON=" + loongson2 + loongson3 + mmi + lsx + lasx +" + +ARCH_EXT_LIST_WASM=" + simd128 +" + +ARCH_EXT_LIST_X86_SIMD=" + aesni + clmul + amd3dnow + amd3dnowext + avx + avx2 + avx512 + avx512icl + fma3 + fma4 + mmx + mmxext + sse + sse2 + sse3 + sse4 + sse42 + ssse3 + xop +" + +ARCH_EXT_LIST_PPC=" + altivec + dcbzl + ldbrx + power8 + ppc4xx + vec_xl + vsx +" + +ARCH_EXT_LIST_RISCV=" + rv + rvv + rv_zicbop + rv_zvbb +" + +ARCH_EXT_LIST_X86=" + $ARCH_EXT_LIST_X86_SIMD + i686 +" + +ARCH_EXT_LIST=" + $ARCH_EXT_LIST_ARM + $ARCH_EXT_LIST_PPC + $ARCH_EXT_LIST_RISCV + $ARCH_EXT_LIST_WASM + $ARCH_EXT_LIST_X86 + $ARCH_EXT_LIST_MIPS + $ARCH_EXT_LIST_LOONGSON +" + +ARCH_FEATURES=" + aligned_stack + fast_64bit + fast_clz + fast_cmov + fast_float16 + simd_align_16 + simd_align_32 + simd_align_64 + x86_sse2avx +" + +BUILTIN_LIST=" + MemoryBarrier + mm_empty + rdtsc + sem_timedwait + int128 +" +HAVE_LIST_CMDLINE=" + inline_asm + symver + x86asm +" + +HAVE_LIST_PUB=" + bigendian + fast_unaligned +" + +HEADERS_LIST=" + arpa_inet_h + asm_hwprobe_h + asm_types_h + cdio_paranoia_h + cdio_paranoia_paranoia_h + cuda_h + dispatch_dispatch_h + direct_h + dirent_h + dxgidebug_h + dxva_h + ES2_gl_h + gsm_h + io_h + linux_dma_buf_h + linux_perf_event_h + malloc_h + poll_h + pthread_np_h + sys_hwprobe_h + sys_param_h + sys_resource_h + sys_select_h + sys_soundcard_h + sys_time_h + sys_un_h + sys_videoio_h + termios_h + udplite_h + unistd_h + valgrind_valgrind_h + windows_h + winsock2_h + spirv_headers_spirv_h + spirv_unified1_spirv_h +" + +INTRINSICS_LIST=" + intrinsics_neon + intrinsics_sse2 +" + +MATH_FUNCS=" + atanf + atan2f + cbrt + cbrtf + copysign + cosf + erf + exp2 + exp2f + expf + hypot + isfinite + isinf + isnan + ldexpf + llrint + llrintf + log2 + log2f + log10f + lrint + lrintf + powf + rint + round + roundf + sinf + trunc + truncf +" + +SYSTEM_FEATURES=" + dos_paths + libc_msvcrt + MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS + section_data_rel_ro + threads + uwp + winrt +" + +SYSTEM_FUNCS=" + access + aligned_malloc + arc4random_buf + clock_gettime + closesocket + CommandLineToArgvW + elf_aux_info + fcntl + getaddrinfo + getauxval + getenv + gethrtime + getopt + GetModuleHandle + GetProcessAffinityMask + GetProcessMemoryInfo + GetProcessTimes + getrusage + GetStdHandle + GetSystemTimeAsFileTime + gettimeofday + glob + glXGetProcAddress + gmtime_r + inet_aton + ioctl + isatty + kbhit + localtime_r + lstat + lzo1x_999_compress + mach_absolute_time + MapViewOfFile + memalign + mkstemp + mmap + mprotect + nanosleep + PeekNamedPipe + posix_memalign + prctl + pthread_cancel + pthread_set_name_np + pthread_setaffinity_np + pthread_setname_np + sched_getaffinity + SecItemImport + SetConsoleTextAttribute + SetConsoleCtrlHandler + SetDllDirectory + setmode + setrlimit + sigaction + siglongjmp + Sleep + strerror_r + sysconf + sysctl + sysctlbyname + tempnam + usleep + UTGetOSTypeFromString + VirtualAlloc + wglGetProcAddress +" + +SYSTEM_LIBRARIES=" + bcrypt + vaapi_drm + vaapi_x11 + vaapi_win32 + vdpau_x11 +" + +TOOLCHAIN_FEATURES=" + as_arch_directive + as_archext_crc_directive + as_archext_dotprod_directive + as_archext_i8mm_directive + as_archext_sha3_directive + as_archext_aes_directive + as_archext_sve_directive + as_archext_sve2_directive + as_archext_sme_directive + as_archext_sme_i16i64_directive + as_archext_sme2_directive + as_dn_directive + as_fpu_directive + as_func + as_object_arch + asm_mod_q + blocks_extension + ebp_available + ebx_available + gnu_as + gnu_windres + ibm_asm + inline_asm_direct_symbol_refs + inline_asm_labels + inline_asm_nonlocal_labels + pragma_deprecated + rsync_contimeout + symver_asm_label + symver_gnu_asm + vfp_args + x86_32_7regs + xform_asm + xmm_clobbers +" + +TYPES_LIST=" + DPI_AWARENESS_CONTEXT + IDXGIOutput5 + __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession5 + IDirect3DDxgiInterfaceAccess + kCMVideoCodecType_HEVC + kCMVideoCodecType_HEVCWithAlpha + kCMVideoCodecType_VP9 + kCMVideoCodecType_AV1 + kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange + kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange + kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange + kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange + kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange + kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange + kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange + kCVPixelFormatType_422YpCbCr8_yuvs + kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ + kCVImageBufferTransferFunction_ITU_R_2100_HLG + kCVImageBufferTransferFunction_Linear + kCVImageBufferYCbCrMatrix_ITU_R_2020 + kCVImageBufferColorPrimaries_ITU_R_2020 + kCVImageBufferTransferFunction_ITU_R_2020 + kCVImageBufferTransferFunction_SMPTE_ST_428_1 + kVTQPModulationLevel_Default + SecPkgContext_KeyingMaterialInfo + socklen_t + struct_addrinfo + struct_group_source_req + struct_ip_mreq_source + struct_ipv6_mreq + struct_msghdr_msg_flags + struct_pollfd + struct_rusage_ru_maxrss + struct_sctp_event_subscribe + struct_sockaddr_in6 + struct_sockaddr_sa_len + struct_sockaddr_storage + struct_stat_st_mtim_tv_nsec + struct_v4l2_frmivalenum_discrete + struct_mfxConfigInterface +" + +HAVE_LIST=" + $ARCH_EXT_LIST + $(add_suffix _external $ARCH_EXT_LIST) + $(add_suffix _inline $ARCH_EXT_LIST) + $ARCH_FEATURES + $BUILTIN_LIST + $HAVE_LIST_CMDLINE + $HAVE_LIST_PUB + $HEADERS_LIST + $INTRINSICS_LIST + $MATH_FUNCS + $SYSTEM_FEATURES + $SYSTEM_FUNCS + $SYSTEM_LIBRARIES + $THREADS_LIST + $TOOLCHAIN_FEATURES + $TYPES_LIST + gzip + ioctl_posix + libdrm_getfb2 + makeinfo_html + opencl_d3d11 + opencl_drm_arm + opencl_drm_beignet + opencl_dxva2 + opencl_vaapi_beignet + opencl_vaapi_intel_media + opencl_videotoolbox + perl + pod2man + texi2html + xmllint + zlib_gzip + openvino2 +" + +# options emitted with CONFIG_ prefix but not available on the command line +CONFIG_EXTRA=" + aandcttables + ac3dsp + adts_header + atsc_a53 + audio_frame_queue + audiodsp + blockdsp + bswapdsp + cabac + cbs + cbs_apv + cbs_apv_lavf + cbs_av1 + cbs_av1_lavf + cbs_h264 + cbs_h265 + cbs_h266 + cbs_jpeg + cbs_lcevc + cbs_mpeg2 + cbs_vp8 + cbs_vp9 + celp_math + d3d12_intra_refresh + d3d12_motion_estimator + d3d12_video_process_reference_info + d3d12va_encode + d3d12va_me_precision_eighth_pixel + deflate_wrapper + dirac_parse + dnn + dovi_rpudec + dovi_rpuenc + dvprofile + evcparse + faandct + faanidct + fdctdsp + fmtconvert + frame_thread_encoder + g722dsp + golomb + gplv3 + h263dsp + h264chroma + h264dsp + h264parse + h264pred + h264qpel + h264_sei + hevcparse + hevc_sei + hpeldsp + huffman + huffyuvdsp + huffyuvencdsp + iamfdec + iamfenc + idctdsp + inflate_wrapper + intrax8 + iso_media + iso_writer + itut_t35 + ividsp + jpegtables + lgplv3 + libx262 + libx264_hdr10 + llauddsp + llviddsp + llvidencdsp + lpc + lzf + me_cmp + mpeg_er + mpegaudio + mpegaudiodsp + mpegaudioheader + mpeg4audio + mpegvideo + mpegvideodec + mpegvideoenc + mpegvideoencdsp + msmpeg4dec + msmpeg4enc + mss34dsp + pixblockdsp + qpeldsp + qsv + qsvdec + qsvenc + qsvvpp + rangecoder + riffdec + riffenc + rtpdec + rtpenc_chain + rv34dsp + scene_sad + sinewin + smpte_436m + snappy + srtp + startcode + texturedsp + texturedspenc + tpeldsp + vaapi_1 + vaapi_encode + vulkan_1_4 + vc1dsp + videodsp + vp3dsp + vp8dsp + vulkan_encode + vvc_sei + wma_freqs + wmv2dsp +" + +CMDLINE_SELECT=" + $ARCH_EXT_LIST + $CONFIG_LIST + $HAVE_LIST_CMDLINE + $THREADS_LIST + asm + cross_compile + debug + extra_warnings + libnpp + logging + optimizations + response_files + rpath + stripping + version_tracking +" + +PATHS_LIST=" + bindir + datadir + docdir + incdir + libdir + mandir + pkgconfigdir + prefix + shlibdir + install_name_dir +" + +CMDLINE_SET=" + $PATHS_LIST + ar + arch + as + assert_level + build_suffix + cc + objcc + cpu + cross_prefix + custom_allocator + cxx + dep_cc + doxygen + makeinfo + env + extra_version + gas + host_cc + host_cflags + host_extralibs + host_ld + host_ldflags + host_os + ignore_tests + install + ld + ln_s + logfile + malloc_prefix + glslc + glslcflags + metalcc + metallib + nm + optflags + nvcc + nvccflags + pkg_config + pkg_config_flags + progs_suffix + random_seed + ranlib + samples + stdc + stdcxx + strip + sws_max_filter_size + sysinclude + sysroot + target_exec + target_os + target_path + target_samples + tempprefix + toolchain + valgrind + windres + x86asmexe +" + +CMDLINE_APPEND=" + extra_cflags + extra_cxxflags + extra_objcflags + host_cppflags +" + +# code dependency declarations + +# architecture extensions + +armv5te_deps="arm" +armv6_deps="arm" +armv6t2_deps="arm" +armv8_deps="aarch64" +neon_deps_any="aarch64 arm" +intrinsics_neon_deps="neon" +intrinsics_sse2_deps="sse2" +vfp_deps="arm" +vfpv3_deps="vfp" +setend_deps="arm" +arm_crc_deps="aarch64" +dotprod_deps="aarch64 neon" +i8mm_deps="aarch64 neon" +pmull_deps="aarch64 neon" +eor3_deps="aarch64 neon" +sve_deps="aarch64 neon" +sve2_deps="aarch64 neon sve" +sme_deps="aarch64 neon sve sve2" +sme_i16i64_deps="aarch64 neon sve sve2 sme" +sme2_deps="aarch64 neon sve sve2 sme" + +map 'eval ${v}_inline_deps=inline_asm' $ARCH_EXT_LIST_ARM + +altivec_deps="ppc" +dcbzl_deps="ppc" +ldbrx_deps="ppc" +ppc4xx_deps="ppc" +vec_xl_deps="altivec" +vsx_deps="altivec" +power8_deps="vsx" + +rv_deps="riscv" +rvv_deps="rv" +rv_zicbop="riscv" +rv_zvbb_deps="rvv" + +loongson2_deps="mips" +loongson3_deps="mips" +mmi_deps_any="loongson2 loongson3" +lsx_deps="loongarch" +lasx_deps="lsx" + +mips32r2_deps="mips" +mips32r5_deps="mips" +mips32r6_deps="mips" +mips64r2_deps="mips" +mips64r6_deps="mips" +mipsfpu_deps="mips" +mipsdsp_deps="mips" +mipsdspr2_deps="mips" +msa_deps="mipsfpu" + +simd128_deps="wasm" + +x86_64_select="i686" +x86_64_suggest="fast_cmov" + +i686_deps="x86" +mmx_deps="x86" +mmxext_deps="mmx" +sse_deps="mmxext" +sse2_deps="sse" +sse3_deps="sse2" +ssse3_deps="sse3" +sse4_deps="ssse3" +sse42_deps="sse4" +aesni_deps="sse42" +clmul_deps="sse42" +avx_deps="sse42" +xop_deps="avx" +fma3_deps="avx" +fma4_deps="avx" +avx2_deps="avx" +avx512_deps="avx2" +avx512icl_deps="avx512" + +mmx_external_deps="x86asm" +mmx_inline_deps="inline_asm x86" +mmx_suggest="mmx_external mmx_inline" + +for ext in $(filter_out mmx $ARCH_EXT_LIST_X86_SIMD); do + eval dep=\$${ext}_deps + eval ${ext}_external_deps='"${dep}_external"' + eval ${ext}_inline_deps='"${dep}_inline"' + eval ${ext}_suggest='"${ext}_external ${ext}_inline"' +done + +aligned_stack_if_any="aarch64 ppc x86" +fast_64bit_if_any="aarch64 ia64 mips64 parisc64 ppc64 riscv64 sparc64 x86_64" +fast_clz_if_any="aarch64 mips ppc x86" +fast_unaligned_if_any="aarch64 ppc x86" +simd_align_16_if_any="altivec neon sse" +simd_align_32_if_any="avx" +simd_align_64_if_any="avx512" + +# system capabilities +linux_perf_deps="linux_perf_event_h" +symver_if_any="symver_asm_label symver_gnu_asm" +valgrind_backtrace_conflict="optimizations" +valgrind_backtrace_deps="valgrind_valgrind_h" + +# threading support +atomics_win32_if="MemoryBarrier" +atomics_native_if_any="$ATOMICS_LIST" +w32threads_deps="atomics_native" +threads_if_any="$THREADS_LIST" + +# subsystems +cbs_apv_select="cbs" +cbs_av1_select="cbs" +cbs_h264_select="cbs" +cbs_h265_select="cbs" +cbs_h266_select="cbs" +cbs_jpeg_select="cbs" +cbs_lcevc_select="cbs" +cbs_mpeg2_select="cbs" +cbs_vp8_select="cbs" +cbs_vp9_select="cbs" +deflate_wrapper_deps="zlib" +dirac_parse_select="golomb" +dovi_rpudec_select="golomb" +dovi_rpuenc_select="golomb" +dnn_deps="avformat swscale" +dnn_deps_any="libtensorflow libopenvino libtorch libonnxruntime" +error_resilience_select="me_cmp" +evcparse_select="golomb" +faandct_deps="faan" +faandct_select="fdctdsp" +faanidct_deps="faan" +faanidct_select="idctdsp" +h264dsp_select="startcode" +h264parse_select="golomb" +h264_sei_select="golomb itut_t35" +hevcparse_select="golomb" +hevc_sei_select="golomb itut_t35" +iso_writer_select="golomb" +frame_thread_encoder_deps="encoders threads" +iamfdec_deps="iamf" +iamfdec_select="iso_media mpeg4audio" +iamfenc_deps="iamf" +inflate_wrapper_deps="zlib" +intrax8_select="blockdsp wmv2dsp" +iso_media_select="mpeg4audio" +itut_t35_select="atsc_a53 dovi_rpudec" +me_cmp_select="idctdsp" +mpeg_er_select="error_resilience" +mpegaudio_select="mpegaudiodsp mpegaudioheader" +mpegvideo_select="blockdsp hpeldsp idctdsp videodsp" +mpegvideodec_select="h264chroma mpegvideo mpeg_er" +mpegvideoenc_select="aandcttables fdctdsp me_cmp mpegvideo mpegvideoencdsp pixblockdsp" +msmpeg4dec_select="h263_decoder" +msmpeg4enc_select="h263_encoder" +vc1dsp_select="h264chroma startcode" +vvc_sei_select="golomb itut_t35" +wmv2dsp_select="idctdsp" + +# decoders / encoders +aac_decoder_select="adts_header mpeg4audio sinewin" +aac_fixed_decoder_select="adts_header mpeg4audio" +aac_encoder_select="audio_frame_queue lpc sinewin" +aac_latm_decoder_select="aac_decoder aac_latm_parser" +ac3_decoder_select="ac3_parser ac3dsp bswapdsp fmtconvert" +ac3_fixed_decoder_select="ac3_parser ac3dsp bswapdsp" +ac3_encoder_select="ac3dsp audiodsp me_cmp" +ac3_fixed_encoder_select="ac3dsp audiodsp me_cmp" +acelp_kelvin_decoder_select="audiodsp celp_math" +adpcm_circus_decoder_deps="lgpl_gpl" +adpcm_g722_decoder_select="g722dsp" +adpcm_g722_encoder_select="g722dsp" +adpcm_ima_escape_decoder_deps="lgpl_gpl" +adpcm_ima_hvqm2_decoder_deps="lgpl_gpl" +adpcm_ima_hvqm4_decoder_deps="lgpl_gpl" +adpcm_ima_magix_decoder_deps="lgpl_gpl" +adpcm_ima_pda_decoder_deps="lgpl_gpl" +adpcm_n64_decoder_deps="lgpl_gpl" +adpcm_psxc_decoder_deps="lgpl_gpl" +agm_decoder_select="idctdsp" +ahx_decoder_deps="lgpl_gpl" +ahx_decoder_select="mpegaudio ahx_to_mp2_bsf" +aic_decoder_select="golomb idctdsp" +alac_encoder_select="lpc" +als_decoder_select="bswapdsp mpeg4audio" +amrnb_decoder_select="lsp celp_math" +amrwb_decoder_select="lsp celp_math" +amv_decoder_select="sp5x_decoder" +amv_encoder_select="jpegtables mpegvideoenc" +ape_decoder_select="bswapdsp llauddsp" +apng_decoder_select="inflate_wrapper" +apng_encoder_select="deflate_wrapper llvidencdsp" +aptx_encoder_select="audio_frame_queue" +aptx_hd_encoder_select="audio_frame_queue" +apv_decoder_select="cbs_apv" +asv1_decoder_select="blockdsp bswapdsp idctdsp" +asv1_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp" +asv2_decoder_select="blockdsp bswapdsp idctdsp" +asv2_encoder_select="aandcttables bswapdsp fdctdsp pixblockdsp" +atrac1_decoder_select="sinewin" +atrac3p_decoder_select="sinewin" +atrac3pal_decoder_select="sinewin" +av1_decoder_select="cbs_av1 itut_t35" +bink_decoder_select="blockdsp hpeldsp" +binkaudio_dct_decoder_select="wma_freqs" +binkaudio_rdft_decoder_select="wma_freqs" +cavs_decoder_select="blockdsp golomb h264chroma idctdsp qpeldsp videodsp" +clearvideo_decoder_select="idctdsp" +cllc_decoder_select="bswapdsp" +comfortnoise_encoder_select="lpc" +cook_decoder_select="audiodsp sinewin" +cri_decoder_select="mjpeg_decoder" +cscd_decoder_suggest="zlib" +dds_decoder_select="texturedsp" +dirac_decoder_select="dirac_parse dwt golomb mpegvideoencdsp qpeldsp videodsp" +dnxhd_decoder_select="blockdsp idctdsp" +dnxhd_encoder_select="blockdsp fdctdsp idctdsp mpegvideoenc pixblockdsp videodsp" +dvvideo_decoder_select="dvprofile idctdsp" +dvvideo_encoder_select="dvprofile fdctdsp me_cmp pixblockdsp" +dxa_decoder_deps="zlib" +dxv_decoder_select="lzf texturedsp" +dxv_encoder_select="texturedspenc" +eac3_decoder_select="ac3_decoder" +eac3_encoder_select="ac3_encoder" +eamad_decoder_select="aandcttables blockdsp bswapdsp" +eatgq_decoder_select="aandcttables" +eatqi_decoder_select="aandcttables blockdsp bswapdsp" +exr_decoder_deps="zlib" +exr_decoder_select="bswapdsp" +exr_encoder_deps="zlib" +ffv1_decoder_select="rangecoder" +ffv1_encoder_select="rangecoder" +ffv1_vulkan_encoder_select="vulkan spirv_compiler" +ffvhuff_decoder_select="huffyuv_decoder" +ffvhuff_encoder_select="huffyuv_encoder" +fic_decoder_select="golomb" +flac_encoder_select="bswapdsp lpc" +flashsv2_decoder_select="inflate_wrapper" +flashsv2_encoder_select="deflate_wrapper" +flashsv_decoder_select="inflate_wrapper" +flashsv_encoder_deps="zlib" +flv_decoder_select="h263_decoder" +flv_encoder_select="h263_encoder" +fourxm_decoder_select="blockdsp bswapdsp" +fraps_decoder_select="bswapdsp huffman" +ftr_decoder_select="adts_header" +g2m_decoder_deps="zlib" +g2m_decoder_select="blockdsp idctdsp jpegtables" +g723_1_decoder_select="celp_math" +g723_1_encoder_select="celp_math" +g729_decoder_select="audiodsp celp_math" +h261_decoder_select="mpegvideodec" +h261_encoder_select="mpegvideoenc" +h263_decoder_select="h263dsp mpegvideodec" +h263_encoder_select="h263dsp mpegvideoenc" +h263i_decoder_select="h263_decoder" +h263p_decoder_select="h263_decoder" +h263p_encoder_select="h263_encoder" +h264_decoder_select="cabac golomb h264chroma h264dsp h264parse h264pred h264qpel h264_sei videodsp" +h264_decoder_suggest="error_resilience" +hap_decoder_select="snappy texturedsp" +hap_encoder_deps="libsnappy" +hap_encoder_select="texturedspenc" +hevc_decoder_select="bswapdsp cabac dovi_rpudec golomb hevcparse hevc_sei videodsp" +huffyuv_decoder_select="bswapdsp huffyuvdsp llviddsp" +huffyuv_encoder_select="bswapdsp huffman huffyuvencdsp llvidencdsp" +hymt_decoder_select="huffyuv_decoder" +iac_decoder_select="imc_decoder" +imc_decoder_select="bswapdsp sinewin" +imm4_decoder_select="bswapdsp idctdsp" +imm5_decoder_select="h264_decoder hevc_decoder" +indeo3_decoder_select="hpeldsp" +indeo4_decoder_select="ividsp" +indeo5_decoder_select="ividsp" +interplay_video_decoder_select="hpeldsp" +ipu_decoder_select="mpegvideodec" +jpegls_decoder_select="mjpeg_decoder" +jv_decoder_select="blockdsp" +lagarith_decoder_select="llviddsp" +lead_decoder_select="blockdsp idctdsp jpegtables" +ljpeg_encoder_select="jpegtables" +lscr_decoder_select="inflate_wrapper" +magicyuv_decoder_select="llviddsp" +magicyuv_encoder_select="llvidencdsp" +mdec_decoder_select="blockdsp bswapdsp idctdsp" +media100_decoder_select="media100_to_mjpegb_bsf mjpegb_decoder" +metasound_decoder_select="lsp sinewin" +mimic_decoder_select="blockdsp bswapdsp hpeldsp idctdsp" +mjpeg_decoder_select="blockdsp idctdsp jpegtables" +mjpeg_encoder_select="jpegtables mpegvideoenc" +mjpegb_decoder_select="hpeldsp mjpeg_decoder" +mlp_decoder_select="mlp_parser" +mlp_encoder_select="lpc audio_frame_queue" +mobiclip_decoder_select="bswapdsp golomb" +motionpixels_decoder_select="bswapdsp" +mp1_decoder_select="mpegaudio" +mp1float_decoder_select="mpegaudio" +mp2_decoder_select="mpegaudio" +mp2float_decoder_select="mpegaudio" +mp3_decoder_select="mpegaudio" +mp3adu_decoder_select="mpegaudio" +mp3adufloat_decoder_select="mpegaudio" +mp3float_decoder_select="mpegaudio" +mp3on4_decoder_select="mpegaudio mpeg4audio" +mp3on4float_decoder_select="mpegaudio mpeg4audio" +mpc7_decoder_select="bswapdsp mpegaudiodsp" +mpc8_decoder_select="mpegaudiodsp" +mpegvideo_decoder_select="mpegvideodec" +mpeg1video_decoder_select="mpegvideodec" +mpeg1video_encoder_select="mpegvideoenc" +mpeg2video_decoder_select="mpegvideodec" +mpeg2video_encoder_select="mpegvideoenc" +mpeg4_decoder_select="h263_decoder qpeldsp" +mpeg4_encoder_select="h263_encoder qpeldsp" +msa1_decoder_select="mss34dsp" +mscc_decoder_select="inflate_wrapper" +msmpeg4v1_decoder_select="msmpeg4dec" +msmpeg4v2_decoder_select="msmpeg4dec" +msmpeg4v2_encoder_select="msmpeg4enc" +msmpeg4v3_decoder_select="msmpeg4dec" +msmpeg4v3_encoder_select="msmpeg4enc" +mss2_decoder_select="mpegvideodec vc1_decoder" +mts2_decoder_select="jpegtables mss34dsp" +mv30_decoder_select="aandcttables blockdsp" +mvha_decoder_select="inflate_wrapper llviddsp" +mwsc_decoder_select="inflate_wrapper" +mxpeg_decoder_select="hpeldsp mjpeg_decoder" +nellymoser_decoder_select="sinewin" +nellymoser_encoder_select="audio_frame_queue sinewin" +notchlc_decoder_select="lzf" +nuv_decoder_select="idctdsp" +opus_decoder_deps="swresample" +opus_encoder_select="audio_frame_queue" +pdv_decoder_select="inflate_wrapper" +pdv_encoder_select="deflate_wrapper" +png_decoder_select="inflate_wrapper" +png_encoder_select="deflate_wrapper llvidencdsp" +prores_decoder_select="blockdsp idctdsp" +prores_encoder_select="fdctdsp" +prores_aw_encoder_select="fdctdsp" +prores_ks_encoder_select="fdctdsp" +prores_ks_vulkan_encoder_select="vulkan spirv_compiler" +prores_raw_decoder_select="blockdsp idctdsp" +qcelp_decoder_select="lsp" +qdm2_decoder_select="mpegaudiodsp" +ra_144_decoder_select="audiodsp" +ra_144_encoder_select="audio_frame_queue lpc audiodsp" +ralf_decoder_select="golomb" +rasc_decoder_select="inflate_wrapper" +rawvideo_decoder_select="bswapdsp" +rscc_decoder_deps="zlib" +rtv1_decoder_select="texturedsp" +rv10_decoder_select="h263_decoder" +rv10_encoder_select="h263_encoder" +rv20_decoder_select="h263_decoder" +rv20_encoder_select="h263_encoder" +rv30_decoder_select="golomb h264pred h264qpel mpegvideodec rv34dsp" +rv40_decoder_select="golomb h264pred h264qpel mpegvideodec rv34dsp" +rv60_decoder_select="videodsp golomb" +screenpresso_decoder_deps="zlib" +shorten_decoder_select="bswapdsp" +sipr_decoder_select="lsp celp_math" +smvjpeg_decoder_select="mjpeg_decoder" +snow_decoder_select="dwt h264qpel rangecoder videodsp" +snow_encoder_select="dwt h264qpel hpeldsp me_cmp mpegvideoencdsp rangecoder videodsp" +sp5x_decoder_select="mjpeg_decoder" +speedhq_decoder_select="blockdsp idctdsp" +speedhq_encoder_select="mpegvideoenc" +srgc_decoder_select="inflate_wrapper" +svq1_decoder_select="hpeldsp" +svq1_encoder_select="hpeldsp me_cmp mpegvideoencdsp" +svq3_decoder_select="golomb h264dsp h264parse h264pred hpeldsp tpeldsp videodsp" +svq3_decoder_suggest="zlib" +tak_decoder_select="audiodsp" +tdsc_decoder_deps="zlib" +tdsc_decoder_select="mjpeg_decoder" +theora_decoder_select="vp3_decoder" +thp_decoder_select="mjpeg_decoder" +tiff_decoder_select="mjpeg_decoder" +tiff_decoder_suggest="zlib lzma" +tiff_encoder_suggest="zlib" +truehd_decoder_select="mlp_parser" +truehd_encoder_select="lpc audio_frame_queue" +truemotion2_decoder_select="bswapdsp" +truespeech_decoder_select="bswapdsp" +tscc_decoder_select="inflate_wrapper" +twinvq_decoder_select="lsp sinewin" +txd_decoder_select="texturedsp" +utvideo_decoder_select="bswapdsp llviddsp" +utvideo_encoder_select="bswapdsp huffman llvidencdsp" +vble_decoder_select="llviddsp" +vbn_decoder_select="texturedsp" +vbn_encoder_select="texturedspenc" +vmix_decoder_select="idctdsp" +vc1_decoder_select="blockdsp h264qpel intrax8 mpegvideodec qpeldsp vc1dsp" +vc1image_decoder_select="vc1_decoder" +vorbis_encoder_select="audio_frame_queue" +vp3_decoder_select="hpeldsp vp3dsp videodsp" +vp4_decoder_select="vp3_decoder" +vp5_decoder_select="h264chroma hpeldsp videodsp vp3dsp" +vp6_decoder_select="h264chroma hpeldsp huffman videodsp vp3dsp" +vp6a_decoder_select="vp6_decoder" +vp6f_decoder_select="vp6_decoder" +vp7_decoder_select="h264pred videodsp vp8dsp" +vp8_decoder_select="h264pred videodsp vp8dsp" +vp9_decoder_select="videodsp vp9_parser cbs_vp9 vp9_superframe_split_bsf" +vvc_decoder_select="cabac cbs_h266 golomb videodsp vvc_sei" +wcmv_decoder_select="inflate_wrapper" +webp_decoder_select="vp8_decoder" +webp_anim_decoder_select="vp8_decoder" +wmalossless_decoder_select="llauddsp" +wmapro_decoder_select="sinewin wma_freqs" +wmav1_decoder_select="sinewin wma_freqs" +wmav1_encoder_select="sinewin wma_freqs" +wmav2_decoder_select="sinewin wma_freqs" +wmav2_encoder_select="sinewin wma_freqs" +wmavoice_decoder_select="lsp sinewin" +wmv1_decoder_select="msmpeg4dec" +wmv1_encoder_select="msmpeg4enc" +wmv2_decoder_select="blockdsp error_resilience idctdsp intrax8 msmpeg4dec qpeldsp videodsp wmv2dsp" +wmv2_encoder_select="msmpeg4enc wmv2dsp" +wmv3_decoder_select="vc1_decoder" +wmv3image_decoder_select="wmv3_decoder" +xma1_decoder_select="wmapro_decoder" +xma2_decoder_select="wmapro_decoder" +ylc_decoder_select="bswapdsp" +zerocodec_decoder_select="inflate_wrapper" +zlib_decoder_select="inflate_wrapper" +zlib_encoder_select="deflate_wrapper" +zmbv_decoder_select="inflate_wrapper" +zmbv_encoder_select="deflate_wrapper" + +# hardware accelerators +cuda_deps="ffnvcodec" +cuvid_deps="ffnvcodec" +d3d11va_deps="dxva_h ID3D11VideoDecoder ID3D11VideoContext" +d3d12va_deps="dxva_h ID3D12Device ID3D12VideoDecoder" +dxva2_deps="dxva2api_h DXVA2_ConfigPictureDecode ole32 user32" +ffnvcodec_deps_any="libdl LoadLibrary" +mediacodec_deps="android mediandk pthreads" +nvdec_deps="ffnvcodec" +vaapi_x11_deps="xlib_x11" +videotoolbox_hwaccel_deps="videotoolbox pthreads" +videotoolbox_hwaccel_extralibs="-framework QuartzCore" +vulkan_deps="threads" +vulkan_deps_any="libdl LoadLibrary" + +apv_vulkan_hwaccel_deps="vulkan spirv_compiler" +apv_vulkan_hwaccel_select="apv_decoder" +av1_d3d11va_hwaccel_deps="d3d11va DXVA_PicParams_AV1" +av1_d3d11va_hwaccel_select="av1_decoder" +av1_d3d11va2_hwaccel_deps="d3d11va DXVA_PicParams_AV1" +av1_d3d11va2_hwaccel_select="av1_decoder" +av1_d3d12va_hwaccel_deps="d3d12va DXVA_PicParams_AV1" +av1_d3d12va_hwaccel_select="av1_decoder" +av1_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_AV1" +av1_dxva2_hwaccel_select="av1_decoder" +av1_nvdec_hwaccel_deps="nvdec CUVIDAV1PICPARAMS" +av1_nvdec_hwaccel_select="av1_decoder" +av1_vaapi_hwaccel_deps="vaapi VADecPictureParameterBufferAV1_bit_depth_idx" +av1_vaapi_hwaccel_select="av1_decoder" +av1_vdpau_hwaccel_deps="vdpau VdpPictureInfoAV1" +av1_vdpau_hwaccel_select="av1_decoder" +av1_videotoolbox_hwaccel_deps="videotoolbox" +av1_videotoolbox_hwaccel_select="av1_decoder" +av1_vulkan_hwaccel_deps="vulkan" +av1_vulkan_hwaccel_select="av1_decoder" +dpx_vulkan_hwaccel_deps="vulkan spirv_compiler" +dpx_vulkan_hwaccel_select="dpx_decoder" +ffv1_vulkan_hwaccel_deps="vulkan spirv_compiler" +ffv1_vulkan_hwaccel_select="ffv1_decoder" +h263_vaapi_hwaccel_deps="vaapi" +h263_vaapi_hwaccel_select="h263_decoder" +h263_videotoolbox_hwaccel_deps="videotoolbox" +h263_videotoolbox_hwaccel_select="h263_decoder" +h264_d3d11va_hwaccel_deps="d3d11va" +h264_d3d11va_hwaccel_select="h264_decoder" +h264_d3d11va2_hwaccel_deps="d3d11va" +h264_d3d11va2_hwaccel_select="h264_decoder" +h264_d3d12va_hwaccel_deps="d3d12va" +h264_d3d12va_hwaccel_select="h264_decoder" +h264_dxva2_hwaccel_deps="dxva2" +h264_dxva2_hwaccel_select="h264_decoder" +h264_nvdec_hwaccel_deps="nvdec" +h264_nvdec_hwaccel_select="h264_decoder" +h264_vaapi_hwaccel_deps="vaapi" +h264_vaapi_hwaccel_select="h264_decoder" +h264_vdpau_hwaccel_deps="vdpau" +h264_vdpau_hwaccel_select="h264_decoder" +h264_videotoolbox_hwaccel_deps="videotoolbox" +h264_videotoolbox_hwaccel_select="h264_decoder" +h264_vulkan_hwaccel_deps="vulkan" +h264_vulkan_hwaccel_select="h264_decoder" +hevc_d3d11va_hwaccel_deps="d3d11va DXVA_PicParams_HEVC" +hevc_d3d11va_hwaccel_select="hevc_decoder" +hevc_d3d11va2_hwaccel_deps="d3d11va DXVA_PicParams_HEVC" +hevc_d3d11va2_hwaccel_select="hevc_decoder" +hevc_d3d12va_hwaccel_deps="d3d12va DXVA_PicParams_HEVC" +hevc_d3d12va_hwaccel_select="hevc_decoder" +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" +hevc_vaapi_hwaccel_deps="vaapi VAPictureParameterBufferHEVC" +hevc_vaapi_hwaccel_select="hevc_decoder" +hevc_vdpau_hwaccel_deps="vdpau VdpPictureInfoHEVC" +hevc_vdpau_hwaccel_select="hevc_decoder" +hevc_videotoolbox_hwaccel_deps="videotoolbox" +hevc_videotoolbox_hwaccel_select="hevc_decoder" +hevc_vulkan_hwaccel_deps="vulkan" +hevc_vulkan_hwaccel_select="hevc_decoder" +mjpeg_nvdec_hwaccel_deps="nvdec" +mjpeg_nvdec_hwaccel_select="mjpeg_decoder" +mjpeg_vaapi_hwaccel_deps="vaapi" +mjpeg_vaapi_hwaccel_select="mjpeg_decoder" +mpeg1_nvdec_hwaccel_deps="nvdec" +mpeg1_nvdec_hwaccel_select="mpeg1video_decoder" +mpeg1_vdpau_hwaccel_deps="vdpau" +mpeg1_vdpau_hwaccel_select="mpeg1video_decoder" +mpeg1_videotoolbox_hwaccel_deps="videotoolbox" +mpeg1_videotoolbox_hwaccel_select="mpeg1video_decoder" +mpeg2_d3d11va_hwaccel_deps="d3d11va" +mpeg2_d3d11va_hwaccel_select="mpeg2video_decoder" +mpeg2_d3d11va2_hwaccel_deps="d3d11va" +mpeg2_d3d11va2_hwaccel_select="mpeg2video_decoder" +mpeg2_d3d12va_hwaccel_deps="d3d12va" +mpeg2_d3d12va_hwaccel_select="mpeg2video_decoder" +mpeg2_dxva2_hwaccel_deps="dxva2" +mpeg2_dxva2_hwaccel_select="mpeg2video_decoder" +mpeg2_nvdec_hwaccel_deps="nvdec" +mpeg2_nvdec_hwaccel_select="mpeg2video_decoder" +mpeg2_vaapi_hwaccel_deps="vaapi" +mpeg2_vaapi_hwaccel_select="mpeg2video_decoder" +mpeg2_vdpau_hwaccel_deps="vdpau" +mpeg2_vdpau_hwaccel_select="mpeg2video_decoder" +mpeg2_videotoolbox_hwaccel_deps="videotoolbox" +mpeg2_videotoolbox_hwaccel_select="mpeg2video_decoder" +mpeg4_nvdec_hwaccel_deps="nvdec" +mpeg4_nvdec_hwaccel_select="mpeg4_decoder" +mpeg4_vaapi_hwaccel_deps="vaapi" +mpeg4_vaapi_hwaccel_select="mpeg4_decoder" +mpeg4_vdpau_hwaccel_deps="vdpau" +mpeg4_vdpau_hwaccel_select="mpeg4_decoder" +mpeg4_videotoolbox_hwaccel_deps="videotoolbox" +mpeg4_videotoolbox_hwaccel_select="mpeg4_decoder" +prores_videotoolbox_hwaccel_deps="videotoolbox" +prores_videotoolbox_hwaccel_select="prores_decoder" +prores_raw_videotoolbox_hwaccel_deps="videotoolbox" +prores_raw_videotoolbox_hwaccel_select="prores_raw_decoder" +prores_raw_vulkan_hwaccel_deps="vulkan spirv_compiler" +prores_raw_vulkan_hwaccel_select="prores_raw_decoder" +prores_vulkan_hwaccel_deps="vulkan spirv_compiler" +prores_vulkan_hwaccel_select="prores_decoder" +vc1_d3d11va_hwaccel_deps="d3d11va" +vc1_d3d11va_hwaccel_select="vc1_decoder" +vc1_d3d11va2_hwaccel_deps="d3d11va" +vc1_d3d11va2_hwaccel_select="vc1_decoder" +vc1_d3d12va_hwaccel_deps="d3d12va" +vc1_d3d12va_hwaccel_select="vc1_decoder" +vc1_dxva2_hwaccel_deps="dxva2" +vc1_dxva2_hwaccel_select="vc1_decoder" +vc1_nvdec_hwaccel_deps="nvdec" +vc1_nvdec_hwaccel_select="vc1_decoder" +vc1_vaapi_hwaccel_deps="vaapi" +vc1_vaapi_hwaccel_select="vc1_decoder" +vc1_vdpau_hwaccel_deps="vdpau" +vc1_vdpau_hwaccel_select="vc1_decoder" +vp8_nvdec_hwaccel_deps="nvdec" +vp8_nvdec_hwaccel_select="vp8_decoder" +vp8_vaapi_hwaccel_deps="vaapi" +vp8_vaapi_hwaccel_select="vp8_decoder" +vp9_d3d11va_hwaccel_deps="d3d11va DXVA_PicParams_VP9" +vp9_d3d11va_hwaccel_select="vp9_decoder" +vp9_d3d11va2_hwaccel_deps="d3d11va DXVA_PicParams_VP9" +vp9_d3d11va2_hwaccel_select="vp9_decoder" +vp9_d3d12va_hwaccel_deps="d3d12va DXVA_PicParams_VP9" +vp9_d3d12va_hwaccel_select="vp9_decoder" +vp9_dxva2_hwaccel_deps="dxva2 DXVA_PicParams_VP9" +vp9_dxva2_hwaccel_select="vp9_decoder" +vp9_nvdec_hwaccel_deps="nvdec" +vp9_nvdec_hwaccel_select="vp9_decoder" +vp9_vaapi_hwaccel_deps="vaapi VADecPictureParameterBufferVP9_bit_depth" +vp9_vaapi_hwaccel_select="vp9_decoder" +vp9_vdpau_hwaccel_deps="vdpau VdpPictureInfoVP9" +vp9_vdpau_hwaccel_select="vp9_decoder" +vp9_videotoolbox_hwaccel_deps="videotoolbox" +vp9_videotoolbox_hwaccel_select="vp9_decoder" +vp9_vulkan_hwaccel_deps="vulkan vulkan_1_4" +vp9_vulkan_hwaccel_select="vp9_decoder" +vvc_vaapi_hwaccel_deps="vaapi VAPictureParameterBufferVVC" +vvc_vaapi_hwaccel_select="vvc_decoder" +wmv3_d3d11va_hwaccel_select="vc1_d3d11va_hwaccel" +wmv3_d3d11va2_hwaccel_select="vc1_d3d11va2_hwaccel" +wmv3_d3d12va_hwaccel_select="vc1_d3d12va_hwaccel" +wmv3_dxva2_hwaccel_select="vc1_dxva2_hwaccel" +wmv3_nvdec_hwaccel_select="vc1_nvdec_hwaccel" +wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" +wmv3_vdpau_hwaccel_select="vc1_vdpau_hwaccel" + +# hardware-accelerated codecs +d3d12va_encode_deps="d3d12va ID3D12VideoEncoder d3d12_encoder_feature" +mediafoundation_deps="mftransform_h MFCreateAlignedMemoryBuffer" +qsv_deps="libmfx" +qsvdec_select="qsv" +qsvenc_select="qsv" +qsvvpp_select="qsv" +vaapi_encode_deps="vaapi" +vulkan_1_4_deps="vulkan" +vulkan_encode_deps="vulkan" +v4l2_m2m_deps="linux_videodev2_h sem_timedwait" + +bilateral_cuda_filter_deps="ffnvcodec" +bilateral_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +chromakey_cuda_filter_deps="ffnvcodec" +chromakey_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +colorspace_cuda_filter_deps="ffnvcodec" +colorspace_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +hwupload_cuda_filter_deps="ffnvcodec" +scale_cuda_filter_deps="ffnvcodec" +scale_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +thumbnail_cuda_filter_deps="ffnvcodec" +thumbnail_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +transpose_cuda_filter_deps="ffnvcodec" +transpose_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +overlay_cuda_filter_deps="ffnvcodec" +overlay_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +pad_cuda_filter_deps="ffnvcodec" +pad_cuda_filter_deps_any="cuda_nvcc cuda_llvm" + +ddagrab_filter_deps="d3d11va IDXGIOutput1 DXGI_OUTDUPL_FRAME_INFO" +gfxcapture_filter_deps="cxx17 threads d3d11va IGraphicsCaptureItemInterop __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession3" +gfxcapture_filter_extralibs="-lstdc++" +scale_d3d11_filter_deps="d3d11va" +scale_d3d12_filter_deps="d3d12va ID3D12VideoProcessor" +deinterlace_d3d12_filter_deps="d3d12va ID3D12VideoProcessor" +mestimate_d3d12_filter_deps="d3d12va ID3D12VideoMotionEstimator d3d12_motion_estimator" + +amf_deps_any="libdl LoadLibrary" +nvenc_deps="ffnvcodec" +nvenc_deps_any="libdl LoadLibrary" + +aac_mediacodec_decoder_deps="mediacodec" +aac_mediacodec_decoder_select="aac_adtstoasc_bsf aac_parser" +aac_mf_encoder_deps="mediafoundation" +ac3_mf_encoder_deps="mediafoundation" +amrnb_mediacodec_decoder_deps="mediacodec" +amrnb_mediacodec_decoder_select="amr_parser" +amrwb_mediacodec_decoder_deps="mediacodec" +amrwb_mediacodec_decoder_select="amr_parser" +av1_amf_encoder_deps="amf" +av1_amf_decoder_deps="amf" +av1_cuvid_decoder_deps="cuvid CUVIDAV1PICPARAMS" +av1_d3d12va_encoder_deps="d3d12va d3d12va_av1_headers" +av1_d3d12va_encoder_select="cbs_av1 d3d12va_encode" +av1_mediacodec_decoder_deps="mediacodec" +av1_mediacodec_encoder_deps="mediacodec" +av1_mediacodec_encoder_select="extract_extradata_bsf" +av1_mf_encoder_deps="mediafoundation" +av1_nvenc_encoder_deps="nvenc NV_ENC_PIC_PARAMS_AV1" +av1_nvenc_encoder_select="atsc_a53" +av1_qsv_decoder_select="qsvdec" +av1_qsv_encoder_deps="libvpl" +av1_qsv_encoder_select="qsvenc" +av1_vaapi_encoder_deps="VAEncPictureParameterBufferAV1" +av1_vaapi_encoder_select="cbs_av1 vaapi_encode" +av1_vulkan_encoder_deps="vulkan_1_4" +av1_vulkan_encoder_select="cbs_av1 vulkan_encode" +h263_v4l2m2m_decoder_deps="v4l2_m2m h263_v4l2_m2m" +h263_v4l2m2m_encoder_deps="v4l2_m2m h263_v4l2_m2m" +h264_amf_encoder_deps="amf" +h264_amf_decoder_deps="amf" +h264_cuvid_decoder_deps="cuvid" +h264_cuvid_decoder_select="h264_mp4toannexb_bsf" +h264_d3d12va_encoder_select="cbs_h264 d3d12va_encode" +h264_mediacodec_decoder_deps="mediacodec" +h264_mediacodec_decoder_select="h264_mp4toannexb_bsf h264_parser" +h264_mediacodec_encoder_deps="mediacodec" +h264_mediacodec_encoder_select="extract_extradata_bsf h264_metadata" +h264_mf_encoder_deps="mediafoundation" +h264_mmal_decoder_deps="mmal" +h264_nvenc_encoder_deps="nvenc" +h264_nvenc_encoder_select="atsc_a53" +h264_oh_decoder_deps="ohcodec" +h264_oh_decoder_select="h264_mp4toannexb_bsf" +h264_oh_encoder_deps="ohcodec" +h264_qsv_decoder_select="h264_mp4toannexb_bsf qsvdec" +h264_qsv_encoder_select="atsc_a53 qsvenc" +h264_rkmpp_decoder_deps="rkmpp" +h264_rkmpp_decoder_select="h264_mp4toannexb_bsf" +h264_rkmpp_encoder_deps="rkmpp" +h264_vaapi_encoder_select="atsc_a53 cbs_h264 vaapi_encode" +h264_vulkan_encoder_select="atsc_a53 cbs_h264 vulkan_encode" +h264_v4l2m2m_decoder_deps="v4l2_m2m h264_v4l2_m2m" +h264_v4l2m2m_decoder_select="h264_mp4toannexb_bsf" +h264_v4l2m2m_encoder_deps="v4l2_m2m h264_v4l2_m2m" +hevc_amf_encoder_deps="amf" +hevc_amf_decoder_deps="amf" +hevc_cuvid_decoder_deps="cuvid" +hevc_cuvid_decoder_select="hevc_mp4toannexb_bsf" +hevc_d3d12va_encoder_select="cbs_h265 d3d12va_encode" +hevc_mediacodec_decoder_deps="mediacodec" +hevc_mediacodec_decoder_select="hevc_mp4toannexb_bsf hevc_parser" +hevc_mediacodec_encoder_deps="mediacodec" +hevc_mediacodec_encoder_select="extract_extradata_bsf hevc_metadata" +hevc_mf_encoder_deps="mediafoundation" +hevc_nvenc_encoder_deps="nvenc" +hevc_nvenc_encoder_select="atsc_a53" +hevc_oh_decoder_deps="ohcodec" +hevc_oh_decoder_select="hevc_mp4toannexb_bsf" +hevc_oh_encoder_deps="ohcodec" +hevc_qsv_decoder_select="hevc_mp4toannexb_bsf qsvdec" +hevc_qsv_encoder_select="hevcparse qsvenc" +hevc_rkmpp_decoder_deps="rkmpp" +hevc_rkmpp_decoder_select="hevc_mp4toannexb_bsf" +hevc_rkmpp_encoder_deps="rkmpp" +hevc_vaapi_encoder_deps="VAEncPictureParameterBufferHEVC" +hevc_vaapi_encoder_select="atsc_a53 cbs_h265 vaapi_encode" +hevc_vulkan_encoder_select="atsc_a53 cbs_h265 vulkan_encode" +hevc_v4l2m2m_decoder_deps="v4l2_m2m hevc_v4l2_m2m" +hevc_v4l2m2m_decoder_select="hevc_mp4toannexb_bsf" +hevc_v4l2m2m_encoder_deps="v4l2_m2m hevc_v4l2_m2m" +mjpeg_cuvid_decoder_deps="cuvid" +mjpeg_qsv_decoder_select="qsvdec" +mjpeg_qsv_encoder_deps="libmfx" +mjpeg_qsv_encoder_select="qsvenc" +mjpeg_vaapi_encoder_deps="VAEncPictureParameterBufferJPEG" +mjpeg_vaapi_encoder_select="cbs_jpeg jpegtables vaapi_encode" +mp3_mf_encoder_deps="mediafoundation" +mp3_mediacodec_decoder_deps="mediacodec" +mp3_mediacodec_decoder_select="mpegaudioheader" +mpeg1_cuvid_decoder_deps="cuvid" +mpeg1_v4l2m2m_decoder_deps="v4l2_m2m mpeg1_v4l2_m2m" +mpeg2_cuvid_decoder_deps="cuvid" +mpeg2_mmal_decoder_deps="mmal" +mpeg2_mediacodec_decoder_deps="mediacodec" +mpeg2_qsv_decoder_select="qsvdec" +mpeg2_qsv_encoder_select="qsvenc" +mpeg2_vaapi_encoder_select="cbs_mpeg2 vaapi_encode" +mpeg2_v4l2m2m_decoder_deps="v4l2_m2m mpeg2_v4l2_m2m" +mpeg4_cuvid_decoder_deps="cuvid" +mpeg4_mediacodec_decoder_deps="mediacodec" +mpeg4_mediacodec_encoder_deps="mediacodec" +mpeg4_mediacodec_encoder_select="extract_extradata_bsf" +mpeg4_mmal_decoder_deps="mmal" +mpeg4_v4l2m2m_decoder_deps="v4l2_m2m mpeg4_v4l2_m2m" +mpeg4_v4l2m2m_encoder_deps="v4l2_m2m mpeg4_v4l2_m2m" +vc1_cuvid_decoder_deps="cuvid" +vc1_mmal_decoder_deps="mmal" +vc1_qsv_decoder_select="qsvdec" +vc1_v4l2m2m_decoder_deps="v4l2_m2m vc1_v4l2_m2m" +vp8_cuvid_decoder_deps="cuvid" +vp8_mediacodec_decoder_deps="mediacodec" +vp8_mediacodec_encoder_deps="mediacodec" +vp8_qsv_decoder_select="qsvdec" +vp8_rkmpp_decoder_deps="rkmpp" +vp8_vaapi_encoder_deps="VAEncPictureParameterBufferVP8" +vp8_vaapi_encoder_select="vaapi_encode" +vp8_v4l2m2m_decoder_deps="v4l2_m2m vp8_v4l2_m2m" +vp8_v4l2m2m_encoder_deps="v4l2_m2m vp8_v4l2_m2m" +vp9_amf_decoder_deps="amf" +vp9_cuvid_decoder_deps="cuvid" +vp9_mediacodec_decoder_deps="mediacodec" +vp9_mediacodec_encoder_deps="mediacodec" +vp9_qsv_decoder_select="qsvdec" +vp9_rkmpp_decoder_deps="rkmpp" +vp9_vaapi_encoder_deps="VAEncPictureParameterBufferVP9" +vp9_vaapi_encoder_select="vaapi_encode" +vp9_qsv_encoder_deps="libmfx MFX_CODEC_VP9" +vp9_qsv_encoder_select="qsvenc" +vp9_v4l2m2m_decoder_deps="v4l2_m2m vp9_v4l2_m2m" +amf_capture_filter_deps="amf" +vvc_qsv_decoder_select="vvc_mp4toannexb_bsf qsvdec" + +# parsers +aac_parser_select="adts_header mpeg4audio" +ahx_parser_deps="lgpl_gpl" +apv_parser_select="cbs_apv" +av1_parser_select="cbs_av1" +evc_parser_select="evcparse" +ffv1_parser_select="rangecoder" +ftr_parser_select="adts_header mpeg4audio" +h264_parser_select="golomb h264dsp h264parse h264_sei" +hevc_parser_select="hevcparse hevc_sei" +mpegaudio_parser_select="mpegaudioheader" +mpeg4video_parser_select="mpegvideodec" +vc1_parser_select="vc1dsp" +vvc_parser_select="cbs_h266" + +# bitstream_filters +aac_adtstoasc_bsf_select="adts_header mpeg4audio" +ahx_to_mp2_bsf_deps="lgpl_gpl" +apv_metadata_bsf_select="cbs_apv" +av1_frame_merge_bsf_select="cbs_av1" +av1_frame_split_bsf_select="cbs_av1" +av1_metadata_bsf_select="cbs_av1" +dovi_rpu_bsf_select="cbs_h265 cbs_av1 dovi_rpudec dovi_rpuenc" +dovi_split_bsf_select="hevcparse" +dts2pts_bsf_select="cbs_h264 h264parse cbs_h265 hevc_parser" +eac3_core_bsf_select="ac3_parser" +eia608_to_smpte436m_bsf_select="smpte_436m" +evc_frame_merge_bsf_select="evcparse" +filter_units_bsf_select="cbs" +h264_metadata_bsf_deps="const_nan" +h264_metadata_bsf_select="cbs_h264" +h264_redundant_pps_bsf_select="cbs_h264" +hevc_metadata_bsf_select="cbs_h265" +lcevc_metadata_bsf_select="cbs_lcevc" +mjpeg2jpeg_bsf_select="jpegtables" +mpeg2_metadata_bsf_select="cbs_mpeg2" +smpte436m_to_eia608_bsf_select="smpte_436m" +trace_headers_bsf_select="cbs cbs_vp8" +vp9_metadata_bsf_select="cbs_vp9" +vvc_metadata_bsf_select="cbs_h266" + +# external libraries +aac_at_decoder_deps="audiotoolbox" +aac_at_decoder_select="aac_adtstoasc_bsf" +ac3_at_decoder_deps="audiotoolbox" +ac3_at_decoder_select="ac3_parser" +adpcm_ima_qt_at_decoder_deps="audiotoolbox" +alac_at_decoder_deps="audiotoolbox" +amr_nb_at_decoder_deps="audiotoolbox" +avisynth_deps_any="libdl LoadLibrary" +avisynth_demuxer_deps="avisynth" +avisynth_demuxer_select="riffdec" +eac3_at_decoder_deps="audiotoolbox" +eac3_at_decoder_select="ac3_parser" +gsm_ms_at_decoder_deps="audiotoolbox" +ilbc_at_decoder_deps="audiotoolbox" +mp1_at_decoder_deps="audiotoolbox" +mp2_at_decoder_deps="audiotoolbox" +mp3_at_decoder_deps="audiotoolbox" +mp1_at_decoder_select="mpegaudioheader" +mp2_at_decoder_select="mpegaudioheader" +mp3_at_decoder_select="mpegaudioheader" +pcm_alaw_at_decoder_deps="audiotoolbox" +pcm_mulaw_at_decoder_deps="audiotoolbox" +qdmc_at_decoder_deps="audiotoolbox" +qdm2_at_decoder_deps="audiotoolbox" +aac_at_encoder_deps="audiotoolbox" +aac_at_encoder_select="audio_frame_queue" +alac_at_encoder_deps="audiotoolbox" +alac_at_encoder_select="audio_frame_queue" +ilbc_at_encoder_deps="audiotoolbox" +ilbc_at_encoder_select="audio_frame_queue" +pcm_alaw_at_encoder_deps="audiotoolbox" +pcm_alaw_at_encoder_select="audio_frame_queue" +pcm_mulaw_at_encoder_deps="audiotoolbox" +pcm_mulaw_at_encoder_select="audio_frame_queue" +chromaprint_muxer_deps="chromaprint" +h264_videotoolbox_encoder_deps="pthreads" +h264_videotoolbox_encoder_select="atsc_a53 videotoolbox_encoder" +hevc_videotoolbox_encoder_deps="pthreads" +hevc_videotoolbox_encoder_select="atsc_a53 videotoolbox_encoder" +prores_videotoolbox_encoder_deps="pthreads" +prores_videotoolbox_encoder_select="videotoolbox_encoder" +libaom_av1_decoder_deps="libaom" +libaom_av1_decoder_select="itut_t35" +libaom_av1_encoder_deps="libaom" +libaom_av1_encoder_select="extract_extradata_bsf dovi_rpuenc" +libaribb24_decoder_deps="libaribb24" +libaribcaption_decoder_deps="libaribcaption" +libcodec2_decoder_deps="libcodec2" +libcodec2_encoder_deps="libcodec2" +libdav1d_decoder_deps="libdav1d" +libdav1d_decoder_select="itut_t35" +libdavs2_decoder_deps="libdavs2" +libdavs2_decoder_select="avs2_parser" +libfdk_aac_decoder_deps="libfdk_aac" +libfdk_aac_encoder_deps="libfdk_aac" +libfdk_aac_encoder_select="audio_frame_queue" +libgme_demuxer_deps="libgme" +libgsm_decoder_deps="libgsm" +libgsm_encoder_deps="libgsm" +libgsm_ms_decoder_deps="libgsm" +libgsm_ms_encoder_deps="libgsm" +libilbc_decoder_deps="libilbc" +libilbc_encoder_deps="libilbc" +libjxl_anim_decoder_deps="libjxl libjxl_threads" +libjxl_anim_encoder_deps="libjxl libjxl_threads" +libjxl_decoder_deps="libjxl libjxl_threads" +libjxl_encoder_deps="libjxl libjxl_threads" +libkvazaar_encoder_deps="libkvazaar" +liblc3_decoder_deps="liblc3" +liblc3_encoder_deps="liblc3" +liblc3_encoder_select="audio_frame_queue" +liblcevc_dec_select="cbs_lcevc" +libmodplug_demuxer_deps="libmodplug" +libmp3lame_encoder_deps="libmp3lame" +libmp3lame_encoder_select="audio_frame_queue mpegaudioheader" +libmpeghdec_decoder_deps="libmpeghdec" +liboapv_encoder_deps="liboapv" +libopencore_amrnb_decoder_deps="libopencore_amrnb" +libopencore_amrnb_encoder_deps="libopencore_amrnb" +libopencore_amrnb_encoder_select="audio_frame_queue" +libopencore_amrwb_decoder_deps="libopencore_amrwb" +libopenh264_decoder_deps="libopenh264" +libopenh264_decoder_select="h264_mp4toannexb_bsf" +libopenh264_encoder_deps="libopenh264" +libopenjpeg_encoder_deps="libopenjpeg" +libopenmpt_demuxer_deps="libopenmpt" +libopus_decoder_deps="libopus" +libopus_encoder_deps="libopus" +libopus_encoder_select="audio_frame_queue" +librav1e_encoder_deps="librav1e" +librsvg_decoder_deps="librsvg" +libshine_encoder_deps="libshine" +libshine_encoder_select="audio_frame_queue mpegaudioheader" +libspeex_decoder_deps="libspeex" +libspeex_encoder_deps="libspeex" +libspeex_encoder_select="audio_frame_queue" +libsvtav1_encoder_deps="libsvtav1" +libsvtjpegxs_encoder_deps="libsvtjpegxs" +libsvtjpegxs_decoder_deps="libsvtjpegxs" +libsvtav1_encoder_select="dovi_rpuenc" +libtheora_encoder_deps="libtheora" +libtwolame_encoder_deps="libtwolame" +libuavs3d_decoder_deps="libuavs3d" +libvo_amrwbenc_encoder_deps="libvo_amrwbenc" +libvorbis_decoder_deps="libvorbis" +libvorbis_encoder_deps="libvorbis libvorbisenc" +libvorbis_encoder_select="audio_frame_queue" +libvpx_vp8_decoder_deps="libvpx" +libvpx_vp8_encoder_deps="libvpx" +libvpx_vp9_decoder_deps="libvpx" +libvpx_vp9_encoder_deps="libvpx" +libvvenc_encoder_deps="libvvenc" +libwebp_encoder_deps="libwebp" +libwebp_anim_encoder_deps="libwebp" +libx262_encoder_deps="libx262" +libx264_encoder_deps="libx264" +libx264_encoder_select="atsc_a53 golomb" +libx264rgb_encoder_deps="libx264" +libx264rgb_encoder_select="libx264_encoder" +libx265_encoder_deps="libx265" +libx265_encoder_select="atsc_a53 dovi_rpuenc" +libxavs_encoder_deps="libxavs" +libxavs2_encoder_deps="libxavs2" +libxevd_decoder_deps_any="libxevd libxevdb" +libxeve_encoder_deps_any="libxeve libxeveb" +libxvid_encoder_deps="libxvid" +libzvbi_teletext_decoder_deps="libzvbi" +vapoursynth_demuxer_deps="vapoursynth" +videotoolbox_suggest="coreservices" +videotoolbox_deps="corefoundation coremedia corevideo VTDecompressionSessionDecodeFrame" +videotoolbox_encoder_deps="videotoolbox VTCompressionSessionPrepareToEncodeFrames" + +# demuxers / muxers +ac3_demuxer_select="ac3_parser" +act_demuxer_select="riffdec" +adts_muxer_select="mpeg4audio" +aiff_muxer_select="iso_media" +amv_muxer_select="riffenc" +apv_demuxer_select="apv_parser" +asf_demuxer_select="riffdec" +asf_o_demuxer_select="riffdec" +asf_muxer_select="riffenc" +asf_stream_muxer_select="asf_muxer" +av1_demuxer_select="av1_frame_merge_bsf av1_parser" +avi_demuxer_select="riffdec" +avi_muxer_select="riffenc" +avif_muxer_select="mov_muxer" +caf_demuxer_select="iso_media" +caf_muxer_select="iso_media" +dash_muxer_select="mp4_muxer" +dash_demuxer_deps="libxml2" +daud_muxer_select="pcm_rechunk_bsf" +dirac_demuxer_select="dirac_parser" +dts_demuxer_select="dca_parser" +dtshd_demuxer_select="dca_parser" +dv_demuxer_select="dvprofile" +dv_muxer_select="dvprofile" +dvdvideo_demuxer_select="mpegps_demuxer" +dvdvideo_demuxer_deps="libdvdnav libdvdread" +dxa_demuxer_select="riffdec" +eac3_demuxer_select="ac3_parser" +evc_demuxer_select="evc_frame_merge_bsf evc_parser" +f4v_muxer_select="mov_muxer" +fifo_muxer_deps="threads" +flac_demuxer_select="flac_parser" +flv_muxer_select="aac_adtstoasc_bsf iso_writer" +gxf_muxer_select="pcm_rechunk_bsf" +hds_muxer_select="flv_muxer" +hls_demuxer_select="aac_demuxer ac3_demuxer adts_header ac3_parser eac3_demuxer mov_demuxer mpegts_demuxer" +hls_muxer_select="mov_muxer mpegts_muxer webvtt_muxer" +hxvs_demuxer_select="h264_parser hevc_parser" +iamf_demuxer_select="iamfdec" +iamf_muxer_select="iamfenc" +image2_alias_pix_demuxer_select="image2_demuxer" +image2_brender_pix_demuxer_select="image2_demuxer" +imf_demuxer_deps="libxml2" +imf_demuxer_select="mxf_demuxer" +ipod_muxer_select="mov_muxer" +ismv_muxer_select="mov_muxer" +ivf_muxer_select="av1_metadata_bsf vp9_superframe_bsf" +latm_muxer_select="aac_adtstoasc_bsf mpeg4audio" +matroska_audio_muxer_select="matroska_muxer" +matroska_demuxer_select="riffdec" +matroska_demuxer_suggest="bzlib zlib" +matroska_muxer_select="iso_writer mpeg4audio riffenc aac_adtstoasc_bsf pgs_frame_merge_bsf vp9_superframe_bsf" +mcc_demuxer_select="smpte_436m" +mcc_muxer_select="smpte_436m" +mcc_muxer_suggest="eia608_to_smpte436m_bsf" +mlp_demuxer_select="mlp_parser" +mmf_muxer_select="riffenc" +mov_demuxer_select="iso_media riffdec" +mov_demuxer_suggest="iamfdec zlib" +mov_muxer_select="cbs_apv_lavf cbs_av1_lavf iso_media iso_writer riffenc rtpenc_chain vp9_superframe_bsf aac_adtstoasc_bsf ac3_parser" +mov_muxer_suggest="iamfenc" +mp3_demuxer_select="mpegaudio_parser" +mp3_muxer_select="mpegaudioheader" +mp4_muxer_select="mov_muxer" +mpegts_demuxer_select="iso_media" +mpegts_muxer_select="ac3_parser adts_muxer latm_muxer h264_mp4toannexb_bsf hevc_mp4toannexb_bsf vvc_mp4toannexb_bsf" +mpegtsraw_demuxer_select="mpegts_demuxer" +mxf_muxer_select="iso_writer pcm_rechunk_bsf rangecoder" +mxf_muxer_suggest="eia608_to_smpte436m_bsf" +mxf_d10_muxer_select="mxf_muxer" +mxf_opatom_muxer_select="mxf_muxer" +nut_muxer_select="riffenc" +nuv_demuxer_select="riffdec" +obu_demuxer_select="av1_frame_merge_bsf av1_parser" +obu_muxer_select="av1_metadata_bsf" +oga_muxer_select="ogg_muxer" +ogg_demuxer_select="dirac_parse" +ogv_muxer_select="ogg_muxer" +opus_muxer_select="ogg_muxer" +psp_muxer_select="mov_muxer" +rtp_demuxer_select="sdp_demuxer" +rtp_muxer_select="iso_writer" +rtp_mpegts_muxer_select="mpegts_muxer rtp_muxer" +rtpdec_select="asf_demuxer mov_demuxer mpegts_demuxer rm_demuxer rtp_protocol srtp" +rtsp_demuxer_select="http_protocol rtpdec" +rtsp_muxer_select="rtp_muxer http_protocol rtp_protocol rtpenc_chain" +sap_demuxer_select="sdp_demuxer" +sap_muxer_select="rtp_muxer rtp_protocol rtpenc_chain" +sdp_demuxer_select="rtpdec" +smoothstreaming_muxer_select="ismv_muxer" +spdif_demuxer_select="adts_header" +spdif_muxer_select="adts_header" +spx_muxer_select="ogg_muxer" +swf_demuxer_suggest="zlib" +tak_demuxer_select="tak_parser" +tee_muxer_select="fifo_muxer" +truehd_demuxer_select="mlp_parser" +tg2_muxer_select="mov_muxer" +tgp_muxer_select="mov_muxer" +vobsub_demuxer_select="mpegps_demuxer" +w64_demuxer_select="wav_demuxer" +w64_muxer_select="wav_muxer" +wav_demuxer_select="riffdec" +wav_muxer_select="riffenc" +webm_chunk_muxer_select="webm_muxer" +webm_dash_manifest_demuxer_select="matroska_demuxer" +whip_muxer_select="dtls_protocol rtp_muxer http_protocol" +wtv_demuxer_select="mpegts_demuxer riffdec" +wtv_muxer_select="mpegts_muxer riffenc" +xmv_demuxer_select="riffdec" +xwma_demuxer_select="riffdec" + +# indevs / outdevs +android_camera_indev_deps="android camera2ndk mediandk pthreads" +alsa_indev_deps="alsa" +alsa_outdev_deps="alsa" +avfoundation_indev_deps="avfoundation corevideo coremedia pthreads AVCaptureSession" +avfoundation_indev_suggest="coregraphics applicationservices" +avfoundation_indev_extralibs="-framework Foundation" +audiotoolbox_outdev_deps="audiotoolbox pthreads AudioObjectPropertyAddress" +audiotoolbox_outdev_extralibs="-framework AudioToolbox -framework CoreAudio" +caca_outdev_deps="libcaca" +decklink_deps_any="libdl LoadLibrary" +decklink_indev_deps="decklink threads" +decklink_indev_extralibs="-lstdc++" +decklink_indev_suggest="libzvbi" +decklink_outdev_deps="decklink threads" +decklink_outdev_suggest="libklvanc" +decklink_outdev_extralibs="-lstdc++" +dshow_indev_deps="IBaseFilter" +dshow_indev_extralibs="-lpsapi -lole32 -lstrmiids -luuid -loleaut32 -lshlwapi" +fbdev_indev_deps="linux_fb_h" +fbdev_outdev_deps="linux_fb_h" +gdigrab_indev_deps="CreateDIBSection" +gdigrab_indev_extralibs="-lgdi32" +gdigrab_indev_select="bmp_decoder" +iec61883_indev_deps="libiec61883" +iec61883_indev_select="dv_demuxer" +jack_indev_deps="libjack" +jack_indev_deps_any="sem_timedwait dispatch_dispatch_h" +kmsgrab_indev_deps="libdrm" +lavfi_indev_deps="avfilter" +libcdio_indev_deps="libcdio" +libdc1394_indev_deps="libdc1394" +openal_indev_deps="openal" +oss_indev_deps_any="sys_soundcard_h" +oss_outdev_deps_any="sys_soundcard_h" +pulse_indev_deps="libpulse" +pulse_outdev_deps="libpulse" +sndio_indev_deps="sndio" +sndio_outdev_deps="sndio" +v4l2_indev_deps_any="linux_videodev2_h sys_videoio_h" +v4l2_indev_suggest="libv4l2" +v4l2_outdev_deps_any="linux_videodev2_h sys_videoio_h" +v4l2_outdev_suggest="libv4l2" +vfwcap_indev_deps="vfw32 vfwcap_defines" +xcbgrab_indev_deps="libxcb" +xcbgrab_indev_suggest="libxcb_shm libxcb_shape libxcb_xfixes" +xv_outdev_deps="xlib_xv xlib_x11 xlib_xext" + +# protocols +android_content_protocol_deps="jni" +android_content_protocol_select="file_protocol" +async_protocol_deps="threads" +bluray_protocol_deps="libbluray" +ffrtmpcrypt_protocol_conflict="librtmp_protocol" +ffrtmpcrypt_protocol_deps_any="gcrypt gmp openssl mbedtls" +ffrtmpcrypt_protocol_select="tcp_protocol" +ffrtmphttp_protocol_conflict="librtmp_protocol" +ffrtmphttp_protocol_select="http_protocol" +ftp_protocol_select="tcp_protocol" +gopher_protocol_select="tcp_protocol" +gophers_protocol_select="tls_protocol" +http_protocol_select="tcp_protocol" +http_protocol_suggest="zlib" +httpproxy_protocol_select="tcp_protocol" +httpproxy_protocol_suggest="zlib" +https_protocol_select="tls_protocol" +https_protocol_suggest="zlib" +icecast_protocol_select="http_protocol" +mmsh_protocol_select="http_protocol" +mmst_protocol_select="network" +rtmp_protocol_conflict="librtmp_protocol" +rtmp_protocol_select="tcp_protocol" +rtmp_protocol_suggest="zlib" +rtmpe_protocol_select="ffrtmpcrypt_protocol" +rtmpe_protocol_suggest="zlib" +rtmps_protocol_conflict="librtmp_protocol" +rtmps_protocol_select="tls_protocol" +rtmps_protocol_suggest="zlib" +rtmpt_protocol_select="ffrtmphttp_protocol" +rtmpt_protocol_suggest="zlib" +rtmpte_protocol_select="ffrtmpcrypt_protocol ffrtmphttp_protocol" +rtmpte_protocol_suggest="zlib" +rtmpts_protocol_select="ffrtmphttp_protocol https_protocol" +rtmpts_protocol_suggest="zlib" +rtp_protocol_select="udp_protocol" +schannel_conflict="openssl gnutls libtls mbedtls" +sctp_protocol_deps="struct_sctp_event_subscribe struct_msghdr_msg_flags" +sctp_protocol_select="network" +securetransport_conflict="openssl gnutls libtls mbedtls" +shared_protocol_deps="mmap stdatomic unistd_h" +srtp_protocol_select="rtp_protocol srtp" +tcp_protocol_select="network" +tls_protocol_deps_any="gnutls openssl schannel securetransport libtls mbedtls" +tls_protocol_select="tcp_protocol" +# TODO: Support libtls. +dtls_protocol_deps_any="openssl schannel gnutls mbedtls" +dtls_protocol_select="udp_protocol" +udp_protocol_select="network" +udplite_protocol_select="network" +unix_protocol_deps="sys_un_h" +unix_protocol_select="network" +ipfs_gateway_protocol_select="https_protocol" +ipns_gateway_protocol_select="https_protocol" + +# external library protocols +libamqp_protocol_deps="librabbitmq" +libamqp_protocol_select="network" +librist_protocol_deps="librist" +librist_protocol_select="network" +librtmp_protocol_deps="librtmp" +librtmpe_protocol_deps="librtmp" +librtmps_protocol_deps="librtmp" +librtmpt_protocol_deps="librtmp" +librtmpte_protocol_deps="librtmp" +libsmbclient_protocol_deps="libsmbclient gplv3" +libsrt_protocol_deps="libsrt" +libsrt_protocol_select="network" +libssh_protocol_deps="libssh" +libtls_conflict="openssl gnutls mbedtls" +libzmq_protocol_deps="libzmq" +libzmq_protocol_select="network" + +# filters +ametadata_filter_deps="avformat" +amovie_filter_deps="avcodec avformat" +aresample_filter_deps="swresample" +asr_filter_deps="pocketsphinx" +ass_filter_deps="libass" +avgblur_opencl_filter_deps="opencl" +avgblur_vulkan_filter_deps="vulkan spirv_compiler" +azmq_filter_deps="libzmq" +blackdetect_vulkan_filter_deps="vulkan spirv_compiler" +blackframe_filter_deps="gpl" +blend_vulkan_filter_deps="vulkan spirv_compiler" +boxblur_filter_deps="gpl" +boxblur_opencl_filter_deps="opencl gpl" +bs2b_filter_deps="libbs2b" +bwdif_cuda_filter_deps="ffnvcodec" +bwdif_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +bwdif_vulkan_filter_deps="vulkan spirv_compiler" +chromaber_vulkan_filter_deps="vulkan spirv_compiler" +color_vulkan_filter_deps="vulkan spirv_compiler" +colorkey_opencl_filter_deps="opencl" +colormatrix_filter_deps="gpl" +convolution_opencl_filter_deps="opencl" +coreimage_filter_deps="coreimage appkit" +coreimage_filter_extralibs="-framework OpenGL" +coreimagesrc_filter_deps="coreimage appkit" +coreimagesrc_filter_extralibs="-framework OpenGL" +cover_rect_filter_deps="avcodec avformat gpl" +cropdetect_filter_deps="gpl" +deinterlace_qsv_filter_deps="libmfx" +deinterlace_qsv_filter_select="qsvvpp" +deinterlace_vaapi_filter_deps="vaapi" +delogo_filter_deps="gpl" +denoise_vaapi_filter_deps="vaapi" +derain_filter_select="dnn" +deshake_filter_select="pixelutils" +deshake_opencl_filter_deps="opencl" +dilation_opencl_filter_deps="opencl" +dnn_classify_filter_select="dnn" +dnn_detect_filter_select="dnn" +dnn_processing_filter_select="dnn" +drawtext_filter_deps="libfreetype libharfbuzz" +drawtext_filter_suggest="libfontconfig libfribidi" +drawvg_filter_deps="cairo" +elbg_filter_deps="avcodec" +eq_filter_deps="gpl" +erosion_opencl_filter_deps="opencl" +find_rect_filter_deps="avcodec avformat gpl" +flip_vulkan_filter_deps="vulkan spirv_compiler" +flite_filter_deps="libflite threads" +framerate_filter_select="scene_sad" +freezedetect_filter_select="scene_sad" +frei0r_deps_any="libdl LoadLibrary" +frei0r_filter_deps="frei0r" +frei0r_src_filter_deps="frei0r" +fspp_filter_deps="gpl" +fsync_filter_deps="avformat" +gblur_vulkan_filter_deps="vulkan spirv_compiler" +hflip_vulkan_filter_deps="vulkan spirv_compiler" +histeq_filter_deps="gpl" +hqdn3d_filter_deps="gpl" +iccdetect_filter_deps="lcms2" +iccgen_filter_deps="lcms2" +identity_filter_select="scene_sad" +interlace_filter_deps="gpl" +interlace_vulkan_filter_deps="vulkan spirv_compiler" +kerndeint_filter_deps="gpl" +ladspa_filter_deps="ladspa libdl" +lcevc_filter_deps="liblcevc_dec" +lensfun_filter_deps="liblensfun version3" +libplacebo_filter_deps="libplacebo vulkan" +lv2_filter_deps="lv2" +mcdeint_filter_deps="avcodec gpl" +metadata_filter_deps="avformat" +movie_filter_deps="avcodec avformat" +mpdecimate_filter_deps="gpl" +mpdecimate_filter_select="pixelutils" +minterpolate_filter_select="scene_sad" +mptestsrc_filter_deps="gpl" +msad_filter_select="scene_sad" +negate_filter_deps="lut_filter" +nlmeans_opencl_filter_deps="opencl" +nlmeans_vulkan_filter_deps="vulkan spirv_compiler" +nnedi_filter_deps="gpl" +ocr_filter_deps="libtesseract" +ocv_filter_deps="libopencv" +openclsrc_filter_deps="opencl" +qrencode_filter_deps="libqrencode" +qrencodesrc_filter_deps="libqrencode" +quirc_filter_deps="libquirc" +ocio_filter_deps="libopencolorio" +libopencolorio_filter_deps="libopencolorio" +overlay_opencl_filter_deps="opencl" +overlay_qsv_filter_deps="libmfx" +overlay_qsv_filter_select="qsvvpp" +overlay_vaapi_filter_deps="vaapi VAProcPipelineCaps_blend_flags" +overlay_vulkan_filter_deps="vulkan spirv_compiler" +owdenoise_filter_deps="gpl" +pad_opencl_filter_deps="opencl" +pan_filter_deps="swresample" +perspective_filter_deps="gpl" +phase_filter_deps="gpl" +pp7_filter_deps="gpl" +prewitt_opencl_filter_deps="opencl" +procamp_vaapi_filter_deps="vaapi" +program_opencl_filter_deps="opencl" +pullup_filter_deps="gpl" +remap_opencl_filter_deps="opencl" +removelogo_filter_deps="avcodec avformat swscale" +repeatfields_filter_deps="gpl" +roberts_opencl_filter_deps="opencl" +rubberband_filter_deps="librubberband" +sab_filter_deps="gpl swscale" +scale2ref_filter_deps="swscale" +scale_filter_deps="swscale" +sr_amf_filter_deps="amf" +vpp_amf_filter_deps="amf" +frc_amf_filter_deps="amf windows_h" +scale_qsv_filter_deps="libmfx" +scale_qsv_filter_select="qsvvpp" +scdet_filter_select="scene_sad" +scdet_vulkan_filter_deps="vulkan spirv_compiler" +select_filter_select="scene_sad" +sharpness_vaapi_filter_deps="vaapi" +showcqt_filter_deps="avformat swscale" +showcqt_filter_suggest="libfontconfig libfreetype" +signature_filter_deps="gpl avcodec avformat" +smartblur_filter_deps="gpl swscale" +sobel_opencl_filter_deps="opencl" +sofalizer_filter_deps="libmysofa" +spp_filter_deps="gpl avcodec" +spp_filter_select="idctdsp fdctdsp pixblockdsp" +sr_filter_deps="avformat swscale" +sr_filter_select="dnn" +stereo3d_filter_deps="gpl" +subtitles_filter_deps="avformat avcodec libass" +super2xsai_filter_deps="gpl" +pixfmts_super2xsai_test_deps="super2xsai_filter" +tinterlace_filter_deps="gpl" +tinterlace_merge_test_deps="tinterlace_filter" +tinterlace_pad_test_deps="tinterlace_filter" +tonemap_filter_deps="const_nan" +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" +transpose_vt_filter_deps="videotoolbox VTPixelRotationSessionCreate" +transpose_vulkan_filter_deps="vulkan spirv_compiler" +unsharp_opencl_filter_deps="opencl" +uspp_filter_deps="gpl avcodec" +v360_vulkan_filter_deps="vulkan spirv_compiler" +vaguedenoiser_filter_deps="gpl" +vflip_vulkan_filter_deps="vulkan spirv_compiler" +vidstabdetect_filter_deps="libvidstab" +vidstabtransform_filter_deps="libvidstab" +libvmaf_filter_deps="libvmaf" +libvmaf_cuda_filter_deps="libvmaf libvmaf_cuda ffnvcodec" +zmq_filter_deps="libzmq" +zoompan_filter_deps="swscale" +zscale_filter_deps="libzimg const_nan" +scale_vaapi_filter_deps="vaapi" +scale_vt_filter_deps="videotoolbox VTPixelTransferSessionCreate" +scale_vulkan_filter_deps="vulkan spirv_compiler swscale" +vpp_qsv_filter_deps="libmfx" +vpp_qsv_filter_select="qsvvpp" +xfade_opencl_filter_deps="opencl" +xfade_vulkan_filter_deps="vulkan spirv_compiler" +yadif_cuda_filter_deps="ffnvcodec" +yadif_cuda_filter_deps_any="cuda_nvcc cuda_llvm" +yadif_videotoolbox_filter_deps="metal corevideo videotoolbox" +hstack_vaapi_filter_deps="vaapi_1" +vstack_vaapi_filter_deps="vaapi_1" +xstack_vaapi_filter_deps="vaapi_1" +hstack_qsv_filter_deps="libmfx" +hstack_qsv_filter_select="qsvvpp" +vstack_qsv_filter_deps="libmfx" +vstack_qsv_filter_select="qsvvpp" +xstack_qsv_filter_deps="libmfx" +xstack_qsv_filter_select="qsvvpp" +pad_vaapi_filter_deps="vaapi_1" +drawbox_vaapi_filter_deps="vaapi_1" +whisper_filter_deps="whisper" + +# examples +avio_http_serve_files_deps="avformat avutil fork" +avio_list_dir_deps="avformat avutil" +avio_read_callback_deps="avformat avcodec avutil" +decode_audio_example_deps="avcodec avutil" +decode_filter_audio_example_deps="avfilter avcodec avformat avutil" +decode_filter_video_example_deps="avfilter avcodec avformat avutil" +decode_video_example_deps="avcodec avutil" +demux_decode_example_deps="avcodec avformat avutil" +encode_audio_example_deps="avcodec avutil" +encode_video_example_deps="avcodec avutil" +extract_mvs_example_deps="avcodec avformat avutil" +filter_audio_example_deps="avfilter avutil" +hw_decode_example_deps="avcodec avformat avutil" +mux_example_deps="avcodec avformat avutil swscale" +qsv_decode_example_deps="avcodec avutil libmfx h264_qsv_decoder" +remux_example_deps="avcodec avformat avutil" +resample_audio_example_deps="avutil swresample" +scale_video_example_deps="avutil swscale" +show_metadata_example_deps="avformat avutil" +transcode_aac_example_deps="avcodec avformat swresample" +transcode_example_deps="avfilter avcodec avformat avutil" +vaapi_encode_example_deps="avcodec avutil h264_vaapi_encoder" +vaapi_transcode_example_deps="avcodec avformat avutil h264_vaapi_encoder" +qsv_transcode_example_deps="avcodec avformat avutil h264_qsv_encoder" + +# EXTRALIBS_LIST +checkasm_extralibs="advapi32_extralibs pthreads_extralibs" +cpu_init_extralibs="pthreads_extralibs" +cws2fws_extralibs="zlib_extralibs" + +# libraries, in any order +avcodec_deps="avutil" +avcodec_suggest="libm stdatomic zlib" +avdevice_deps="avformat avcodec avutil" +avdevice_suggest="libm stdatomic" +avfilter_deps="avutil" +avfilter_suggest="libm stdatomic zlib" +avformat_deps="avcodec avutil" +avformat_suggest="libm network zlib stdatomic" +avutil_suggest="amf clock_gettime ffnvcodec gcrypt libm zlib libdrm libmfx opencl openssl user32 vaapi vulkan videotoolbox corefoundation corevideo coremedia bcrypt stdatomic" +swresample_deps="avutil" +swresample_suggest="libm libsoxr stdatomic" +swscale_deps="avutil" +swscale_suggest="libm stdatomic" +shader_compression_suggest="zlib" + +avcodec_extralibs="pthreads_extralibs iconv_extralibs dxva2_extralibs liblcevc_dec_extralibs lcms2_extralibs" +avfilter_extralibs="pthreads_extralibs" +avutil_extralibs="d3d11va_extralibs d3d12va_extralibs mediacodec_extralibs nanosleep_extralibs pthreads_extralibs vaapi_drm_extralibs vaapi_x11_extralibs vaapi_win32_extralibs vdpau_x11_extralibs" + +# programs +ffmpeg_deps="avcodec avfilter avformat threads" +ffmpeg_select="aformat_filter anull_filter atrim_filter crop_filter + format_filter hflip_filter null_filter rotate_filter + transpose_filter trim_filter vflip_filter" +ffmpeg_suggest="ole32 psapi shell32" +ffplay_deps="avcodec avformat avfilter swscale swresample sdl2" +ffplay_select="crop_filter transpose_filter hflip_filter vflip_filter rotate_filter" +ffplay_suggest="shell32 libplacebo vulkan" +ffprobe_deps="avcodec avformat" +ffprobe_suggest="shell32" + +# documentation +podpages_deps="perl" +manpages_deps="perl pod2man" +htmlpages_deps="perl" +htmlpages_deps_any="makeinfo_html texi2html" +txtpages_deps="perl makeinfo_command" +doc_deps_any="manpages htmlpages podpages txtpages" + +# default parameters + +logfile="ffbuild/config.log" + +# installation paths +prefix_default="/usr/local" +bindir_default='${prefix}/bin' +datadir_default='${prefix}/share/ffmpeg' +docdir_default='${prefix}/share/doc/ffmpeg' +incdir_default='${prefix}/include' +libdir_default='${prefix}/lib' +mandir_default='${prefix}/share/man' + +# toolchain +ar_default="ar" +cc_default="gcc" +stdc_default="c17" +stdcxx_default="c++17" +cxx_default="g++" +host_cc_default="gcc" +doxygen_default="doxygen" +makeinfo_default="makeinfo" +install="install" +ln_s_default="ln -s -f" +glslc_default="glslc" +metalcc_default="xcrun -sdk macosx metal" +metallib_default="xcrun -sdk macosx metallib" +nm_default="nm -g" +pkg_config_default=pkg-config +ranlib_default="ranlib" +strip_default="strip" +version_script='--version-script' +objformat="elf32" +x86asmexe_default="nasm" +windres_default="windres" +striptype="direct" +response_files_default="auto" + +# OS +target_os_default=$(tolower $(uname -s)) +host_os=$target_os_default + +# machine +if test "$target_os_default" = aix; then + arch_default=$(uname -p) + strip_default="strip -X32_64" + nm_default="nm -g -X32_64" +elif test "$MSYSTEM_CARCH" != ""; then + arch_default="$MSYSTEM_CARCH" +else + arch_default=$(uname -m) +fi +cpu="generic" +intrinsics="none" + +# configurable options +enable $PROGRAM_LIST +enable $DOCUMENT_LIST +enable $EXAMPLE_LIST +enable $LIBRARY_LIST +enable stripping +enable version_tracking + +enable asm +enable checkasm +enable debug +enable doc +enable faan faandct faanidct +enable iamf +enable large_tests +enable optimizations +enable shader_compression +enable resource_compression +enable runtime_cpudetect +enable safe_bitstream_reader +enable static +enable swscale_alpha +enable unstable +enable valgrind_backtrace + +sws_max_filter_size_default=256 +set_default sws_max_filter_size + +# internal components are enabled by default +enable $EXTRALIBS_LIST + +# Avoid external, non-system, libraries getting enabled by dependency resolution +disable $EXTERNAL_LIBRARY_LIST $HWACCEL_LIBRARY_LIST + +# build settings +SHFLAGS='-shared -Wl,-soname,$$(@F)' +LIBPREF="lib" +LIBSUF=".a" +FULLNAME='$(NAME)$(BUILDSUF)' +LIBNAME='$(LIBPREF)$(FULLNAME)$(LIBSUF)' +SLIBPREF="lib" +SLIBSUF=".so" +SLIBNAME='$(SLIBPREF)$(FULLNAME)$(SLIBSUF)' +SLIBNAME_WITH_VERSION='$(SLIBNAME).$(LIBVERSION)' +SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)' +LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"' +SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)' +SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)' +VERSION_SCRIPT_POSTPROCESS_CMD="cat" + +asflags_filter=echo +cflags_filter=echo +objcflags_filter=echo +ldflags_filter=echo + +AS_C='-c' +AS_O='-o $@' +CC_C='-c' +CC_E='-E -P -o $@' +CC_O='-o $@' +CXX_C='-c' +CXX_O='-o $@' +OBJCC_C='-c' +OBJCC_E='-E -P -o $@' +OBJCC_O='-o $@' +X86ASM_O='-o $@' +LD_O='-o $@' +LD_LIB='-l%' +LD_PATH='-L' +HOSTCC_C='-c' +HOSTCC_E='-E -P -o $@' +HOSTCC_O='-o $@' +HOSTLD_O='-o $@' +GLSLC_O='-o $@' +NVCC_C='-c' +NVCC_O='-o $@' + +host_extralibs= +host_cflags_filter=echo +host_ldflags_filter=echo + +target_path='$(CURDIR)' + +# since the object filename is not given with the -MM flag, the compiler +# is only able to print the basename, and we must add the path ourselves +DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< 2>/dev/null | sed -e "/^\#.*/d" -e "s,^[[:space:]]*$(@F),$(@D)/$(@F)," > $(@:.o=.d)' +DEPFLAGS='-MM' + +mkdir -p ffbuild + +# find source path +if test -f configure; then + source_path=. +elif test -f src/configure; then + source_path=./src +else + source_path=$(cd $(dirname "$0"); pwd) + case "$source_path" in + *[[:blank:]]*) die "Out of tree builds are impossible with whitespace in source path." ;; + esac + test -e "$source_path/config.h" && + die "Out of tree builds are impossible with config.h in source dir." +fi + +for v in "$@"; do + r=${v#*=} + l=${v%"$r"} + r=$(sh_quote "$r") + FFMPEG_CONFIGURATION="${FFMPEG_CONFIGURATION# } ${l}${r}" +done + +find_things_extern(){ + thing=$1 + pattern=$2 + file=$source_path/$3 + out=${4:-$thing} + sed -n "s/^[^#]*extern.*$pattern *ff_\([^ ]*\)_$thing;/\1_$out/p" "$file" +} + +find_filters_extern(){ + file=$source_path/$1 + sed -n 's/^extern const FFFilter ff_[avfsinkrc]\{2,5\}_\([[:alnum:]_]\{1,\}\);/\1_filter/p' $file +} + +FILTER_LIST=$(find_filters_extern libavfilter/allfilters.c) +OUTDEV_LIST=$(find_things_extern muxer FFOutputFormat libavdevice/alldevices.c outdev) +INDEV_LIST=$(find_things_extern demuxer FFInputFormat libavdevice/alldevices.c indev) +MUXER_LIST=$(find_things_extern muxer FFOutputFormat libavformat/allformats.c) +DEMUXER_LIST=$(find_things_extern demuxer FFInputFormat libavformat/allformats.c) +ENCODER_LIST=$(find_things_extern encoder FFCodec libavcodec/allcodecs.c) +DECODER_LIST=$(find_things_extern decoder FFCodec libavcodec/allcodecs.c) +CODEC_LIST=" + $ENCODER_LIST + $DECODER_LIST +" +PARSER_LIST=$(find_things_extern parser FFCodecParser libavcodec/parsers.c) +BSF_LIST=$(find_things_extern bsf FFBitStreamFilter libavcodec/bitstream_filters.c) +HWACCEL_LIST=$(find_things_extern hwaccel FFHWAccel libavcodec/hwaccels.h) +PROTOCOL_LIST=$(find_things_extern protocol URLProtocol libavformat/protocols.c) + +AVCODEC_COMPONENTS_LIST=" + $BSF_LIST + $DECODER_LIST + $ENCODER_LIST + $HWACCEL_LIST + $PARSER_LIST +" + +AVDEVICE_COMPONENTS_LIST=" + $INDEV_LIST + $OUTDEV_LIST +" + +AVFILTER_COMPONENTS_LIST=" + $FILTER_LIST +" + +AVFORMAT_COMPONENTS_LIST=" + $DEMUXER_LIST + $MUXER_LIST + $PROTOCOL_LIST +" + +ALL_COMPONENTS=" + $AVCODEC_COMPONENTS_LIST + $AVDEVICE_COMPONENTS_LIST + $AVFILTER_COMPONENTS_LIST + $AVFORMAT_COMPONENTS_LIST +" + +for n in $COMPONENT_LIST; do + v=$(toupper ${n%s})_LIST + eval enable \$$v + eval ${n}_if_any="\$$v" +done + +enable $ARCH_EXT_LIST + +die_unknown(){ + echo "Unknown option \"$1\"." + echo "See $0 --help for available options." + exit 1 +} + +print_in_columns() { + tr ' ' '\n' | sort | tr '\r\n' ' ' | awk -v col_width=24 -v width="$ncols" ' + { + num_cols = width > col_width ? int(width / col_width) : 1; + num_rows = int((NF + num_cols-1) / num_cols); + y = x = 1; + for (y = 1; y <= num_rows; y++) { + i = y; + for (x = 1; x <= num_cols; x++) { + if (i <= NF) { + line = sprintf("%s%-" col_width "s", line, $i); + } + i = i + num_rows; + } + print line; line = ""; + } + }' | sed 's/ *$//' +} + +show_list() { + suffix=_$1 + shift + echo $* | sed s/$suffix//g | print_in_columns + exit 0 +} + +rand_list(){ + IFS=', ' + set -- $* + unset IFS + for thing; do + comp=${thing%:*} + prob=${thing#$comp} + prob=${prob#:} + is_in ${comp} $COMPONENT_LIST && eval comp=\$$(toupper ${comp%s})_LIST + echo "prob ${prob:-0.5}" + printf '%s\n' $comp + done +} + +do_random(){ + action=$1 + shift + random_seed=$(awk "BEGIN { srand($random_seed); print srand() }") + $action $(rand_list "$@" | awk "BEGIN { srand($random_seed) } \$1 == \"prob\" { prob = \$2; next } rand() < prob { print }") +} + +for opt do + optval="${opt#*=}" + case "$opt" in + --extra-ldflags=*) + add_ldflags $optval + ;; + --extra-ldexeflags=*) + add_ldexeflags $optval + ;; + --extra-ldsoflags=*) + add_ldsoflags $optval + ;; + --extra-ldlibflags=*) + warn "The --extra-ldlibflags option is only provided for compatibility and will be\n"\ + "removed in the future. Use --extra-ldsoflags instead." + add_ldsoflags $optval + ;; + --extra-libs=*) + add_extralibs $optval + ;; + --disable-devices) + disable $INDEV_LIST $OUTDEV_LIST + ;; + --enable-debug=*) + debuglevel="$optval" + ;; + --disable-programs) + disable $PROGRAM_LIST + ;; + --disable-everything) + map 'eval unset \${$(toupper ${v%s})_LIST}' $COMPONENT_LIST + ;; + --disable-all) + map 'eval unset \${$(toupper ${v%s})_LIST}' $COMPONENT_LIST + disable $LIBRARY_LIST $PROGRAM_LIST doc + enable avutil + ;; + --enable-random|--disable-random) + action=${opt%%-random} + do_random ${action#--} $COMPONENT_LIST + ;; + --enable-random=*|--disable-random=*) + action=${opt%%-random=*} + do_random ${action#--} $optval + ;; + --enable-sdl) + enable sdl2 + ;; + --enable-lto*) + lto=-f${opt#--enable-} + ;; + --enable-*=*|--disable-*=*) + eval $(echo "${opt%%=*}" | sed 's/--/action=/;s/-/ thing=/') + is_in "${thing}s" $COMPONENT_LIST || die_unknown "$opt" + eval list=\$$(toupper $thing)_LIST + name=$(echo "${optval}" | sed "s/,/_${thing}|/g")_${thing} + list=$(filter "$name" $list) + [ "$list" = "" ] && warn "Option $opt did not match anything" + test $action = enable && warn_if_gets_disabled $list + $action $list + ;; + --enable-?*|--disable-?*) + eval $(echo "$opt" | sed 's/--/action=/;s/-/ option=/;s/-/_/g') + if is_in $option $COMPONENT_LIST; then + test $action = disable && action=unset + eval $action \$$(toupper ${option%s})_LIST + elif is_in $option $CMDLINE_SELECT; then + $action $option + else + die_unknown $opt + fi + ;; + --list-*) + NAME="${opt#--list-}" + is_in $NAME $COMPONENT_LIST || die_unknown $opt + NAME=${NAME%s} + eval show_list $NAME \$$(toupper $NAME)_LIST + ;; + --help|-h) show_help + ;; + --quiet|-q) quiet=yes + ;; + --fatal-warnings) enable fatal_warnings + ;; + --libfuzzer=*) + libfuzzer_path="$optval" + ;; + *) + optname="${opt%%=*}" + optname="${optname#--}" + optname=$(echo "$optname" | sed 's/-/_/g') + if is_in $optname $CMDLINE_SET; then + eval $optname='$optval' + elif is_in $optname $CMDLINE_APPEND; then + append $optname "$optval" + else + die_unknown $opt + fi + ;; + esac +done + +enabled libnpp && warn "libnpp has been removed and enabling it does nothing." + +for e in $env; do + eval "export $e" +done + +if disabled autodetect; then + + # Unless iconv is explicitly disabled by the user, we still want to probe + # for the iconv from the libc. + disabled iconv || enable libc_iconv + + disable_weak $EXTERNAL_AUTODETECT_LIBRARY_LIST + disable_weak $HWACCEL_AUTODETECT_LIBRARY_LIST +fi +# Mark specifically enabled, but normally autodetected libraries as requested. +for lib in $AUTODETECT_LIBS; do + enabled $lib && request $lib +done +#TODO: switch to $AUTODETECT_LIBS when $THREADS_LIST is supported the same way +enable_weak $EXTERNAL_AUTODETECT_LIBRARY_LIST +enable_weak $HWACCEL_AUTODETECT_LIBRARY_LIST + +disabled logging && logfile=/dev/null + +# command line configuration sanity checks + +# we need to build at least one lib type +if ! enabled_any static shared; then + cat < $logfile +set >> $logfile + +test -n "$valgrind" && toolchain="valgrind-memcheck" + +add_sanitizer_flags(){ + case "$1" in + asan) + add_allcflags -fsanitize=address + add_ldflags -fsanitize=address + ;; + fuzz) + add_allcflags -fsanitize=fuzzer-no-link + add_ldflags -fsanitize=fuzzer-no-link + : "${libfuzzer_path:=-fsanitize=fuzzer}" + ;; + lsan) + add_allcflags -fsanitize=leak + add_ldflags -fsanitize=leak + ;; + msan) + add_allcflags -fsanitize=memory -fsanitize-memory-track-origins + add_ldflags -fsanitize=memory + ;; + tsan) + add_allcflags -fsanitize=thread + add_ldflags -fsanitize=thread + ;; + usan|ubsan) + add_allcflags -fsanitize=undefined + add_ldflags -fsanitize=undefined + ;; + ?*) + die "Unknown sanitizer $1" + ;; + esac +} + +add_sanitizers(){ + IFS=- + set -- $* + unset IFS + for sanitizer; do + add_sanitizer_flags "$sanitizer" + done + add_allcflags -fno-omit-frame-pointer +} + +case "$toolchain" in + clang-*) + add_sanitizers "${toolchain#clang-}" + cc_default="clang" + cxx_default="clang++" + ;; + llvm|llvm-*) + cc_default="clang" + cxx_default="clang++" + ar_default="llvm-ar" + nm_default="llvm-nm -g" + ranlib_default="llvm-ranlib" + strip_default="llvm-strip" + windres_default="llvm-windres" + test "$toolchain" != "llvm" && add_sanitizers "${toolchain#llvm-}" + ;; + gcc-*) + add_sanitizers "${toolchain#gcc-}" + cc_default="gcc" + cxx_default="g++" + # In case of tsan with gcc, PIC has to be enabled + if [ "${toolchain#gcc-}" = "tsan" ]; then + add_allcflags -fPIC + add_ldflags -fPIC + fi + ;; + valgrind-*) + target_exec_default="valgrind" + case "$toolchain" in + valgrind-massif) + target_exec_args="--tool=massif --alloc-fn=av_malloc --alloc-fn=av_mallocz --alloc-fn=av_calloc --alloc-fn=av_fast_padded_malloc --alloc-fn=av_fast_malloc --alloc-fn=av_realloc_f --alloc-fn=av_fast_realloc --alloc-fn=av_realloc" + ;; + valgrind-memcheck) + target_exec_args="--error-exitcode=1 --malloc-fill=0x2a --track-origins=yes --leak-check=full --gen-suppressions=all --suppressions=$source_path/tests/fate-valgrind.supp" + ;; + esac + ;; + msvc) + cl_major_ver=$(cl.exe 2>&1 | sed -n 's/.*Version \([[:digit:]]\{1,\}\)\..*/\1/p') + if [ -z "$cl_major_ver" ] || [ $cl_major_ver -ge 18 ]; then + cc_default="cl.exe" + cxx_default="cl.exe" + else + die "Unsupported MSVC version (2013 or newer required)" + fi + ld_default="$source_path/compat/windows/mslink" + windres_default="$source_path/compat/windows/mswindres" + nm_default="dumpbin.exe -symbols" + ar_default="lib.exe" + case "${arch:-$arch_default}" in + aarch64|arm64) + as_default="armasm64.exe" + ;; + arm*) + as_default="armasm.exe" + ;; + esac + target_os_default="win32" + # Use a relative path for TMPDIR. This makes sure all the + # ffconf temp files are written with a relative path, avoiding + # issues with msys/win32 path conversion for MSVC parameters + # such as -Fo or -out:. + TMPDIR=. + ;; + icl) + cc_default="icl" + ld_default="xilink" + nm_default="dumpbin -symbols" + ar_default="xilib" + target_os_default="win32" + TMPDIR=. + ;; + gcov) + add_allcflags -fprofile-arcs -ftest-coverage + add_ldflags -fprofile-arcs -ftest-coverage + ;; + llvm-cov) + add_allcflags -fprofile-arcs -ftest-coverage + add_ldflags --coverage + ;; + hardened) + add_cppflags -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 + add_allcflags -fno-strict-overflow -fstack-protector-all + add_ldflags -Wl,-z,relro -Wl,-z,now + add_allcflags -fPIE + add_ldexeflags -fPIE -pie + ;; + ?*) + die "Unknown toolchain $toolchain" + ;; +esac + +if test -n "$cross_prefix"; then + test -n "$arch" && test -n "$target_os" || + die "Must specify target arch (--arch) and OS (--target-os) when cross-compiling" + enable cross_compile +fi + +set_default target_os +if test "$target_os" = android; then + cc_default="clang" +fi + +ar_default="${cross_prefix}${ar_default}" +cc_default="${cross_prefix}${cc_default}" +cxx_default="${cross_prefix}${cxx_default}" +nm_default="${cross_prefix}${nm_default}" +pkg_config_default="${cross_prefix}${pkg_config_default}" +ranlib_default="${cross_prefix}${ranlib_default}" +strip_default="${cross_prefix}${strip_default}" +windres_default="${cross_prefix}${windres_default}" + +sysinclude_default="${sysroot}/usr/include" + +if enabled cuda_sdk; then + warn "Option --enable-cuda-sdk is deprecated. Use --enable-cuda-nvcc instead." + enable cuda_nvcc +fi + +if enabled cuda_nvcc; then + nvcc_default="nvcc" + nvccflags_default="-gencode arch=compute_30,code=sm_30 -O2" +else + nvcc_default="clang" + nvccflags_default="--cuda-gpu-arch=sm_30 -O2" + NVCC_C="" +fi + +set_default nvcc + +if enabled cuda_nvcc; then + if $nvcc $nvccflags_default 2>&1 | grep -qi unsupported; then + nvccflags_default="-gencode arch=compute_60,code=sm_60 -O2" + fi + if $nvcc $nvccflags_default 2>&1 | grep -qi unsupported; then + nvccflags_default="-gencode arch=compute_75,code=sm_75 -O2" + fi +fi + +set_default arch cc cxx doxygen pkg_config ranlib strip sysinclude \ + target_exec x86asmexe glslc metalcc metallib stdc stdcxx makeinfo +enabled cross_compile || host_cc_default=$cc +set_default host_cc + +if ${ranlib} 2>&1 | grep -q "\-D "; then + ranlib="${ranlib} -D" +fi + +pkg_config_fail_message="" +if ! $pkg_config --version >/dev/null 2>&1; then + warn "$pkg_config not found, library detection may fail." + pkg_config=false +elif is_in -static $cc $LDFLAGS && ! is_in --static $pkg_config $pkg_config_flags; then + pkg_config_fail_message=" +Note: When building a static binary, add --pkg-config-flags=\"--static\"." +fi + +if test "$doxygen" != "$doxygen_default" && \ + ! $doxygen --version >/dev/null 2>&1; then + warn "Specified doxygen \"$doxygen\" not found, API documentation will fail to build." +fi + +exesuf() { + case $1 in + mingw32*|mingw64*|msys*|win32|win64|cygwin*|*-dos|freedos|opendos|os/2*|symbian|windows_nt) echo .exe ;; + esac +} + +EXESUF=$(exesuf $target_os) +HOSTEXESUF=$(exesuf $host_os) + +# set temporary file name +: ${TMPDIR:=$TEMPDIR} +: ${TMPDIR:=$TMP} +: ${TMPDIR:=/tmp} + +if [ -n "$tempprefix" ] ; then + mktemp(){ + tmpname="$tempprefix.${HOSTNAME}.${UID}" + echo "$tmpname" + mkdir "$tmpname" + } +elif ! test_cmd mktemp -u XXXXXX; then + # simple replacement for missing mktemp + # NOT SAFE FOR GENERAL USE + mktemp(){ + tmpname="${2%%XXX*}.${HOSTNAME}.${UID}.$$" + echo "$tmpname" + mkdir "$tmpname" + } +fi + +FFTMPDIR=$(mktemp -d "${TMPDIR}/ffconf.XXXXXXXX" 2> /dev/null) || + die "Unable to create temporary directory in $TMPDIR." + +tmpfile(){ + tmp="${FFTMPDIR}/test"$2 + (set -C; exec > $tmp) 2> /dev/null || + die "Unable to create temporary file in $FFTMPDIR." + eval $1=$tmp +} + +trap 'rm -rf -- "$FFTMPDIR"' EXIT +trap 'exit 2' INT + +tmpfile TMPASM .asm +tmpfile TMPC .c +tmpfile TMPCPP .cpp +tmpfile TMPE $EXESUF +tmpfile TMPH .h +tmpfile TMPM .m +tmpfile TMPCU .cu +tmpfile TMPGLSL .comp.glsl +tmpfile TMPO .o +tmpfile TMPS .S +tmpfile TMPSH .sh +tmpfile TMPV .ver +tmpfile TMPRSP .rsp + +unset -f mktemp + +chmod +x $TMPE + +# make sure we can execute files in $TMPDIR +cat > $TMPSH 2>> $logfile <> $logfile 2>&1 +if ! $TMPSH >> $logfile 2>&1; then + cat <&1 | grep -q '^GNU assembler'; then + true # no-op to avoid reading stdin in following checks + elif $_cc -v 2>&1 | grep -q '^gcc.*LLVM'; then + _type=llvm_gcc + gcc_extra_ver=$(expr "$($_cc --version 2>/dev/null | head -n1)" : '.*\((.*)\)') + _ident="llvm-gcc $($_cc -dumpversion 2>/dev/null) $gcc_extra_ver" + _depflags='-MMD -MF $(@:.o=.d) -MT $@' + _cflags_speed='-O3' + _cflags_size='-Os' + elif $_cc -v 2>&1 | grep -qi ^gcc; then + _type=gcc + gcc_version=$($_cc --version | head -n1) + gcc_basever=$($_cc -dumpversion) + gcc_pkg_ver=$(expr "$gcc_version" : '[^ ]* \(([^)]*)\)') + gcc_ext_ver=$(expr "$gcc_version" : ".*$gcc_pkg_ver $gcc_basever \\(.*\\)") + _ident=$(cleanws "gcc $gcc_basever $gcc_pkg_ver $gcc_ext_ver") + case $gcc_basever in + 2) ;; + 2.*) ;; + *) _depflags='-MMD -MF $(@:.o=.d) -MT $@' ;; + esac + if [ "$first" = true ]; then + case $gcc_basever in + 4.2*) + warn "gcc 4.2 is outdated and may miscompile FFmpeg. Please use a newer compiler." ;; + esac + fi + _cflags_speed='-O3' + _cflags_size='-Os' + elif $_cc --version 2>/dev/null | grep -q ^icc; then + _type=icc + _ident=$($_cc --version | head -n1) + _depflags='-MMD' + _cflags_speed='-O3' + _cflags_size='-Os' + _flags_filter=icc_flags + elif $_cc -v 2>&1 | grep -q xlc; then + _type=xlc + _ident=$($_cc -qversion 2>/dev/null | head -n1) + _cflags_speed='-O5' + _cflags_size='-O5 -qcompact' + elif $_cc --vsn 2>/dev/null | grep -Eq "ARM (C/C\+\+ )?Compiler"; then + test -d "$sysroot" || die "No valid sysroot specified." + _type=armcc + _ident=$($_cc --vsn | grep -i build | head -n1 | sed 's/.*: //') + armcc_conf="$PWD/armcc.conf" + $_cc --arm_linux_configure \ + --arm_linux_config_file="$armcc_conf" \ + --configure_sysroot="$sysroot" \ + --configure_cpp_headers="$sysinclude" >>$logfile 2>&1 || + die "Error creating armcc configuration file." + $_cc --vsn | grep -q RVCT && armcc_opt=rvct || armcc_opt=armcc + _flags="--arm_linux_config_file=$armcc_conf --translate_gcc" + as_default="${cross_prefix}gcc" + _depflags='-MMD' + _cflags_speed='-O3' + _cflags_size='-Os' + elif $_cc -v 2>&1 | grep -q clang && ! $_cc -? > /dev/null 2>&1; then + _type=clang + _ident=$($_cc --version 2>/dev/null | head -n1) + _depflags='-MMD -MF $(@:.o=.d) -MT $@' + _cflags_speed='-O3' + _cflags_size='-Oz' + elif $_cc -V 2>&1 | grep -q Sun; then + _type=suncc + _ident=$($_cc -V 2>&1 | head -n1 | cut -d' ' -f 2-) + _DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< | sed -e "1s,^.*: ,$@: ," -e "\$$!s,\$$, \\\," -e "1!s,^.*: , ," > $(@:.o=.d)' + _DEPFLAGS='-xM1 -x$stdc' + _ldflags='-std=$stdc' + _cflags_speed='-O5' + _cflags_size='-O5 -xspace' + _flags_filter=suncc_flags + elif $_cc -v 2>&1 | grep -q 'PathScale\|Path64'; then + _type=pathscale + _ident=$($_cc -v 2>&1 | head -n1 | tr -d :) + _depflags='-MMD -MF $(@:.o=.d) -MT $@' + _cflags_speed='-O2' + _cflags_size='-Os' + _flags_filter='filter_out -Wdisabled-optimization' + elif $_cc -v 2>&1 | grep -q Open64; then + _type=open64 + _ident=$($_cc -v 2>&1 | head -n1 | tr -d :) + _depflags='-MMD -MF $(@:.o=.d) -MT $@' + _cflags_speed='-O2' + _cflags_size='-Os' + _flags_filter='filter_out -Wdisabled-optimization|-Wtype-limits|-fno-signed-zeros' + elif $_cc 2>&1 | grep -q 'Microsoft.*ARM.*Assembler'; then + _type=armasm + _ident=$($_cc | head -n1) + # 4509: "This form of conditional instruction is deprecated" + _flags="-nologo -ignore 4509" + _flags_filter=armasm_flags + elif $_cc 2>&1 | grep -q Intel; then + _type=icl + _ident=$($_cc 2>&1 | head -n1) + _depflags='-QMMD -QMF$(@:.o=.d) -QMT$@' + # Not only is O3 broken on 13.x+ but it is slower on all previous + # versions (tested) as well. + _cflags_speed="-O2" + _cflags_size="-O1 -Oi" # -O1 without -Oi miscompiles stuff + if $_cc 2>&1 | grep -q Linker; then + _ld_o='-out:$@' + else + _ld_o='-Fe$@' + fi + _cc_o='-Fo$@' + _cc_e='-P -EP' + _flags_filter=icl_flags + _ld_lib='%.lib' + _ld_path='-libpath:' + # -Qdiag-error to make icl error when seeing certain unknown arguments + _flags='-nologo -Qdiag-error:4044,10157' + # -Qvec- -Qsimd- to prevent miscompilation, -GS, fp:precise for consistency + # with MSVC which enables it by default. + _cflags='-Qms0 -Qvec- -Qsimd- -GS -fp:precise' + disable stripping + elif $_cc -? 2>/dev/null | grep -q 'LLVM.*Linker'; then + # lld can emulate multiple different linkers; in ms link.exe mode, + # the -? parameter gives the help output which contains an identifiable + # string, while it gives an error in other modes. + _type=lld-link + # The link.exe mode doesn't have a switch for getting the version, + # but we can force it back to gnu mode and get the version from there. + _ident=$($_cc -flavor gnu --version 2>/dev/null) + _ld_o='-out:$@' + _flags_filter=msvc_flags_link + _ld_lib='%.lib' + _ld_path='-libpath:' + elif VSLANG=1033 $_cc -nologo- 2>&1 | grep -q ^Microsoft || { $_cc -v 2>&1 | grep -q clang && $_cc -? > /dev/null 2>&1; }; then + _type=msvc + if VSLANG=1033 $_cc -nologo- 2>&1 | grep -q ^Microsoft; then + # Depending on the tool (cl.exe or link.exe), the version number + # is printed on the first line of stderr or stdout + _ident=$(VSLANG=1033 $_cc 2>&1 | grep ^Microsoft | head -n1 | tr -d '\r') + else + _ident=$($_cc --version 2>/dev/null | head -n1 | tr -d '\r') + fi + if [ -x "$(command -v wslpath)" ]; then + _DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< 2>&1 | awk '\''/including/ { sub(/^.*file: */, ""); if (!match($$0, / /)) { print $$0 } }'\'' | xargs -r -d\\n -n1 wslpath -u | awk '\''BEGIN { printf "%s:", "$@" }; { sub(/\r/,""); printf " %s", $$0 }; END { print "" }'\'' > $(@:.o=.d)' + + else + _DEPCMD='$(DEP$(1)) $(DEP$(1)FLAGS) $($(1)DEP_FLAGS) $< 2>&1 | awk '\''/including/ { sub(/^.*file: */, ""); gsub(/\\/, "/"); if (!match($$0, / /)) print "$@:", $$0 }'\'' > $(@:.o=.d)' + fi + _DEPFLAGS='$(CPPFLAGS) -showIncludes -Zs' + _DEPCCFLAGS='$(CFLAGS)' + _DEPCXXFLAGS='$(CXXFLAGS)' + _cflags_speed="-O2" + _cflags_size="-O1" + if $_cc -nologo- 2>&1 | grep -q Linker; then + _ld_o='-out:$@' + _flags_filter=msvc_flags_link + else + _ld_o='-Fe$@' + _flags_filter=msvc_flags + fi + _cc_o='-Fo$@' + _cc_e='-P -EP -Fi$@' + _ld_lib='%.lib' + _ld_path='-libpath:' + _flags='-nologo' + _cxxflags='-Zc:__cplusplus -EHsc' + disable stripping + elif $_cc --version 2>/dev/null | grep -q ^cparser; then + _type=cparser + _ident=$($_cc --version | head -n1) + _depflags='-MMD' + _cflags_speed='-O4' + _cflags_size='-O2' + _flags_filter=cparser_flags + fi + + eval ${pfx}_type=\$_type + eval ${pfx}_ident=\$_ident +} + +set_ccvars(){ + eval ${1}_C=\${_cc_c-\${${1}_C}} + eval ${1}_E=\${_cc_e-\${${1}_E}} + eval ${1}_O=\${_cc_o-\${${1}_O}} + + if [ -n "$_depflags" ]; then + eval "${1}_DEPFLAGS=\"\$_depflags\"" + else + eval "${1}DEP=\"\${_DEPCMD:-\$DEPCMD}\"" + eval "${1}DEP_FLAGS=\"\${_DEPFLAGS:-\$DEPFLAGS} \${_DEP${1}FLAGS:-\$DEP${1}FLAGS}\"" + eval "DEP${1}FLAGS=\"\$_flags\"" + fi +} + +probe_cc cc "$cc" "true" +cflags_filter=$_flags_filter +cflags_speed=$_cflags_speed +cflags_size=$_cflags_size +cflags_noopt=$_cflags_noopt +add_cflags $_flags $_cflags +add_cxxflags $_flags $_cxxflags +cc_ldflags=$_ldflags +set_ccvars CC +set_ccvars CXX + +probe_cc hostcc "$host_cc" +host_cflags_filter=$_flags_filter +host_cflags_speed=$_cflags_speed +add_host_cflags $_flags $_cflags +set_ccvars HOSTCC + +test -n "$cc_type" && enable $cc_type || + warn "Unknown C compiler $cc, unable to select optimal CFLAGS" + +: ${as_default:=$cc} +: ${objcc_default:=$cc} +: ${dep_cc_default:=$cc} +: ${ld_default:=$cc} +: ${host_ld_default:=$host_cc} +set_default ar as objcc dep_cc ld ln_s host_ld windres response_files + +probe_cc as "$as" +asflags_filter=$_flags_filter +add_asflags $_flags $_cflags +set_ccvars AS + +probe_cc objcc "$objcc" +objcflags_filter=$_flags_filter +add_objcflags $_flags $_cflags +set_ccvars OBJC + +probe_cc ld "$ld" +ldflags_filter=$_flags_filter +add_ldflags $_flags $_ldflags +test "$cc_type" != "$ld_type" && add_ldflags $cc_ldflags +LD_O=${_ld_o-$LD_O} +LD_LIB=${_ld_lib-$LD_LIB} +LD_PATH=${_ld_path-$LD_PATH} + +probe_cc hostld "$host_ld" +host_ldflags_filter=$_flags_filter +add_host_ldflags $_flags $_ldflags +HOSTLD_O=${_ld_o-$HOSTLD_O} + +if [ -z "$CC_DEPFLAGS" ] && [ "$dep_cc" != "$cc" ]; then + probe_cc depcc "$dep_cc" + CCDEP=${_DEPCMD:-$DEPCMD} + CXXDEP=${CCDEP} + CCDEP_FLAGS=${_DEPFLAGS:=$DEPFLAGS} + CXXDEP_FLAGS=${CCDEP_FLAGS} + DEPCCFLAGS=$_flags $_cflags + DEPCXXFLAGS=$_flags $_cxxflags +fi + +if VSLANG=1033 $ar 2>&1 | grep -q ^Microsoft; then + arflags="-nologo" + ar_o='-out:$@' +elif $ar 2>&1 | grep -q "\[D\] "; then + arflags="rcD" + ar_o='$@' +else + arflags="rc" + ar_o='$@' +fi + +# Treat unrecognized flags as errors on MSVC +test_cpp_condition windows.h "_MSC_FULL_VER >= 193030705" && + check_cflags -options:strict +test_host_cpp_condition windows.h "_MSC_FULL_VER >= 193030705" && + check_host_cflags -options:strict + +add_cflags $extra_cflags +add_cxxflags $extra_cxxflags +add_objcflags $extra_objcflags +add_asflags $extra_cflags + +if test -n "$sysroot"; then + case "$cc_type" in + gcc|llvm_gcc|clang) + add_cppflags --sysroot="$sysroot" + add_ldflags --sysroot="$sysroot" + ;; + esac +fi + +if test "$cpu" = host; then + enabled cross_compile && + warn "--cpu=host makes no sense when cross-compiling." + + case "$cc_type" in + gcc|llvm_gcc) + check_native(){ + : > $TMPC + $cc $1=native -v -c -o $TMPO $TMPC >$TMPE 2>&1 || return + sed -n "/cc1.*$1=/{ + s/.*$1=\\([^ ]*\\).*/\\1/ + p + q + }" $TMPE + } + if cpu=$(check_native -march); then + native_cpuflags="-march=native" + elif cpu=$(check_native -mcpu); then + native_cpuflags="-mcpu=native" + fi + ;; + clang) + check_native(){ + : > $TMPC + $cc $1=native -v -c -o $TMPO $TMPC >$TMPE 2>&1 || return + sed -n "/cc1.*-target-cpu /{ + s/.*-target-cpu \\([^ ]*\\).*/\\1/ + p + q + }" $TMPE + } + cpu=$(check_native -march) && native_cpuflags="-march=native" + ;; + esac + + test "${cpu:-host}" = host && + die "--cpu=host not supported with compiler $cc" +fi + +# Deal with common $arch aliases +case "$arch" in + aarch64|arm64) + arch="aarch64" + ;; + arm*|iPad*|iPhone*) + arch="arm" + ;; + loongarch*|loong64) + arch="loongarch" + ;; + mips*|IP*) + case "$arch" in + *el) + add_cppflags -EL + add_ldflags -EL + ;; + *eb) + add_cppflags -EB + add_ldflags -EB + ;; + esac + arch="mips" + ;; + parisc*|hppa*) + arch="parisc" + ;; + "Power Macintosh"|ppc*|powerpc*) + arch="ppc" + ;; + riscv*) + arch="riscv" + ;; + s390|s390x) + arch="s390" + ;; + sun4*|sparc*) + arch="sparc" + ;; + tilegx|tile-gx) + arch="tilegx" + ;; + wasm*) + arch="wasm" + ;; + i[3-6]86*|i86pc|BePC|x86pc|x86_64|x86_32|amd64) + arch="x86" + ;; +esac + +is_in $arch $ARCH_LIST || warn "unknown architecture $arch" +enable $arch + +# Add processor-specific flags +if enabled aarch64; then + + case $cpu in + armv*) + cpuflags="-march=$cpu" + ;; + *) + cpuflags="-mcpu=$cpu" + ;; + esac + +elif enabled arm; then + + check_arm_arch() { + test_cpp_condition stddef.h \ + "defined __ARM_ARCH_${1}__ || defined __TARGET_ARCH_${2:-$1}" \ + $cpuflags + } + + probe_arm_arch() { + if check_arm_arch 4; then echo armv4 + elif check_arm_arch 4T; then echo armv4t + elif check_arm_arch 5; then echo armv5 + elif check_arm_arch 5E; then echo armv5e + elif check_arm_arch 5T; then echo armv5t + elif check_arm_arch 5TE; then echo armv5te + elif check_arm_arch 5TEJ; then echo armv5te + elif check_arm_arch 6; then echo armv6 + elif check_arm_arch 6J; then echo armv6j + elif check_arm_arch 6K; then echo armv6k + elif check_arm_arch 6Z; then echo armv6z + elif check_arm_arch 6KZ; then echo armv6zk + elif check_arm_arch 6ZK; then echo armv6zk + elif check_arm_arch 6T2; then echo armv6t2 + elif check_arm_arch 7; then echo armv7 + elif check_arm_arch 7A 7_A; then echo armv7-a + elif check_arm_arch 7S; then echo armv7-a + elif check_arm_arch 7R 7_R; then echo armv7-r + elif check_arm_arch 7M 7_M; then echo armv7-m + elif check_arm_arch 7EM 7E_M; then echo armv7-m + elif check_arm_arch 8A 8_A; then echo armv8-a + fi + } + + + case $cpu in + generic) + subarch=$(probe_arm_arch | sed 's/[^a-z0-9]//g') + ;; + armv*) + cpuflags="-march=$cpu" + subarch=$(echo $cpu | sed 's/[^a-z0-9]//g') + ;; + *) + cpuflags="-mcpu=$cpu" + case $cpu in + cortex-a*) subarch=armv7a ;; + cortex-r*) subarch=armv7r ;; + cortex-m*) enable thumb; subarch=armv7m ;; + arm11*) subarch=armv6 ;; + arm[79]*e*|arm9[24]6*|arm96*|arm102[26]) subarch=armv5te ;; + armv4*|arm7*|arm9[24]*) subarch=armv4 ;; + *) subarch=$(probe_arm_arch) ;; + esac + ;; + esac + + case "$subarch" in + armv5t*) enable fast_clz ;; + armv[6-8]*) + enable fast_clz + disabled fast_unaligned || enable fast_unaligned + ;; + esac + + check_allcflags -mfp16-format=ieee + +elif enabled loongarch; then + + enable simd_align_32 + enable fast_64bit + enable fast_clz + enable fast_unaligned + case $cpu in + la464) + cpuflags="-march=$cpu" + ;; + esac +elif enabled mips; then + + if [ "$cpu" != "generic" ]; then + disable mips32r2 + disable mips32r5 + disable mips64r2 + disable mips32r6 + disable mips64r6 + disable loongson2 + disable loongson3 + disable mipsdsp + disable mipsdspr2 + + cpuflags="-march=$cpu" + + case $cpu in + # General ISA levels + mips1|mips3) + ;; + mips32r2) + enable mips32r2 + ;; + mips32r5) + enable mips32r2 + enable mips32r5 + ;; + mips64r2|mips64r5) + enable mips64r2 + enable loongson3 + ;; + # Cores from MIPS(MTI) + 24kc) + disable mipsfpu + enable mips32r2 + ;; + 24kf*|24kec|34kc|74Kc|1004kc) + enable mips32r2 + ;; + 24kef*|34kf*|1004kf*) + enable mipsdsp + enable mips32r2 + ;; + p5600) + enable mips32r2 + enable mips32r5 + check_cflags "-mtune=p5600" && check_cflags "-msched-weight -mload-store-pairs -funroll-loops" + ;; + i6400) + enable mips64r6 + check_cflags "-mtune=i6400 -mabi=64" && check_cflags "-msched-weight -mload-store-pairs -funroll-loops" && check_ldflags "-mabi=64" + ;; + p6600) + enable mips64r6 + check_cflags "-mtune=p6600 -mabi=64" && check_cflags "-msched-weight -mload-store-pairs -funroll-loops" && check_ldflags "-mabi=64" + ;; + # Cores from Loongson + loongson2e|loongson2f|loongson3*) + enable simd_align_16 + enable fast_64bit + enable fast_clz + enable fast_cmov + enable fast_unaligned + disable aligned_stack + # When gcc version less than 5.3.0, add -fno-expensive-optimizations flag. + if test "$cc_type" = "gcc"; then + case $gcc_basever in + 2|2.*|3.*|4.*|5.0|5.1|5.2) + expensive_optimization_flag="-fno-expensive-optimizations" + ;; + *) + expensive_optimization_flag="" + ;; + esac + fi + + case $cpu in + loongson3*) + enable loongson3 + cpuflags="-march=loongson3a -mhard-float $expensive_optimization_flag" + ;; + loongson2e) + enable loongson2 + cpuflags="-march=loongson2e -mhard-float $expensive_optimization_flag" + ;; + loongson2f) + enable loongson2 + cpuflags="-march=loongson2f -mhard-float $expensive_optimization_flag" + ;; + esac + ;; + *) + warn "unknown MIPS CPU" + ;; + esac + + else + disable mipsdsp + disable mipsdspr2 + # Disable DSP stuff for generic CPU, it can't be detected at runtime. + warn 'generic cpu selected' + fi + +elif enabled ppc; then + + disable ldbrx + + case $(tolower $cpu) in + 601|ppc601|powerpc601) + cpuflags="-mcpu=601" + disable altivec + ;; + 603*|ppc603*|powerpc603*) + cpuflags="-mcpu=603" + disable altivec + ;; + 604*|ppc604*|powerpc604*) + cpuflags="-mcpu=604" + disable altivec + ;; + g3|75*|ppc75*|powerpc75*) + cpuflags="-mcpu=750" + disable altivec + ;; + g4|745*|ppc745*|powerpc745*) + cpuflags="-mcpu=7450" + disable vsx + ;; + 74*|ppc74*|powerpc74*) + cpuflags="-mcpu=7400" + disable vsx + ;; + g5|970|ppc970|powerpc970) + cpuflags="-mcpu=970" + disable vsx + ;; + power[3-6]*) + cpuflags="-mcpu=$cpu" + disable vsx + ;; + power[7-9]*|power10) + cpuflags="-mcpu=$cpu" + ;; + cell) + cpuflags="-mcpu=cell" + enable ldbrx + disable vsx + ;; + e500mc) + cpuflags="-mcpu=e500mc" + disable altivec + ;; + e500v2) + cpuflags="-mcpu=8548 -mhard-float -mfloat-gprs=double" + disable altivec + disable dcbzl + ;; + e500) + cpuflags="-mcpu=8540 -mhard-float" + disable altivec + disable dcbzl + ;; + esac + +elif enabled riscv; then + + check_headers asm/hwprobe.h + check_headers sys/hwprobe.h + + if test_cpp_condition stddef.h "__riscv_zbb"; then + enable fast_clz + fi + if test_cpp_condition stddef.h "__riscv_zfhmin"; then + enable fast_float16 + fi + +elif enabled sparc; then + + case $cpu in + cypress|f93[04]|tsc701|sparcl*|supersparc|hypersparc|niagara|v[789]) + cpuflags="-mcpu=$cpu" + ;; + ultrasparc*|niagara[234]) + cpuflags="-mcpu=$cpu" + ;; + esac + +elif enabled x86; then + + case $cpu in + i[345]86|pentium) + cpuflags="-march=$cpu" + disable i686 + disable mmx + ;; + # targets that do NOT support nopl and conditional mov (cmov) + pentium-mmx|k6|k6-[23]|winchip-c6|winchip2|c3) + cpuflags="-march=$cpu" + disable i686 + ;; + # targets that do support conditional mov but on which it's slow + pentium4|pentium4m|prescott|nocona) + cpuflags="-march=$cpu" + enable i686 + disable fast_cmov + ;; + # everything else should support nopl and conditional mov (cmov) + *) + cpuflags="-march=$cpu" + enable i686 + enable fast_cmov + ;; + esac + +else + + if test_cpp_condition inttypes.h "UINTPTR_MAX >= UINT64_MAX"; then + enable fast_64bit + fi + +fi + +if [ "$cpu" != generic ]; then + # For --cpu=host, use the compiler's =native for codegen. $cpu is + # only the resolved name used for feature gating above. + test -n "$native_cpuflags" && cpuflags=$native_cpuflags + add_allcflags $cpuflags + add_asflags $cpuflags + test "$cc_type" = "$ld_type" && add_ldflags $cpuflags +fi + +# compiler sanity check +test_exec <= 201103L" || + { check_cxxflags -std=c++11 && stdcxx="c++11" || { check_cxxflags -std=c++0x && stdcxx="c++0x"; }; } + +test_cxxflags_cc -std=$stdcxx ctype.h "__cplusplus >= 201703L" && enable cxx17 + +# some compilers silently accept -std=c11, so we also need to check that the +# version macro is defined properly +check_cflags_cc -std=$stdc ctype.h "__STDC_VERSION__ >= 201112L" || + { check_cflags_cc -std=c11 ctype.h "__STDC_VERSION__ >= 201112L" && stdc="c11" || die "Compiler lacks C11 support"; } + +test_cc < +#include +struct Foo { + int a; + void *ptr; +} obj; +static_assert(offsetof(struct Foo, a) == 0, + "First element of struct does not have offset 0"); +_Static_assert(offsetof(struct Foo, ptr) >= offsetof(struct Foo, a) + sizeof(obj.a), + "elements not properly ordered in struct"); +EOF + +check_cppflags -D_FILE_OFFSET_BITS=64 +check_cppflags -D_LARGEFILE_SOURCE + +add_host_cppflags -D_ISOC11_SOURCE +check_host_cflags_cc -std=$stdc ctype.h "__STDC_VERSION__ >= 201112L" || + check_host_cflags_cc -std=c11 ctype.h "__STDC_VERSION__ >= 201112L" || die "Host compiler lacks C11 support" + +check_host_cflags -Wall +check_host_cflags $host_cflags_speed + +check_64bit(){ + arch32=$1 + arch64=$2 + expr=${3:-'sizeof(void *) > 4'} + test_code cc "" "int test[2*($expr) - 1]" && + subarch=$arch64 || subarch=$arch32 + enable $subarch +} + +case "$arch" in + aarch64|ia64) + enabled shared && enable_weak pic + ;; + loongarch) + check_64bit loongarch32 loongarch64 + enabled loongarch64 && disable loongarch32 + enabled shared && enable_weak pic + ;; + mips) + check_64bit mips mips64 '_MIPS_SIM > 1' + enabled shared && enable_weak pic + ;; + parisc) + check_64bit parisc parisc64 + enabled shared && enable_weak pic + ;; + ppc) + check_64bit ppc ppc64 + enabled shared && enable_weak pic + ;; + riscv) + check_64bit riscv32 riscv64 + enabled shared && enable_weak pic + ;; + s390) + check_64bit s390 s390x + enabled shared && enable_weak pic + ;; + sparc) + check_64bit sparc sparc64 + enabled shared && enable_weak pic + ;; + x86) + check_64bit x86_32 x86_64 + # Treat x32 as x64 for now. Note it also needs pic if shared + test "$subarch" = "x86_32" && test_cpp_condition stddef.h 'defined(__x86_64__)' && + subarch=x86_64 && enable x86_64 && disable x86_32 + if enabled x86_64; then + enabled shared && enable_weak pic + objformat=elf64 + fi + ;; +esac + +# OS specific +case $target_os in + aix) + SHFLAGS=-shared + add_cppflags '-I\$(SRC_PATH)/compat/aix' + enabled shared && add_ldflags -Wl,-brtl + arflags='-Xany -r -c' + striptype="" + ;; + android) + disable symver + enable section_data_rel_ro + add_allcflags -fPIE + add_ldexeflags -fPIE -pie + SLIB_INSTALL_NAME='$(SLIBNAME)' + SLIB_INSTALL_LINKS= + SHFLAGS='-shared -Wl,-soname,$(SLIBNAME)' + ;; + haiku) + prefix_default="/boot/common" + network_extralibs="-lnetwork" + host_extralibs= + ;; + sunos) + SHFLAGS='-shared -Wl,-h,$$(@F)' + enabled x86 && append SHFLAGS -mimpure-text + network_extralibs="-lsocket -lnsl" + add_cppflags -D__EXTENSIONS__ + # When using suncc to build, the Solaris linker will mark + # an executable with each instruction set encountered by + # the Solaris assembler. As our libraries contain their own + # guards for processor-specific code, instead suppress + # generation of the HWCAPS ELF section on Solaris x86 only. + enabled_all suncc x86 && + echo "hwcap_1 = OVERRIDE;" > mapfile && + add_ldflags -Wl,-M,mapfile + nm_default='nm -P -g' + striptype="" + version_script='-M' + VERSION_SCRIPT_POSTPROCESS_CMD='perl $(SRC_PATH)/compat/solaris/make_sunver.pl - $(OBJS)' + ;; + netbsd) + disable symver + enable section_data_rel_ro + oss_indev_extralibs="-lossaudio" + oss_outdev_extralibs="-lossaudio" + enabled gcc || check_ldflags -Wl,-zmuldefs + ;; + openbsd) + disable symver + enable section_data_rel_ro + striptype="" + SHFLAGS='-shared' + SLIB_INSTALL_NAME='$(SLIBNAME).$(LIBMAJOR).$(LIBMINOR)' + SLIB_INSTALL_LINKS= + oss_indev_extralibs="-lossaudio" + oss_outdev_extralibs="-lossaudio" + ;; + dragonfly) + disable symver + ;; + freebsd) + enable section_data_rel_ro + ;; + bsd/os) + add_extralibs -lpoll -lgnugetopt + strip="strip -d" + ;; + darwin) + enabled ppc && add_asflags -force_cpusubtype_ALL + install_name_dir_default='$(SHLIBDIR)' + SHFLAGS='-dynamiclib -Wl,-single_module -Wl,-install_name,$(INSTALL_NAME_DIR)/$(SLIBNAME_WITH_MAJOR),-current_version,$(LIBVERSION),-compatibility_version,$(LIBMAJOR)' + enabled x86_32 && append SHFLAGS -Wl,-read_only_relocs,suppress + strip="${strip} -x" + add_ldflags -Wl,-dynamic,-search_paths_first + check_allcflags -Werror=partial-availability + SLIBSUF=".dylib" + SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME).$(LIBVERSION)$(SLIBSUF)' + SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME).$(LIBMAJOR)$(SLIBSUF)' + enabled x86_64 && objformat="macho64" || objformat="macho32" + enabled_any pic shared x86_64 || + { check_cflags -mdynamic-no-pic && add_asflags -mdynamic-no-pic; } + check_headers dispatch/dispatch.h && + add_cppflags '-I\$(SRC_PATH)/compat/dispatch_semaphore' + if test -n "$sysroot"; then + is_in -isysroot $cc $CPPFLAGS $CFLAGS || check_cppflags -isysroot $sysroot + is_in -isysroot $ld $LDFLAGS || check_ldflags -isysroot $sysroot + fi + version_script='-exported_symbols_list' + VERSION_SCRIPT_POSTPROCESS_CMD='tr " " "\n" | sed -n /global:/,/local:/p | grep ";" | tr ";" "\n" | sed -E "s/(.+)/_\1/g" | sed -E "s/(.+[^*])$$$$/\1*/"' + # Workaround for Xcode 11 -fstack-check bug + if enabled clang; then + clang_version=$($cc -dumpversion) + test ${clang_version%%.*} -eq 11 && add_allcflags -fno-stack-check + fi + + # Xcode Clang doesn't default to -fno-common while upstream llvm.org + # Clang (and GCC) do. This avoids linker warnings on Xcode 16.3 about + # "reducing alignment of section __DATA,__common from 0x8000 to 0x4000 + # because it exceeds segment maximum alignment". + check_cflags -fno-common + + ;; + msys*) + die "Native MSYS builds are discouraged, please use the MINGW environment." + ;; + mingw32*|mingw64*) + target_os=mingw32 + LIBTARGET=i386 + if enabled x86_64; then + LIBTARGET="i386:x86-64" + elif enabled arm; then + LIBTARGET="arm" + elif enabled aarch64; then + LIBTARGET="arm64" + fi + if enabled shared; then + # Cannot build both shared and static libs when using dllimport. + disable static + fi + ! enabled small && test_cmd $windres --version && enable gnu_windres + enabled x86_32 && check_ldflags -Wl,--large-address-aware + add_cppflags -DWIN32_LEAN_AND_MEAN + shlibdir_default="$bindir_default" + SLIBPREF="" + SLIBSUF=".dll" + SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME)-$(LIBVERSION)$(SLIBSUF)' + SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)' + if test_cmd lib.exe -list; then + SLIB_EXTRA_CMD=-'lib.exe -nologo -machine:$(LIBTARGET) -def:$$(@:$(SLIBSUF)=.def) -out:$(SUBDIR)$(SLIBNAME:$(SLIBSUF)=.lib)' + if enabled x86_64; then + LIBTARGET=x64 + fi + else + SLIB_EXTRA_CMD=-'$(DLLTOOL) -m $(LIBTARGET) -d $$(@:$(SLIBSUF)=.def) -l $(SUBDIR)$(SLIBNAME:$(SLIBSUF)=.lib) -D $(SLIBNAME_WITH_MAJOR)' + fi + SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)' + SLIB_INSTALL_LINKS= + SLIB_INSTALL_EXTRA_SHLIB='$(SLIBNAME:$(SLIBSUF)=.lib)' + SLIB_INSTALL_EXTRA_LIB='lib$(SLIBNAME:$(SLIBSUF)=.dll.a) $(SLIBNAME_WITH_MAJOR:$(SLIBSUF)=.def)' + SLIB_CREATE_DEF_CMD='EXTERN_PREFIX="$(EXTERN_PREFIX)" AR="$(AR_CMD)" NM="$(NM_CMD)" $(SRC_PATH)/compat/windows/makedef $(SUBDIR)lib$(NAME).ver $(SLIB_CREATE_DEF_INPUTS) > $$(@:$(SLIBSUF)=.def)' + SHFLAGS='-shared -Wl,--out-implib,$(SUBDIR)lib$(SLIBNAME:$(SLIBSUF)=.dll.a) -Wl,--disable-auto-image-base $$(@:$(SLIBSUF)=.def)' + enabled x86_64 && objformat="win64" || objformat="win32" + dlltool="${cross_prefix}dlltool" + ranlib=: + enable dos_paths + check_ldflags -Wl,--nxcompat,--dynamicbase + # Lets work around some stupidity in binutils. + # ld will strip relocations from executables even though we need them + # for dynamicbase (ASLR). Using -pie does retain the reloc section + # however ld then forgets what the entry point should be (oops) so we + # have to manually (re)set it. + if enabled x86_32; then + disabled debug && add_ldexeflags -Wl,--pic-executable,-e,_mainCRTStartup + elif enabled x86_64; then + disabled debug && add_ldexeflags -Wl,--pic-executable,-e,mainCRTStartup + check_ldflags -Wl,--high-entropy-va # binutils 2.25 + # Set image base >4GB for extra entropy with HEASLR + add_ldexeflags -Wl,--image-base,0x140000000 + append SHFLAGS -Wl,--image-base,0x180000000 + fi + ;; + win32|win64) + disable symver + LIBSUF=".lib" + if enabled shared; then + # Cannot build both shared and static libs with MSVC or icl. + disable static + LIBSUF="-static.lib" + fi + ! enabled small && test_cmd $windres --version && enable gnu_windres + enabled x86_32 && check_ldflags -LARGEADDRESSAWARE + add_cppflags -DWIN32_LEAN_AND_MEAN + shlibdir_default="$bindir_default" + LIBPREF="" + SLIBPREF="" + SLIBSUF=".dll" + SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME)-$(LIBVERSION)$(SLIBSUF)' + SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)' + SLIB_CREATE_DEF_CMD='LDFLAGS="$(LDFLAGS)" EXTERN_PREFIX="$(EXTERN_PREFIX)" $(SRC_PATH)/compat/windows/makedef $(SUBDIR)lib$(NAME).ver $(SLIB_CREATE_DEF_INPUTS) > $$(@:$(SLIBSUF)=.def)' + SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)' + SLIB_INSTALL_LINKS= + SLIB_INSTALL_EXTRA_SHLIB='$(SLIBNAME:$(SLIBSUF)=.lib)' + SLIB_INSTALL_EXTRA_LIB='$(SLIBNAME_WITH_MAJOR:$(SLIBSUF)=.def)' + if test $ld_type = "clang"; then + SHFLAGS='-Wl,-dll -Wl,-def:$$(@:$(SLIBSUF)=.def) -Wl,-implib:$(SUBDIR)$(SLIBNAME:$(SLIBSUF)=.lib)' + else + SHFLAGS='-dll -def:$$(@:$(SLIBSUF)=.def) -implib:$(SUBDIR)$(SLIBNAME:$(SLIBSUF)=.lib)' + fi + enabled x86_64 && objformat="win64" || objformat="win32" + ranlib=: + enable dos_paths + ;; + cygwin*) + target_os=cygwin + shlibdir_default="$bindir_default" + SLIBPREF="cyg" + SLIBSUF=".dll" + SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME)-$(LIBVERSION)$(SLIBSUF)' + SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)' + SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)' + SLIB_INSTALL_LINKS= + SLIB_INSTALL_EXTRA_LIB='lib$(FULLNAME).dll.a' + SHFLAGS='-shared -Wl,--out-implib,$(SUBDIR)lib$(FULLNAME).dll.a' + enabled x86_64 && objformat="win64" || objformat="win32" + enable dos_paths + ! enabled small && test_cmd $windres --version && enable gnu_windres + add_cppflags -DWIN32_LEAN_AND_MEAN + add_cppflags -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 + ;; + *-dos|freedos|opendos) + network_extralibs="-lsocket" + objformat="coff" + enable dos_paths + ;; + linux) + enable section_data_rel_ro + enabled_any arm aarch64 && enable_weak linux_perf + ;; + irix*) + target_os=irix + ranlib="echo ignoring ranlib" + ;; + os/2*) + strip="lxlite -CS" + striptype="" + objformat="aout" + add_cppflags -D_GNU_SOURCE + add_ldflags -Zomf -Zbin-files -Zargs-wild -Zhigh-mem -Zmap + SHFLAGS='$(SUBDIR)$(NAME).def -Zdll -Zomf' + LIBSUF="_s.a" + SLIBPREF="" + SLIBSUF=".dll" + SLIBNAME_WITH_VERSION='$(SLIBPREF)$(FULLNAME)-$(LIBVERSION)$(SLIBSUF)' + SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(shell echo $(FULLNAME) | cut -c1-6)$(LIBMAJOR)$(SLIBSUF)' + SLIB_CREATE_DEF_CMD='echo LIBRARY $(SLIBNAME_WITH_MAJOR:$(SLIBSUF)=) INITINSTANCE TERMINSTANCE > $(SUBDIR)$(FULLNAME).def; \ + echo CODE PRELOAD MOVEABLE DISCARDABLE >> $(SUBDIR)$(FULLNAME).def; \ + echo DATA PRELOAD MOVEABLE MULTIPLE NONSHARED >> $(SUBDIR)$(FULLNAME).def; \ + echo EXPORTS >> $(SUBDIR)$(FULLNAME).def; \ + emxexp $(OBJS) >> $(SUBDIR)$(FULLNAME).def' + SLIB_EXTRA_CMD='emximp -o $(SUBDIR)$(LIBPREF)$(FULLNAME)_dll.a $(SUBDIR)$(FULLNAME).def; \ + emximp -o $(SUBDIR)$(LIBPREF)$(FULLNAME)_dll.lib $(SUBDIR)$(FULLNAME).def;' + SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)' + SLIB_INSTALL_LINKS= + SLIB_INSTALL_EXTRA_LIB='$(LIBPREF)$(FULLNAME)_dll.a $(LIBPREF)$(FULLNAME)_dll.lib' + enable dos_paths + enable_weak os2threads + ;; + gnu/kfreebsd) + add_cppflags -D_BSD_SOURCE + ;; + gnu) + ;; + qnx) + add_cppflags -D_QNX_SOURCE + network_extralibs="-lsocket" + ;; + symbian) + SLIBSUF=".dll" + enable dos_paths + add_cflags --include=$sysinclude/gcce/gcce.h -fvisibility=default + add_cppflags -D__GCCE__ -D__SYMBIAN32__ -DSYMBIAN_OE_POSIX_SIGNALS + add_ldflags -Wl,--target1-abs,--no-undefined \ + -Wl,-Ttext,0x80000,-Tdata,0x1000000 -shared \ + -Wl,--entry=_E32Startup -Wl,-u,_E32Startup + add_extralibs -l:eexe.lib -l:usrt2_2.lib -l:dfpaeabi.dso \ + -l:drtaeabi.dso -l:scppnwdl.dso -lsupc++ -lgcc \ + -l:libc.dso -l:libm.dso -l:euser.dso -l:libcrt0.lib + ;; + minix) + ;; + none) + ;; + *) + die "Unknown OS '$target_os'." + ;; +esac + +# test if creating links works +link_dest=$(mktemp -u $TMPDIR/dest_XXXXXXXX) +link_name=$(mktemp -u $TMPDIR/name_XXXXXXXX) +mkdir "$link_dest" +$ln_s "$link_dest" "$link_name" +touch "$link_dest/test_file" +if [ "$source_path" != "." ] && [ "$source_path" != "./src" ] && ([ ! -d src ] || [ -L src ]) && [ -e "$link_name/test_file" ]; then + # create link to source path + [ -e src ] && rm src + $ln_s "$source_path" src + source_link=src +else + # creating directory links doesn't work + # fall back to using the full source path + source_link="$source_path" +fi +# cleanup +rm -r "$link_dest" +rm -r "$link_name" + +# determine libc flavour + +probe_libc(){ + pfx=$1 + pfx_no_=${pfx%_} + # uclibc defines __GLIBC__, so it needs to be checked before glibc. + if test_${pfx}cpp_condition features.h "defined __UCLIBC__"; then + eval ${pfx}libc_type=uclibc + add_${pfx}cppflags -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 + elif test_${pfx}cpp_condition features.h "defined __GLIBC__"; then + eval ${pfx}libc_type=glibc + add_${pfx}cppflags -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 + # MinGW headers can be installed on Cygwin, so check for newlib first. + elif test_${pfx}cpp_condition newlib.h "defined _NEWLIB_VERSION"; then + eval ${pfx}libc_type=newlib + add_${pfx}cflags -U__STRICT_ANSI__ + add_${pfx}cppflags -D_XOPEN_SOURCE=600 + # MinGW64 is backwards compatible with MinGW32, so check for it first. + elif test_${pfx}cpp_condition _mingw.h "defined __MINGW64_VERSION_MAJOR"; then + eval ${pfx}libc_type=mingw64 + if test_${pfx}cpp_condition _mingw.h "__MINGW64_VERSION_MAJOR < 3"; then + add_compat msvcrt/snprintf.o + add_allcflags "-include $source_path/compat/msvcrt/snprintf.h" + fi + add_${pfx}cflags -U__STRICT_ANSI__ + if ! test_${pfx}cpp_condition crtdefs.h "defined(_UCRT)"; then + add_${pfx}cppflags -D__USE_MINGW_ANSI_STDIO=1 + fi + test_${pfx}cpp_condition windows.h "!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600" && + add_${pfx}cppflags -D_WIN32_WINNT=0x0600 + add_${pfx}cppflags -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 + elif test_${pfx}cpp_condition _mingw.h "defined __MINGW_VERSION" || + test_${pfx}cpp_condition _mingw.h "defined __MINGW32_VERSION"; then + eval ${pfx}libc_type=mingw32 + test_${pfx}cpp_condition _mingw.h "__MINGW32_MAJOR_VERSION > 3 || \ + (__MINGW32_MAJOR_VERSION == 3 && __MINGW32_MINOR_VERSION >= 15)" || + die "ERROR: MinGW32 runtime version must be >= 3.15." + add_${pfx}cflags -U__STRICT_ANSI__ + if ! test_${pfx}cpp_condition crtdefs.h "defined(_UCRT)"; then + add_${pfx}cppflags -D__USE_MINGW_ANSI_STDIO=1 + fi + test_${pfx}cpp_condition _mingw.h "__MSVCRT_VERSION__ < 0x0700" && + add_${pfx}cppflags -D__MSVCRT_VERSION__=0x0700 + test_${pfx}cpp_condition windows.h "!defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600" && + add_${pfx}cppflags -D_WIN32_WINNT=0x0600 + add_${pfx}cppflags -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 + elif test_${pfx}cpp_condition crtversion.h "defined _VC_CRT_MAJOR_VERSION"; then + eval ${pfx}libc_type=msvcrt + if test_${pfx}cpp_condition crtversion.h "_VC_CRT_MAJOR_VERSION < 14"; then + if [ "$pfx" = host_ ]; then + add_host_cppflags -Dsnprintf=_snprintf + else + add_compat strtod.o strtod=avpriv_strtod + add_compat msvcrt/snprintf.o snprintf=avpriv_snprintf \ + _snprintf=avpriv_snprintf \ + vsnprintf=avpriv_vsnprintf + fi + fi + add_${pfx}cppflags -D_USE_MATH_DEFINES -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS + # The MSVC 2010 headers (Win 7.0 SDK) set _WIN32_WINNT to + # 0x601 by default unless something else is set by the user. + # This can easily lead to us detecting functions only present + # in such new versions and producing binaries requiring windows 7.0. + # Therefore explicitly set the default to Vista unless the user has + # set something else on the command line. + # Don't do this if WINAPI_FAMILY is set and is set to a non-desktop + # family. For these cases, configure is free to use any functions + # found in the SDK headers by default. (Alternatively, we could force + # _WIN32_WINNT to 0x0602 in that case.) + test_${pfx}cpp_condition stdlib.h "defined(_WIN32_WINNT)" || + { test_${pfx}cpp < +#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#error not desktop +#endif +#endif +EOF + if [ "$pfx" = "" ]; then + check_func strtoll || add_allcflags -Dstrtoll=_strtoi64 + check_func strtoull || add_allcflags -Dstrtoull=_strtoui64 + fi + elif test_${pfx}cpp_condition stddef.h "defined __KLIBC__"; then + eval ${pfx}libc_type=klibc + elif test_${pfx}cpp_condition sys/cdefs.h "defined __BIONIC__"; then + eval ${pfx}libc_type=bionic + elif test_${pfx}cpp_condition sys/brand.h "defined LABELED_BRAND_NAME"; then + eval ${pfx}libc_type=solaris + add_${pfx}cppflags -D__EXTENSIONS__ -D_XOPEN_SOURCE=600 + elif test_${pfx}cpp_condition sys/version.h "defined __DJGPP__"; then + eval ${pfx}libc_type=djgpp + add_cppflags -U__STRICT_ANSI__ + add_allcflags "-include $source_path/compat/djgpp/math.h" + add_compat djgpp/math.o + fi + test_${pfx}cc < +void *v = localtime_r; +EOF +test "$?" != 0 && test_${pfx}cc -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 < +void *v = localtime_r; +EOF + + eval test -n "\${${pfx}libc_type}" && enable ${pfx}libc_${libc_type} +} + +probe_libc +probe_libc host_ + +# hacks for compiler/libc/os combinations + +case $libc_type in + bionic) + add_compat strtod.o strtod=avpriv_strtod + ;; +esac + +check_compile_assert flt_lim "float.h limits.h" "DBL_MAX == (double)DBL_MAX" || + add_cppflags '-I\$(SRC_PATH)/compat/float' + +test_cpp_condition stdlib.h "defined(__PIC__) || defined(__pic__) || defined(PIC)" && enable_weak pic + +set_default libdir +: ${shlibdir_default:="$libdir"} +: ${pkgconfigdir_default:="$libdir/pkgconfig"} + +set_default $PATHS_LIST +set_default nm + +disabled optimizations || enabled ossfuzz || echo "$CFLAGS" | grep -q -- '-fsanitize=' || check_allcflags -fomit-frame-pointer + +enable_weak_pic() { + disabled pic && return + enable pic + add_cppflags -DPIC + case "$target_os" in + mingw*|cygwin*|win*) + ;; + *) + add_allcflags -fPIC + add_asflags -fPIC + ;; + esac +} + +enabled pic && enable_weak_pic + +test_cc <" to not have any effect unless it + # had an extra "+" included - but it was activated on the next + # ".arch_extension" directive. Check if we can include "+fp" as dummy + # feature to make the .arch directive behave as expected and take + # effect right away. + check_arch_level "${as_arch_level}+fp" + fi + + enabled armv8 && check_insn armv8 'prfm pldl1strm, [x0]' + # internal assembler in clang 3.3 does not support this instruction + enabled neon && check_insn neon 'ext v0.8B, v0.8B, v1.8B, #1' + + archext_list="arm_crc dotprod i8mm pmull eor3 sve sve2 sme sme_i16i64 sme2" + enabled arm_crc && check_archext_name_insn arm_crc crc 'crc32x w0, w0, x0' + enabled pmull && check_archext_name_insn pmull aes 'pmull v0.1q, v0.1d, v0.1d' + enabled eor3 && check_archext_name_insn eor3 sha3 'eor3 v0.16b, v1.16b, v2.16b, v3.16b' + enabled dotprod && check_archext_insn dotprod 'udot v0.4s, v0.16b, v0.16b' + enabled i8mm && check_archext_insn i8mm 'usdot v0.4s, v0.16b, v0.16b' + enabled sve && check_archext_insn sve 'whilelt p0.s, x0, x1' + enabled sve2 && check_archext_insn sve2 'sqrdmulh z0.s, z0.s, z0.s' + enabled sme && check_archext_insn sme 'smstart' 'cntb x0' + enabled sme_i16i64 && check_archext_name_insn sme_i16i64 sme-i16i64 'smstart' 'smopa za0.d, p0/m, p0/m, z0.h, z1.h' + enabled sme2 && check_archext_insn sme2 'smstart' 'sdot za.s[w10, 0], {z0.b-z3.b}, {z4.b-z7.b}' + + # Disable the main feature (e.g. HAVE_NEON) if neither inline nor external + # assembly support the feature out of the box. Skip this for the features + # checked with check_archext_insn above, as that function takes care of + # updating all the variables as necessary. + for v in $ARCH_EXT_LIST_ARM; do + is_in $v $archext_list && continue + enabled_any ${v}_external ${v}_inline || disable $v + done + +elif enabled arm; then + + enabled msvc && check_cpp_condition thumb stddef.h "defined _M_ARMT" + test_cpp_condition stddef.h "defined __thumb__" && test_cc <= 30"; then + : + elif ! test_cpp_condition stddef.h "defined __ARM_PCS || defined __SOFTFP__" && [ $target_os != darwin ]; then + case "${cross_prefix:-$cc}" in + *hardfloat*) enable vfp_args; fpabi=vfp ;; + *) check_ld "cc" vfp_args </dev/null) + require_pkg_config custom_allocator jemalloc jemalloc/jemalloc.h ${malloc_prefix}malloc + ;; + tcmalloc) + require_pkg_config custom_allocator libtcmalloc gperftools/tcmalloc.h tc_malloc + malloc_prefix=tc_ + ;; + *) + require_pkg_config custom_allocator "$custom_allocator" stdlib.h malloc + ;; +esac + +if test -n "$custom_allocator"; then + add_extralibs "$custom_allocator_extralibs" +fi + +check_func_headers malloc.h _aligned_malloc && enable aligned_malloc +check_func ${malloc_prefix}memalign && enable memalign +check_func ${malloc_prefix}posix_memalign && enable posix_memalign + +check_func access +check_func_headers stdlib.h arc4random_buf +check_lib clock_gettime time.h clock_gettime || check_lib clock_gettime time.h clock_gettime -lrt +check_func fcntl +check_func fork +check_func gethrtime +check_func getopt +check_func getrusage +check_func gettimeofday +check_func isatty +check_func mkstemp +check_func mmap +check_func mprotect +# Solaris has nanosleep in -lrt, OpenSolaris no longer needs that +check_func_headers time.h nanosleep || check_lib nanosleep time.h nanosleep -lrt +check_func_headers sys/prctl.h prctl +check_func sched_getaffinity +check_func setrlimit +check_struct "sys/stat.h" "struct stat" st_mtim.tv_nsec -D_BSD_SOURCE +check_func strerror_r +check_func sysconf +check_func sysctl +check_func tempnam +check_func usleep + +check_func_headers conio.h kbhit +check_func_headers io.h setmode +check_func_headers lzo/lzo1x.h lzo1x_999_compress +check_func_headers mach/mach_time.h mach_absolute_time +check_func_headers setjmp.h siglongjmp +check_func_headers signal.h sigaction +check_func_headers stdlib.h getenv +check_func_headers sys/ioctl.h ioctl +check_func_headers sys/stat.h lstat +check_func_headers sys/auxv.h getauxval +check_func_headers sys/auxv.h elf_aux_info +check_func_headers sys/sysctl.h sysctlbyname + +check_func_headers windows.h GetModuleHandle +check_func_headers windows.h GetProcessAffinityMask +check_func_headers windows.h GetProcessTimes +check_func_headers windows.h GetStdHandle +check_func_headers windows.h GetSystemTimeAsFileTime +check_func_headers windows.h LoadLibrary +check_func_headers windows.h MapViewOfFile +check_func_headers windows.h PeekNamedPipe +check_func_headers windows.h SetConsoleTextAttribute +check_func_headers windows.h SetConsoleCtrlHandler +check_func_headers windows.h SetDllDirectory +check_func_headers windows.h Sleep +check_func_headers windows.h VirtualAlloc +check_func_headers glob.h glob + +if enabled xlib; then + check_pkg_config xlib_x11 x11 "X11/Xlib.h" XPending || + check_lib xlib_x11 "X11/Xlib.h" XPending -lX11 || + disable xlib + + check_pkg_config xlib_xext xext "X11/Xlib.h X11/extensions/XShm.h" XShmAttach || + check_lib xlib_xext "X11/Xlib.h X11/extensions/XShm.h" XShmAttach -lXext || + disable xlib + + check_pkg_config xlib_xv xv "X11/Xlib.h X11/extensions/Xvlib.h" XvGetPortAttribute || + check_lib xlib_xv "X11/Xlib.h X11/extensions/Xvlib.h" XvGetPortAttribute -lXv || + disable xlib +fi + +check_headers direct.h +check_headers dirent.h +check_headers dxgidebug.h +check_headers dxva.h +check_headers dxva2api.h -D_WIN32_WINNT=0x0600 +check_headers io.h +enabled libdrm && + check_headers linux/dma-buf.h + +check_headers linux/perf_event.h +check_headers malloc.h +check_headers mftransform.h +check_headers net/udplite.h +check_headers poll.h +check_headers pthread_np.h +check_headers sys/param.h +check_headers sys/resource.h +check_headers sys/select.h +check_headers sys/time.h +check_headers sys/un.h +check_headers termios.h +check_headers unistd.h +check_headers valgrind/valgrind.h +check_func_headers VideoToolbox/VTCompressionSession.h VTCompressionSessionPrepareToEncodeFrames -framework VideoToolbox +check_func_headers VideoToolbox/VideoToolbox.h VTDecompressionSessionDecodeFrame -framework VideoToolbox +check_func_headers VideoToolbox/VideoToolbox.h VTPixelTransferSessionCreate -framework VideoToolbox +check_func_headers VideoToolbox/VideoToolbox.h VTPixelRotationSessionCreate -framework VideoToolbox +check_headers windows.h +check_headers asm/types.h + +# it seems there are versions of clang in some distros that try to use the +# gcc headers, which explodes for stdatomic +# so we also check that atomics actually work here +# +# some configurations also require linking to libatomic, so try +# both with -latomic and without +for LATOMIC in "-latomic" ""; do + check_builtin stdatomic stdatomic.h \ + "atomic_int foo, bar = -1; atomic_store(&foo, 0); foo += bar" \ + $LATOMIC && eval stdatomic_extralibs="\$LATOMIC" && break +done + +check_lib advapi32 "windows.h" RegCloseKey -ladvapi32 +check_lib bcrypt "windows.h bcrypt.h" BCryptGenRandom -lbcrypt && + check_cpp_condition bcrypt bcrypt.h "defined BCRYPT_RNG_ALGORITHM" +check_lib ole32 "windows.h objbase.h" CoTaskMemFree -lole32 +check_lib shell32 "windows.h shellapi.h" CommandLineToArgvW -lshell32 +check_lib psapi "windows.h psapi.h" GetProcessMemoryInfo -lpsapi + +check_lib android android/native_window.h ANativeWindow_acquire -landroid +check_lib mediandk "stdint.h media/NdkMediaFormat.h" AMediaFormat_new -lmediandk +check_lib camera2ndk "stdbool.h stdint.h camera/NdkCameraManager.h" ACameraManager_create -lcamera2ndk + +enabled appkit && check_apple_framework AppKit +enabled audiotoolbox && check_apple_framework AudioToolbox +enabled avfoundation && check_apple_framework AVFoundation +enabled coreimage && check_apple_framework CoreImage +enabled metal && check_apple_framework Metal +enabled videotoolbox && check_apple_framework VideoToolbox + +check_apple_framework CoreFoundation +check_apple_framework CoreMedia +check_apple_framework CoreVideo +check_apple_framework CoreAudio + +enabled audiotoolbox && { + check_type AudioToolbox/AudioToolbox.h AudioObjectPropertyAddress +} + +enabled avfoundation && { + disable coregraphics applicationservices + check_lib coregraphics CoreGraphics/CoreGraphics.h CGGetActiveDisplayList "-framework CoreGraphics" || + check_lib applicationservices ApplicationServices/ApplicationServices.h CGGetActiveDisplayList "-framework ApplicationServices" + check_objc_class AVFoundation/AVFoundation.h AVCaptureSession +} + +enabled videotoolbox && { + check_lib coreservices CoreServices/CoreServices.h UTGetOSTypeFromString "-framework CoreServices" + check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_HEVC "-framework CoreMedia" + check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_HEVCWithAlpha "-framework CoreMedia" + check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_VP9 "-framework CoreMedia" + check_func_headers CoreMedia/CMFormatDescription.h kCMVideoCodecType_AV1 "-framework CoreMedia" + check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange "-framework CoreVideo" + check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange "-framework CoreVideo" + check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange "-framework CoreVideo" + check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange "-framework CoreVideo" + check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange "-framework CoreVideo" + check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange "-framework CoreVideo" + check_func_headers CoreVideo/CVPixelBuffer.h kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_SMPTE_ST_2084_PQ "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_ITU_R_2100_HLG "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_Linear "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferYCbCrMatrix_ITU_R_2020 "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferColorPrimaries_ITU_R_2020 "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_ITU_R_2020 "-framework CoreVideo" + check_func_headers CoreVideo/CVImageBuffer.h kCVImageBufferTransferFunction_SMPTE_ST_428_1 "-framework CoreVideo" + check_func_headers VideoToolbox/VTCompressionProperties.h kVTQPModulationLevel_Default "-framework CoreVideo" +} + +enabled metal && test_cmd $metalcc -v || disable metal + +check_struct "sys/time.h sys/resource.h" "struct rusage" ru_maxrss + +check_type "windows.h dxva.h" "DXVA_PicParams_AV1" -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -D_CRT_BUILD_DESKTOP_APP=0 +check_type "windows.h dxva.h" "DXVA_PicParams_HEVC" -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -D_CRT_BUILD_DESKTOP_APP=0 +check_type "windows.h dxva.h" "DXVA_PicParams_VP9" -DWINAPI_FAMILY=WINAPI_FAMILY_DESKTOP_APP -D_CRT_BUILD_DESKTOP_APP=0 +check_type "windows.h dxgi1_2.h" "DXGI_OUTDUPL_FRAME_INFO" +check_type "windows.h dxgi1_2.h" "IDXGIOutput1" +check_type "windows.h dxgi1_5.h" "IDXGIOutput5" +check_type "windows.h d3d11.h" "ID3D11VideoDecoder" +check_type "windows.h d3d11.h" "ID3D11VideoContext" +check_type "windows.h d3d12.h" "ID3D12Device" +check_type "windows.h d3d12video.h" "ID3D12VideoDecoder" +check_type "windows.h d3d12video.h" "ID3D12VideoEncoder" +check_type "windows.h d3d12video.h" "ID3D12VideoProcessor" +check_type "windows.h d3d12video.h" "ID3D12VideoMotionEstimator" +test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_VIDEO feature = D3D12_FEATURE_VIDEO_ENCODER_CODEC" && \ +test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_ENCODER_RESOURCE_REQUIREMENTS req" && enable d3d12_encoder_feature +test_code cc "windows.h d3d12video.h" "D3D12_VIDEO_ENCODER_CODEC c = D3D12_VIDEO_ENCODER_CODEC_AV1; (void)c;" && enable d3d12va_av1_headers +test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_ENCODER_INTRA_REFRESH_MODE check = { 0 };" && enable d3d12_intra_refresh +test_code cc "windows.h d3d12video.h" "D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE m = D3D12_VIDEO_ENCODER_MOTION_ESTIMATION_PRECISION_MODE_EIGHTH_PIXEL; (void)m;" && enable d3d12va_me_precision_eighth_pixel +test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_MOTION_ESTIMATOR check = { 0 };" && enable d3d12_motion_estimator +test_code cc "windows.h d3d12video.h" "D3D12_FEATURE_DATA_VIDEO_PROCESS_REFERENCE_INFO check = { 0 };" && enable d3d12_video_process_reference_info +check_type "windows.h" "DPI_AWARENESS_CONTEXT" -D_WIN32_WINNT=0x0A00 +check_type "windows.h security.h schnlsp.h" SecPkgContext_KeyingMaterialInfo -DSECURITY_WIN32 +check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602 +check_func_headers mfapi.h MFCreateAlignedMemoryBuffer -lmfplat +check_type "windows.h windows.graphics.capture.h" __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession3 -D_WIN32_WINNT=0x0A00 -DWINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION=0x130000 -DCOBJMACROS +check_type "windows.h windows.graphics.capture.h" __x_ABI_CWindows_CGraphics_CCapture_CIGraphicsCaptureSession5 -D_WIN32_WINNT=0x0A00 -DWINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION=0x130000 -DCOBJMACROS +check_type "windows.h windows.graphics.capture.interop.h" IGraphicsCaptureItemInterop -D_WIN32_WINNT=0x0A00 -DWINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION=0x130000 -DCOBJMACROS +check_type "windows.h windows.graphics.directx.direct3d11.interop.h" IDirect3DDxgiInterfaceAccess -D_WIN32_WINNT=0x0A00 -DWINDOWS_FOUNDATION_UNIVERSALAPICONTRACT_VERSION=0x130000 -DCOBJMACROS + +check_type "vdpau/vdpau.h" "VdpPictureInfoHEVC" +check_type "vdpau/vdpau.h" "VdpPictureInfoVP9" +check_type "vdpau/vdpau.h" "VdpPictureInfoAV1" + +if [ -z "$nvccflags" ]; then + nvccflags=$nvccflags_default +fi + +nvccflags="$nvccflags -std=c++11" + +if enabled x86_64 || enabled ppc64 || enabled aarch64; then + nvccflags="$nvccflags -m64" +else + nvccflags="$nvccflags -m32" +fi + +if enabled cuda_nvcc; then + nvccflags="$nvccflags -ptx" +else + nvccflags="$nvccflags -S -nocudalib -nocudainc --cuda-device-only -Wno-c++11-narrowing -include ${source_link}/compat/cuda/cuda_runtime.h" + check_nvcc cuda_llvm +fi + +if ! disabled ffnvcodec; then + ffnv_hdr_list="ffnvcodec/nvEncodeAPI.h ffnvcodec/dynlink_cuda.h ffnvcodec/dynlink_cuviddec.h ffnvcodec/dynlink_nvcuvid.h" + check_pkg_config ffnvcodec "ffnvcodec >= 12.1.14.0" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 12.0.16.1 ffnvcodec < 12.1" "$ffnv_hdr_list" "" || \ + check_pkg_config ffnvcodec "ffnvcodec >= 11.1.5.3 ffnvcodec < 12.0" "$ffnv_hdr_list" "" +fi + +check_cpp_condition winrt windows.h "!WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)" + +if ! disabled w32threads && ! enabled pthreads; then + check_func_headers "windows.h process.h" _beginthreadex && + check_type "windows.h" CONDITION_VARIABLE && + check_type "windows.h" INIT_ONCE && + enable w32threads || disable w32threads + if ! enabled w32threads && enabled winrt; then + check_func_headers "windows.h" CreateThread && + enable w32threads || disable w32threads + fi +fi + +# check for some common methods of building with pthread support +# do this before the optional library checks as some of them require pthreads +if ! disabled pthreads && ! enabled w32threads && ! enabled os2threads; then + if check_lib pthreads pthread.h pthread_join -pthread && + check_lib pthreads pthread.h pthread_create -pthread; then + add_allcflags -pthread + elif check_lib pthreads pthread.h pthread_join -pthreads && + check_lib pthreads pthread.h pthread_create -pthreads; then + add_allcflags -pthreads + elif check_lib pthreads pthread.h pthread_join -ldl -pthread && + check_lib pthreads pthread.h pthread_create -ldl -pthread; then + add_allcflags -ldl -pthread + elif check_lib pthreads pthread.h pthread_join -lpthreadGC2 && + check_lib pthreads pthread.h pthread_create -lpthreadGC2; then + : + elif check_lib pthreads pthread.h pthread_join -lpthread && + check_lib pthreads pthread.h pthread_create -lpthread; then + : + elif check_func pthread_join && check_func pthread_create; then + enable pthreads + fi + check_cc pthreads "pthread.h" "static pthread_mutex_t atomic_lock = PTHREAD_MUTEX_INITIALIZER" + + if enabled pthreads; then + check_builtin sem_timedwait semaphore.h "sem_t *s; sem_init(s,0,0); sem_timedwait(s,0); sem_destroy(s)" $pthreads_extralibs + check_func pthread_cancel $pthreads_extralibs + hdrs=pthread.h + if enabled pthread_np_h; then + hdrs="$hdrs pthread_np.h" + fi + check_lib pthread_set_name_np "$hdrs" pthread_set_name_np -lpthread + check_lib pthread_setname_np "$hdrs" pthread_setname_np -lpthread + # _GNU_SOURCE gets defined locally in checkasm where + # pthread_setaffinity_np gets used. + check_lib pthread_setaffinity_np "$hdrs" pthread_setaffinity_np -lpthread -D_GNU_SOURCE + fi +fi + +enabled zlib && { check_pkg_config zlib zlib "zlib.h" zlibVersion || + check_lib zlib zlib.h zlibVersion -lz; } +enabled bzlib && check_lib bzlib bzlib.h BZ2_bzlibVersion -lbz2 +enabled lzma && check_lib lzma lzma.h lzma_version_number -llzma + +enabled zlib && test_exec $zlib_extralibs < +int main(void) { + if (zlibCompileFlags() & (1 << 17)) return 1; + return 0; +} +EOF + +[ -x "$(command -v gzip)" ] && enable gzip + +enabled zlib_gzip && enabled gzip || disable shader_compression + +enabled zlib_gzip && enabled gzip || disable resource_compression + +# On some systems dynamic loading requires no extra linker flags +check_lib libdl dlfcn.h "dlopen dlsym" || check_lib libdl dlfcn.h "dlopen dlsym" -ldl + +check_lib libm math.h sin -lm +check_host_lib math.h sin -lm + +atan2f_args=2 +copysign_args=2 +hypot_args=2 +ldexpf_args=2 +powf_args=2 + +for func in $MATH_FUNCS; do + eval check_mathfunc $func \${${func}_args:-1} $libm_extralibs +done + +# these are off by default, so fail if requested and not available +enabled avisynth && { require_headers "avisynth/avisynth_c.h avisynth/avs/version.h" && + { test_cpp_condition avisynth/avs/version.h "AVS_MAJOR_VER >= 3 && AVS_MINOR_VER >= 7 && AVS_BUGFIX_VER >= 3 || AVS_MAJOR_VER >= 3 && AVS_MINOR_VER > 7 || AVS_MAJOR_VER > 3" || + die "ERROR: AviSynth+ header version must be >= 3.7.3"; } } +enabled cairo && require_pkg_config cairo cairo "cairo.h" cairo_create +enabled cuda_nvcc && { check_nvcc cuda_nvcc || die "ERROR: failed checking for nvcc."; } +enabled chromaprint && { check_pkg_config chromaprint libchromaprint "chromaprint.h" chromaprint_get_version || + require chromaprint chromaprint.h chromaprint_get_version -lchromaprint; } +enabled decklink && { require_headers DeckLinkAPI.h && + { test_cpp_condition DeckLinkAPIVersion.h "BLACKMAGIC_DECKLINK_API_VERSION >= 0x0a0b0000" || die "ERROR: Decklink API version must be >= 10.11"; } } +enabled frei0r && require_headers "frei0r.h" +enabled gmp && require gmp gmp.h mpz_export -lgmp +enabled gnutls && require_pkg_config gnutls gnutls gnutls/gnutls.h gnutls_global_init +enabled jni && { [ $target_os = "android" ] && check_headers jni.h && enabled pthreads || die "ERROR: jni not found"; } +enabled ladspa && require_headers "ladspa.h dlfcn.h" +enabled lcms2 && require_pkg_config lcms2 "lcms2 >= 2.13" lcms2.h cmsCreateContext +enabled libaom && require_pkg_config libaom "aom >= 2.0.0" aom/aom_codec.h aom_codec_version +enabled liboapv && require_pkg_config liboapv "oapv >= 0.2.0.0" "oapv/oapv.h" oapve_encode +enabled libaribb24 && { check_pkg_config libaribb24 "aribb24 > 1.0.3" "aribb24/aribb24.h" arib_instance_new || + { enabled gpl && require_pkg_config libaribb24 aribb24 "aribb24/aribb24.h" arib_instance_new; } || + die "ERROR: libaribb24 requires version higher than 1.0.3 or --enable-gpl."; } +enabled libaribcaption && require_pkg_config libaribcaption "libaribcaption >= 1.1.1" "aribcaption/aribcaption.h" aribcc_context_alloc +enabled lv2 && require_pkg_config lv2 lilv-0 "lilv/lilv.h" lilv_world_new +enabled libiec61883 && require libiec61883 libiec61883/iec61883.h iec61883_cmp_connect -lraw1394 -lavc1394 -lrom1394 -liec61883 +enabled libass && require_pkg_config libass "libass >= 0.11.0" ass/ass.h ass_library_init +enabled libbluray && require_pkg_config libbluray libbluray libbluray/bluray.h bd_open +enabled libbs2b && require_pkg_config libbs2b libbs2b bs2b.h bs2b_open +enabled libcaca && require_pkg_config libcaca caca caca.h caca_create_canvas +enabled libcodec2 && require libcodec2 codec2/codec2.h codec2_create -lcodec2 +enabled libdav1d && require_pkg_config libdav1d "dav1d >= 1.0.0" "dav1d/dav1d.h" dav1d_version +enabled libdavs2 && require_pkg_config libdavs2 "davs2 >= 1.6.0" davs2.h davs2_decoder_open +enabled libdc1394 && require_pkg_config libdc1394 libdc1394-2 dc1394/dc1394.h dc1394_new +enabled libdrm && check_pkg_config libdrm libdrm xf86drm.h drmGetVersion +enabled libdvdnav && require_pkg_config libdvdnav "dvdnav >= 6.1.0" dvdnav/dvdnav.h dvdnav_open2 +enabled libdvdread && require_pkg_config libdvdread "dvdread >= 6.1.1" dvdread/dvd_reader.h DVDOpen2 +enabled libfdk_aac && { check_pkg_config libfdk_aac fdk-aac "fdk-aac/aacenc_lib.h" aacEncOpen || + { require libfdk_aac fdk-aac/aacenc_lib.h aacEncOpen -lfdk-aac && + warn "using libfdk without pkg-config"; } } +flite_extralibs="-lflite_cmu_time_awb -lflite_cmu_us_awb -lflite_cmu_us_kal -lflite_cmu_us_kal16 -lflite_cmu_us_rms -lflite_cmu_us_slt -lflite_usenglish -lflite_cmulex -lflite" +enabled libflite && require libflite "flite/flite.h" flite_init $flite_extralibs +enabled fontconfig && enable libfontconfig +enabled libfontconfig && require_pkg_config libfontconfig fontconfig "fontconfig/fontconfig.h" FcInit +enabled libfreetype && require_pkg_config libfreetype freetype2 "ft2build.h FT_FREETYPE_H" FT_Init_FreeType +enabled libfribidi && require_pkg_config libfribidi fribidi fribidi.h fribidi_version_info +enabled libharfbuzz && require_pkg_config libharfbuzz harfbuzz hb.h hb_buffer_create +enabled libgme && { check_pkg_config libgme libgme gme/gme.h gme_new_emu || + require libgme gme/gme.h gme_new_emu -lgme -lstdc++; } +enabled libgsm && { for gsm_hdr in "gsm.h" "gsm/gsm.h"; do + check_lib libgsm "${gsm_hdr}" gsm_create -lgsm && break; + done || die "ERROR: libgsm not found"; } +enabled libilbc && require libilbc ilbc.h WebRtcIlbcfix_InitDecode -lilbc $pthreads_extralibs +enabled libjxl && require_pkg_config libjxl "libjxl >= 0.7.0" jxl/decode.h JxlDecoderVersion && + require_pkg_config libjxl_threads "libjxl_threads >= 0.7.0" jxl/thread_parallel_runner.h JxlThreadParallelRunner +enabled libklvanc && require libklvanc libklvanc/vanc.h klvanc_context_create -lklvanc +enabled libkvazaar && require_pkg_config libkvazaar "kvazaar >= 2.0.0" kvazaar.h kvz_api_get +enabled liblc3 && require_pkg_config liblc3 "lc3 >= 1.1.0" lc3.h lc3_hr_setup_encoder +enabled liblensfun && require_pkg_config liblensfun lensfun lensfun.h lf_db_create +enabled liblcevc_dec && require_pkg_config liblcevc_dec "lcevc_dec >= 4.0.0" "LCEVC/lcevc_dec.h" LCEVC_CreateDecoder + +if enabled libmfx && enabled libvpl; then + die "ERROR: can not use libmfx and libvpl together" +# While it may appear that require is being used as a pkg-config +# fallback for libmfx, it is actually being used to detect a different +# installation route altogether. If libmfx is installed via the Intel +# Media SDK or Intel Media Server Studio, these don't come with +# pkg-config support. Instead, users should make sure that the build +# can find the libraries and headers through other means. +elif enabled libmfx; then + { check_pkg_config libmfx "libmfx >= 1.28 libmfx < 2.0" "mfxvideo.h" MFXInit || +# Some old versions of libmfx have the following settings in libmfx.pc: +# includedir=/usr/include +# Cflags: -I${includedir} +# So add -I${includedir}/mfx to CFLAGS + { check_pkg_config libmfx "libmfx >= 1.28 libmfx < 2.0" "mfx/mfxvideo.h" MFXInit && add_cppflags -I${libmfx_incdir}/mfx; } || + { require libmfx "mfxvideo.h mfxdefs.h" MFXInit "-llibmfx $advapi32_extralibs" && + { test_cpp_condition mfxdefs.h "MFX_VERSION >= 1028 && MFX_VERSION < 2000" || die "ERROR: libmfx version must be >= 1.28 and < 2.0"; } && + warn "using libmfx without pkg-config"; } } && + warn "libmfx is deprecated. Please run configure with --enable-libvpl to use libvpl instead." +elif enabled libvpl; then +# Consider pkg-config only. The name of libmfx is still passed to check_pkg_config function for --enable-libvpl option +# because QSV has dependency on libmfx, we can use the same dependency if using libmfx in this check. The package name +# is extracted from "vpl >= 2.6" + check_pkg_config libmfx "vpl >= 2.6" "mfxvideo.h mfxdispatcher.h" MFXLoad || \ + die "ERROR: libvpl >= 2.6 not found" + add_cppflags -DMFX_DEPRECATED_OFF + check_type "vpl/mfxdefs.h vpl/mfxvideo.h" "struct mfxConfigInterface" +fi + +if enabled libmfx; then + check_cc MFX_CODEC_VP9 "mfxdefs.h mfxstructures.h" "MFX_CODEC_VP9" +fi + +enabled libmodplug && require_pkg_config libmodplug libmodplug libmodplug/modplug.h ModPlug_Load +enabled libmp3lame && require "libmp3lame >= 3.98.3" lame/lame.h lame_set_VBR_quality -lmp3lame $libm_extralibs +enabled libmpeghdec && require_pkg_config libmpeghdec "mpeghdec >= 3.0.0" mpeghdec/mpeghdecoder.h mpeghdecoder_init +enabled libmysofa && { check_pkg_config libmysofa libmysofa mysofa.h mysofa_neighborhood_init_withstepdefine || + require libmysofa mysofa.h mysofa_neighborhood_init_withstepdefine -lmysofa $zlib_extralibs; } +enabled libonnxruntime && require libonnxruntime onnxruntime_c_api.h OrtGetApiBase -lonnxruntime +enabled libopencore_amrnb && { check_pkg_config libopencore_amrnb opencore-amrnb opencore-amrnb/interf_dec.h Decoder_Interface_init || + require libopencore_amrnb opencore-amrnb/interf_dec.h Decoder_Interface_init -lopencore-amrnb; } +enabled libopencore_amrwb && { check_pkg_config libopencore_amrwb opencore-amrwb opencore-amrwb/dec_if.h D_IF_init || + require libopencore_amrwb opencore-amrwb/dec_if.h D_IF_init -lopencore-amrwb; } +enabled libopencv && { check_pkg_config libopencv opencv4 opencv2/core/core_c.h cvCreateImageHeader || + require libopencv opencv2/core/core_c.h cvCreateImageHeader -lopencv_core -lopencv_imgproc; } +enabled libopencolorio && require_pkg_config_cxx libopencolorio "OpenColorIO" OpenColorIO/OpenColorIO.h OCIO_NAMESPACE::Config +enabled libopenh264 && require_pkg_config libopenh264 "openh264 >= 1.3.0" wels/codec_api.h WelsGetCodecVersion +enabled libopenjpeg && { check_pkg_config libopenjpeg "libopenjp2 >= 2.1.0" openjpeg.h opj_version || + { require_pkg_config libopenjpeg "libopenjp2 >= 2.1.0" openjpeg.h opj_version -DOPJ_STATIC && add_cppflags -DOPJ_STATIC; } } +enabled libopenmpt && require_pkg_config libopenmpt "libopenmpt >= 0.2.6557" libopenmpt/libopenmpt.h openmpt_module_create -lstdc++ && append libopenmpt_extralibs "-lstdc++" +enabled libopenvino && { { check_pkg_config libopenvino openvino openvino/c/openvino.h ov_core_create && enable openvino2; } || + { check_pkg_config libopenvino openvino c_api/ie_c_api.h ie_c_api_version || + require libopenvino c_api/ie_c_api.h ie_c_api_version -linference_engine_c_api; } } +enabled libopus && { + enabled libopus_decoder && { + require_pkg_config libopus opus opus_multistream.h opus_multistream_decoder_create + } + enabled libopus_encoder && { + require_pkg_config libopus opus opus_multistream.h opus_multistream_surround_encoder_create + } +} +enabled libplacebo && require_pkg_config libplacebo "libplacebo >= 7.351.0" libplacebo/vulkan.h pl_vulkan_create +enabled libpulse && require_pkg_config libpulse libpulse pulse/pulseaudio.h pa_context_new +enabled libqrencode && require_pkg_config libqrencode libqrencode qrencode.h QRcode_encodeString +enabled libquirc && require libquirc quirc.h quirc_decode -lquirc +enabled librabbitmq && require_pkg_config librabbitmq "librabbitmq >= 0.7.1" amqp.h amqp_new_connection +enabled librav1e && require_pkg_config librav1e "rav1e >= 0.5.0" rav1e.h rav1e_context_new +enabled librist && require_pkg_config librist "librist >= 0.2.7" librist/librist.h rist_receiver_create +enabled librsvg && require_pkg_config librsvg librsvg-2.0 librsvg-2.0/librsvg/rsvg.h rsvg_handle_new_from_data +enabled librtmp && require_pkg_config librtmp librtmp librtmp/rtmp.h RTMP_Socket +enabled librubberband && require_pkg_config librubberband "rubberband >= 1.8.1" rubberband/rubberband-c.h rubberband_new -lstdc++ && append librubberband_extralibs "-lstdc++" +enabled libshine && require_pkg_config libshine shine shine/layer3.h shine_encode_buffer +enabled libsmbclient && { check_pkg_config libsmbclient smbclient libsmbclient.h smbc_init || + require libsmbclient libsmbclient.h smbc_init -lsmbclient; } +enabled libsnappy && require libsnappy snappy-c.h snappy_compress -lsnappy -lstdc++ +enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr +enabled libssh && require_pkg_config libssh "libssh >= 0.6.0" libssh/sftp.h sftp_init +enabled libspeex && require_pkg_config libspeex speex speex/speex.h speex_decoder_init +enabled libsrt && require_pkg_config libsrt "srt >= 1.3.0" srt/srt.h srt_socket +enabled libsvtav1 && require_pkg_config libsvtav1 "SvtAv1Enc >= 0.9.0" EbSvtAv1Enc.h svt_av1_enc_init_handle +enabled libsvtjpegxs && require_pkg_config libsvtjpegxs "SvtJpegxs >= 0.10.0" SvtJpegxsEnc.h svt_jpeg_xs_encoder_init +enabled libtensorflow && require libtensorflow tensorflow/c/c_api.h TF_Version -ltensorflow +enabled libtesseract && require_pkg_config libtesseract tesseract tesseract/capi.h TessBaseAPICreate +enabled libtheora && { check_pkg_config libtheora theoraenc theora/theoraenc.h th_info_init || + require libtheora theora/theoraenc.h th_info_init -ltheoraenc -ltheoradec -logg; } +enabled libtls && require_pkg_config libtls libtls tls.h tls_configure && + { enabled gpl && ! enabled nonfree && die "ERROR: LibreSSL is incompatible with the gpl"; } +enabled libtorch && check_cxxflags -std=c++17 && require_cxx libtorch torch/torch.h "torch::Tensor" -ltorch -lc10 -ltorch_cpu -lstdc++ -lpthread +enabled libtwolame && require libtwolame twolame.h twolame_init -ltwolame && + { check_lib libtwolame twolame.h twolame_encode_buffer_float32_interleaved -ltwolame || + die "ERROR: libtwolame must be installed and version must be >= 0.3.10"; } +enabled libuavs3d && require_pkg_config libuavs3d "uavs3d >= 1.1.41" uavs3d.h uavs3d_decode +enabled libv4l2 && require_pkg_config libv4l2 libv4l2 libv4l2.h v4l2_ioctl +enabled libvidstab && require_pkg_config libvidstab "vidstab >= 0.98" vid.stab/libvidstab.h vsMotionDetectInit +enabled libvmaf && require_pkg_config libvmaf "libvmaf >= 2.0.0" libvmaf.h vmaf_init +enabled libvmaf && check_pkg_config libvmaf_cuda "libvmaf >= 2.0.0" libvmaf_cuda.h vmaf_cuda_state_init +enabled libvo_amrwbenc && { check_pkg_config libvo_amrwbenc vo-amrwbenc vo-amrwbenc/enc_if.h E_IF_init || + require libvo_amrwbenc vo-amrwbenc/enc_if.h E_IF_init -lvo-amrwbenc; } +enabled libvorbis && require_pkg_config libvorbis vorbis vorbis/codec.h vorbis_info_init && + require_pkg_config libvorbisenc vorbisenc vorbis/vorbisenc.h vorbis_encode_init + +enabled whisper && require_pkg_config whisper "whisper >= 1.7.5" whisper.h whisper_init_from_file_with_params + +enabled libvpx && { + enabled libvpx_vp8_decoder && { + check_pkg_config libvpx_vp8_decoder "vpx >= 1.4.0" "vpx/vpx_decoder.h vpx/vp8dx.h" vpx_codec_vp8_dx || + check_lib libvpx_vp8_decoder "vpx/vpx_decoder.h vpx/vp8dx.h" "vpx_codec_vp8_dx VPX_IMG_FMT_HIGHBITDEPTH" "-lvpx $libm_extralibs $pthreads_extralibs" + } + enabled libvpx_vp8_encoder && { + check_pkg_config libvpx_vp8_encoder "vpx >= 1.4.0" "vpx/vpx_encoder.h vpx/vp8cx.h" vpx_codec_vp8_cx || + check_lib libvpx_vp8_encoder "vpx/vpx_encoder.h vpx/vp8cx.h" "vpx_codec_vp8_cx VPX_IMG_FMT_HIGHBITDEPTH" "-lvpx $libm_extralibs $pthreads_extralibs" + } + enabled libvpx_vp9_decoder && { + check_pkg_config libvpx_vp9_decoder "vpx >= 1.4.0" "vpx/vpx_decoder.h vpx/vp8dx.h" vpx_codec_vp9_dx || + check_lib libvpx_vp9_decoder "vpx/vpx_decoder.h vpx/vp8dx.h" "vpx_codec_vp9_dx VPX_IMG_FMT_HIGHBITDEPTH" "-lvpx $libm_extralibs $pthreads_extralibs" + } + enabled libvpx_vp9_encoder && { + check_pkg_config libvpx_vp9_encoder "vpx >= 1.4.0" "vpx/vpx_encoder.h vpx/vp8cx.h" vpx_codec_vp9_cx || + check_lib libvpx_vp9_encoder "vpx/vpx_encoder.h vpx/vp8cx.h" "vpx_codec_vp9_cx VPX_IMG_FMT_HIGHBITDEPTH" "-lvpx $libm_extralibs $pthreads_extralibs" + } + if disabled_all libvpx_vp8_decoder libvpx_vp9_decoder libvpx_vp8_encoder libvpx_vp9_encoder; then + die "libvpx enabled but no supported decoders found" + fi +} + +enabled libvvenc && require_pkg_config libvvenc "libvvenc >= 1.6.1" "vvenc/vvenc.h" vvenc_get_version +enabled libwebp && { + enabled libwebp_encoder && require_pkg_config libwebp "libwebp >= 0.2.0" webp/encode.h WebPGetEncoderVersion + enabled libwebp_anim_encoder && check_pkg_config libwebp_anim_encoder "libwebpmux >= 0.4.0" webp/mux.h WebPAnimEncoderOptionsInit; } +enabled libx264 && require_pkg_config libx264 x264 "stdint.h x264.h" x264_encoder_encode && + require_cpp_condition libx264 x264.h "X264_BUILD >= 155" && { + [ "$toolchain" != "msvc" ] || + require_cpp_condition libx264 x264.h "X264_BUILD >= 158"; } && + check_cpp_condition libx264_hdr10 x264.h "X264_BUILD >= 163" && + check_cpp_condition libx262 x264.h "X264_MPEG2" +enabled libx265 && require_pkg_config libx265 x265 x265.h x265_api_get && + require_cpp_condition libx265 x265.h "X265_BUILD >= 89" +enabled libxavs && require libxavs "stdint.h xavs.h" xavs_encoder_encode "-lxavs $pthreads_extralibs $libm_extralibs" +enabled libxavs2 && require_pkg_config libxavs2 "xavs2 >= 1.3.0" "stdint.h xavs2.h" xavs2_api_get +enabled libxevd && require_pkg_config libxevd "xevd >= 0.4.1" "xevd.h" xevd_decode +enabled libxevdb && require_pkg_config libxevdb "xevdb >= 0.4.1" "xevd.h" xevd_decode +enabled libxeve && require_pkg_config libxeve "xeve >= 0.5.1" "xeve.h" xeve_encode +enabled libxeveb && require_pkg_config libxeveb "xeveb >= 0.5.1" "xeve.h" xeve_encode +enabled libxvid && require libxvid xvid.h xvid_global -lxvidcore +enabled libzimg && require_pkg_config libzimg "zimg >= 2.7.0" zimg.h zimg_get_api_version +enabled libzmq && require_pkg_config libzmq "libzmq >= 4.2.1" zmq.h zmq_ctx_new +enabled libzvbi && require_pkg_config libzvbi zvbi-0.2 libzvbi.h vbi_decoder_new && + { test_cpp_condition libzvbi.h "VBI_VERSION_MAJOR > 0 || VBI_VERSION_MINOR > 2 || VBI_VERSION_MINOR == 2 && VBI_VERSION_MICRO >= 28" || + enabled gpl || die "ERROR: libzvbi requires version 0.2.28 or --enable-gpl."; } +enabled libxml2 && require_pkg_config libxml2 libxml-2.0 libxml2/libxml/xmlversion.h xmlCheckVersion +enabled mbedtls && { check_pkg_config mbedtls mbedtls mbedtls/x509_crt.h mbedtls_x509_crt_init || + check_pkg_config mbedtls mbedtls mbedtls/ssl.h mbedtls_ssl_init || + 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"; } && + add_compat android/binder.o +enabled mmal && { check_lib mmal interface/mmal/mmal.h mmal_port_connect -lmmal_core -lmmal_util -lmmal_vc_client -lbcm_host || + { ! enabled cross_compile && + add_cflags -isystem/opt/vc/include/ -isystem/opt/vc/include/interface/vmcs_host/linux -isystem/opt/vc/include/interface/vcos/pthreads -fgnu89-inline && + add_ldflags -L/opt/vc/lib/ && + check_lib mmal interface/mmal/mmal.h mmal_port_connect -lmmal_core -lmmal_util -lmmal_vc_client -lbcm_host; } || + die "ERROR: mmal not found" && + check_func_headers interface/mmal/mmal.h "MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS"; } +enabled ohcodec && { check_lib ohcodec "multimedia/player_framework/native_avcodec_videodecoder.h multimedia/player_framework/native_avcodec_videoencoder.h" \ + "OH_VideoDecoder_CreateByName OH_VideoEncoder_CreateByName" \ + -lnative_media_vdec -lnative_media_venc -lnative_media_codecbase -lnative_media_core -lnative_window || + die "ERROR: missing native_media libs"; } +enabled openal && { check_pkg_config openal "openal >= 1.1" "AL/al.h" alGetError || + { for al_extralibs in "${OPENAL_LIBS}" "-lopenal" "-lOpenAL32"; do + check_lib openal 'AL/al.h' alGetError "${al_extralibs}" && break; done } || + die "ERROR: openal not found"; } && + { test_cpp_condition "AL/al.h" "defined(AL_VERSION_1_1)" || + die "ERROR: openal must be installed and version must be 1.1 or compatible"; } +enabled opencl && { check_pkg_config opencl OpenCL CL/cl.h clEnqueueNDRangeKernel || + check_lib opencl OpenCL/cl.h clEnqueueNDRangeKernel "-framework OpenCL" || + check_lib opencl CL/cl.h clEnqueueNDRangeKernel -lOpenCL || + die "ERROR: opencl not found"; } && + { test_cpp_condition "OpenCL/cl.h" "defined(CL_VERSION_1_2)" || + test_cpp_condition "CL/cl.h" "defined(CL_VERSION_1_2)" || + die "ERROR: opencl must be installed and version must be 1.2 or compatible"; } +enabled opengl && { check_lib opengl GL/glx.h glXGetProcAddress "-lGL" || + check_lib opengl windows.h wglGetProcAddress "-lopengl32 -lgdi32" || + check_lib opengl OpenGL/gl3.h glGetError "-framework OpenGL" || + check_lib opengl ES2/gl.h glGetError "-isysroot=${sysroot} -framework OpenGLES" || + die "ERROR: opengl not found." + } + +enabled openssl && { { check_pkg_config openssl "openssl >= 3.0.0" openssl/ssl.h DTLS_get_data_mtu && + { enabled gplv3 || ! enabled gpl || enabled nonfree || die "ERROR: OpenSSL >=3.0.0 requires --enable-version3"; }; } || + { enabled gpl && ! enabled nonfree && die "ERROR: OpenSSL <3.0.0 is incompatible with the gpl"; } || + check_pkg_config openssl "openssl >= 1.1.1" openssl/ssl.h DTLS_get_data_mtu || + check_lib openssl openssl/ssl.h DTLS_get_data_mtu -lssl -lcrypto || + check_lib openssl openssl/ssl.h DTLS_get_data_mtu -lssl -lcrypto -lws2_32 -lgdi32 || + die "ERROR: openssl (>= 1.1.1) not found"; } +enabled pocketsphinx && require_pkg_config pocketsphinx pocketsphinx pocketsphinx/pocketsphinx.h ps_init +enabled rkmpp && { require_pkg_config rkmpp "rockchip_mpp >= 1.3.8" "rockchip/rk_mpi.h rockchip/mpp_buffer.h" "mpp_create mpp_buffer_sync_begin_f" && + { enabled libdrm || + die "ERROR: rkmpp requires --enable-libdrm"; } + } +enabled vapoursynth && require_headers "vapoursynth/VSScript4.h vapoursynth/VapourSynth4.h" + +if enabled gcrypt; then + GCRYPT_CONFIG="${cross_prefix}libgcrypt-config" + if "${GCRYPT_CONFIG}" --version > /dev/null 2>&1; then + gcrypt_cflags=$("${GCRYPT_CONFIG}" --cflags) + gcrypt_extralibs=$("${GCRYPT_CONFIG}" --libs) + check_func_headers gcrypt.h gcry_mpi_new $gcrypt_cflags $gcrypt_extralibs || + die "ERROR: gcrypt not found" + add_cflags $gcrypt_cflags + else + require gcrypt gcrypt.h gcry_mpi_new -lgcrypt + fi +fi + +if enabled sdl2; then + SDL2_CONFIG="${cross_prefix}sdl2-config" + test_pkg_config sdl2 "sdl2 >= 2.0.1 sdl2 < 3.0.0" SDL_events.h SDL_PollEvent + if disabled sdl2 && "${SDL2_CONFIG}" --version > /dev/null 2>&1; then + sdl2_cflags=$("${SDL2_CONFIG}" --cflags) + sdl2_extralibs=$("${SDL2_CONFIG}" --libs) + test_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) >= 0x020001" $sdl2_cflags && + test_cpp_condition SDL.h "(SDL_MAJOR_VERSION<<16 | SDL_MINOR_VERSION<<8 | SDL_PATCHLEVEL) < 0x030000" $sdl2_cflags && + check_func_headers SDL_events.h SDL_PollEvent $sdl2_extralibs $sdl2_cflags && + enable sdl2 + fi + if test $target_os = "mingw32"; then + sdl2_extralibs=$(filter_out '-mwindows' $sdl2_extralibs) + fi +fi + +if enabled decklink; then + case $target_os in + mingw32*|mingw64*|win32|win64) + decklink_outdev_extralibs="$decklink_outdev_extralibs -lole32 -luuid -loleaut32" + decklink_indev_extralibs="$decklink_indev_extralibs -lole32 -luuid -loleaut32" + ;; + esac +fi + +enabled securetransport && + check_func SecIdentityCreate "-framework CoreFoundation -framework Security" && + check_lib securetransport "Security/SecureTransport.h Security/Security.h" "SSLCreateContext" "-framework CoreFoundation -framework Security" || + disable securetransport + +enabled securetransport && + check_func SecItemImport "-framework CoreFoundation -framework Security" + +enabled schannel && + check_func_headers "windows.h security.h" InitializeSecurityContext -DSECURITY_WIN32 -lsecur32 && + check_func_headers "windows.h ncrypt.h" NCryptOpenStorageProvider -DSECURITY_WIN32 -lncrypt && + check_func_headers "windows.h wincrypt.h" CertCreateSelfSignCertificate -DSECURITY_WIN32 -lcrypt32 && + test_cpp_condition winerror.h "defined(SEC_I_CONTEXT_EXPIRED)" && + schannel_extralibs="-lsecur32 -lncrypt -lcrypt32" || + disable schannel + +enabled schannel && check_cc dtls_protocol "windows.h security.h schnlsp.h" "int i = SECPKG_ATTR_DTLS_MTU;" -DSECURITY_WIN32 + +$makeinfo --version > /dev/null 2>&1 && enable makeinfo_command || disable makeinfo_command makeinfo_html +if enabled makeinfo_command; then + [ 0$($makeinfo --version | grep "texinfo" | sed 's/.*texinfo[^0-9]*\([0-9]*\)\..*/\1/') -ge 5 ] \ + && enable makeinfo_html || disable makeinfo_html +elif test "$makeinfo" != "$makeinfo_default" ; then + warn "Specified makeinfo \"$makeinfo\" not found." +fi + +disabled makeinfo_html && texi2html --help 2> /dev/null | grep -q 'init-file' && enable texi2html || disable texi2html +perl -v > /dev/null 2>&1 && enable perl || disable perl +pod2man --help > /dev/null 2>&1 && enable pod2man || disable pod2man +rsync --help 2> /dev/null | grep -q 'contimeout=' && enable rsync_contimeout || disable rsync_contimeout +xmllint --version > /dev/null 2>&1 && enable xmllint || disable xmllint + +check_headers linux/fb.h +check_headers linux/videodev2.h +test_code cc linux/videodev2.h "struct v4l2_frmsizeenum vfse; vfse.discrete.width = 0;" && enable_sanitized struct_v4l2_frmivalenum_discrete +test_code cc sys/ioctl.h "int ioctl(int, int, ...)" && enable ioctl_posix + +# check V4L2 codecs available in the API +if enabled v4l2_m2m; then + check_cc v4l2_m2m linux/videodev2.h "int i = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_VIDEO_M2M | V4L2_BUF_FLAG_LAST;" + check_cc vc1_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VC1_ANNEX_G;" + check_cc mpeg1_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG1;" + check_cc mpeg2_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG2;" + check_cc mpeg4_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_MPEG4;" + check_cc hevc_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_HEVC;" + check_cc h263_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_H263;" + check_cc h264_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_H264;" + check_cc vp8_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP8;" + check_cc vp9_v4l2_m2m linux/videodev2.h "int i = V4L2_PIX_FMT_VP9;" +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 + +check_lib user32 "windows.h winuser.h" GetShellWindow -luser32 +check_lib vfw32 "windows.h vfw.h" capCreateCaptureWindow -lvfw32 +# check that WM_CAP_DRIVER_CONNECT is defined to the proper value +# w32api 3.12 had it defined wrong +check_cpp_condition vfwcap_defines vfw.h "WM_CAP_DRIVER_CONNECT > WM_USER" + +check_type "dshow.h" IBaseFilter + +if check_struct sys/soundcard.h audio_buf_info bytes; then + enable_sanitized sys/soundcard.h +else + test_cc -D__BSD_VISIBLE -D__XSI_VISIBLE < + audio_buf_info abc; +EOF +fi + +enabled alsa && { check_pkg_config alsa alsa "alsa/asoundlib.h" snd_pcm_htimestamp || + check_lib alsa alsa/asoundlib.h snd_pcm_htimestamp -lasound; } + +enabled libjack && + require_pkg_config libjack jack jack/jack.h jack_port_get_latency_range + +enabled sndio && check_pkg_config sndio sndio sndio.h sio_open + +if enabled libcdio; then + check_pkg_config libcdio libcdio_paranoia "cdio/cdda.h cdio/paranoia.h" cdio_cddap_open || + check_pkg_config libcdio libcdio_paranoia "cdio/paranoia/cdda.h cdio/paranoia/paranoia.h" cdio_cddap_open || + check_lib libcdio "cdio/cdda.h cdio/paranoia.h" cdio_cddap_open -lcdio_paranoia -lcdio_cdda -lcdio || + check_lib libcdio "cdio/paranoia/cdda.h cdio/paranoia/paranoia.h" cdio_cddap_open -lcdio_paranoia -lcdio_cdda -lcdio || + die "ERROR: No usable libcdio/cdparanoia found" +fi + +enabled libxcb && check_pkg_config libxcb "xcb >= 1.4" xcb/xcb.h xcb_connect || + disable libxcb_shm libxcb_shape libxcb_xfixes + +if enabled libxcb; then + enabled libxcb_shm && check_pkg_config libxcb_shm xcb-shm xcb/shm.h xcb_shm_attach + enabled libxcb_shape && check_pkg_config libxcb_shape xcb-shape xcb/shape.h xcb_shape_get_rectangles + enabled libxcb_xfixes && check_pkg_config libxcb_xfixes xcb-xfixes xcb/xfixes.h xcb_xfixes_get_cursor_image +fi + +check_func_headers "windows.h" CreateDIBSection "$gdigrab_indev_extralibs" + +# check if building for desktop or uwp +test_cpp < +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#error desktop, not uwp +#else +// WINAPI_FAMILY_APP, WINAPI_FAMILY_PHONE_APP => UWP +#endif +#else +#error no family set +#endif +EOF + +mediafoundation_extralibs="-lmfuuid -lole32 -lstrmiids" + +if enabled uwp; then + # In UWP mode, we can't use LoadLibrary+GetProcAddress to conditionally + # try to load these APIs at runtime, like we do in regular desktop mode - + # therefore, we need to link directly against these APIs. + d3d11va_extralibs="-ldxgi -ld3d11" + d3d12va_extralibs="-ldxgi -ld3d12" + vaapi_win32_extralibs="-ldxgi" + mediafoundation_extralibs="-lmfplat $mediafoundation_extralibs" +fi + +enabled libdrm && + check_pkg_config libdrm_getfb2 libdrm "xf86drmMode.h" drmModeGetFB2 + +enabled vaapi && + check_pkg_config vaapi "libva >= 0.35.0" "va/va.h" vaInitialize + +if enabled vaapi; then + case $target_os in + mingw32*|mingw64*|win32|win64) + check_pkg_config vaapi_win32 "libva-win32" "va/va_win32.h" vaGetDisplayWin32 + ;; + *) + check_pkg_config vaapi_drm "libva-drm" "va/va_drm.h" vaGetDisplayDRM + ;; + esac + + if enabled xlib_x11; then + check_pkg_config vaapi_x11 "libva-x11" "va/va_x11.h" vaGetDisplay + fi + + check_cpp_condition vaapi_1 "va/va.h" "VA_CHECK_VERSION(1, 0, 0)" + + check_type "va/va.h va/va_dec_hevc.h" "VAPictureParameterBufferHEVC" + check_type "va/va.h va/va_dec_vvc.h" "VAPictureParameterBufferVVC" + check_struct "va/va.h" "VADecPictureParameterBufferVP9" bit_depth + check_struct "va/va.h" "VADecPictureParameterBufferAV1" bit_depth_idx + check_type "va/va.h va/va_vpp.h" "VAProcFilterParameterBufferHDRToneMapping" + check_struct "va/va.h va/va_vpp.h" "VAProcPipelineCaps" rotation_flags + check_struct "va/va.h va/va_vpp.h" "VAProcPipelineCaps" blend_flags + check_type "va/va.h va/va_enc_hevc.h" "VAEncPictureParameterBufferHEVC" + check_type "va/va.h va/va_enc_jpeg.h" "VAEncPictureParameterBufferJPEG" + check_type "va/va.h va/va_enc_vp8.h" "VAEncPictureParameterBufferVP8" + check_type "va/va.h va/va_enc_vp9.h" "VAEncPictureParameterBufferVP9" + check_type "va/va.h va/va_enc_av1.h" "VAEncPictureParameterBufferAV1" +fi + +if enabled_all opencl libdrm ; then + check_type "CL/cl_intel.h" "clCreateImageFromFdINTEL_fn" && + enable opencl_drm_beignet + check_func_headers "CL/cl_ext.h" clImportMemoryARM && + enable opencl_drm_arm +fi + +if enabled_all opencl vaapi ; then + if enabled opencl_drm_beignet ; then + enable opencl_vaapi_beignet + else + check_type "CL/cl.h CL/cl_va_api_media_sharing_intel.h" "clCreateFromVA_APIMediaSurfaceINTEL_fn" && + enable opencl_vaapi_intel_media + fi +fi + +if enabled_all opencl dxva2 ; then + check_type "CL/cl_dx9_media_sharing.h" cl_dx9_surface_info_khr && + enable opencl_dxva2 +fi + +if enabled_all opencl d3d11va ; then + check_type "CL/cl_d3d11.h" clGetDeviceIDsFromD3D11KHR_fn && + enable opencl_d3d11 +fi + +if enabled_all opencl videotoolbox ; then + check_func_headers OpenCL/cl_gl_ext.h clCreateImageFromIOSurfaceWithPropertiesAPPLE -framework VideoToolbox -framework OpenCL && + enable opencl_videotoolbox +fi + +enabled vdpau && + check_cpp_condition vdpau vdpau/vdpau.h "defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP" + +enabled vdpau && + check_lib vdpau_x11 "vdpau/vdpau.h vdpau/vdpau_x11.h" vdp_device_create_x11 -lvdpau -lX11 + +# Check for the Vulkan headers +if enabled_all vulkan vulkan_static; then + check_pkg_config vulkan "vulkan >= 1.3.277" "vulkan/vulkan.h" "defined VK_VERSION_1_3" || + check_lib vulkan "vulkan/vulkan.h" vkGetInstanceProcAddr -lvulkan +elif enabled vulkan; then + check_pkg_config_header_only vulkan "vulkan >= 1.3.277" "vulkan/vulkan.h" "defined VK_VERSION_1_3" || + check_cpp_condition vulkan "vulkan/vulkan.h" "defined(VK_VERSION_1_4) || (defined(VK_VERSION_1_3) && VK_HEADER_VERSION >= 277)" $vulkan_incflags +fi + +probe_glslc(){ + glslc_probe=$1 + if test_cmd $glslc_probe -v; then + # glslang/glslangValidator + glslc=$glslc_probe + glslcflags="-V --target-env spirv1.6 --glsl-version 460" + glslc_opt_speed="" + glslc_opt_size="-Os" + glslc_opt_none="-Od" + glslc_debug="-gVS" + GLSLC_DEPFLAGS='--depfile $(@:.spv=.d)' + elif test_cmd $glslc_probe --version; then + # glslc + glslc=$glslc_probe + glslcflags="--target-env=vulkan1.4 --target-spv=spv1.6 -std=460" + glslc_opt_speed="-O" + glslc_opt_size="-Os" + glslc_opt_none="-O0" + glslc_debug="-g" + GLSLC_DEPFLAGS='-MD -MF $(@:.spv=.d) -MT $@' + else + disable spirv_compiler + return 1 + fi + check_glslc spirv_compiler || return 0 + + append GLSLCFLAGS $glslcflags + if enabled small; then + append GLSLCFLAGS $glslc_opt_size + elif enabled optimizations; then + append GLSLCFLAGS $glslc_opt_speed + else + append GLSLCFLAGS $glslc_opt_none + fi + if enabled debug; then + append GLSLCFLAGS $glslc_debug + fi +} + +if enabled vulkan; then + check_pkg_config_header_only vulkan_1_4 "vulkan >= 1.4.317" "vulkan/vulkan.h" "defined VK_VERSION_1_4" || + check_cpp_condition vulkan_1_4 "vulkan/vulkan.h" "defined(VK_VERSION_1_5) || (defined(VK_VERSION_1_4) && VK_HEADER_VERSION >= 317)" $vulkan_incflags + for program in $glslc glslc glslang glslangValidator; do + probe_glslc $program && break + done +else + disable spirv_compiler +fi + +if enabled vulkan; then + check_headers spirv-headers/spirv.h || + check_headers spirv/unified1/spirv.h || + { requested vulkan && warn "spirv-headers not found, swscale SPIR-V backend unavailable"; } +fi + +if enabled x86; then + case $target_os in + freebsd|mingw32*|mingw64*|win32|win64|linux|cygwin*) + ;; + *) + disable ffnvcodec cuvid nvdec nvenc + ;; + esac +elif enabled_any aarch64 ppc64 && ! enabled bigendian; then + case $target_os in + linux|mingw32*|mingw64*|win32|win64) + ;; + *) + disable ffnvcodec cuvid nvdec nvenc + ;; + esac +else + disable ffnvcodec cuvid nvdec nvenc +fi + +enabled ffnvcodec && enable cuda + +enabled nvenc && + test_cc -I$source_path < +NV_ENCODE_API_FUNCTION_LIST flist; +void f(void) { struct { const GUID guid; } s[] = { { NV_ENC_CODEC_H264_GUID } }; } +int main(void) { return 0; } +EOF + +if enabled nvenc; then + check_type "ffnvcodec/nvEncodeAPI.h" "NV_ENC_PIC_PARAMS_AV1" +fi + +if enabled_any nvdec cuvid; then + check_type "ffnvcodec/dynlink_cuda.h ffnvcodec/dynlink_cuviddec.h" "CUVIDAV1PICPARAMS" +fi + +enabled amf && + check_cpp_condition amf "AMF/core/Version.h" \ + "(AMF_VERSION_MAJOR << 48 | AMF_VERSION_MINOR << 32 | AMF_VERSION_RELEASE << 16 | AMF_VERSION_BUILD_NUM) >= 0x1000500020000" + +# Funny iconv installations are not unusual, so check it after all flags have been set +if enabled libc_iconv; then + check_func_headers iconv.h iconv +elif enabled iconv; then + check_func_headers iconv.h iconv || check_lib iconv iconv.h iconv -liconv +fi + +enabled debug && add_allcflags -g"$debuglevel" && add_asflags -g"$debuglevel" + +for lang in c cxx objc; do + test_${lang}flags -Werror=unused-command-line-argument && + append unknown_warning_${lang}flags "-Werror=unused-command-line-argument" + test_${lang}flags -Werror=unknown-warning-option && + append unknown_warning_${lang}flags "-Werror=unknown-warning-option" +done + +check_warning(){ + warning_flag=$1 + flag_to_add=${2:-$1} + test_cflags $unknown_warning_cflags $warning_flag && add_cflags $flag_to_add + test_cxxflags -Werror $unknown_warning_cxxflags $warning_flag && add_cxxflags $flag_to_add + test_objcflags $unknown_warning_objcflags $warning_flag && add_objcflags $flag_to_add +} + +check_disable_warning(){ + warning_flag=-W${1#-Wno-} + check_warning $warning_flag $1 +} + +check_c_warning(){ + warning_flag=$1 + test_cflags $unknown_warning_cflags $warning_flag && add_cflags $warning_flag +} + +# add some useful compiler flags if supported +check_warning -Wall +check_warning -Wdisabled-optimization +check_warning -Wpointer-arith +check_warning -Wredundant-decls +check_warning -Wwrite-strings +check_warning -Wtype-limits +check_warning -Wundef +check_warning -Wempty-body +# Warn about excessive stack usage, using a 120 KiB per-function threshold. +# This roughly matches the default thread stack size on Musl, which is 128 KiB, +# leaving some headroom for caller frames. +check_warning -Wstack-usage=122880 + +# GCC accepts warning option level 5 here to warn about all fallthroughs +# that are not explicitly marked with the appropriate attribute +if enabled gcc; then + check_warning -Wimplicit-fallthrough=5 +else + check_warning -Wimplicit-fallthrough +fi + +check_c_warning -Wmissing-prototypes +check_c_warning -Wstrict-prototypes +check_c_warning -Wunterminated-string-initialization + +if enabled extra_warnings; then + check_warning -Wcast-qual + check_warning -Wextra + check_warning -Wpedantic +fi + +check_disable_warning -Wno-parentheses +check_disable_warning -Wno-switch +check_disable_warning -Wno-format-zero-length +check_disable_warning -Wno-pointer-sign +check_disable_warning -Wno-unused-const-variable +check_disable_warning -Wno-bool-operation +check_disable_warning -Wno-char-subscripts +check_disable_warning -Wno-implicit-const-int-float-conversion +check_disable_warning -Wno-microsoft-enum-forward-reference + +check_disable_warning_headers(){ + warning_flag=-W${1#-Wno-} + test_cflags $unknown_warning_cflags $warning_flag && add_cflags_headers $1 +} + +check_disable_warning_headers -Wno-deprecated-declarations +check_disable_warning_headers -Wno-unused-variable + +test_cc < $TMPV + if test_ldflags -Wl,${version_script},$TMPV; then + append SHFLAGS '-Wl,${version_script},\$(SUBDIR)lib\$(NAME).ver' + quotes='""' + test_cc <= 1400" && + add_cflags -Qansi-alias + # Some inline asm is not compilable in debug + if enabled debug; then + disable ebp_available + disable ebx_available + fi + fi + # msvcrt10 x64 incorrectly enables log2, only msvcrt12 (MSVC 2013) onwards actually has log2. + check_cpp_condition log2 crtversion.h "_VC_CRT_MAJOR_VERSION >= 12" + # the new SSA optimizer in VS2015 U3 is mis-optimizing some parts of the code + # Issue has been fixed in MSVC v19.00.24218. + test_cpp_condition windows.h "_MSC_FULL_VER >= 190024218" || + check_cflags -d2SSAOptimizer- + # enable utf-8 source processing on VS2015 U2 and newer + test_cpp_condition windows.h "_MSC_FULL_VER >= 190023918" && + add_cflags -utf-8 +fi + +for pfx in "" host_; do + varname=${pfx%_}cc_type + eval "type=\$$varname" + if [ "$type" = "msvc" ]; then + test_${pfx}cc < "$TMPRSP" + respfile="@$TMPRSP" + out_arg="$(echo $ar_o | sed "s;\$@;$ar_out;g")" + if test_cmd $ar $arflags $out_arg $respfile; then + response_files="yes" + elif [ "$response_files" = "auto" ]; then + response_files="no" + else + die "Response files are not available with this toolchain. Exiting" + fi +fi + +enable frame_thread_encoder + +enabled asm || { arch=c; disable $ARCH_LIST $ARCH_EXT_LIST; } + +check_deps $CONFIG_LIST \ + $CONFIG_EXTRA \ + $HAVE_LIST \ + $ALL_COMPONENTS \ + +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 threads || warn \ + "Threading support was disabled or is not available on the target platform." \ + "This means that not only is this build not multi-threaded, but also" \ + "that the libraries from this build MUST NOT be used in a multi-threaded"\ + "environment." + +case $target_os in +*-dos|freedos|opendos) + if test_cpp_condition sys/version.h "defined(__DJGPP__) && __DJGPP__ == 2 && __DJGPP_MINOR__ == 5"; then + disable memalign + fi + ;; +esac + +if ! enabled_any memalign posix_memalign aligned_malloc; then + if enabled_any asm inline_asm; then + die "ERROR: Building with assembly enabled is not supported on platforms "\ + "without aligned memory allocations! Try reconfiguring with --disable-asm" + fi +fi + +flatten_extralibs(){ + nested_entries= + list_name=$1 + eval list=\$${1} + for entry in $list; do + entry_copy=$entry + resolve entry_copy + flat_entries= + for e in $entry_copy; do + case $e in + *_extralibs) nested_entries="$nested_entries$e ";; + *) flat_entries="$flat_entries$e ";; + esac + done + eval $entry="\$flat_entries" + done + append $list_name "$nested_entries" + + resolve nested_entries + if test -n "$(filter '*_extralibs' $nested_entries)"; then + flatten_extralibs $list_name + fi +} + +flatten_extralibs_wrapper(){ + list_name=$1 + flatten_extralibs $list_name + unique $list_name + resolve $list_name + eval $list_name=\$\(\$ldflags_filter \$$list_name\) + eval printf \''%s'\' \""\$$list_name"\" +} + +for linkunit in $LIBRARY_LIST; do + unset current_extralibs + eval components=\$$(toupper ${linkunit})_COMPONENTS_LIST + for comp in ${components}; do + enabled $comp || continue + comp_extralibs="${comp}_extralibs" + append current_extralibs $comp_extralibs + done + eval prepend ${linkunit}_extralibs $current_extralibs +done + +for linkunit in $LIBRARY_LIST $PROGRAM_LIST $EXTRALIBS_LIST; do + eval ${linkunit}_extralibs=\$\(flatten_extralibs_wrapper ${linkunit}_extralibs\) +done + +map 'enabled $v && intrinsics=${v#intrinsics_}' $INTRINSICS_LIST + +for thread in $THREADS_LIST; do + if enabled $thread; then + test -n "$thread_type" && + die "ERROR: Only one thread type must be selected." || + thread_type="$thread" + fi +done + +if disabled stdatomic; then + if enabled atomics_win32; then + add_cppflags '-I\$(SRC_PATH)/compat/atomics/win32' + else + enabled threads && die "Threading is enabled, but no atomics are available" + add_cppflags '-I\$(SRC_PATH)/compat/atomics/dummy' + fi +fi + +check_builtin stdbit "stdbit.h assert.h" \ + 'static_assert(__STDC_VERSION_STDBIT_H__ >= 202311L, "Compiler lacks stdbit.h")' || \ + add_cppflags '-I\$(SRC_PATH)/compat/stdbit' + +# Check if requested libraries were found. +for lib in $AUTODETECT_LIBS; do + requested $lib && ! enabled $lib && die "ERROR: $lib requested but not found"; +done + +enabled zlib && add_cppflags -DZLIB_CONST + +# conditional library dependencies, in any order +enabled amovie_filter && prepend avfilter_deps "avformat avcodec" +enabled aresample_filter && prepend avfilter_deps "swresample" +enabled cover_rect_filter && prepend avfilter_deps "avformat avcodec" +enabled ebur128_filter && enabled swresample && prepend avfilter_deps "swresample" +enabled elbg_filter && prepend avfilter_deps "avcodec" +enabled find_rect_filter && prepend avfilter_deps "avformat avcodec" +enabled fsync_filter && prepend avfilter_deps "avformat" +enabled mcdeint_filter && prepend avfilter_deps "avcodec" +enabled movie_filter && prepend avfilter_deps "avformat avcodec" +enabled pan_filter && prepend avfilter_deps "swresample" +enabled qrencode_filter && prepend avfilter_deps "swscale" +enabled qrencodesrc_filter && prepend avfilter_deps "swscale" +enabled removelogo_filter && prepend avfilter_deps "avformat avcodec swscale" +enabled sab_filter && prepend avfilter_deps "swscale" +enabled scale_filter && prepend avfilter_deps "swscale" +enabled scale2ref_filter && prepend avfilter_deps "swscale" +enabled showcqt_filter && prepend avfilter_deps "avformat swscale" +enabled signature_filter && prepend avfilter_deps "avcodec avformat" +enabled smartblur_filter && prepend avfilter_deps "swscale" +enabled spp_filter && prepend avfilter_deps "avcodec" +enabled sr_filter && prepend avfilter_deps "avformat swscale" +enabled subtitles_filter && prepend avfilter_deps "avformat avcodec" +enabled uspp_filter && prepend avfilter_deps "avcodec" +enabled zoompan_filter && prepend avfilter_deps "swscale" + +enabled lavfi_indev && prepend avdevice_deps "avfilter" + +enabled opus_decoder && prepend avcodec_deps "swresample" + +# reorder the items at var $1 to align with the items order at var $2 . +# die if an item at $1 is not at $2 . +reorder_by(){ + eval rb_in=\$$1 + eval rb_ordered=\$$2 + + for rb in $rb_in; do + is_in $rb $rb_ordered || die "$rb at \$$1 is not at \$$2" + done + + rb_out= + for rb in $rb_ordered; do + is_in $rb $rb_in && rb_out="$rb_out$rb " + done + eval $1=\$rb_out +} + +# deps-expand fflib $1: N x {append all expanded deps; unique} +# within a set of N items, N expansions are enough to expose a cycle. +expand_deps(){ + unique ${1}_deps # required for the early break test. + for dummy in $LIBRARY_LIST; do # N iteratios + eval deps=\$${1}_deps + append ${1}_deps $(map 'eval echo \$${v}_deps' $deps) + unique ${1}_deps + eval '[ ${#deps} = ${#'${1}_deps'} ]' && break # doesn't expand anymore + done + + eval is_in $1 \$${1}_deps && die "Dependency cycle at ${1}_deps" + reorder_by ${1}_deps LIBRARY_LIST # linking order is expected later +} + +map 'expand_deps $v' $LIBRARY_LIST + +if test "$quiet" != "yes"; then + +echo "install prefix $prefix" +echo "source path $source_path" +echo "C compiler $cc" +echo "C library $libc_type" +if test "$host_cc" != "$cc"; then + echo "host C compiler $host_cc" + echo "host C library $host_libc_type" +fi +echo "ARCH $arch ($cpu)" +if test "$build_suffix" != ""; then + echo "build suffix $build_suffix" +fi +if test "$progs_suffix" != ""; then + echo "progs suffix $progs_suffix" +fi +if test "$extra_version" != ""; then + echo "version string suffix $extra_version" +fi +echo "big-endian ${bigendian-no}" +echo "runtime cpu detection ${runtime_cpudetect-no}" +if enabled x86; then + echo "standalone assembly ${x86asm-no}" + echo "x86 assembler ${x86asmexe}" + echo "MMX enabled ${mmx-no}" + echo "MMXEXT enabled ${mmxext-no}" + echo "SSE enabled ${sse-no}" + echo "SSSE3 enabled ${ssse3-no}" + echo "AESNI enabled ${aesni-no}" + echo "CLMUL enabled ${clmul-no}" + echo "AVX enabled ${avx-no}" + echo "AVX2 enabled ${avx2-no}" + echo "AVX-512 enabled ${avx512-no}" + echo "AVX-512ICL enabled ${avx512icl-no}" + echo "XOP enabled ${xop-no}" + echo "FMA3 enabled ${fma3-no}" + echo "FMA4 enabled ${fma4-no}" + echo "i686 features enabled ${i686-no}" + echo "CMOV is fast ${fast_cmov-no}" + echo "EBX available ${ebx_available-no}" + echo "EBP available ${ebp_available-no}" +fi +if enabled x86_32; then + echo "7 registers available ${x86_32_7regs-no}" +fi +if enabled aarch64; then + echo "NEON enabled ${neon-no}" + echo "DOTPROD enabled ${dotprod-no}" + echo "I8MM enabled ${i8mm-no}" + echo "PMULL enabled ${pmull-no}" + echo "EOR3 enabled ${eor3-no}" + echo "SVE enabled ${sve-no}" + echo "SVE2 enabled ${sve2-no}" + echo "SME enabled ${sme-no}" + echo "SME-I16I64 enabled ${sme_i16i64-no}" + echo "SME2 enabled ${sme2-no}" +fi +if enabled arm; then + echo "ARMv5TE enabled ${armv5te-no}" + echo "ARMv6 enabled ${armv6-no}" + echo "ARMv6T2 enabled ${armv6t2-no}" + echo "VFP enabled ${vfp-no}" + echo "NEON enabled ${neon-no}" + echo "THUMB enabled ${thumb-no}" +fi +if enabled mips; then + echo "MIPS FPU enabled ${mipsfpu-no}" + echo "MIPS DSP R1 enabled ${mipsdsp-no}" + echo "MIPS DSP R2 enabled ${mipsdspr2-no}" + echo "MIPS MSA enabled ${msa-no}" + echo "LOONGSON MMI enabled ${mmi-no}" +fi +if enabled ppc; then + echo "AltiVec enabled ${altivec-no}" + echo "VSX enabled ${vsx-no}" + echo "POWER8 enabled ${power8-no}" + echo "PPC 4xx optimizations ${ppc4xx-no}" + echo "dcbzl available ${dcbzl-no}" + echo "vec_xl available ${vec_xl-no}" +fi +if enabled loongarch; then + echo "LSX enabled ${lsx-no}" + echo "LASX enabled ${lasx-no}" +fi +if enabled riscv; then + echo "RISC-V CBO Prefetch ${rv_zicbop-no}" + echo "RISC-V Vector enabled ${rvv-no}" +fi +echo "debug symbols ${debug-no}" +echo "strip symbols ${stripping-no}" +echo "optimize for size ${small-no}" +echo "optimizations ${optimizations-no}" +echo "static ${static-no}" +echo "shared ${shared-no}" +echo "network support ${network-no}" +echo "threading support ${thread_type-no}" +echo "safe bitstream reader ${safe_bitstream_reader-no}" +echo "texi2html enabled ${texi2html-no}" +echo "perl enabled ${perl-no}" +echo "pod2man enabled ${pod2man-no}" +echo "makeinfo enabled ${makeinfo_command-no}" +echo "makeinfo supports HTML ${makeinfo_html-no}" +echo "experimental features ${unstable-no}" +echo "xmllint enabled ${xmllint-no}" +test -n "$random_seed" && + echo "random seed ${random_seed}" +echo + +echo "External libraries:" +print_enabled '' $EXTERNAL_LIBRARY_LIST $EXTERNAL_AUTODETECT_LIBRARY_LIST | print_in_columns +echo + +echo "External libraries providing hardware acceleration:" +print_enabled '' $HWACCEL_LIBRARY_LIST $HWACCEL_AUTODETECT_LIBRARY_LIST | print_in_columns +echo + +echo "Libraries:" +print_enabled '' $LIBRARY_LIST | print_in_columns +echo + +echo "Programs:" +print_enabled '' $PROGRAM_LIST | print_in_columns +echo + +for type in decoder encoder hwaccel parser demuxer muxer protocol filter bsf indev outdev; do + echo "Enabled ${type}s:" + eval list=\$$(toupper $type)_LIST + print_enabled '_*' $list | print_in_columns + echo +done + +if test -n "$ignore_tests"; then + ignore_tests=$(echo $ignore_tests | tr ',' ' ') + echo "Ignored FATE tests:" + echo $ignore_tests | print_in_columns + echo +fi + +echo "License: $license" + +fi # test "$quiet" != "yes" + +if test -n "$WARN_IF_GETS_DISABLED_LIST"; then + for cfg in $WARN_IF_GETS_DISABLED_LIST; do + if disabled $cfg; then + varname=${cfg}_disable_reason + eval "warn \"Disabled $cfg because \$$varname\"" + fi + done +fi + +if test -n "$WARNINGS"; then + printf "\n%s%s$WARNINGS%s" "$warn_color" "$bold_color" "$reset_color" + enabled fatal_warnings && exit 1 +fi + +test -e Makefile || echo "include $source_path/Makefile" > Makefile + +esc(){ + echo "$*" | sed 's/%/%25/g;s/:/%3a/g' +} + +echo "config:$arch:$subarch:$cpu:$target_os:$(esc $cc_ident):$(esc $FFMPEG_CONFIGURATION)" > ffbuild/config.fate + +enabled stripping || strip="echo skipping strip" +enabled stripping || striptype="" + +config_files="$TMPH ffbuild/config.mak doc/config.texi" + +cat > ffbuild/config.mak <> ffbuild/config.mak' $LIBRARY_LIST + +for entry in $LIBRARY_LIST $PROGRAM_LIST $EXTRALIBS_LIST; do + eval echo "EXTRALIBS-${entry}=\$${entry}_extralibs" >> ffbuild/config.mak +done + +cat > $TMPH <>$TMPH + +test -n "$malloc_prefix" && + echo "#define MALLOC_PREFIX $malloc_prefix" >>$TMPH + +enabled aarch64 && + echo "#define AS_ARCH_LEVEL $as_arch_level" >>$TMPH + +if enabled x86asm; then + append config_files $TMPASM + cat > $TMPASM <> $TMPH + + +mkdir -p doc +mkdir -p tests +mkdir -p tests/api +echo "@c auto-generated by configure - do not modify! " > doc/config.texi + +print_config ARCH_ "$config_files" $ARCH_LIST +print_config HAVE_ "$config_files" $HAVE_LIST +print_config CONFIG_ "$config_files" $CONFIG_LIST \ + $CONFIG_EXTRA \ + +echo "#endif /* FFMPEG_CONFIG_H */" >> $TMPH + +# Do not overwrite an unchanged config.h to avoid superfluous rebuilds. +cp_if_changed $TMPH config.h +touch ffbuild/.config + +# Copy config.asm and reopen a new TMPASM for config_components.asm +enabled x86asm && cp_if_changed $TMPASM config.asm && cat > $TMPASM < $TMPH <> $TMPH +echo "endif # FFMPEG_CONFIG_MAK" >> ffbuild/config.mak + +cp_if_changed $TMPH config_components.h +enabled x86asm && cp_if_changed $TMPASM config_components.asm + +if enabled checkasm; then + + # Reopen a new TMPH for checkasm_config_generated.h + cat > $TMPH <> $TMPH < $TMPH <> $TMPH <> $TMPH <> $TMPH < $TMPASM < $TMPH <> $TMPH + +cp_if_changed $TMPH libavutil/avconfig.h + +# full_filter_name_foo=vf_foo +# full_filter_name_bar=asrc_bar +# ... +eval "$(sed -n "s/^extern const FFFilter ff_\([avfsinkrc]\{2,5\}\)_\(.*\);/full_filter_name_\2=\1_\2/p" $source_path/libavfilter/allfilters.c)" + +# generate the lists of enabled components +print_enabled_components(){ + file=$1 + struct_name=$2 + name=$3 + shift 3 + echo "static const $struct_name * const $name[] = {" > $TMPH + for c in $*; do + if enabled $c; then + case $name in + filter_list) + eval c=\$full_filter_name_${c%_filter} + ;; + indev_list) + c=${c%_indev}_demuxer + ;; + outdev_list) + c=${c%_outdev}_muxer + ;; + esac + printf " &ff_%s,\n" $c >> $TMPH + fi + done + if [ "$name" = "filter_list" ]; then + for c in asrc_abuffer vsrc_buffer asink_abuffer vsink_buffer; do + printf " &ff_%s,\n" $c >> $TMPH + done + fi + echo " NULL };" >> $TMPH + cp_if_changed $TMPH $file +} + +print_enabled_components libavfilter/filter_list.c FFFilter filter_list $FILTER_LIST +print_enabled_components libavcodec/codec_list.c FFCodec codec_list $CODEC_LIST +print_enabled_components libavcodec/parser_list.c FFCodecParser parser_list $PARSER_LIST +print_enabled_components libavcodec/bsf_list.c FFBitStreamFilter bitstream_filters $BSF_LIST +print_enabled_components libavformat/demuxer_list.c FFInputFormat demuxer_list $DEMUXER_LIST +print_enabled_components libavformat/muxer_list.c FFOutputFormat muxer_list $MUXER_LIST +print_enabled_components libavdevice/indev_list.c FFInputFormat indev_list $INDEV_LIST +print_enabled_components libavdevice/outdev_list.c FFOutputFormat outdev_list $OUTDEV_LIST +print_enabled_components libavformat/protocol_list.c URLProtocol url_protocols $PROTOCOL_LIST + +# Settings for pkg-config files + +cat > $TMPH <> $TMPH +done + +cp_if_changed $TMPH ffbuild/config.sh diff --git a/thirdparty/ffmpeg/libavcodec/Makefile b/thirdparty/ffmpeg/libavcodec/Makefile index 95ba30850..d3ffcc0ba 100644 --- a/thirdparty/ffmpeg/libavcodec/Makefile +++ b/thirdparty/ffmpeg/libavcodec/Makefile @@ -185,6 +185,7 @@ OBJS-$(CONFIG_VIDEODSP) += videodsp.o OBJS-$(CONFIG_VP3DSP) += vp3dsp.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_REQUEST) += v4l2_request.o OBJS-$(CONFIG_WMA_FREQS) += wma_freqs.o OBJS-$(CONFIG_WMV2DSP) += wmv2dsp.o @@ -1048,6 +1049,7 @@ OBJS-$(CONFIG_AV1_D3D11VA_HWACCEL) += dxva2_av1.o OBJS-$(CONFIG_AV1_DXVA2_HWACCEL) += dxva2_av1.o OBJS-$(CONFIG_AV1_D3D12VA_HWACCEL) += dxva2_av1.o d3d12va_av1.o OBJS-$(CONFIG_AV1_NVDEC_HWACCEL) += nvdec_av1.o +OBJS-$(CONFIG_AV1_V4L2REQUEST_HWACCEL) += v4l2_request_av1.o OBJS-$(CONFIG_AV1_VAAPI_HWACCEL) += vaapi_av1.o OBJS-$(CONFIG_AV1_VDPAU_HWACCEL) += vdpau_av1.o OBJS-$(CONFIG_AV1_VIDEOTOOLBOX_HWACCEL) += videotoolbox_av1.o @@ -1061,6 +1063,7 @@ OBJS-$(CONFIG_H264_DXVA2_HWACCEL) += dxva2_h264.o OBJS-$(CONFIG_H264_D3D12VA_HWACCEL) += dxva2_h264.o d3d12va_h264.o OBJS-$(CONFIG_H264_NVDEC_HWACCEL) += nvdec_h264.o OBJS-$(CONFIG_H264_QSV_HWACCEL) += qsvdec.o +OBJS-$(CONFIG_H264_V4L2REQUEST_HWACCEL) += v4l2_request_h264.o OBJS-$(CONFIG_H264_VAAPI_HWACCEL) += vaapi_h264.o OBJS-$(CONFIG_H264_VDPAU_HWACCEL) += vdpau_h264.o OBJS-$(CONFIG_H264_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o @@ -1070,6 +1073,7 @@ OBJS-$(CONFIG_HEVC_DXVA2_HWACCEL) += dxva2_hevc.o OBJS-$(CONFIG_HEVC_D3D12VA_HWACCEL) += dxva2_hevc.o d3d12va_hevc.o OBJS-$(CONFIG_HEVC_NVDEC_HWACCEL) += nvdec_hevc.o OBJS-$(CONFIG_HEVC_QSV_HWACCEL) += qsvdec.o +OBJS-$(CONFIG_HEVC_V4L2REQUEST_HWACCEL) += v4l2_request_hevc.o OBJS-$(CONFIG_HEVC_VAAPI_HWACCEL) += vaapi_hevc.o h265_profile_level.o OBJS-$(CONFIG_HEVC_VDPAU_HWACCEL) += vdpau_hevc.o h265_profile_level.o OBJS-$(CONFIG_HEVC_VULKAN_HWACCEL) += vulkan_decode.o vulkan_hevc.o @@ -1083,6 +1087,7 @@ OBJS-$(CONFIG_MPEG2_DXVA2_HWACCEL) += dxva2_mpeg2.o OBJS-$(CONFIG_MPEG2_D3D12VA_HWACCEL) += dxva2_mpeg2.o d3d12va_mpeg2.o OBJS-$(CONFIG_MPEG2_NVDEC_HWACCEL) += nvdec_mpeg12.o OBJS-$(CONFIG_MPEG2_QSV_HWACCEL) += qsvdec.o +OBJS-$(CONFIG_MPEG2_V4L2REQUEST_HWACCEL) += v4l2_request_mpeg2.o OBJS-$(CONFIG_MPEG2_VAAPI_HWACCEL) += vaapi_mpeg2.o OBJS-$(CONFIG_MPEG2_VDPAU_HWACCEL) += vdpau_mpeg12.o OBJS-$(CONFIG_MPEG2_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o @@ -1098,11 +1103,13 @@ OBJS-$(CONFIG_VC1_QSV_HWACCEL) += qsvdec.o OBJS-$(CONFIG_VC1_VAAPI_HWACCEL) += vaapi_vc1.o OBJS-$(CONFIG_VC1_VDPAU_HWACCEL) += vdpau_vc1.o OBJS-$(CONFIG_VP8_NVDEC_HWACCEL) += nvdec_vp8.o +OBJS-$(CONFIG_VP8_V4L2REQUEST_HWACCEL) += v4l2_request_vp8.o OBJS-$(CONFIG_VP8_VAAPI_HWACCEL) += vaapi_vp8.o OBJS-$(CONFIG_VP9_D3D11VA_HWACCEL) += dxva2_vp9.o OBJS-$(CONFIG_VP9_DXVA2_HWACCEL) += dxva2_vp9.o OBJS-$(CONFIG_VP9_D3D12VA_HWACCEL) += dxva2_vp9.o d3d12va_vp9.o OBJS-$(CONFIG_VP9_NVDEC_HWACCEL) += nvdec_vp9.o +OBJS-$(CONFIG_VP9_V4L2REQUEST_HWACCEL) += v4l2_request_vp9.o OBJS-$(CONFIG_VP9_VAAPI_HWACCEL) += vaapi_vp9.o OBJS-$(CONFIG_VP9_VDPAU_HWACCEL) += vdpau_vp9.o OBJS-$(CONFIG_VP9_VIDEOTOOLBOX_HWACCEL) += videotoolbox_vp9.o @@ -1357,6 +1364,7 @@ SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX) += videotoolbox.h vt_internal.h SKIPHEADERS-$(CONFIG_VULKAN) += ffv1_vulkan.h prores_vulkan.h vulkan_video.h \ vulkan_encode.h vulkan_decode.h SKIPHEADERS-$(CONFIG_V4L2_M2M) += v4l2_buffers.h v4l2_context.h v4l2_m2m.h +SKIPHEADERS-$(CONFIG_V4L2_REQUEST) += v4l2_request.h SKIPHEADERS-$(CONFIG_ZLIB) += zlib_wrapper.h TESTPROGS = avcodec \ diff --git a/thirdparty/ffmpeg/libavcodec/Makefile.orig b/thirdparty/ffmpeg/libavcodec/Makefile.orig new file mode 100644 index 000000000..95ba30850 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/Makefile.orig @@ -0,0 +1,1438 @@ +NAME = avcodec +DESC = FFmpeg codec library + +HEADERS = ac3_parser.h \ + adts_parser.h \ + avcodec.h \ + avdct.h \ + bsf.h \ + codec.h \ + codec_desc.h \ + codec_id.h \ + codec_par.h \ + d3d11va.h \ + defs.h \ + dirac.h \ + dv_profile.h \ + dxva2.h \ + exif.h \ + jni.h \ + mediacodec.h \ + packet.h \ + qsv.h \ + smpte_436m.h \ + vdpau.h \ + version.h \ + version_major.h \ + videotoolbox.h \ + vorbis_parser.h \ + +OBJS = ac3_parser.o \ + adts_parser.o \ + allcodecs.o \ + avcodec.o \ + avdct.o \ + packet.o \ + bitstream.o \ + bitstream_filters.o \ + bsf.o \ + codec_desc.o \ + codec_par.o \ + d3d11va.o \ + decode.o \ + dirac.o \ + dv_profile.o \ + encode.o \ + exif.o \ + get_buffer.o \ + imgconvert.o \ + jni.o \ + mathtables.o \ + mediacodec.o \ + mpeg12framerate.o \ + options.o \ + parser.o \ + parsers.o \ + profiles.o \ + qsv_api.o \ + raw.o \ + threadprogress.o \ + tiff_common.o \ + utils.o \ + version.o \ + vlc.o \ + vorbis_parser.o \ + xiph.o \ + +SHLIBOBJS = float_scalarproduct.o \ + timecode_internal.o \ + +# subsystems +include $(SRC_PATH)/libavcodec/aac/Makefile +include $(SRC_PATH)/libavcodec/hevc/Makefile +include $(SRC_PATH)/libavcodec/opus/Makefile +include $(SRC_PATH)/libavcodec/vvc/Makefile +include $(SRC_PATH)/libavcodec/vulkan/Makefile +-include $(SRC_PATH)/libavcodec/$(ARCH)/hevc/Makefile +-include $(SRC_PATH)/libavcodec/$(ARCH)/vvc/Makefile +OBJS-$(CONFIG_AANDCTTABLES) += aandcttab.o +OBJS-$(CONFIG_AC3DSP) += ac3dsp.o ac3.o ac3tab.o +OBJS-$(CONFIG_ADTS_HEADER) += adts_header.o mpeg4audio_sample_rates.o +OBJS-$(CONFIG_AMF) += amfenc.o amfdec.o +OBJS-$(CONFIG_AUDIO_FRAME_QUEUE) += audio_frame_queue.o +OBJS-$(CONFIG_ATSC_A53) += atsc_a53.o +OBJS-$(CONFIG_AUDIODSP) += audiodsp.o +OBJS-$(CONFIG_BLOCKDSP) += blockdsp.o +OBJS-$(CONFIG_BSWAPDSP) += bswapdsp.o +OBJS-$(CONFIG_CABAC) += cabac.o +OBJS-$(CONFIG_CBS) += cbs.o cbs_bsf.o +OBJS-$(CONFIG_CBS_APV) += cbs_apv.o +OBJS-$(CONFIG_CBS_AV1) += cbs_av1.o +OBJS-$(CONFIG_CBS_H264) += cbs_h264.o cbs_h2645.o cbs_sei.o h2645_parse.o +OBJS-$(CONFIG_CBS_H265) += cbs_h265.o cbs_h2645.o cbs_sei.o h2645_parse.o +OBJS-$(CONFIG_CBS_H266) += cbs_h266.o cbs_h2645.o cbs_sei.o h2645_parse.o +OBJS-$(CONFIG_CBS_LCEVC) += cbs_lcevc.o cbs_h2645.o cbs_sei.o h2645_parse.o +OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o +OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o +OBJS-$(CONFIG_CBS_VP8) += cbs_vp8.o vp8data.o +OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o +OBJS-$(CONFIG_CELP_MATH) += celp_math.o +OBJS-$(CONFIG_D3D12VA_ENCODE) += d3d12va_encode.o hw_base_encode.o +OBJS-$(CONFIG_DEFLATE_WRAPPER) += zlib_wrapper.o +OBJS-$(CONFIG_DOVI_RPUDEC) += dovi_rpu.o dovi_rpudec.o +OBJS-$(CONFIG_DOVI_RPUENC) += dovi_rpu.o dovi_rpuenc.o +OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o +OBJS-$(CONFIG_EVCPARSE) += evc_parse.o evc_ps.o +OBJS-$(CONFIG_FAANDCT) += faandct.o +OBJS-$(CONFIG_FAANIDCT) += faanidct.o +OBJS-$(CONFIG_FDCTDSP) += fdctdsp.o jfdctfst.o jfdctint.o +OBJS-$(CONFIG_FMTCONVERT) += fmtconvert.o +OBJS-$(CONFIG_GOLOMB) += golomb.o +OBJS-$(CONFIG_H263DSP) += h263dsp.o +OBJS-$(CONFIG_H264CHROMA) += h264chroma.o +OBJS-$(CONFIG_H264DSP) += h264dsp.o h264idct.o +OBJS-$(CONFIG_H264PARSE) += h264_parse.o h264_ps.o h264data.o \ + h2645data.o h2645_parse.o h2645_vui.o +OBJS-$(CONFIG_H264PRED) += h264pred.o +OBJS-$(CONFIG_H264QPEL) += h264qpel.o +OBJS-$(CONFIG_H264_SEI) += h264_sei.o h2645_sei.o +OBJS-$(CONFIG_HEVCPARSE) += h2645data.o h2645_parse.o h2645_vui.o +OBJS-$(CONFIG_HEVC_SEI) += h2645_sei.o +OBJS-$(CONFIG_HPELDSP) += hpeldsp.o +OBJS-$(CONFIG_HUFFMAN) += huffman.o +OBJS-$(CONFIG_HUFFYUVDSP) += huffyuvdsp.o +OBJS-$(CONFIG_HUFFYUVENCDSP) += huffyuvencdsp.o +OBJS-$(CONFIG_IDCTDSP) += idctdsp.o simple_idct.o jrevdct.o +OBJS-$(CONFIG_INFLATE_WRAPPER) += zlib_wrapper.o +OBJS-$(CONFIG_INTRAX8) += intrax8.o intrax8dsp.o msmpeg4_vc1_data.o +OBJS-$(CONFIG_ITUT_T35) += itut35.o aom_film_grain.o dynamic_hdr_vivid.o +OBJS-$(CONFIG_IVIDSP) += ivi_dsp.o +OBJS-$(CONFIG_JNI) += ffjni.o jni.o +OBJS-$(CONFIG_JPEGTABLES) += jpegtables.o +OBJS-$(CONFIG_LCMS2) += fflcms2.o +OBJS-$(CONFIG_LIBLCEVC_DEC) += lcevcdec.o lcevctab.o +OBJS-$(CONFIG_LLAUDDSP) += lossless_audiodsp.o +OBJS-$(CONFIG_LLVIDDSP) += lossless_videodsp.o +OBJS-$(CONFIG_LLVIDENCDSP) += lossless_videoencdsp.o +OBJS-$(CONFIG_LPC) += lpc.o +OBJS-$(CONFIG_LSP) += lsp.o +OBJS-$(CONFIG_LZF) += lzf.o +OBJS-$(CONFIG_ME_CMP) += me_cmp.o +OBJS-$(CONFIG_MEDIACODEC) += mediacodecdec_common.o mediacodec_surface.o mediacodec_wrapper.o mediacodec_sw_buffer.o +OBJS-$(CONFIG_MPEG_ER) += mpeg_er.o +OBJS-$(CONFIG_MPEGAUDIO) += mpegaudio.o mpegaudiodec_common.o \ + mpegaudiodata.o +OBJS-$(CONFIG_MPEGAUDIODSP) += mpegaudiodsp.o \ + mpegaudiodsp_data.o \ + mpegaudiodsp_fixed.o \ + mpegaudiodsp_float.o \ + dct32_fixed.o dct32_float.o +OBJS-$(CONFIG_MPEGAUDIOHEADER) += mpegaudiodecheader.o mpegaudiotabs.o +OBJS-$(CONFIG_MPEG4AUDIO) += mpeg4audio.o mpeg4audio_sample_rates.o +OBJS-$(CONFIG_MPEGVIDEO) += mpegvideo.o rl.o \ + mpegvideo_motion.o \ + mpegvideo_unquantize.o \ + mpegvideodata.o mpegpicture.o \ + to_upper4.o +OBJS-$(CONFIG_MPEGVIDEODEC) += mpegvideo_dec.o mpegutils.o +OBJS-$(CONFIG_MPEGVIDEOENC) += mpegvideo_enc.o mpeg12data.o \ + motion_est.o ratecontrol.o +OBJS-$(CONFIG_MPEGVIDEOENCDSP) += mpegvideoencdsp.o +OBJS-$(CONFIG_MSMPEG4DEC) += msmpeg4dec.o msmpeg4.o msmpeg4data.o \ + msmpeg4_vc1_data.o mpeg4videodec.o +OBJS-$(CONFIG_MSMPEG4ENC) += msmpeg4enc.o msmpeg4.o msmpeg4data.o \ + msmpeg4_vc1_data.o +OBJS-$(CONFIG_MSS34DSP) += mss34dsp.o jpegquanttables.o +OBJS-$(CONFIG_PIXBLOCKDSP) += pixblockdsp.o +OBJS-$(CONFIG_QPELDSP) += qpeldsp.o +OBJS-$(CONFIG_QSV) += qsv.o +OBJS-$(CONFIG_QSVDEC) += qsvdec.o +OBJS-$(CONFIG_QSVENC) += qsvenc.o +OBJS-$(CONFIG_RANGECODER) += rangecoder.o +OBJS-$(CONFIG_RV34DSP) += rv34dsp.o +OBJS-$(CONFIG_SINEWIN) += sinewin.o +OBJS-$(CONFIG_SMPTE_436M) += smpte_436m.o +OBJS-$(CONFIG_SNAPPY) += snappy.o +OBJS-$(CONFIG_STARTCODE) += startcode.o +OBJS-$(CONFIG_TEXTUREDSP) += texturedsp.o +OBJS-$(CONFIG_TEXTUREDSPENC) += texturedspenc.o +OBJS-$(CONFIG_TPELDSP) += tpeldsp.o +OBJS-$(CONFIG_VAAPI_ENCODE) += vaapi_encode.o hw_base_encode.o +OBJS-$(CONFIG_AV1_AMF_ENCODER) += amfenc_av1.o +OBJS-$(CONFIG_AV1_AMF_DECODER) += amfdec.o +OBJS-$(CONFIG_VC1DSP) += vc1dsp.o +OBJS-$(CONFIG_VIDEODSP) += videodsp.o +OBJS-$(CONFIG_VP3DSP) += vp3dsp.o +OBJS-$(CONFIG_VP8DSP) += vp8dsp.o +OBJS-$(CONFIG_V4L2_M2M) += v4l2_m2m.o v4l2_context.o v4l2_buffers.o v4l2_fmt.o +OBJS-$(CONFIG_WMA_FREQS) += wma_freqs.o +OBJS-$(CONFIG_WMV2DSP) += wmv2dsp.o + +# decoders/encoders +OBJS-$(CONFIG_ZERO12V_DECODER) += 012v.o +OBJS-$(CONFIG_A64MULTI_ENCODER) += a64multienc.o elbg.o +OBJS-$(CONFIG_A64MULTI5_ENCODER) += a64multienc.o elbg.o +OBJS-$(CONFIG_AAC_DECODER) += aactab.o \ + aacsbr.o aacps_common.o aacps_float.o \ + kbdwin.o \ + sbrdsp.o aacpsdsp_float.o cbrt_data.o \ + $(if $(!CONFIG_HARDCODED_TABLES), cbrt_tablegen_common.o) +OBJS-$(CONFIG_AAC_FIXED_DECODER) += aactab.o \ + aacsbr_fixed.o aacps_common.o aacps_fixed.o \ + kbdwin.o \ + sbrdsp_fixed.o aacpsdsp_fixed.o cbrt_data_fixed.o \ + $(if $(!CONFIG_HARDCODED_TABLES), cbrt_tablegen_common.o) +OBJS-$(CONFIG_AAC_ENCODER) += aacenc.o aaccoder.o aacenctab.o \ + aacpsy.o aactab.o aacencdsp.o \ + aacenc_is.o \ + aacenc_tns.o \ + psymodel.o kbdwin.o \ + mpeg4audio_sample_rates.o +OBJS-$(CONFIG_AAC_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_AAC_MF_ENCODER) += mfenc.o mf_utils.o +OBJS-$(CONFIG_AASC_DECODER) += aasc.o msrledec.o +OBJS-$(CONFIG_AC3_DECODER) += ac3dec_float.o ac3dec_data.o ac3.o \ + kbdwin.o ac3tab.o ac3_channel_layout_tab.o +OBJS-$(CONFIG_AC3_FIXED_DECODER) += ac3dec_fixed.o ac3dec_data.o ac3.o \ + kbdwin.o ac3tab.o ac3_channel_layout_tab.o +OBJS-$(CONFIG_AC3_ENCODER) += ac3enc_float.o ac3enc.o ac3tab.o \ + ac3.o kbdwin.o +OBJS-$(CONFIG_AC3_FIXED_ENCODER) += ac3enc_fixed.o ac3enc.o ac3tab.o ac3.o kbdwin.o +OBJS-$(CONFIG_AC3_MF_ENCODER) += mfenc.o mf_utils.o +OBJS-$(CONFIG_ACELP_KELVIN_DECODER) += g729dec.o lsp.o celp_filters.o acelp_filters.o acelp_pitch_delay.o acelp_vectors.o g729postfilter.o +OBJS-$(CONFIG_AGM_DECODER) += agm.o jpegquanttables.o +OBJS-$(CONFIG_AHX_DECODER) += mpegaudiodec_float.o +OBJS-$(CONFIG_AIC_DECODER) += aic.o +OBJS-$(CONFIG_ALAC_DECODER) += alac.o alac_data.o alacdsp.o +OBJS-$(CONFIG_ALAC_ENCODER) += alacenc.o alac_data.o +OBJS-$(CONFIG_ALIAS_PIX_DECODER) += aliaspixdec.o +OBJS-$(CONFIG_ALIAS_PIX_ENCODER) += aliaspixenc.o +OBJS-$(CONFIG_ALS_DECODER) += alsdec.o bgmc.o mlz.o +OBJS-$(CONFIG_AMRNB_DECODER) += amrnbdec.o celp_filters.o \ + acelp_filters.o \ + acelp_vectors.o \ + acelp_pitch_delay.o +OBJS-$(CONFIG_AMRWB_DECODER) += amrwbdec.o celp_filters.o \ + acelp_filters.o \ + acelp_vectors.o \ + acelp_pitch_delay.o +OBJS-$(CONFIG_AMRNB_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_AMRWB_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_AMV_ENCODER) += mjpegenc.o mjpegenc_common.o +OBJS-$(CONFIG_ANM_DECODER) += anm.o +OBJS-$(CONFIG_ANULL_DECODER) += null.o +OBJS-$(CONFIG_ANULL_ENCODER) += null.o +OBJS-$(CONFIG_ANSI_DECODER) += ansi.o cga_data.o +OBJS-$(CONFIG_APAC_DECODER) += apac.o +OBJS-$(CONFIG_APE_DECODER) += apedec.o +OBJS-$(CONFIG_APTX_DECODER) += aptxdec.o aptx.o +OBJS-$(CONFIG_APTX_ENCODER) += aptxenc.o aptx.o +OBJS-$(CONFIG_APTX_HD_DECODER) += aptxdec.o aptx.o +OBJS-$(CONFIG_APTX_HD_ENCODER) += aptxenc.o aptx.o +OBJS-$(CONFIG_APNG_DECODER) += png.o pngdec.o pngdsp.o +OBJS-$(CONFIG_APNG_ENCODER) += png.o pngenc.o +OBJS-$(CONFIG_APV_DECODER) += apv_decode.o apv_entropy.o apv_dsp.o +OBJS-$(CONFIG_ARBC_DECODER) += arbc.o +OBJS-$(CONFIG_ARGO_DECODER) += argo.o +OBJS-$(CONFIG_SSA_DECODER) += assdec.o ass.o +OBJS-$(CONFIG_SSA_ENCODER) += assenc.o ass.o +OBJS-$(CONFIG_ASS_DECODER) += assdec.o ass.o +OBJS-$(CONFIG_ASS_ENCODER) += assenc.o ass.o +OBJS-$(CONFIG_ASV1_DECODER) += asvdec.o asv.o mpeg12data.o +OBJS-$(CONFIG_ASV1_ENCODER) += asvenc.o asv.o mpeg12data.o +OBJS-$(CONFIG_ASV2_DECODER) += asvdec.o asv.o mpeg12data.o +OBJS-$(CONFIG_ASV2_ENCODER) += asvenc.o asv.o mpeg12data.o +OBJS-$(CONFIG_ATRAC1_DECODER) += atrac1.o atrac.o +OBJS-$(CONFIG_ATRAC3_DECODER) += atrac3.o atrac.o +OBJS-$(CONFIG_ATRAC3AL_DECODER) += atrac3.o atrac.o +OBJS-$(CONFIG_ATRAC3P_DECODER) += atrac3plusdec.o atrac3plus.o \ + atrac3plusdsp.o atrac.o +OBJS-$(CONFIG_ATRAC3PAL_DECODER) += atrac3plusdec.o atrac3plus.o \ + atrac3plusdsp.o atrac.o +OBJS-$(CONFIG_ATRAC9_DECODER) += atrac9dec.o +OBJS-$(CONFIG_AURA_DECODER) += cyuv.o +OBJS-$(CONFIG_AURA2_DECODER) += aura.o +OBJS-$(CONFIG_AV1_DECODER) += av1dec.o av1_parse.o +OBJS-$(CONFIG_AV1_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_AV1_D3D12VA_ENCODER) += d3d12va_encode_av1.o av1_levels.o +OBJS-$(CONFIG_AV1_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_AV1_MEDIACODEC_ENCODER) += mediacodecenc.o +OBJS-$(CONFIG_AV1_NVENC_ENCODER) += nvenc_av1.o nvenc.o +OBJS-$(CONFIG_AV1_QSV_ENCODER) += qsvenc_av1.o +OBJS-$(CONFIG_AV1_VAAPI_ENCODER) += vaapi_encode_av1.o av1_levels.o +OBJS-$(CONFIG_AV1_VULKAN_ENCODER) += vulkan_encode.o vulkan_encode_av1.o \ + hw_base_encode.o av1_levels.o +OBJS-$(CONFIG_AVRN_DECODER) += avrndec.o +OBJS-$(CONFIG_AVRP_DECODER) += r210dec.o +OBJS-$(CONFIG_AVRP_ENCODER) += r210enc.o +OBJS-$(CONFIG_AVS_DECODER) += avs.o +OBJS-$(CONFIG_AVUI_DECODER) += avuidec.o +OBJS-$(CONFIG_AVUI_ENCODER) += avuienc.o +OBJS-$(CONFIG_BETHSOFTVID_DECODER) += bethsoftvideo.o +OBJS-$(CONFIG_BFI_DECODER) += bfi.o +OBJS-$(CONFIG_BINK_DECODER) += bink.o binkdsp.o +OBJS-$(CONFIG_BINKAUDIO_DCT_DECODER) += binkaudio.o +OBJS-$(CONFIG_BINKAUDIO_RDFT_DECODER) += binkaudio.o +OBJS-$(CONFIG_BINTEXT_DECODER) += bintext.o cga_data.o +OBJS-$(CONFIG_BITPACKED_DECODER) += bitpacked_dec.o +OBJS-$(CONFIG_BITPACKED_ENCODER) += bitpacked_enc.o +OBJS-$(CONFIG_BMP_DECODER) += bmp.o msrledec.o +OBJS-$(CONFIG_BMP_ENCODER) += bmpenc.o +OBJS-$(CONFIG_BMV_AUDIO_DECODER) += bmvaudio.o +OBJS-$(CONFIG_BMV_VIDEO_DECODER) += bmvvideo.o +OBJS-$(CONFIG_BONK_DECODER) += bonk.o +OBJS-$(CONFIG_BRENDER_PIX_DECODER) += brenderpix.o +OBJS-$(CONFIG_C93_DECODER) += c93.o +OBJS-$(CONFIG_CAVS_DECODER) += cavs.o cavsdec.o cavsdsp.o \ + cavsdata.o +OBJS-$(CONFIG_CBD2_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_CCAPTION_DECODER) += ccaption_dec.o ass.o +OBJS-$(CONFIG_CDGRAPHICS_DECODER) += cdgraphics.o +OBJS-$(CONFIG_CDTOONS_DECODER) += cdtoons.o +OBJS-$(CONFIG_CDXL_DECODER) += cdxl.o +OBJS-$(CONFIG_CFHD_DECODER) += cfhd.o cfhddata.o cfhddsp.o +OBJS-$(CONFIG_CFHD_ENCODER) += cfhdenc.o cfhddata.o cfhdencdsp.o +OBJS-$(CONFIG_CINEPAK_DECODER) += cinepak.o +OBJS-$(CONFIG_CINEPAK_ENCODER) += cinepakenc.o elbg.o +OBJS-$(CONFIG_CLEARVIDEO_DECODER) += clearvideo.o +OBJS-$(CONFIG_CLJR_DECODER) += cljrdec.o +OBJS-$(CONFIG_CLJR_ENCODER) += cljrenc.o +OBJS-$(CONFIG_CLLC_DECODER) += cllc.o canopus.o +OBJS-$(CONFIG_COMFORTNOISE_DECODER) += cngdec.o celp_filters.o +OBJS-$(CONFIG_COMFORTNOISE_ENCODER) += cngenc.o +OBJS-$(CONFIG_COOK_DECODER) += cook.o +OBJS-$(CONFIG_CPIA_DECODER) += cpia.o +OBJS-$(CONFIG_CRI_DECODER) += cri.o +OBJS-$(CONFIG_CSCD_DECODER) += cscd.o +OBJS-$(CONFIG_CYUV_DECODER) += cyuv.o +OBJS-$(CONFIG_DCA_DECODER) += dcadec.o dca.o dcadata.o dcahuff.o \ + dca_core.o dca_exss.o dca_xll.o dca_lbr.o \ + dcadsp.o dcadct.o dca_sample_rate_tab.o \ + synth_filter.o +OBJS-$(CONFIG_DCA_ENCODER) += dcaenc.o dcadata.o dcahuff.o \ + dcaadpcm.o +OBJS-$(CONFIG_DDS_DECODER) += dds.o +OBJS-$(CONFIG_DERF_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_DIRAC_DECODER) += diracdec.o dirac.o diracdsp.o diractab.o \ + dirac_arith.o dirac_dwt.o dirac_vlc.o +OBJS-$(CONFIG_DFA_DECODER) += dfa.o +OBJS-$(CONFIG_DFPWM_DECODER) += dfpwmdec.o +OBJS-$(CONFIG_DFPWM_ENCODER) += dfpwmenc.o +OBJS-$(CONFIG_DNXHD_DECODER) += dnxhddec.o dnxhddata.o +OBJS-$(CONFIG_DNXHD_ENCODER) += dnxhdenc.o dnxhddata.o +OBJS-$(CONFIG_DOLBY_E_DECODER) += dolby_e.o dolby_e_parse.o kbdwin.o +OBJS-$(CONFIG_DPX_DECODER) += dpx.o +OBJS-$(CONFIG_DPX_ENCODER) += dpxenc.o +OBJS-$(CONFIG_DSD_LSBF_DECODER) += dsddec.o dsd.o +OBJS-$(CONFIG_DSD_MSBF_DECODER) += dsddec.o dsd.o +OBJS-$(CONFIG_DSD_LSBF_PLANAR_DECODER) += dsddec.o dsd.o +OBJS-$(CONFIG_DSD_MSBF_PLANAR_DECODER) += dsddec.o dsd.o +OBJS-$(CONFIG_DSICINAUDIO_DECODER) += dsicinaudio.o +OBJS-$(CONFIG_DSICINVIDEO_DECODER) += dsicinvideo.o +OBJS-$(CONFIG_DSS_SP_DECODER) += dss_sp.o +OBJS-$(CONFIG_DST_DECODER) += dstdec.o dsd.o +OBJS-$(CONFIG_DVBSUB_DECODER) += dvbsubdec.o +OBJS-$(CONFIG_DVBSUB_ENCODER) += dvbsubenc.o +OBJS-$(CONFIG_DVDSUB_DECODER) += dvdsubdec.o dvdsub.o +OBJS-$(CONFIG_DVDSUB_ENCODER) += dvdsubenc.o dvdsub.o +OBJS-$(CONFIG_DVAUDIO_DECODER) += dvaudiodec.o +OBJS-$(CONFIG_DVVIDEO_DECODER) += dvdec.o dv.o dvdata.o +OBJS-$(CONFIG_DVVIDEO_ENCODER) += dvenc.o dv.o dvdata.o +OBJS-$(CONFIG_DXA_DECODER) += dxa.o +OBJS-$(CONFIG_DXTORY_DECODER) += dxtory.o +OBJS-$(CONFIG_DXV_DECODER) += dxv.o +OBJS-$(CONFIG_DXV_ENCODER) += dxvenc.o hashtable.o +OBJS-$(CONFIG_EAC3_DECODER) += eac3_data.o +OBJS-$(CONFIG_EAC3_ENCODER) += eac3enc.o eac3_data.o +OBJS-$(CONFIG_EACMV_DECODER) += eacmv.o +OBJS-$(CONFIG_EAMAD_DECODER) += eamad.o eaidct.o mpeg12.o \ + mpeg12data.o +OBJS-$(CONFIG_EATGQ_DECODER) += eatgq.o eaidct.o +OBJS-$(CONFIG_EATGV_DECODER) += eatgv.o +OBJS-$(CONFIG_EATQI_DECODER) += eatqi.o eaidct.o mpeg12.o \ + mpeg12data.o +OBJS-$(CONFIG_EIGHTBPS_DECODER) += 8bps.o +OBJS-$(CONFIG_EIGHTSVX_EXP_DECODER) += 8svx.o +OBJS-$(CONFIG_EIGHTSVX_FIB_DECODER) += 8svx.o +OBJS-$(CONFIG_ESCAPE124_DECODER) += escape124.o +OBJS-$(CONFIG_ESCAPE130_DECODER) += escape130.o +OBJS-$(CONFIG_EVRC_DECODER) += evrcdec.o acelp_vectors.o lsp.o +OBJS-$(CONFIG_EXR_DECODER) += exr.o exrdsp.o +OBJS-$(CONFIG_EXR_ENCODER) += exrenc.o +OBJS-$(CONFIG_FASTAUDIO_DECODER) += fastaudio.o +OBJS-$(CONFIG_FFV1_DECODER) += ffv1dec.o ffv1_parse.o ffv1.o +OBJS-$(CONFIG_FFV1_ENCODER) += ffv1enc.o ffv1_parse.o ffv1.o +OBJS-$(CONFIG_FFV1_VULKAN_ENCODER) += ffv1enc.o ffv1.o ffv1_vulkan.o ffv1enc_vulkan.o +OBJS-$(CONFIG_FFWAVESYNTH_DECODER) += ffwavesynth.o +OBJS-$(CONFIG_FIC_DECODER) += fic.o +OBJS-$(CONFIG_FITS_DECODER) += fitsdec.o fits.o +OBJS-$(CONFIG_FITS_ENCODER) += fitsenc.o +OBJS-$(CONFIG_FLAC_DECODER) += flacdec.o flacdata.o flacdsp.o flac.o +OBJS-$(CONFIG_FLAC_ENCODER) += flacenc.o flacdata.o flacencdsp.o +OBJS-$(CONFIG_FLASHSV_DECODER) += flashsv.o +OBJS-$(CONFIG_FLASHSV_ENCODER) += flashsvenc.o +OBJS-$(CONFIG_FLASHSV2_ENCODER) += flashsv2enc.o +OBJS-$(CONFIG_FLASHSV2_DECODER) += flashsv.o +OBJS-$(CONFIG_FLIC_DECODER) += flicvideo.o +OBJS-$(CONFIG_FLV_DECODER) += flvdec.o +OBJS-$(CONFIG_FLV_ENCODER) += flvenc.o +OBJS-$(CONFIG_FMVC_DECODER) += fmvc.o +OBJS-$(CONFIG_FOURXM_DECODER) += 4xm.o +OBJS-$(CONFIG_FRAPS_DECODER) += fraps.o +OBJS-$(CONFIG_FRWU_DECODER) += frwu.o +OBJS-$(CONFIG_FTR_DECODER) += ftr.o +OBJS-$(CONFIG_G2M_DECODER) += g2meet.o elsdec.o mjpegdec_common.o +OBJS-$(CONFIG_G723_1_DECODER) += g723_1dec.o g723_1.o \ + acelp_vectors.o celp_filters.o +OBJS-$(CONFIG_G723_1_ENCODER) += g723_1enc.o g723_1.o \ + acelp_vectors.o celp_filters.o +OBJS-$(CONFIG_G728_DECODER) += g728dec.o celp_filters.o +OBJS-$(CONFIG_G729_DECODER) += g729dec.o lsp.o celp_filters.o acelp_filters.o acelp_pitch_delay.o acelp_vectors.o g729postfilter.o +OBJS-$(CONFIG_GDV_DECODER) += gdv.o +OBJS-$(CONFIG_GEM_DECODER) += gemdec.o +OBJS-$(CONFIG_GIF_DECODER) += gifdec.o lzw.o +OBJS-$(CONFIG_GIF_ENCODER) += gif.o lzwenc.o +OBJS-$(CONFIG_GREMLIN_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_GSM_DECODER) += gsmdec.o gsmdec_data.o msgsmdec.o +OBJS-$(CONFIG_GSM_MS_DECODER) += gsmdec.o gsmdec_data.o msgsmdec.o +OBJS-$(CONFIG_H261_DECODER) += h261dec.o h261data.o h261.o +OBJS-$(CONFIG_H261_ENCODER) += h261enc.o h261data.o h261.o +OBJS-$(CONFIG_H263_DECODER) += h263dec.o h263.o ituh263dec.o \ + mpeg4video.o mpeg4videodec.o \ + h263data.o +OBJS-$(CONFIG_H263I_DECODER) += intelh263dec.o +OBJS-$(CONFIG_H263_ENCODER) += mpeg4video.o \ + h263.o ituh263enc.o h263data.o +OBJS-$(CONFIG_H263_V4L2M2M_DECODER) += v4l2_m2m_dec.o +OBJS-$(CONFIG_H263_V4L2M2M_ENCODER) += v4l2_m2m_enc.o +OBJS-$(CONFIG_H264_DECODER) += h264dec.o h264_cabac.o h264_cavlc.o \ + h264_direct.o h264_loopfilter.o \ + h264_mb.o h264_picture.o \ + h264_refs.o \ + h264_slice.o h264data.o h274.o +OBJS-$(CONFIG_H264_AMF_ENCODER) += amfenc_h264.o +OBJS-$(CONFIG_H264_AMF_DECODER) += amfdec.o +OBJS-$(CONFIG_H264_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_H264_D3D12VA_ENCODER) += d3d12va_encode_h264.o h264_levels.o \ + h2645data.o hw_base_encode_h264.o +OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_H264_MEDIACODEC_ENCODER) += mediacodecenc.o +OBJS-$(CONFIG_H264_MF_ENCODER) += mfenc.o mf_utils.o +OBJS-$(CONFIG_H264_MMAL_DECODER) += mmaldec.o +OBJS-$(CONFIG_H264_NVENC_ENCODER) += nvenc_h264.o nvenc.o +OBJS-$(CONFIG_H264_OH_DECODER) += ohcodec.o ohdec.o +OBJS-$(CONFIG_H264_OH_ENCODER) += ohcodec.o ohenc.o +OBJS-$(CONFIG_H264_QSV_DECODER) += qsvdec.o +OBJS-$(CONFIG_H264_QSV_ENCODER) += qsvenc_h264.o +OBJS-$(CONFIG_H264_RKMPP_DECODER) += rkmppdec.o +OBJS-$(CONFIG_H264_RKMPP_ENCODER) += rkmppenc.o +OBJS-$(CONFIG_H264_VAAPI_ENCODER) += vaapi_encode_h264.o h264_levels.o \ + h2645data.o hw_base_encode_h264.o +OBJS-$(CONFIG_H264_VULKAN_ENCODER) += vulkan_encode.o vulkan_encode_h264.o \ + hw_base_encode.o hw_base_encode_h264.o \ + h264_levels.o h2645data.o +OBJS-$(CONFIG_H264_VIDEOTOOLBOX_ENCODER) += videotoolboxenc.o +OBJS-$(CONFIG_H264_V4L2M2M_DECODER) += v4l2_m2m_dec.o +OBJS-$(CONFIG_H264_V4L2M2M_ENCODER) += v4l2_m2m_enc.o +OBJS-$(CONFIG_HAP_DECODER) += hapdec.o hap.o +OBJS-$(CONFIG_HAP_ENCODER) += hapenc.o hap.o +OBJS-$(CONFIG_HCA_DECODER) += hcadec.o +OBJS-$(CONFIG_HCOM_DECODER) += hcom.o +OBJS-$(CONFIG_HDR_DECODER) += hdrdec.o +OBJS-$(CONFIG_HDR_ENCODER) += hdrenc.o +OBJS-$(CONFIG_HEVC_DECODER) += h274.o +OBJS-$(CONFIG_HEVC_AMF_ENCODER) += amfenc_hevc.o +OBJS-$(CONFIG_HEVC_AMF_DECODER) += amfdec.o +OBJS-$(CONFIG_HEVC_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_HEVC_D3D12VA_ENCODER) += d3d12va_encode_hevc.o h265_profile_level.o \ + h2645data.o hw_base_encode_h265.o +OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_HEVC_MEDIACODEC_ENCODER) += mediacodecenc.o +OBJS-$(CONFIG_HEVC_MF_ENCODER) += mfenc.o mf_utils.o +OBJS-$(CONFIG_HEVC_NVENC_ENCODER) += nvenc_hevc.o nvenc.o +OBJS-$(CONFIG_HEVC_OH_DECODER) += ohcodec.o ohdec.o +OBJS-$(CONFIG_HEVC_OH_ENCODER) += ohcodec.o ohenc.o +OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec.o +OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc/ps_enc.o +OBJS-$(CONFIG_HEVC_RKMPP_DECODER) += rkmppdec.o +OBJS-$(CONFIG_HEVC_RKMPP_ENCODER) += rkmppenc.o +OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += vaapi_encode_h265.o h265_profile_level.o \ + h2645data.o hw_base_encode_h265.o +OBJS-$(CONFIG_HEVC_VULKAN_ENCODER) += vulkan_encode.o vulkan_encode_h265.o \ + hw_base_encode.o hw_base_encode_h265.o \ + h265_profile_level.o h2645data.o +OBJS-$(CONFIG_HEVC_V4L2M2M_DECODER) += v4l2_m2m_dec.o +OBJS-$(CONFIG_HEVC_V4L2M2M_ENCODER) += v4l2_m2m_enc.o +OBJS-$(CONFIG_HEVC_VIDEOTOOLBOX_ENCODER) += videotoolboxenc.o +OBJS-$(CONFIG_HNM4_VIDEO_DECODER) += hnm4video.o +OBJS-$(CONFIG_HQ_HQA_DECODER) += hq_hqa.o hq_hqadsp.o hq_common.o canopus.o +OBJS-$(CONFIG_HQX_DECODER) += hqx.o hqxdsp.o hq_common.o canopus.o +OBJS-$(CONFIG_HUFFYUV_DECODER) += huffyuv.o huffyuvdec.o +OBJS-$(CONFIG_HUFFYUV_ENCODER) += huffyuv.o huffyuvenc.o +OBJS-$(CONFIG_HYMT_DECODER) += huffyuv.o huffyuvdec.o +OBJS-$(CONFIG_IDCIN_DECODER) += idcinvideo.o +OBJS-$(CONFIG_IDF_DECODER) += bintext.o cga_data.o +OBJS-$(CONFIG_IFF_ILBM_DECODER) += iff.o +OBJS-$(CONFIG_ILBC_DECODER) += ilbcdec.o +OBJS-$(CONFIG_IMC_DECODER) += imc.o +OBJS-$(CONFIG_IMM4_DECODER) += imm4.o +OBJS-$(CONFIG_IMM5_DECODER) += imm5.o +OBJS-$(CONFIG_INDEO2_DECODER) += indeo2.o +OBJS-$(CONFIG_INDEO3_DECODER) += indeo3.o +OBJS-$(CONFIG_INDEO4_DECODER) += indeo4.o ivi.o +OBJS-$(CONFIG_INDEO5_DECODER) += indeo5.o ivi.o +OBJS-$(CONFIG_INTERPLAY_ACM_DECODER) += interplayacm.o +OBJS-$(CONFIG_INTERPLAY_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_INTERPLAY_VIDEO_DECODER) += interplayvideo.o +OBJS-$(CONFIG_IPU_DECODER) += mpeg12dec.o mpeg12.o mpeg12data.o +OBJS-$(CONFIG_JACOSUB_DECODER) += jacosubdec.o ass.o +OBJS-$(CONFIG_JPEG2000_ENCODER) += j2kenc.o mqcenc.o mqc.o jpeg2000.o \ + jpeg2000dwt.o +OBJS-$(CONFIG_JPEG2000_DECODER) += jpeg2000dec.o jpeg2000.o jpeg2000dsp.o \ + jpeg2000dwt.o mqcdec.o mqc.o jpeg2000htdec.o +OBJS-$(CONFIG_JPEGLS_DECODER) += jpeglsdec.o jpegls.o +OBJS-$(CONFIG_JPEGLS_ENCODER) += jpeglsenc.o jpegls.o +OBJS-$(CONFIG_JV_DECODER) += jvdec.o +OBJS-$(CONFIG_KGV1_DECODER) += kgv1dec.o +OBJS-$(CONFIG_KMVC_DECODER) += kmvc.o +OBJS-$(CONFIG_LAGARITH_DECODER) += lagarith.o lagarithrac.o +OBJS-$(CONFIG_LEAD_DECODER) += leaddec.o jpegquanttables.o +OBJS-$(CONFIG_LJPEG_ENCODER) += ljpegenc.o mjpegenc_common.o +OBJS-$(CONFIG_LOCO_DECODER) += loco.o +OBJS-$(CONFIG_LSCR_DECODER) += lscrdec.o png.o pngdec.o pngdsp.o +OBJS-$(CONFIG_M101_DECODER) += m101.o +OBJS-$(CONFIG_MACE3_DECODER) += mace.o +OBJS-$(CONFIG_MACE6_DECODER) += mace.o +OBJS-$(CONFIG_MAGICYUV_DECODER) += magicyuv.o +OBJS-$(CONFIG_MAGICYUV_ENCODER) += magicyuvenc.o +OBJS-$(CONFIG_MDEC_DECODER) += mdec.o mpeg12.o mpeg12data.o +OBJS-$(CONFIG_MEDIA100_DECODER) += mjpegbdec.o +OBJS-$(CONFIG_METASOUND_DECODER) += metasound.o twinvq.o +OBJS-$(CONFIG_MICRODVD_DECODER) += microdvddec.o ass.o +OBJS-$(CONFIG_MIMIC_DECODER) += mimic.o +OBJS-$(CONFIG_MISC4_DECODER) += misc4.o +OBJS-$(CONFIG_MJPEG_DECODER) += mjpegdec.o mjpegdec_common.o +OBJS-$(CONFIG_MJPEG_QSV_DECODER) += qsvdec.o +OBJS-$(CONFIG_MJPEG_ENCODER) += mjpegenc.o mjpegenc_common.o \ + mjpegenc_huffman.o +OBJS-$(CONFIG_MJPEGB_DECODER) += mjpegbdec.o +OBJS-$(CONFIG_MJPEG_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_MJPEG_QSV_ENCODER) += qsvenc_jpeg.o +OBJS-$(CONFIG_MJPEG_VAAPI_ENCODER) += vaapi_encode_mjpeg.o +OBJS-$(CONFIG_MLP_DECODER) += mlpdec.o mlpdsp.o +OBJS-$(CONFIG_MLP_ENCODER) += mlpenc.o mlp.o +OBJS-$(CONFIG_MMVIDEO_DECODER) += mmvideo.o +OBJS-$(CONFIG_MOBICLIP_DECODER) += mobiclip.o +OBJS-$(CONFIG_MOTIONPIXELS_DECODER) += motionpixels.o +OBJS-$(CONFIG_MOVTEXT_DECODER) += movtextdec.o ass.o +OBJS-$(CONFIG_MOVTEXT_ENCODER) += movtextenc.o ass_split.o +OBJS-$(CONFIG_MP1_DECODER) += mpegaudiodec_fixed.o +OBJS-$(CONFIG_MP1FLOAT_DECODER) += mpegaudiodec_float.o +OBJS-$(CONFIG_MP2_DECODER) += mpegaudiodec_fixed.o +OBJS-$(CONFIG_MP2_ENCODER) += mpegaudioenc.o mpegaudio.o \ + mpegaudiodata.o mpegaudiodsp_data.o \ + mpegaudiotabs.o +OBJS-$(CONFIG_MP2FIXED_ENCODER) += mpegaudioenc.o mpegaudio.o \ + mpegaudiodata.o mpegaudiodsp_data.o \ + mpegaudiotabs.o +OBJS-$(CONFIG_MP2FLOAT_DECODER) += mpegaudiodec_float.o +OBJS-$(CONFIG_MP3_DECODER) += mpegaudiodec_fixed.o +OBJS-$(CONFIG_MP3_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_MP3_MF_ENCODER) += mfenc.o mf_utils.o +OBJS-$(CONFIG_MP3ADU_DECODER) += mpegaudiodec_fixed.o +OBJS-$(CONFIG_MP3ADUFLOAT_DECODER) += mpegaudiodec_float.o +OBJS-$(CONFIG_MP3FLOAT_DECODER) += mpegaudiodec_float.o +OBJS-$(CONFIG_MP3ON4_DECODER) += mpegaudiodec_fixed.o +OBJS-$(CONFIG_MP3ON4FLOAT_DECODER) += mpegaudiodec_float.o +OBJS-$(CONFIG_MPC7_DECODER) += mpc7.o mpc.o +OBJS-$(CONFIG_MPC8_DECODER) += mpc8.o mpc.o +OBJS-$(CONFIG_MPEGVIDEO_DECODER) += mpeg12dec.o mpeg12.o mpeg12data.o +OBJS-$(CONFIG_MPEG1VIDEO_DECODER) += mpeg12dec.o mpeg12.o mpeg12data.o +OBJS-$(CONFIG_MPEG1VIDEO_ENCODER) += mpeg12enc.o +OBJS-$(CONFIG_MPEG1_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_MPEG1_V4L2M2M_DECODER) += v4l2_m2m_dec.o +OBJS-$(CONFIG_MPEG2_MMAL_DECODER) += mmaldec.o +OBJS-$(CONFIG_MPEG2_QSV_DECODER) += qsvdec.o +OBJS-$(CONFIG_MPEG2_QSV_ENCODER) += qsvenc_mpeg2.o +OBJS-$(CONFIG_MPEG2VIDEO_DECODER) += mpeg12dec.o mpeg12.o mpeg12data.o +OBJS-$(CONFIG_MPEG2VIDEO_ENCODER) += mpeg12enc.o +OBJS-$(CONFIG_MPEG2_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_MPEG2_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_MPEG2_VAAPI_ENCODER) += vaapi_encode_mpeg2.o +OBJS-$(CONFIG_MPEG2_V4L2M2M_DECODER) += v4l2_m2m_dec.o +OBJS-$(CONFIG_MPEG4_DECODER) += mpeg4videodec.o mpeg4videodsp.o \ + xvididct.o +OBJS-$(CONFIG_MPEG4_ENCODER) += mpeg4videoenc.o +OBJS-$(CONFIG_MPEG4_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_MPEG4_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_MPEG4_MEDIACODEC_ENCODER) += mediacodecenc.o +OBJS-$(CONFIG_MPEG4_V4L2M2M_DECODER) += v4l2_m2m_dec.o +OBJS-$(CONFIG_MPEG4_V4L2M2M_ENCODER) += v4l2_m2m_enc.o +OBJS-$(CONFIG_MPL2_DECODER) += mpl2dec.o ass.o +OBJS-$(CONFIG_MSA1_DECODER) += mss3.o +OBJS-$(CONFIG_MSCC_DECODER) += mscc.o +OBJS-$(CONFIG_MSNSIREN_DECODER) += siren.o +OBJS-$(CONFIG_MSP2_DECODER) += msp2dec.o +OBJS-$(CONFIG_MSRLE_ENCODER) += msrleenc.o +OBJS-$(CONFIG_MSRLE_DECODER) += msrle.o msrledec.o +OBJS-$(CONFIG_MSS1_DECODER) += mss1.o mss12.o +OBJS-$(CONFIG_MSS2_DECODER) += mss2.o mss12.o mss2dsp.o wmv2data.o +OBJS-$(CONFIG_MSVIDEO1_DECODER) += msvideo1.o +OBJS-$(CONFIG_MSVIDEO1_ENCODER) += msvideo1enc.o elbg.o +OBJS-$(CONFIG_MSZH_DECODER) += lcldec.o +OBJS-$(CONFIG_MTS2_DECODER) += mss4.o +OBJS-$(CONFIG_MV30_DECODER) += mv30.o +OBJS-$(CONFIG_MVC1_DECODER) += mvcdec.o +OBJS-$(CONFIG_MVC2_DECODER) += mvcdec.o +OBJS-$(CONFIG_MVDV_DECODER) += midivid.o +OBJS-$(CONFIG_MVHA_DECODER) += mvha.o +OBJS-$(CONFIG_MWSC_DECODER) += mwsc.o +OBJS-$(CONFIG_MXPEG_DECODER) += mxpegdec.o +OBJS-$(CONFIG_NELLYMOSER_DECODER) += nellymoserdec.o nellymoser.o +OBJS-$(CONFIG_NELLYMOSER_ENCODER) += nellymoserenc.o nellymoser.o +OBJS-$(CONFIG_NOTCHLC_DECODER) += notchlc.o +OBJS-$(CONFIG_NUV_DECODER) += nuv.o rtjpeg.o jpegquanttables.o +OBJS-$(CONFIG_ON2AVC_DECODER) += on2avc.o on2avcdata.o +OBJS-$(CONFIG_OPUS_DECODER) += vorbis_data.o +OBJS-$(CONFIG_OSQ_DECODER) += osq.o +OBJS-$(CONFIG_PAF_AUDIO_DECODER) += pafaudio.o +OBJS-$(CONFIG_PAF_VIDEO_DECODER) += pafvideo.o +OBJS-$(CONFIG_PAM_DECODER) += pnmdec.o pnm.o +OBJS-$(CONFIG_PAM_ENCODER) += pamenc.o +OBJS-$(CONFIG_PBM_DECODER) += pnmdec.o pnm.o +OBJS-$(CONFIG_PBM_ENCODER) += pnmenc.o +OBJS-$(CONFIG_PCX_DECODER) += pcx.o +OBJS-$(CONFIG_PCX_ENCODER) += pcxenc.o +OBJS-$(CONFIG_PDV_DECODER) += pdvdec.o +OBJS-$(CONFIG_PDV_ENCODER) += pdvenc.o +OBJS-$(CONFIG_PFM_DECODER) += pnmdec.o pnm.o +OBJS-$(CONFIG_PFM_ENCODER) += pnmenc.o +OBJS-$(CONFIG_PGM_DECODER) += pnmdec.o pnm.o +OBJS-$(CONFIG_PGM_ENCODER) += pnmenc.o +OBJS-$(CONFIG_PGMYUV_DECODER) += pnmdec.o pnm.o +OBJS-$(CONFIG_PGMYUV_ENCODER) += pnmenc.o +OBJS-$(CONFIG_PGSSUB_DECODER) += pgssubdec.o +OBJS-$(CONFIG_PGX_DECODER) += pgxdec.o +OBJS-$(CONFIG_PHM_DECODER) += pnmdec.o pnm.o +OBJS-$(CONFIG_PHM_ENCODER) += pnmenc.o +OBJS-$(CONFIG_PHOTOCD_DECODER) += photocd.o +OBJS-$(CONFIG_PICTOR_DECODER) += pictordec.o cga_data.o +OBJS-$(CONFIG_PIXLET_DECODER) += pixlet.o +OBJS-$(CONFIG_PJS_DECODER) += textdec.o ass.o +OBJS-$(CONFIG_PNG_DECODER) += png.o pngdec.o pngdsp.o +OBJS-$(CONFIG_PNG_ENCODER) += png.o pngenc.o +OBJS-$(CONFIG_PPM_DECODER) += pnmdec.o pnm.o +OBJS-$(CONFIG_PPM_ENCODER) += pnmenc.o +OBJS-$(CONFIG_PRORES_DECODER) += proresdec.o proresdsp.o proresdata.o +OBJS-$(CONFIG_PRORES_ENCODER) += proresenc_anatoliy.o proresdata.o +OBJS-$(CONFIG_PRORES_AW_ENCODER) += proresenc_anatoliy.o proresdata.o +OBJS-$(CONFIG_PRORES_KS_ENCODER) += proresenc_kostya.o proresdata.o proresenc_kostya_common.o +OBJS-$(CONFIG_PRORES_KS_VULKAN_ENCODER) += proresenc_kostya_vulkan.o proresdata.o proresenc_kostya_common.o +OBJS-$(CONFIG_PRORES_RAW_DECODER) += prores_raw.o proresdsp.o proresdata.o +OBJS-$(CONFIG_PRORES_VIDEOTOOLBOX_ENCODER) += videotoolboxenc.o +OBJS-$(CONFIG_PROSUMER_DECODER) += prosumer.o +OBJS-$(CONFIG_PSD_DECODER) += psd.o +OBJS-$(CONFIG_PTX_DECODER) += ptx.o +OBJS-$(CONFIG_QCELP_DECODER) += qcelpdec.o \ + celp_filters.o acelp_vectors.o \ + acelp_filters.o +OBJS-$(CONFIG_QDM2_DECODER) += qdm2.o +OBJS-$(CONFIG_QDMC_DECODER) += qdmc.o +OBJS-$(CONFIG_QDRAW_DECODER) += qdrw.o +OBJS-$(CONFIG_QOA_DECODER) += qoadec.o +OBJS-$(CONFIG_QOI_DECODER) += qoidec.o +OBJS-$(CONFIG_QOI_ENCODER) += qoienc.o +OBJS-$(CONFIG_QPEG_DECODER) += qpeg.o +OBJS-$(CONFIG_QTRLE_DECODER) += qtrle.o +OBJS-$(CONFIG_QTRLE_ENCODER) += qtrleenc.o +OBJS-$(CONFIG_R10K_DECODER) += r210dec.o +OBJS-$(CONFIG_R10K_ENCODER) += r210enc.o +OBJS-$(CONFIG_R210_DECODER) += r210dec.o +OBJS-$(CONFIG_R210_ENCODER) += r210enc.o +OBJS-$(CONFIG_RA_144_DECODER) += ra144dec.o ra144.o celp_filters.o +OBJS-$(CONFIG_RA_144_ENCODER) += ra144enc.o ra144.o celp_filters.o +OBJS-$(CONFIG_RA_288_DECODER) += ra288.o celp_filters.o +OBJS-$(CONFIG_RALF_DECODER) += ralf.o +OBJS-$(CONFIG_RASC_DECODER) += rasc.o +OBJS-$(CONFIG_RAWVIDEO_DECODER) += rawdec.o +OBJS-$(CONFIG_RAWVIDEO_ENCODER) += rawenc.o +OBJS-$(CONFIG_REALTEXT_DECODER) += realtextdec.o ass.o +OBJS-$(CONFIG_RKA_DECODER) += rka.o +OBJS-$(CONFIG_RL2_DECODER) += rl2.o +OBJS-$(CONFIG_ROQ_DECODER) += roqvideodec.o roqvideo.o +OBJS-$(CONFIG_ROQ_ENCODER) += roqvideoenc.o roqvideo.o elbg.o +OBJS-$(CONFIG_ROQ_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_ROQ_DPCM_ENCODER) += roqaudioenc.o +OBJS-$(CONFIG_RPZA_DECODER) += rpza.o +OBJS-$(CONFIG_RPZA_ENCODER) += rpzaenc.o +OBJS-$(CONFIG_RSCC_DECODER) += rscc.o +OBJS-$(CONFIG_RTV1_DECODER) += rtv1.o +OBJS-$(CONFIG_RV10_DECODER) += rv10.o +OBJS-$(CONFIG_RV10_ENCODER) += rv10enc.o +OBJS-$(CONFIG_RV20_DECODER) += rv10.o +OBJS-$(CONFIG_RV20_ENCODER) += rv20enc.o +OBJS-$(CONFIG_RV30_DECODER) += rv30.o rv34.o rv30dsp.o +OBJS-$(CONFIG_RV40_DECODER) += rv40.o rv34.o rv40dsp.o +OBJS-$(CONFIG_RV60_DECODER) += rv60dec.o rv60dsp.o +OBJS-$(CONFIG_SAMI_DECODER) += samidec.o ass.o htmlsubtitles.o +OBJS-$(CONFIG_S302M_DECODER) += s302m.o +OBJS-$(CONFIG_S302M_ENCODER) += s302menc.o +OBJS-$(CONFIG_SANM_DECODER) += sanm.o +OBJS-$(CONFIG_SCPR_DECODER) += scpr.o +OBJS-$(CONFIG_SCREENPRESSO_DECODER) += screenpresso.o +OBJS-$(CONFIG_SDX2_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_SGA_DECODER) += sga.o +OBJS-$(CONFIG_SGI_DECODER) += sgidec.o +OBJS-$(CONFIG_SGI_ENCODER) += sgienc.o rle.o +OBJS-$(CONFIG_SGIRLE_DECODER) += sgirledec.o +OBJS-$(CONFIG_SHEERVIDEO_DECODER) += sheervideo.o +OBJS-$(CONFIG_SHORTEN_DECODER) += shorten.o +OBJS-$(CONFIG_SIPR_DECODER) += sipr.o acelp_pitch_delay.o \ + acelp_vectors.o \ + acelp_filters.o celp_filters.o \ + sipr16k.o +OBJS-$(CONFIG_SIREN_DECODER) += siren.o +OBJS-$(CONFIG_SIMBIOSIS_IMX_DECODER) += imx.o +OBJS-$(CONFIG_SMACKAUD_DECODER) += smacker.o +OBJS-$(CONFIG_SMACKER_DECODER) += smacker.o +OBJS-$(CONFIG_SMC_DECODER) += smc.o +OBJS-$(CONFIG_SMC_ENCODER) += smcenc.o +OBJS-$(CONFIG_SNOW_DECODER) += snowdec.o snow.o snow_dwt.o +OBJS-$(CONFIG_SNOW_ENCODER) += snowenc.o snow.o snow_dwt.o \ + h263data.o ituh263enc.o \ + motion_est.o ratecontrol.o +OBJS-$(CONFIG_SOL_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_SPEEDHQ_DECODER) += speedhqdec.o speedhq.o mpeg12.o \ + mpeg12data.o +OBJS-$(CONFIG_SPEEDHQ_ENCODER) += speedhq.o mpeg12data.o mpeg12enc.o speedhqenc.o +OBJS-$(CONFIG_SPEEX_DECODER) += speexdec.o +OBJS-$(CONFIG_SP5X_DECODER) += sp5xdec.o +OBJS-$(CONFIG_SRGC_DECODER) += mscc.o +OBJS-$(CONFIG_SRT_DECODER) += srtdec.o ass.o htmlsubtitles.o +OBJS-$(CONFIG_SRT_ENCODER) += srtenc.o ass_split.o +OBJS-$(CONFIG_STL_DECODER) += textdec.o ass.o +OBJS-$(CONFIG_SUBRIP_DECODER) += srtdec.o ass.o htmlsubtitles.o +OBJS-$(CONFIG_SUBRIP_ENCODER) += srtenc.o ass_split.o +OBJS-$(CONFIG_SUBVIEWER1_DECODER) += textdec.o ass.o +OBJS-$(CONFIG_SUBVIEWER_DECODER) += subviewerdec.o ass.o +OBJS-$(CONFIG_SUNRAST_DECODER) += sunrast.o +OBJS-$(CONFIG_SUNRAST_ENCODER) += sunrastenc.o +OBJS-$(CONFIG_SBC_DECODER) += sbcdec.o sbc.o +OBJS-$(CONFIG_SBC_ENCODER) += sbcenc.o sbc.o sbcdsp.o +OBJS-$(CONFIG_SVQ1_DECODER) += svq1dec.o svq1.o h263data.o +OBJS-$(CONFIG_SVQ1_ENCODER) += svq1enc.o svq1.o h263data.o \ + h263.o ituh263enc.o motion_est.o +OBJS-$(CONFIG_SVQ3_DECODER) += svq3.o mpegutils.o h264data.o +OBJS-$(CONFIG_TEXT_DECODER) += textdec.o ass.o +OBJS-$(CONFIG_TEXT_ENCODER) += srtenc.o ass_split.o +OBJS-$(CONFIG_TAK_DECODER) += takdec.o tak.o takdsp.o +OBJS-$(CONFIG_TARGA_DECODER) += targa.o +OBJS-$(CONFIG_TARGA_ENCODER) += targaenc.o rle.o +OBJS-$(CONFIG_TARGA_Y216_DECODER) += targa_y216dec.o +OBJS-$(CONFIG_TDSC_DECODER) += tdsc.o +OBJS-$(CONFIG_TIERTEXSEQVIDEO_DECODER) += tiertexseqv.o +OBJS-$(CONFIG_TIFF_DECODER) += tiff.o lzw.o faxcompr.o tiff_common.o +OBJS-$(CONFIG_TIFF_ENCODER) += tiffenc.o rle.o lzwenc.o +OBJS-$(CONFIG_TMV_DECODER) += tmv.o cga_data.o +OBJS-$(CONFIG_TRUEHD_DECODER) += mlpdec.o mlpdsp.o +OBJS-$(CONFIG_TRUEHD_ENCODER) += mlpenc.o mlp.o +OBJS-$(CONFIG_TRUEMOTION1_DECODER) += truemotion1.o +OBJS-$(CONFIG_TRUEMOTION2_DECODER) += truemotion2.o +OBJS-$(CONFIG_TRUEMOTION2RT_DECODER) += truemotion2rt.o +OBJS-$(CONFIG_TRUESPEECH_DECODER) += truespeech.o +OBJS-$(CONFIG_TSCC_DECODER) += tscc.o msrledec.o +OBJS-$(CONFIG_TSCC2_DECODER) += tscc2.o +OBJS-$(CONFIG_TTA_DECODER) += tta.o ttadata.o ttadsp.o +OBJS-$(CONFIG_TTA_ENCODER) += ttaenc.o ttaencdsp.o ttadata.o +OBJS-$(CONFIG_TTML_ENCODER) += ttmlenc.o ass_split.o +OBJS-$(CONFIG_TWINVQ_DECODER) += twinvqdec.o twinvq.o +OBJS-$(CONFIG_TXD_DECODER) += txd.o +OBJS-$(CONFIG_ULTI_DECODER) += ulti.o +OBJS-$(CONFIG_UTVIDEO_DECODER) += utvideodec.o utvideodsp.o +OBJS-$(CONFIG_UTVIDEO_ENCODER) += utvideoenc.o +OBJS-$(CONFIG_V210_DECODER) += v210dec.o +OBJS-$(CONFIG_V210_ENCODER) += v210enc.o +OBJS-$(CONFIG_V210X_DECODER) += v210x.o +OBJS-$(CONFIG_V308_DECODER) += v308dec.o +OBJS-$(CONFIG_V308_ENCODER) += v308enc.o +OBJS-$(CONFIG_V408_DECODER) += v408dec.o +OBJS-$(CONFIG_V408_ENCODER) += v408enc.o +OBJS-$(CONFIG_V410_DECODER) += v410dec.o +OBJS-$(CONFIG_V410_ENCODER) += v410enc.o +OBJS-$(CONFIG_VB_DECODER) += vb.o +OBJS-$(CONFIG_VBN_DECODER) += vbndec.o +OBJS-$(CONFIG_VBN_ENCODER) += vbnenc.o +OBJS-$(CONFIG_VBLE_DECODER) += vble.o +OBJS-$(CONFIG_VC1_DECODER) += vc1dec.o vc1_block.o vc1_loopfilter.o \ + vc1_mc.o vc1_pred.o vc1.o vc1data.o \ + msmpeg4_vc1_data.o wmv2data.o +OBJS-$(CONFIG_VC1_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_VC1_MMAL_DECODER) += mmaldec.o +OBJS-$(CONFIG_VC1_QSV_DECODER) += qsvdec.o +OBJS-$(CONFIG_VC1_V4L2M2M_DECODER) += v4l2_m2m_dec.o +OBJS-$(CONFIG_VC2_ENCODER) += vc2enc.o vc2enc_dwt.o diractab.o +OBJS-$(CONFIG_VCR1_DECODER) += vcr1.o +OBJS-$(CONFIG_VMDAUDIO_DECODER) += vmdaudio.o +OBJS-$(CONFIG_VMDVIDEO_DECODER) += vmdvideo.o +OBJS-$(CONFIG_VMIX_DECODER) += vmixdec.o +OBJS-$(CONFIG_VMNC_DECODER) += vmnc.o +OBJS-$(CONFIG_VNULL_DECODER) += null.o +OBJS-$(CONFIG_VNULL_ENCODER) += null.o +OBJS-$(CONFIG_VORBIS_DECODER) += vorbisdec.o vorbisdsp.o vorbis.o \ + vorbis_data.o +OBJS-$(CONFIG_VORBIS_ENCODER) += vorbisenc.o vorbis.o \ + vorbis_data.o +OBJS-$(CONFIG_VP3_DECODER) += vp3.o jpegquanttables.o +OBJS-$(CONFIG_VP5_DECODER) += vp5.o vp56.o vp56data.o \ + vp5dsp.o vpx_rac.o +OBJS-$(CONFIG_VP6_DECODER) += vp6.o vp56.o vp56data.o \ + vp6dsp.o vpx_rac.o +OBJS-$(CONFIG_VP7_DECODER) += vp8.o vp8data.o vpx_rac.o +OBJS-$(CONFIG_VP8_DECODER) += vp8.o vp8data.o vpx_rac.o +OBJS-$(CONFIG_VP8_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_VP8_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_VP8_MEDIACODEC_ENCODER) += mediacodecenc.o +OBJS-$(CONFIG_VP8_QSV_DECODER) += qsvdec.o +OBJS-$(CONFIG_VP8_RKMPP_DECODER) += rkmppdec.o +OBJS-$(CONFIG_VP8_VAAPI_ENCODER) += vaapi_encode_vp8.o +OBJS-$(CONFIG_VP8_V4L2M2M_DECODER) += v4l2_m2m_dec.o +OBJS-$(CONFIG_VP8_V4L2M2M_ENCODER) += v4l2_m2m_enc.o +OBJS-$(CONFIG_VP9_DECODER) += vp9.o vp9data.o vp9dsp.o vp9lpf.o vp9recon.o \ + vp9block.o vp9prob.o vp9mvs.o vpx_rac.o \ + vp9dsp_8bpp.o vp9dsp_10bpp.o vp9dsp_12bpp.o +OBJS-$(CONFIG_VP9_AMF_DECODER) += amfdec.o +OBJS-$(CONFIG_VP9_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_VP9_MEDIACODEC_DECODER) += mediacodecdec.o +OBJS-$(CONFIG_VP9_MEDIACODEC_ENCODER) += mediacodecenc.o +OBJS-$(CONFIG_VP9_RKMPP_DECODER) += rkmppdec.o +OBJS-$(CONFIG_VP9_VAAPI_ENCODER) += vaapi_encode_vp9.o +OBJS-$(CONFIG_VP9_QSV_ENCODER) += qsvenc_vp9.o +OBJS-$(CONFIG_VPLAYER_DECODER) += textdec.o ass.o +OBJS-$(CONFIG_VP9_V4L2M2M_DECODER) += v4l2_m2m_dec.o +OBJS-$(CONFIG_VQA_DECODER) += vqavideo.o +OBJS-$(CONFIG_VQC_DECODER) += vqcdec.o +OBJS-$(CONFIG_VVC_DECODER) += executor.o h2645data.o +OBJS-$(CONFIG_VVC_SEI) += h2645_sei.o h274.o +OBJS-$(CONFIG_WADY_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_WAVARC_DECODER) += wavarc.o +OBJS-$(CONFIG_WAVPACK_DECODER) += wavpack.o wavpackdata.o dsd.o +OBJS-$(CONFIG_WAVPACK_ENCODER) += wavpackdata.o wavpackenc.o +OBJS-$(CONFIG_WBMP_DECODER) += wbmpdec.o +OBJS-$(CONFIG_WBMP_ENCODER) += wbmpenc.o +OBJS-$(CONFIG_WCMV_DECODER) += wcmv.o +OBJS-$(CONFIG_WEBP_DECODER) += webp.o +OBJS-$(CONFIG_WEBP_ANIM_DECODER) += webp.o +OBJS-$(CONFIG_WEBVTT_DECODER) += webvttdec.o ass.o +OBJS-$(CONFIG_WEBVTT_ENCODER) += webvttenc.o ass_split.o +OBJS-$(CONFIG_WMALOSSLESS_DECODER) += wmalosslessdec.o wma_common.o +OBJS-$(CONFIG_WMAPRO_DECODER) += wmaprodec.o wma.o wma_common.o +OBJS-$(CONFIG_WMAV1_DECODER) += wmadec.o wma.o wma_common.o aactab.o +OBJS-$(CONFIG_WMAV1_ENCODER) += wmaenc.o wma.o wma_common.o aactab.o +OBJS-$(CONFIG_WMAV2_DECODER) += wmadec.o wma.o wma_common.o aactab.o +OBJS-$(CONFIG_WMAV2_ENCODER) += wmaenc.o wma.o wma_common.o aactab.o +OBJS-$(CONFIG_WMAVOICE_DECODER) += wmavoice.o \ + celp_filters.o \ + acelp_vectors.o acelp_filters.o +OBJS-$(CONFIG_WMV2_DECODER) += wmv2dec.o wmv2data.o +OBJS-$(CONFIG_WMV2_ENCODER) += wmv2enc.o wmv2data.o +OBJS-$(CONFIG_WNV1_DECODER) += wnv1.o +OBJS-$(CONFIG_WRAPPED_AVFRAME_DECODER) += wrapped_avframe.o +OBJS-$(CONFIG_WRAPPED_AVFRAME_ENCODER) += wrapped_avframe.o +OBJS-$(CONFIG_WS_SND1_DECODER) += ws-snd1.o +OBJS-$(CONFIG_XAN_DPCM_DECODER) += dpcm.o +OBJS-$(CONFIG_XAN_WC3_DECODER) += xan.o +OBJS-$(CONFIG_XAN_WC4_DECODER) += xxan.o +OBJS-$(CONFIG_XBIN_DECODER) += bintext.o cga_data.o +OBJS-$(CONFIG_XBM_DECODER) += xbmdec.o +OBJS-$(CONFIG_XBM_ENCODER) += xbmenc.o +OBJS-$(CONFIG_XFACE_DECODER) += xfacedec.o xface.o +OBJS-$(CONFIG_XFACE_ENCODER) += xfaceenc.o xface.o +OBJS-$(CONFIG_XL_DECODER) += xl.o +OBJS-$(CONFIG_XMA1_DECODER) += wmaprodec.o wma.o wma_common.o +OBJS-$(CONFIG_XMA2_DECODER) += wmaprodec.o wma.o wma_common.o +OBJS-$(CONFIG_XPM_DECODER) += xpmdec.o +OBJS-$(CONFIG_XSUB_DECODER) += xsubdec.o +OBJS-$(CONFIG_XSUB_ENCODER) += xsubenc.o +OBJS-$(CONFIG_XWD_DECODER) += xwddec.o +OBJS-$(CONFIG_XWD_ENCODER) += xwdenc.o +OBJS-$(CONFIG_Y41P_DECODER) += y41pdec.o +OBJS-$(CONFIG_Y41P_ENCODER) += y41penc.o +OBJS-$(CONFIG_YLC_DECODER) += ylc.o +OBJS-$(CONFIG_YOP_DECODER) += yop.o +OBJS-$(CONFIG_YUV4_DECODER) += yuv4dec.o +OBJS-$(CONFIG_YUV4_ENCODER) += yuv4enc.o +OBJS-$(CONFIG_ZEROCODEC_DECODER) += zerocodec.o +OBJS-$(CONFIG_ZLIB_DECODER) += lcldec.o +OBJS-$(CONFIG_ZLIB_ENCODER) += lclenc.o +OBJS-$(CONFIG_ZMBV_DECODER) += zmbv.o +OBJS-$(CONFIG_ZMBV_ENCODER) += zmbvenc.o + +# (AD)PCM decoders/encoders +OBJS-$(CONFIG_PCM_ALAW_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_ALAW_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_BLURAY_ENCODER) += pcm-blurayenc.o +OBJS-$(CONFIG_PCM_BLURAY_DECODER) += pcm-bluray.o +OBJS-$(CONFIG_PCM_DVD_DECODER) += pcm-dvd.o +OBJS-$(CONFIG_PCM_DVD_ENCODER) += pcm-dvdenc.o +OBJS-$(CONFIG_PCM_F16LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_F24LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_F32BE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_F32BE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_F32LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_F32LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_F64BE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_F64BE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_F64LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_F64LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_LXF_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_MULAW_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_MULAW_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S8_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S8_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S8_PLANAR_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S8_PLANAR_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S16BE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S16BE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S16BE_PLANAR_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S16BE_PLANAR_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S16LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S16LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S16LE_PLANAR_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S16LE_PLANAR_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S24BE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S24BE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S24DAUD_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S24DAUD_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S24LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S24LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S24LE_PLANAR_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S24LE_PLANAR_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S32BE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S32BE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S32LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S32LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S32LE_PLANAR_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S32LE_PLANAR_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S64BE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S64BE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_S64LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_S64LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_SGA_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_U8_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_U8_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_U16BE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_U16BE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_U16LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_U16LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_U24BE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_U24BE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_U24LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_U24LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_U32BE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_U32BE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_U32LE_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_U32LE_ENCODER) += pcm.o +OBJS-$(CONFIG_PCM_VIDC_DECODER) += pcm.o +OBJS-$(CONFIG_PCM_VIDC_ENCODER) += pcm.o + +OBJS-$(CONFIG_ADPCM_4XM_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_ADX_DECODER) += adxdec.o adx.o +OBJS-$(CONFIG_ADPCM_ADX_ENCODER) += adxenc.o adx.o +OBJS-$(CONFIG_ADPCM_AFC_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_AGM_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_AICA_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_ARGO_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_ARGO_ENCODER) += adpcm.o adpcm_data.o adpcmenc.o +OBJS-$(CONFIG_ADPCM_CIRCUS_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_CT_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_DTK_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_EA_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_EA_MAXIS_XA_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_EA_R1_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_EA_R2_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_EA_R3_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_EA_XAS_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_G722_DECODER) += g722.o g722dsp.o g722dec.o +OBJS-$(CONFIG_ADPCM_G722_ENCODER) += g722.o g722dsp.o g722enc.o +OBJS-$(CONFIG_ADPCM_G726_DECODER) += g726.o +OBJS-$(CONFIG_ADPCM_G726_ENCODER) += g726.o +OBJS-$(CONFIG_ADPCM_G726LE_DECODER) += g726.o +OBJS-$(CONFIG_ADPCM_G726LE_ENCODER) += g726.o +OBJS-$(CONFIG_ADPCM_IMA_ACORN_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_AMV_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_AMV_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_ALP_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_ALP_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_APC_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_APM_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_APM_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_CUNNING_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_DAT4_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_DK3_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_DK4_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_EA_EACS_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_EA_SEAD_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_ESCAPE_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_HVQM2_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_HVQM4_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_ISS_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_MAGIX_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_MOFLEX_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_MTF_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_PDA_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_OKI_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_QT_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_QT_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_RAD_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_SSI_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_SSI_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_SMJPEG_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_WAV_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_WAV_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_WS_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_WS_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_IMA_XBOX_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_MS_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_MS_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_MTAF_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_N64_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_PSX_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_PSXC_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_SANYO_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_SBPRO_2_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_SBPRO_3_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_SBPRO_4_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_SWF_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_SWF_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_THP_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_THP_LE_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_VIMA_DECODER) += vima.o adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_XA_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_XMD_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_YAMAHA_DECODER) += adpcm.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_YAMAHA_ENCODER) += adpcmenc.o adpcm_data.o +OBJS-$(CONFIG_ADPCM_ZORK_DECODER) += adpcm.o adpcm_data.o + +# hardware accelerators +OBJS-$(CONFIG_D3D11VA) += dxva2.o +OBJS-$(CONFIG_D3D12VA) += dxva2.o d3d12va_decode.o +OBJS-$(CONFIG_DXVA2) += dxva2.o +OBJS-$(CONFIG_NVDEC) += nvdec.o +OBJS-$(CONFIG_VAAPI) += vaapi_decode.o +OBJS-$(CONFIG_VIDEOTOOLBOX) += videotoolbox.o +OBJS-$(CONFIG_VDPAU) += vdpau.o +OBJS-$(CONFIG_VULKAN) += vulkan.o vulkan_video.o + +OBJS-$(CONFIG_APV_VULKAN_HWACCEL) += vulkan_decode.o vulkan_apv.o +OBJS-$(CONFIG_AV1_D3D11VA_HWACCEL) += dxva2_av1.o +OBJS-$(CONFIG_AV1_DXVA2_HWACCEL) += dxva2_av1.o +OBJS-$(CONFIG_AV1_D3D12VA_HWACCEL) += dxva2_av1.o d3d12va_av1.o +OBJS-$(CONFIG_AV1_NVDEC_HWACCEL) += nvdec_av1.o +OBJS-$(CONFIG_AV1_VAAPI_HWACCEL) += vaapi_av1.o +OBJS-$(CONFIG_AV1_VDPAU_HWACCEL) += vdpau_av1.o +OBJS-$(CONFIG_AV1_VIDEOTOOLBOX_HWACCEL) += videotoolbox_av1.o +OBJS-$(CONFIG_AV1_VULKAN_HWACCEL) += vulkan_decode.o vulkan_av1.o +OBJS-$(CONFIG_DPX_VULKAN_HWACCEL) += vulkan_decode.o vulkan_dpx.o +OBJS-$(CONFIG_FFV1_VULKAN_HWACCEL) += vulkan_decode.o ffv1_vulkan.o vulkan_ffv1.o +OBJS-$(CONFIG_H263_VAAPI_HWACCEL) += vaapi_mpeg4.o +OBJS-$(CONFIG_H263_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o +OBJS-$(CONFIG_H264_D3D11VA_HWACCEL) += dxva2_h264.o +OBJS-$(CONFIG_H264_DXVA2_HWACCEL) += dxva2_h264.o +OBJS-$(CONFIG_H264_D3D12VA_HWACCEL) += dxva2_h264.o d3d12va_h264.o +OBJS-$(CONFIG_H264_NVDEC_HWACCEL) += nvdec_h264.o +OBJS-$(CONFIG_H264_QSV_HWACCEL) += qsvdec.o +OBJS-$(CONFIG_H264_VAAPI_HWACCEL) += vaapi_h264.o +OBJS-$(CONFIG_H264_VDPAU_HWACCEL) += vdpau_h264.o +OBJS-$(CONFIG_H264_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o +OBJS-$(CONFIG_H264_VULKAN_HWACCEL) += vulkan_decode.o vulkan_h264.o +OBJS-$(CONFIG_HEVC_D3D11VA_HWACCEL) += dxva2_hevc.o +OBJS-$(CONFIG_HEVC_DXVA2_HWACCEL) += dxva2_hevc.o +OBJS-$(CONFIG_HEVC_D3D12VA_HWACCEL) += dxva2_hevc.o d3d12va_hevc.o +OBJS-$(CONFIG_HEVC_NVDEC_HWACCEL) += nvdec_hevc.o +OBJS-$(CONFIG_HEVC_QSV_HWACCEL) += qsvdec.o +OBJS-$(CONFIG_HEVC_VAAPI_HWACCEL) += vaapi_hevc.o h265_profile_level.o +OBJS-$(CONFIG_HEVC_VDPAU_HWACCEL) += vdpau_hevc.o h265_profile_level.o +OBJS-$(CONFIG_HEVC_VULKAN_HWACCEL) += vulkan_decode.o vulkan_hevc.o +OBJS-$(CONFIG_MJPEG_NVDEC_HWACCEL) += nvdec_mjpeg.o +OBJS-$(CONFIG_MJPEG_VAAPI_HWACCEL) += vaapi_mjpeg.o +OBJS-$(CONFIG_MPEG1_NVDEC_HWACCEL) += nvdec_mpeg12.o +OBJS-$(CONFIG_MPEG1_VDPAU_HWACCEL) += vdpau_mpeg12.o +OBJS-$(CONFIG_MPEG1_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o +OBJS-$(CONFIG_MPEG2_D3D11VA_HWACCEL) += dxva2_mpeg2.o +OBJS-$(CONFIG_MPEG2_DXVA2_HWACCEL) += dxva2_mpeg2.o +OBJS-$(CONFIG_MPEG2_D3D12VA_HWACCEL) += dxva2_mpeg2.o d3d12va_mpeg2.o +OBJS-$(CONFIG_MPEG2_NVDEC_HWACCEL) += nvdec_mpeg12.o +OBJS-$(CONFIG_MPEG2_QSV_HWACCEL) += qsvdec.o +OBJS-$(CONFIG_MPEG2_VAAPI_HWACCEL) += vaapi_mpeg2.o +OBJS-$(CONFIG_MPEG2_VDPAU_HWACCEL) += vdpau_mpeg12.o +OBJS-$(CONFIG_MPEG2_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o +OBJS-$(CONFIG_MPEG4_NVDEC_HWACCEL) += nvdec_mpeg4.o +OBJS-$(CONFIG_MPEG4_VAAPI_HWACCEL) += vaapi_mpeg4.o +OBJS-$(CONFIG_MPEG4_VDPAU_HWACCEL) += vdpau_mpeg4.o +OBJS-$(CONFIG_MPEG4_VIDEOTOOLBOX_HWACCEL) += videotoolbox.o +OBJS-$(CONFIG_VC1_D3D11VA_HWACCEL) += dxva2_vc1.o +OBJS-$(CONFIG_VC1_DXVA2_HWACCEL) += dxva2_vc1.o +OBJS-$(CONFIG_VC1_D3D12VA_HWACCEL) += dxva2_vc1.o d3d12va_vc1.o +OBJS-$(CONFIG_VC1_NVDEC_HWACCEL) += nvdec_vc1.o +OBJS-$(CONFIG_VC1_QSV_HWACCEL) += qsvdec.o +OBJS-$(CONFIG_VC1_VAAPI_HWACCEL) += vaapi_vc1.o +OBJS-$(CONFIG_VC1_VDPAU_HWACCEL) += vdpau_vc1.o +OBJS-$(CONFIG_VP8_NVDEC_HWACCEL) += nvdec_vp8.o +OBJS-$(CONFIG_VP8_VAAPI_HWACCEL) += vaapi_vp8.o +OBJS-$(CONFIG_VP9_D3D11VA_HWACCEL) += dxva2_vp9.o +OBJS-$(CONFIG_VP9_DXVA2_HWACCEL) += dxva2_vp9.o +OBJS-$(CONFIG_VP9_D3D12VA_HWACCEL) += dxva2_vp9.o d3d12va_vp9.o +OBJS-$(CONFIG_VP9_NVDEC_HWACCEL) += nvdec_vp9.o +OBJS-$(CONFIG_VP9_VAAPI_HWACCEL) += vaapi_vp9.o +OBJS-$(CONFIG_VP9_VDPAU_HWACCEL) += vdpau_vp9.o +OBJS-$(CONFIG_VP9_VIDEOTOOLBOX_HWACCEL) += videotoolbox_vp9.o +OBJS-$(CONFIG_VP9_VULKAN_HWACCEL) += vulkan_decode.o vulkan_vp9.o +OBJS-$(CONFIG_VP8_QSV_HWACCEL) += qsvdec.o +OBJS-$(CONFIG_VVC_VAAPI_HWACCEL) += vaapi_vvc.o +OBJS-$(CONFIG_PRORES_RAW_VULKAN_HWACCEL) += vulkan_decode.o vulkan_prores_raw.o +OBJS-$(CONFIG_PRORES_VULKAN_HWACCEL) += vulkan_decode.o vulkan_prores.o + +# Objects duplicated from other libraries for shared builds +SHLIBOBJS += log2_tab.o reverse.o + +SHLIBOBJS-$(CONFIG_EXR_DECODER) += half2float.o float2half.o +SHLIBOBJS-$(CONFIG_EXR_ENCODER) += float2half.o +SHLIBOBJS-$(CONFIG_PHM_DECODER) += half2float.o +SHLIBOBJS-$(CONFIG_PHM_ENCODER) += float2half.o + +# General libavformat dependencies +OBJS-$(CONFIG_FITS_DEMUXER) += fits.o +OBJS-$(CONFIG_TAK_DEMUXER) += tak.o + +# libavformat dependencies for static builds +STLIBOBJS-$(CONFIG_AVFORMAT) += h2645_parse.o lcevctab.o to_upper4.o +STLIBOBJS-$(CONFIG_ISO_MEDIA) += mpegaudiotabs.o +STLIBOBJS-$(CONFIG_FLV_MUXER) += mpeg4audio_sample_rates.o +STLIBOBJS-$(CONFIG_HLS_DEMUXER) += ac3_channel_layout_tab.o +STLIBOBJS-$(CONFIG_IMAGE_JPEGXL_PIPE_DEMUXER) += jpegxl_parse.o +STLIBOBJS-$(CONFIG_JPEGXL_ANIM_DEMUXER) += jpegxl_parse.o +STLIBOBJS-$(CONFIG_MATROSKA_DEMUXER) += mpeg4audio_sample_rates.o +STLIBOBJS-$(CONFIG_MOV_DEMUXER) += ac3_channel_layout_tab.o +STLIBOBJS-$(CONFIG_MXF_MUXER) += golomb.o +STLIBOBJS-$(CONFIG_MP3_MUXER) += mpegaudiotabs.o +STLIBOBJS-$(CONFIG_NUT_MUXER) += mpegaudiotabs.o +STLIBOBJS-$(CONFIG_RTPDEC) += jpegtables.o +STLIBOBJS-$(CONFIG_RTP_MUXER) += golomb.o jpegtables.o \ + mpeg4audio_sample_rates.o +STLIBOBJS-$(CONFIG_SPDIF_MUXER) += dca_sample_rate_tab.o + +# libavfilter dependencies +OBJS-$(CONFIG_ELBG_FILTER) += elbg.o + +# external codec libraries +OBJS-$(CONFIG_AAC_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_AC3_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_ADPCM_IMA_QT_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_ALAC_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_AMR_NB_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_EAC3_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_GSM_MS_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_ILBC_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_MP1_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_MP2_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_MP3_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_PCM_MULAW_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_PCM_ALAW_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_QDMC_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_QDM2_AT_DECODER) += audiotoolboxdec.o +OBJS-$(CONFIG_AAC_AT_ENCODER) += audiotoolboxenc.o +OBJS-$(CONFIG_ALAC_AT_ENCODER) += audiotoolboxenc.o +OBJS-$(CONFIG_ILBC_AT_ENCODER) += audiotoolboxenc.o +OBJS-$(CONFIG_PCM_ALAW_AT_ENCODER) += audiotoolboxenc.o +OBJS-$(CONFIG_PCM_MULAW_AT_ENCODER) += audiotoolboxenc.o +OBJS-$(CONFIG_LIBAOM_AV1_DECODER) += libaomdec.o libaom.o +OBJS-$(CONFIG_LIBAOM_AV1_ENCODER) += libaomenc.o libaom.o +OBJS-$(CONFIG_LIBARIBB24_DECODER) += libaribb24.o ass.o +OBJS-$(CONFIG_LIBARIBCAPTION_DECODER) += libaribcaption.o ass.o +OBJS-$(CONFIG_LIBCODEC2_DECODER) += libcodec2.o +OBJS-$(CONFIG_LIBCODEC2_ENCODER) += libcodec2.o +OBJS-$(CONFIG_LIBDAV1D_DECODER) += libdav1d.o av1_parse.o +OBJS-$(CONFIG_LIBDAVS2_DECODER) += libdavs2.o +OBJS-$(CONFIG_LIBFDK_AAC_DECODER) += libfdk-aacdec.o +OBJS-$(CONFIG_LIBFDK_AAC_ENCODER) += libfdk-aacenc.o +OBJS-$(CONFIG_LIBGSM_DECODER) += libgsmdec.o +OBJS-$(CONFIG_LIBGSM_ENCODER) += libgsmenc.o +OBJS-$(CONFIG_LIBGSM_MS_DECODER) += libgsmdec.o +OBJS-$(CONFIG_LIBGSM_MS_ENCODER) += libgsmenc.o +OBJS-$(CONFIG_LIBILBC_DECODER) += libilbc.o +OBJS-$(CONFIG_LIBILBC_ENCODER) += libilbc.o +OBJS-$(CONFIG_LIBJXL_DECODER) += libjxldec.o libjxl.o +OBJS-$(CONFIG_LIBJXL_ENCODER) += libjxlenc.o libjxl.o +OBJS-$(CONFIG_LIBKVAZAAR_ENCODER) += libkvazaar.o +OBJS-$(CONFIG_LIBLC3_ENCODER) += liblc3enc.o +OBJS-$(CONFIG_LIBLC3_DECODER) += liblc3dec.o +OBJS-$(CONFIG_LIBMP3LAME_ENCODER) += libmp3lame.o +OBJS-$(CONFIG_LIBMPEGHDEC_DECODER) += libmpeghdec.o +OBJS-$(CONFIG_LIBOAPV_ENCODER) += liboapvenc.o +OBJS-$(CONFIG_LIBOPENCORE_AMRNB_DECODER) += libopencore-amr.o +OBJS-$(CONFIG_LIBOPENCORE_AMRNB_ENCODER) += libopencore-amr.o +OBJS-$(CONFIG_LIBOPENCORE_AMRWB_DECODER) += libopencore-amr.o +OBJS-$(CONFIG_LIBOPENH264_DECODER) += libopenh264dec.o libopenh264.o +OBJS-$(CONFIG_LIBOPENH264_ENCODER) += libopenh264enc.o libopenh264.o +OBJS-$(CONFIG_LIBOPENJPEG_ENCODER) += libopenjpegenc.o +OBJS-$(CONFIG_LIBOPUS_DECODER) += libopusdec.o libopus.o \ + vorbis_data.o +OBJS-$(CONFIG_LIBOPUS_ENCODER) += libopusenc.o libopus.o \ + vorbis_data.o +OBJS-$(CONFIG_LIBRAV1E_ENCODER) += librav1e.o +OBJS-$(CONFIG_LIBRSVG_DECODER) += librsvgdec.o +OBJS-$(CONFIG_LIBSHINE_ENCODER) += libshine.o +OBJS-$(CONFIG_LIBSPEEX_DECODER) += libspeexdec.o +OBJS-$(CONFIG_LIBSPEEX_ENCODER) += libspeexenc.o +OBJS-$(CONFIG_LIBSVTAV1_ENCODER) += libsvtav1.o +OBJS-$(CONFIG_LIBSVTJPEGXS_DECODER) += libsvtjpegxsdec.o +OBJS-$(CONFIG_LIBSVTJPEGXS_ENCODER) += libsvtjpegxsenc.o +OBJS-$(CONFIG_LIBTHEORA_ENCODER) += libtheoraenc.o +OBJS-$(CONFIG_LIBTWOLAME_ENCODER) += libtwolame.o +OBJS-$(CONFIG_LIBUAVS3D_DECODER) += libuavs3d.o +OBJS-$(CONFIG_LIBVO_AMRWBENC_ENCODER) += libvo-amrwbenc.o +OBJS-$(CONFIG_LIBVORBIS_DECODER) += libvorbisdec.o +OBJS-$(CONFIG_LIBVORBIS_ENCODER) += libvorbisenc.o \ + vorbis_data.o +OBJS-$(CONFIG_LIBVPX_VP8_DECODER) += libvpxdec.o +OBJS-$(CONFIG_LIBVPX_VP8_ENCODER) += libvpxenc.o +OBJS-$(CONFIG_LIBVPX_VP9_DECODER) += libvpxdec.o +OBJS-$(CONFIG_LIBVPX_VP9_ENCODER) += libvpxenc.o +OBJS-$(CONFIG_LIBVVENC_ENCODER) += libvvenc.o +OBJS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.o libwebpenc.o +OBJS-$(CONFIG_LIBWEBP_ANIM_ENCODER) += libwebpenc_common.o libwebpenc_animencoder.o +OBJS-$(CONFIG_LIBX262_ENCODER) += libx264.o +OBJS-$(CONFIG_LIBX264_ENCODER) += libx264.o +OBJS-$(CONFIG_LIBX265_ENCODER) += libx265.o +OBJS-$(CONFIG_LIBXAVS_ENCODER) += libxavs.o +OBJS-$(CONFIG_LIBXAVS2_ENCODER) += libxavs2.o +OBJS-$(CONFIG_LIBXEVD_DECODER) += libxevd.o +OBJS-$(CONFIG_LIBXEVE_ENCODER) += libxeve.o +OBJS-$(CONFIG_LIBXVID_ENCODER) += libxvid.o +OBJS-$(CONFIG_LIBZVBI_TELETEXT_DECODER) += libzvbi-teletextdec.o ass.o + +# parsers +OBJS-$(CONFIG_AAC_LATM_PARSER) += latm_parser.o +OBJS-$(CONFIG_AAC_PARSER) += aac_parser.o aac_ac3_parser.o +OBJS-$(CONFIG_AC3_PARSER) += aac_ac3_parser.o ac3tab.o \ + ac3_channel_layout_tab.o +OBJS-$(CONFIG_ADX_PARSER) += adx_parser.o +OBJS-$(CONFIG_AHX_PARSER) += ahx_parser.o +OBJS-$(CONFIG_AMR_PARSER) += amr_parser.o +OBJS-$(CONFIG_APV_PARSER) += apv_parser.o +OBJS-$(CONFIG_AV1_PARSER) += av1_parser.o av1_parse.o +OBJS-$(CONFIG_AVS2_PARSER) += avs2.o avs2_parser.o +OBJS-$(CONFIG_AVS3_PARSER) += avs3_parser.o +OBJS-$(CONFIG_BMP_PARSER) += bmp_parser.o +OBJS-$(CONFIG_CAVSVIDEO_PARSER) += cavs_parser.o +OBJS-$(CONFIG_COOK_PARSER) += cook_parser.o +OBJS-$(CONFIG_CRI_PARSER) += cri_parser.o +OBJS-$(CONFIG_DCA_PARSER) += dca_parser.o dca_exss.o dca.o \ + dca_sample_rate_tab.o +OBJS-$(CONFIG_DIRAC_PARSER) += dirac_parser.o +OBJS-$(CONFIG_DNXHD_PARSER) += dnxhd_parser.o dnxhddata.o +OBJS-$(CONFIG_DNXUC_PARSER) += dnxuc_parser.o +OBJS-$(CONFIG_DOLBY_E_PARSER) += dolby_e_parser.o dolby_e_parse.o +OBJS-$(CONFIG_DPX_PARSER) += dpx_parser.o +OBJS-$(CONFIG_DVAUDIO_PARSER) += dvaudio_parser.o +OBJS-$(CONFIG_DVBSUB_PARSER) += dvbsub_parser.o +OBJS-$(CONFIG_DVD_NAV_PARSER) += dvd_nav_parser.o +OBJS-$(CONFIG_DVDSUB_PARSER) += dvdsub_parser.o +OBJS-$(CONFIG_EVC_PARSER) += evc_parser.o +OBJS-$(CONFIG_FFV1_PARSER) += ffv1_parser.o ffv1_parse.o ffv1.o +OBJS-$(CONFIG_FLAC_PARSER) += flac_parser.o flacdata.o flac.o +OBJS-$(CONFIG_FTR_PARSER) += ftr_parser.o +OBJS-$(CONFIG_G723_1_PARSER) += g723_1_parser.o +OBJS-$(CONFIG_G729_PARSER) += g729_parser.o +OBJS-$(CONFIG_GIF_PARSER) += gif_parser.o +OBJS-$(CONFIG_GSM_PARSER) += gsm_parser.o +OBJS-$(CONFIG_H261_PARSER) += h261_parser.o +OBJS-$(CONFIG_H263_PARSER) += h263_parser.o +OBJS-$(CONFIG_H264_PARSER) += h264_parser.o h264data.o +OBJS-$(CONFIG_HDR_PARSER) += hdr_parser.o +OBJS-$(CONFIG_IPU_PARSER) += ipu_parser.o +OBJS-$(CONFIG_JPEG2000_PARSER) += jpeg2000_parser.o +OBJS-$(CONFIG_JPEGXL_PARSER) += jpegxl_parser.o jpegxl_parse.o +OBJS-$(CONFIG_JPEGXS_PARSER) += jpegxs_parser.o +OBJS-$(CONFIG_LCEVC_PARSER) += lcevc_parser.o lcevctab.o +OBJS-$(CONFIG_MISC4_PARSER) += misc4_parser.o +OBJS-$(CONFIG_MJPEG_PARSER) += mjpeg_parser.o +OBJS-$(CONFIG_MLP_PARSER) += mlp_parse.o mlp_parser.o mlp.o +OBJS-$(CONFIG_MPEG4VIDEO_PARSER) += mpeg4video_parser.o h263.o \ + mpeg4videodec.o mpeg4video.o \ + ituh263dec.o h263data.o +OBJS-$(CONFIG_MPEGAUDIO_PARSER) += mpegaudio_parser.o +OBJS-$(CONFIG_MPEGVIDEO_PARSER) += mpegvideo_parser.o mpeg12data.o +OBJS-$(CONFIG_OPUS_PARSER) += vorbis_data.o +OBJS-$(CONFIG_PNG_PARSER) += png_parser.o +OBJS-$(CONFIG_PNM_PARSER) += pnm_parser.o pnm.o +OBJS-$(CONFIG_PRORES_RAW_PARSER) += prores_raw_parser.o +OBJS-$(CONFIG_QOI_PARSER) += qoi_parser.o +OBJS-$(CONFIG_PRORES_PARSER) += prores_parser.o +OBJS-$(CONFIG_RV34_PARSER) += rv34_parser.o +OBJS-$(CONFIG_SBC_PARSER) += sbc_parser.o +OBJS-$(CONFIG_SIPR_PARSER) += sipr_parser.o +OBJS-$(CONFIG_TAK_PARSER) += tak_parser.o tak.o +OBJS-$(CONFIG_VC1_PARSER) += vc1_parser.o vc1.o vc1data.o \ + wmv2data.o +OBJS-$(CONFIG_VP3_PARSER) += vp3_parser.o +OBJS-$(CONFIG_VP8_PARSER) += vp8_parser.o +OBJS-$(CONFIG_VP9_PARSER) += vp9_parser.o +OBJS-$(CONFIG_VVC_PARSER) += vvc_parser.o +OBJS-$(CONFIG_WEBP_PARSER) += webp_parser.o +OBJS-$(CONFIG_XBM_PARSER) += xbm_parser.o +OBJS-$(CONFIG_XMA_PARSER) += xma_parser.o +OBJS-$(CONFIG_XWD_PARSER) += xwd_parser.o + +# bitstream filters +include $(SRC_PATH)/libavcodec/bsf/Makefile + +OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += av1_parse.o h2645_parse.o +OBJS-$(CONFIG_H264_METADATA_BSF) += h264_levels.o h2645data.o +OBJS-$(CONFIG_HAPQA_EXTRACT_BSF) += hap.o +OBJS-$(CONFIG_HEVC_METADATA_BSF) += h265_profile_level.o h2645data.o +OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += av1_parse.o +OBJS-$(CONFIG_TRUEHD_CORE_BSF) += mlp_parse.o mlp.o + +# thread libraries +OBJS-$(HAVE_LIBC_MSVCRT) += file_open.o +OBJS-$(HAVE_THREADS) += pthread.o pthread_slice.o pthread_frame.o + +OBJS-$(CONFIG_FRAME_THREAD_ENCODER) += frame_thread_encoder.o + +# Windows resource file +SHLIBOBJS-$(HAVE_GNU_WINDRES) += avcodecres.o + +SKIPHEADERS += %_tablegen.h \ + %_tables.h \ + tableprint.h \ + tableprint_vlc.h \ + aaccoder_twoloop.h \ + aaccoder_trellis.h \ + aacenc_quantization.h \ + aacenc_quantization_misc.h \ + bitstream_template.h \ + $(ARCH)/vpx_arith.h \ + +SKIPHEADERS-$(CONFIG_AMF) += amfenc.h amfdec.h +SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h +SKIPHEADERS-$(CONFIG_D3D12VA) += d3d12va_decode.h d3d12va_encode.h +SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h +SKIPHEADERS-$(CONFIG_JNI) += ffjni.h +SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h +SKIPHEADERS-$(CONFIG_LIBAOM) += libaom.h +SKIPHEADERS-$(CONFIG_LIBJXL) += libjxl.h +SKIPHEADERS-$(CONFIG_LIBVPX) += libvpx.h +SKIPHEADERS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.h +SKIPHEADERS-$(CONFIG_MEDIACODEC) += mediacodecdec_common.h mediacodec_surface.h mediacodec_wrapper.h mediacodec_sw_buffer.h +SKIPHEADERS-$(CONFIG_MEDIAFOUNDATION) += mf_utils.h +SKIPHEADERS-$(CONFIG_NVDEC) += nvdec.h +SKIPHEADERS-$(CONFIG_NVENC) += nvenc.h +SKIPHEADERS-$(CONFIG_OHCODEC) += ohcodec.h +SKIPHEADERS-$(CONFIG_QSV) += qsv.h qsv_internal.h +SKIPHEADERS-$(CONFIG_QSVENC) += qsvenc.h +SKIPHEADERS-$(CONFIG_VAAPI) += vaapi_decode.h vaapi_hevc.h vaapi_encode.h +SKIPHEADERS-$(CONFIG_VDPAU) += vdpau.h vdpau_internal.h +SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX) += videotoolbox.h vt_internal.h +SKIPHEADERS-$(CONFIG_VULKAN) += ffv1_vulkan.h prores_vulkan.h vulkan_video.h \ + vulkan_encode.h vulkan_decode.h +SKIPHEADERS-$(CONFIG_V4L2_M2M) += v4l2_buffers.h v4l2_context.h v4l2_m2m.h +SKIPHEADERS-$(CONFIG_ZLIB) += zlib_wrapper.h + +TESTPROGS = avcodec \ + avpacket \ + bitstream_be \ + bitstream_le \ + codec_desc \ + encinfo \ + htmlsubtitles \ + jpeg2000dwt \ + mathops \ + +TESTPROGS-$(CONFIG_APV_DECODER) += apv +TESTPROGS-$(CONFIG_AV1_VAAPI_ENCODER) += av1_levels +TESTPROGS-$(CONFIG_CABAC) += cabac +TESTPROGS-$(CONFIG_CELP_MATH) += celp_math +TESTPROGS-$(CONFIG_GOLOMB) += golomb +TESTPROGS-$(CONFIG_IDCTDSP) += dct +TESTPROGS-$(CONFIG_DXV_ENCODER) += hashtable +TESTPROGS-$(CONFIG_MJPEG_ENCODER) += mjpegenc_huffman +TESTPROGS-$(CONFIG_MPEGVIDEO) += mpeg12framerate +TESTPROGS-$(CONFIG_H264_METADATA_BSF) += h264_levels +TESTPROGS-$(CONFIG_HEVC_METADATA_BSF) += h265_levels +TESTPROGS-$(CONFIG_RANGECODER) += rangecoder +TESTPROGS-$(CONFIG_SNOW_ENCODER) += snowenc + +TESTOBJS = dctref.o + +TOOLS = fourcc2pixfmt + +HOSTPROGS = aacps_tablegen \ + aacps_fixed_tablegen \ + cbrt_tablegen \ + cbrt_fixed_tablegen \ + dv_tablegen \ + motionpixels_tablegen \ + mpegaudio_tablegen \ + mpegaudiodec_common_tablegen \ + pcm_tablegen \ + qdm2_tablegen \ + sinewin_tablegen \ + sinewin_fixed_tablegen \ + +CLEANFILES = *_tables.c *_tables.h *_tablegen$(HOSTEXESUF) + +$(SUBDIR)tests/dct$(EXESUF): $(SUBDIR)dctref.o $(SUBDIR)aandcttab.o +$(SUBDIR)dv_tablegen$(HOSTEXESUF): $(SUBDIR)dvdata_host.o + +ifdef CONFIG_SMALL +$(SUBDIR)%_tablegen$(HOSTEXESUF): HOSTCFLAGS += -DCONFIG_SMALL=1 +else +$(SUBDIR)%_tablegen$(HOSTEXESUF): HOSTCFLAGS += -DCONFIG_SMALL=0 +endif + +GEN_HEADERS = cbrt_tables.h cbrt_fixed_tables.h aacps_tables.h aacps_fixed_tables.h \ + dv_tables.h \ + sinewin_tables.h sinewin_fixed_tables.h mpegaudio_tables.h \ + mpegaudiodec_common_tables.h motionpixels_tables.h \ + pcm_tables.h qdm2_tables.h +GEN_HEADERS := $(addprefix $(SUBDIR), $(GEN_HEADERS)) + +$(GEN_HEADERS): $(SUBDIR)%_tables.h: $(SUBDIR)%_tablegen$(HOSTEXESUF) + $(M)./$< > $@ + +ifdef CONFIG_HARDCODED_TABLES +$(SUBDIR)cbrt_data.o: $(SUBDIR)cbrt_tables.h +$(SUBDIR)cbrt_data_fixed.o: $(SUBDIR)cbrt_fixed_tables.h +$(SUBDIR)aac/aacdec_fixed.o: $(SUBDIR)sinewin_fixed_tables.h +$(SUBDIR)aacps_float.o: $(SUBDIR)aacps_tables.h +$(SUBDIR)aacps_fixed.o: $(SUBDIR)aacps_fixed_tables.h +$(SUBDIR)dvenc.o: $(SUBDIR)dv_tables.h +$(SUBDIR)motionpixels.o: $(SUBDIR)motionpixels_tables.h +$(SUBDIR)mpegaudiodec_common.o: $(SUBDIR)mpegaudiodec_common_tables.h +$(SUBDIR)mpegaudiodec_fixed.o: $(SUBDIR)mpegaudio_tables.h +$(SUBDIR)mpegaudiodec_float.o: $(SUBDIR)mpegaudio_tables.h +$(SUBDIR)pcm.o: $(SUBDIR)pcm_tables.h +$(SUBDIR)qdm2.o: $(SUBDIR)qdm2_tables.h +$(SUBDIR)sinewin.o: $(SUBDIR)sinewin_tables.h +endif diff --git a/thirdparty/ffmpeg/libavcodec/av1dec.c b/thirdparty/ffmpeg/libavcodec/av1dec.c index d614c8d68..c3fa6b170 100644 --- a/thirdparty/ffmpeg/libavcodec/av1dec.c +++ b/thirdparty/ffmpeg/libavcodec/av1dec.c @@ -543,6 +543,7 @@ static int get_pixel_format(AVCodecContext *avctx) CONFIG_AV1_D3D11VA_HWACCEL * 2 + \ CONFIG_AV1_D3D12VA_HWACCEL + \ CONFIG_AV1_NVDEC_HWACCEL + \ + CONFIG_AV1_V4L2REQUEST_HWACCEL + \ CONFIG_AV1_VAAPI_HWACCEL + \ CONFIG_AV1_VDPAU_HWACCEL + \ CONFIG_AV1_VIDEOTOOLBOX_HWACCEL + \ @@ -578,6 +579,9 @@ static int get_pixel_format(AVCodecContext *avctx) #endif #if CONFIG_AV1_VULKAN_HWACCEL *fmtp++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_AV1_V4L2REQUEST_HWACCEL + *fmtp++ = AV_PIX_FMT_DRM_PRIME; #endif break; case AV_PIX_FMT_YUV420P10: @@ -605,6 +609,9 @@ static int get_pixel_format(AVCodecContext *avctx) #endif #if CONFIG_AV1_VULKAN_HWACCEL *fmtp++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_AV1_V4L2REQUEST_HWACCEL + *fmtp++ = AV_PIX_FMT_DRM_PRIME; #endif break; case AV_PIX_FMT_YUV420P12: @@ -1567,6 +1574,9 @@ const FFCodec ff_av1_decoder = { #if CONFIG_AV1_VULKAN_HWACCEL HWACCEL_VULKAN(av1), #endif +#if CONFIG_AV1_V4L2REQUEST_HWACCEL + HWACCEL_V4L2REQUEST(av1), +#endif NULL }, diff --git a/thirdparty/ffmpeg/libavcodec/av1dec.c.orig b/thirdparty/ffmpeg/libavcodec/av1dec.c.orig new file mode 100644 index 000000000..d614c8d68 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/av1dec.c.orig @@ -0,0 +1,1573 @@ +/* + * AV1 video decoder + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_components.h" + +#include "libavutil/attributes.h" +#include "libavutil/hdr_dynamic_metadata.h" +#include "libavutil/film_grain_params.h" +#include "libavutil/mastering_display_metadata.h" +#include "libavutil/mem.h" +#include "libavutil/pixdesc.h" +#include "libavutil/opt.h" +#include "avcodec.h" +#include "av1_parse.h" +#include "av1dec.h" +#include "atsc_a53.h" +#include "bytestream.h" +#include "codec_internal.h" +#include "decode.h" +#include "hwaccel_internal.h" +#include "internal.h" +#include "itut35.h" +#include "hwconfig.h" +#include "profiles.h" +#include "progressframe.h" +#include "libavutil/refstruct.h" + +/** same with Div_Lut defined in spec 7.11.3.7 */ +static const uint16_t div_lut[AV1_DIV_LUT_NUM] = { + 16384, 16320, 16257, 16194, 16132, 16070, 16009, 15948, 15888, 15828, 15768, + 15709, 15650, 15592, 15534, 15477, 15420, 15364, 15308, 15252, 15197, 15142, + 15087, 15033, 14980, 14926, 14873, 14821, 14769, 14717, 14665, 14614, 14564, + 14513, 14463, 14413, 14364, 14315, 14266, 14218, 14170, 14122, 14075, 14028, + 13981, 13935, 13888, 13843, 13797, 13752, 13707, 13662, 13618, 13574, 13530, + 13487, 13443, 13400, 13358, 13315, 13273, 13231, 13190, 13148, 13107, 13066, + 13026, 12985, 12945, 12906, 12866, 12827, 12788, 12749, 12710, 12672, 12633, + 12596, 12558, 12520, 12483, 12446, 12409, 12373, 12336, 12300, 12264, 12228, + 12193, 12157, 12122, 12087, 12053, 12018, 11984, 11950, 11916, 11882, 11848, + 11815, 11782, 11749, 11716, 11683, 11651, 11619, 11586, 11555, 11523, 11491, + 11460, 11429, 11398, 11367, 11336, 11305, 11275, 11245, 11215, 11185, 11155, + 11125, 11096, 11067, 11038, 11009, 10980, 10951, 10923, 10894, 10866, 10838, + 10810, 10782, 10755, 10727, 10700, 10673, 10645, 10618, 10592, 10565, 10538, + 10512, 10486, 10460, 10434, 10408, 10382, 10356, 10331, 10305, 10280, 10255, + 10230, 10205, 10180, 10156, 10131, 10107, 10082, 10058, 10034, 10010, 9986, + 9963, 9939, 9916, 9892, 9869, 9846, 9823, 9800, 9777, 9754, 9732, + 9709, 9687, 9664, 9642, 9620, 9598, 9576, 9554, 9533, 9511, 9489, + 9468, 9447, 9425, 9404, 9383, 9362, 9341, 9321, 9300, 9279, 9259, + 9239, 9218, 9198, 9178, 9158, 9138, 9118, 9098, 9079, 9059, 9039, + 9020, 9001, 8981, 8962, 8943, 8924, 8905, 8886, 8867, 8849, 8830, + 8812, 8793, 8775, 8756, 8738, 8720, 8702, 8684, 8666, 8648, 8630, + 8613, 8595, 8577, 8560, 8542, 8525, 8508, 8490, 8473, 8456, 8439, + 8422, 8405, 8389, 8372, 8355, 8339, 8322, 8306, 8289, 8273, 8257, + 8240, 8224, 8208, 8192 +}; + +static uint32_t inverse_recenter(int r, uint32_t v) +{ + if (v > 2 * r) + return v; + else if (v & 1) + return r - ((v + 1) >> 1); + else + return r + (v >> 1); +} + +static uint32_t decode_unsigned_subexp_with_ref(uint32_t sub_exp, + int mx, int r) +{ + if ((r << 1) <= mx) { + return inverse_recenter(r, sub_exp); + } else { + return mx - 1 - inverse_recenter(mx - 1 - r, sub_exp); + } +} + +static int32_t decode_signed_subexp_with_ref(uint32_t sub_exp, int low, + int high, int r) +{ + int32_t x = decode_unsigned_subexp_with_ref(sub_exp, high - low, r - low); + return x + low; +} + +static void read_global_param(AV1DecContext *s, int type, int ref, int idx) +{ + int primary_frame; + uint32_t abs_bits, prec_bits, round, prec_diff, sub, mx; + int32_t r, prev_gm_param; + + primary_frame = s->raw_frame_header->primary_ref_frame; + abs_bits = AV1_GM_ABS_ALPHA_BITS; + prec_bits = AV1_GM_ALPHA_PREC_BITS; + + /* setup_past_independence() sets PrevGmParams to default values. We can + * simply point to the current's frame gm_params as they will be initialized + * with defaults at this point. + */ + if (s->raw_frame_header->primary_ref_frame == AV1_PRIMARY_REF_NONE) + prev_gm_param = s->cur_frame.gm_params[ref][idx]; + else { + int prev_frame = s->raw_frame_header->ref_frame_idx[primary_frame]; + prev_gm_param = s->ref[prev_frame].gm_params[ref][idx]; + } + + if (idx < 2) { + if (type == AV1_WARP_MODEL_TRANSLATION) { + abs_bits = AV1_GM_ABS_TRANS_ONLY_BITS - + !s->raw_frame_header->allow_high_precision_mv; + prec_bits = AV1_GM_TRANS_ONLY_PREC_BITS - + !s->raw_frame_header->allow_high_precision_mv; + } else { + abs_bits = AV1_GM_ABS_TRANS_BITS; + prec_bits = AV1_GM_TRANS_PREC_BITS; + } + } + round = (idx % 3) == 2 ? (1 << AV1_WARPEDMODEL_PREC_BITS) : 0; + prec_diff = AV1_WARPEDMODEL_PREC_BITS - prec_bits; + sub = (idx % 3) == 2 ? (1 << prec_bits) : 0; + mx = 1 << abs_bits; + r = (prev_gm_param >> prec_diff) - sub; + + s->cur_frame.gm_params[ref][idx] = + (decode_signed_subexp_with_ref(s->raw_frame_header->gm_params[ref][idx], + -mx, mx + 1, r) << prec_diff) + round; +} + +static uint64_t round_two(uint64_t x, uint16_t n) +{ + if (n == 0) + return x; + return ((x + ((uint64_t)1 << (n - 1))) >> n); +} + +static int64_t round_two_signed(int64_t x, uint16_t n) +{ + return ((x<0) ? -((int64_t)round_two(-x, n)) : (int64_t)round_two(x, n)); +} + +/** + * Resolve divisor process. + * see spec 7.11.3.7 + */ +static int16_t resolve_divisor(uint32_t d, uint16_t *shift) +{ + int32_t e, f; + + *shift = av_log2(d); + e = d - (1 << (*shift)); + if (*shift > AV1_DIV_LUT_BITS) + f = round_two(e, *shift - AV1_DIV_LUT_BITS); + else + f = e << (AV1_DIV_LUT_BITS - (*shift)); + + *shift += AV1_DIV_LUT_PREC_BITS; + + return div_lut[f]; +} + +/** + * check if global motion params is valid. + * see spec 7.11.3.6 + */ +static uint8_t get_shear_params_valid(AV1DecContext *s, int idx) +{ + int16_t alpha, beta, gamma, delta, divf, divs; + int64_t v, w; + int32_t *param = &s->cur_frame.gm_params[idx][0]; + if (param[2] <= 0) + return 0; + + alpha = av_clip_int16(param[2] - (1 << AV1_WARPEDMODEL_PREC_BITS)); + beta = av_clip_int16(param[3]); + divf = resolve_divisor(abs(param[2]), &divs); + v = (int64_t)param[4] * (1 << AV1_WARPEDMODEL_PREC_BITS); + w = (int64_t)param[3] * param[4]; + gamma = av_clip_int16((int)round_two_signed((v * divf), divs)); + delta = av_clip_int16(param[5] - (int)round_two_signed((w * divf), divs) - (1 << AV1_WARPEDMODEL_PREC_BITS)); + + alpha = round_two_signed(alpha, AV1_WARP_PARAM_REDUCE_BITS) << AV1_WARP_PARAM_REDUCE_BITS; + beta = round_two_signed(beta, AV1_WARP_PARAM_REDUCE_BITS) << AV1_WARP_PARAM_REDUCE_BITS; + gamma = round_two_signed(gamma, AV1_WARP_PARAM_REDUCE_BITS) << AV1_WARP_PARAM_REDUCE_BITS; + delta = round_two_signed(delta, AV1_WARP_PARAM_REDUCE_BITS) << AV1_WARP_PARAM_REDUCE_BITS; + + if ((4 * abs(alpha) + 7 * abs(beta)) >= (1 << AV1_WARPEDMODEL_PREC_BITS) || + (4 * abs(gamma) + 4 * abs(delta)) >= (1 << AV1_WARPEDMODEL_PREC_BITS)) + return 0; + + return 1; +} + +/** +* update gm type/params, since cbs already implemented part of this function, +* so we don't need to full implement spec. +*/ +static void global_motion_params(AV1DecContext *s) +{ + const AV1RawFrameHeader *header = s->raw_frame_header; + int type, ref; + + for (ref = AV1_REF_FRAME_LAST; ref <= AV1_REF_FRAME_ALTREF; ref++) { + s->cur_frame.gm_type[ref] = AV1_WARP_MODEL_IDENTITY; + for (int i = 0; i < 6; i++) + s->cur_frame.gm_params[ref][i] = (i % 3 == 2) ? + 1 << AV1_WARPEDMODEL_PREC_BITS : 0; + } + if (header->frame_type == AV1_FRAME_KEY || + header->frame_type == AV1_FRAME_INTRA_ONLY) + return; + + for (ref = AV1_REF_FRAME_LAST; ref <= AV1_REF_FRAME_ALTREF; ref++) { + if (header->is_global[ref]) { + if (header->is_rot_zoom[ref]) { + type = AV1_WARP_MODEL_ROTZOOM; + } else { + type = header->is_translation[ref] ? AV1_WARP_MODEL_TRANSLATION + : AV1_WARP_MODEL_AFFINE; + } + } else { + type = AV1_WARP_MODEL_IDENTITY; + } + s->cur_frame.gm_type[ref] = type; + + if (type >= AV1_WARP_MODEL_ROTZOOM) { + read_global_param(s, type, ref, 2); + read_global_param(s, type, ref, 3); + if (type == AV1_WARP_MODEL_AFFINE) { + read_global_param(s, type, ref, 4); + read_global_param(s, type, ref, 5); + } else { + s->cur_frame.gm_params[ref][4] = -s->cur_frame.gm_params[ref][3]; + s->cur_frame.gm_params[ref][5] = s->cur_frame.gm_params[ref][2]; + } + } + if (type >= AV1_WARP_MODEL_TRANSLATION) { + read_global_param(s, type, ref, 0); + read_global_param(s, type, ref, 1); + } + if (type <= AV1_WARP_MODEL_AFFINE) { + s->cur_frame.gm_invalid[ref] = !get_shear_params_valid(s, ref); + } + } +} + +static int get_relative_dist(const AV1RawSequenceHeader *seq, + unsigned int a, unsigned int b) +{ + unsigned int diff = a - b; + unsigned int m = 1 << seq->order_hint_bits_minus_1; + return (diff & (m - 1)) - (diff & m); +} + +static void skip_mode_params(AV1DecContext *s) +{ + const AV1RawFrameHeader *header = s->raw_frame_header; + const AV1RawSequenceHeader *seq = s->raw_seq; + + int forward_idx, backward_idx; + int forward_hint, backward_hint; + int second_forward_idx, second_forward_hint; + int ref_hint, dist, i; + + if (header->frame_type == AV1_FRAME_KEY || + header->frame_type == AV1_FRAME_INTRA_ONLY || + !header->reference_select || !seq->enable_order_hint) + return; + + forward_idx = -1; + backward_idx = -1; + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + if (!s->ref[header->ref_frame_idx[i]].raw_frame_header) + return; + ref_hint = s->ref[header->ref_frame_idx[i]].raw_frame_header->order_hint; + dist = get_relative_dist(seq, ref_hint, header->order_hint); + if (dist < 0) { + if (forward_idx < 0 || + get_relative_dist(seq, ref_hint, forward_hint) > 0) { + forward_idx = i; + forward_hint = ref_hint; + } + } else if (dist > 0) { + if (backward_idx < 0 || + get_relative_dist(seq, ref_hint, backward_hint) < 0) { + backward_idx = i; + backward_hint = ref_hint; + } + } + } + + if (forward_idx < 0) { + return; + } else if (backward_idx >= 0) { + s->cur_frame.skip_mode_frame_idx[0] = + AV1_REF_FRAME_LAST + FFMIN(forward_idx, backward_idx); + s->cur_frame.skip_mode_frame_idx[1] = + AV1_REF_FRAME_LAST + FFMAX(forward_idx, backward_idx); + return; + } + + second_forward_idx = -1; + for (i = 0; i < AV1_REFS_PER_FRAME; i++) { + ref_hint = s->ref[header->ref_frame_idx[i]].raw_frame_header->order_hint; + if (get_relative_dist(seq, ref_hint, forward_hint) < 0) { + if (second_forward_idx < 0 || + get_relative_dist(seq, ref_hint, second_forward_hint) > 0) { + second_forward_idx = i; + second_forward_hint = ref_hint; + } + } + } + + if (second_forward_idx < 0) + return; + + s->cur_frame.skip_mode_frame_idx[0] = + AV1_REF_FRAME_LAST + FFMIN(forward_idx, second_forward_idx); + s->cur_frame.skip_mode_frame_idx[1] = + AV1_REF_FRAME_LAST + FFMAX(forward_idx, second_forward_idx); +} + +static void coded_lossless_param(AV1DecContext *s) +{ + const AV1RawFrameHeader *header = s->raw_frame_header; + int i; + + if (header->delta_q_y_dc || header->delta_q_u_ac || + header->delta_q_u_dc || header->delta_q_v_ac || + header->delta_q_v_dc) { + s->cur_frame.coded_lossless = 0; + return; + } + + s->cur_frame.coded_lossless = 1; + for (i = 0; i < AV1_MAX_SEGMENTS; i++) { + int qindex; + if (header->feature_enabled[i][AV1_SEG_LVL_ALT_Q]) { + qindex = (header->base_q_idx + + header->feature_value[i][AV1_SEG_LVL_ALT_Q]); + } else { + qindex = header->base_q_idx; + } + qindex = av_clip_uintp2(qindex, 8); + + if (qindex) { + s->cur_frame.coded_lossless = 0; + return; + } + } +} + +static void order_hint_info(AV1DecContext *s) +{ + const AV1RawFrameHeader *header = s->raw_frame_header; + const AV1RawSequenceHeader *seq = s->raw_seq; + AV1Frame *frame = &s->cur_frame; + + frame->order_hint = header->order_hint; + + for (int i = 0; i < AV1_REFS_PER_FRAME; i++) { + int ref_name = i + AV1_REF_FRAME_LAST; + int ref_slot = header->ref_frame_idx[i]; + int ref_order_hint = s->ref[ref_slot].order_hint; + + frame->order_hints[ref_name] = ref_order_hint; + if (!seq->enable_order_hint) { + frame->ref_frame_sign_bias[ref_name] = 0; + } else { + frame->ref_frame_sign_bias[ref_name] = + get_relative_dist(seq, ref_order_hint, + frame->order_hint) > 0; + } + } +} + +static void load_grain_params(AV1DecContext *s) +{ + const AV1RawFrameHeader *header = s->raw_frame_header; + const AV1RawFilmGrainParams *film_grain = &header->film_grain, *src; + AV1RawFilmGrainParams *dst = &s->cur_frame.film_grain; + + if (!film_grain->apply_grain) + return; + + if (film_grain->update_grain) { + memcpy(dst, film_grain, sizeof(*dst)); + return; + } + + src = &s->ref[film_grain->film_grain_params_ref_idx].film_grain; + + memcpy(dst, src, sizeof(*dst)); + dst->grain_seed = film_grain->grain_seed; +} + +static int init_tile_data(AV1DecContext *s) + +{ + int cur_tile_num = + s->raw_frame_header->tile_cols * s->raw_frame_header->tile_rows; + if (s->tile_num < cur_tile_num) { + int ret = av_reallocp_array(&s->tile_group_info, cur_tile_num, + sizeof(TileGroupInfo)); + if (ret < 0) { + s->tile_num = 0; + return ret; + } + } + s->tile_num = cur_tile_num; + + return 0; +} + +static int get_tiles_info(AVCodecContext *avctx, const AV1RawTileGroup *tile_group) +{ + AV1DecContext *s = avctx->priv_data; + GetByteContext gb; + uint16_t tile_num, tile_row, tile_col; + uint32_t size = 0, size_bytes = 0; + + bytestream2_init(&gb, tile_group->tile_data.data, + tile_group->tile_data.data_size); + s->tg_start = tile_group->tg_start; + s->tg_end = tile_group->tg_end; + + for (tile_num = tile_group->tg_start; tile_num <= tile_group->tg_end; tile_num++) { + tile_row = tile_num / s->raw_frame_header->tile_cols; + tile_col = tile_num % s->raw_frame_header->tile_cols; + + if (tile_num == tile_group->tg_end) { + s->tile_group_info[tile_num].tile_size = bytestream2_get_bytes_left(&gb); + s->tile_group_info[tile_num].tile_offset = bytestream2_tell(&gb); + s->tile_group_info[tile_num].tile_row = tile_row; + s->tile_group_info[tile_num].tile_column = tile_col; + return 0; + } + size_bytes = s->raw_frame_header->tile_size_bytes_minus1 + 1; + if (bytestream2_get_bytes_left(&gb) < size_bytes) + return AVERROR_INVALIDDATA; + size = 0; + for (int i = 0; i < size_bytes; i++) + size |= bytestream2_get_byteu(&gb) << 8 * i; + if (bytestream2_get_bytes_left(&gb) <= size) + return AVERROR_INVALIDDATA; + size++; + + s->tile_group_info[tile_num].tile_size = size; + s->tile_group_info[tile_num].tile_offset = bytestream2_tell(&gb); + s->tile_group_info[tile_num].tile_row = tile_row; + s->tile_group_info[tile_num].tile_column = tile_col; + + bytestream2_skipu(&gb, size); + } + + return 0; + +} + +static enum AVPixelFormat get_sw_pixel_format(void *logctx, + const AV1RawSequenceHeader *seq) +{ + int bit_depth; + enum AVPixelFormat pix_fmt = AV_PIX_FMT_NONE; + + if (seq->seq_profile == 2 && seq->color_config.high_bitdepth) + bit_depth = seq->color_config.twelve_bit ? 12 : 10; + else if (seq->seq_profile <= 2) + bit_depth = seq->color_config.high_bitdepth ? 10 : 8; + else { + av_log(logctx, AV_LOG_ERROR, + "Unknown AV1 profile %d.\n", seq->seq_profile); + return AV_PIX_FMT_NONE; + } + + if (!seq->color_config.mono_chrome) { + // 4:4:4 x:0 y:0, 4:2:2 x:1 y:0, 4:2:0 x:1 y:1 + if (seq->color_config.subsampling_x == 0 && + seq->color_config.subsampling_y == 0) { + if (bit_depth == 8) + pix_fmt = AV_PIX_FMT_YUV444P; + else if (bit_depth == 10) + pix_fmt = AV_PIX_FMT_YUV444P10; + else if (bit_depth == 12) + pix_fmt = AV_PIX_FMT_YUV444P12; + else + av_assert0(0); + } else if (seq->color_config.subsampling_x == 1 && + seq->color_config.subsampling_y == 0) { + if (bit_depth == 8) + pix_fmt = AV_PIX_FMT_YUV422P; + else if (bit_depth == 10) + pix_fmt = AV_PIX_FMT_YUV422P10; + else if (bit_depth == 12) + pix_fmt = AV_PIX_FMT_YUV422P12; + else + av_assert0(0); + } else if (seq->color_config.subsampling_x == 1 && + seq->color_config.subsampling_y == 1) { + if (bit_depth == 8) + pix_fmt = AV_PIX_FMT_YUV420P; + else if (bit_depth == 10) + pix_fmt = AV_PIX_FMT_YUV420P10; + else if (bit_depth == 12) + pix_fmt = AV_PIX_FMT_YUV420P12; + else + av_assert0(0); + } + } else { + if (bit_depth == 8) + pix_fmt = AV_PIX_FMT_GRAY8; + else if (bit_depth == 10) + pix_fmt = AV_PIX_FMT_GRAY10; + else if (bit_depth == 12) + pix_fmt = AV_PIX_FMT_GRAY12; + else + av_assert0(0); + } + + return pix_fmt; +} + +static int get_pixel_format(AVCodecContext *avctx) +{ + AV1DecContext *s = avctx->priv_data; + const AV1RawSequenceHeader *seq = s->raw_seq; + int ret; + enum AVPixelFormat pix_fmt = get_sw_pixel_format(avctx, seq); +#define HWACCEL_MAX (CONFIG_AV1_DXVA2_HWACCEL + \ + CONFIG_AV1_D3D11VA_HWACCEL * 2 + \ + CONFIG_AV1_D3D12VA_HWACCEL + \ + CONFIG_AV1_NVDEC_HWACCEL + \ + CONFIG_AV1_VAAPI_HWACCEL + \ + CONFIG_AV1_VDPAU_HWACCEL + \ + CONFIG_AV1_VIDEOTOOLBOX_HWACCEL + \ + CONFIG_AV1_VULKAN_HWACCEL) + enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmtp = pix_fmts; + + if (pix_fmt == AV_PIX_FMT_NONE) + return -1; + + switch (pix_fmt) { + case AV_PIX_FMT_YUV420P: +#if CONFIG_AV1_DXVA2_HWACCEL + *fmtp++ = AV_PIX_FMT_DXVA2_VLD; +#endif +#if CONFIG_AV1_D3D11VA_HWACCEL + *fmtp++ = AV_PIX_FMT_D3D11VA_VLD; + *fmtp++ = AV_PIX_FMT_D3D11; +#endif +#if CONFIG_AV1_D3D12VA_HWACCEL + *fmtp++ = AV_PIX_FMT_D3D12; +#endif +#if CONFIG_AV1_NVDEC_HWACCEL + *fmtp++ = AV_PIX_FMT_CUDA; +#endif +#if CONFIG_AV1_VAAPI_HWACCEL + *fmtp++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_AV1_VDPAU_HWACCEL + *fmtp++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_AV1_VIDEOTOOLBOX_HWACCEL + *fmtp++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV420P10: +#if CONFIG_AV1_DXVA2_HWACCEL + *fmtp++ = AV_PIX_FMT_DXVA2_VLD; +#endif +#if CONFIG_AV1_D3D11VA_HWACCEL + *fmtp++ = AV_PIX_FMT_D3D11VA_VLD; + *fmtp++ = AV_PIX_FMT_D3D11; +#endif +#if CONFIG_AV1_D3D12VA_HWACCEL + *fmtp++ = AV_PIX_FMT_D3D12; +#endif +#if CONFIG_AV1_NVDEC_HWACCEL + *fmtp++ = AV_PIX_FMT_CUDA; +#endif +#if CONFIG_AV1_VAAPI_HWACCEL + *fmtp++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_AV1_VDPAU_HWACCEL + *fmtp++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_AV1_VIDEOTOOLBOX_HWACCEL + *fmtp++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV420P12: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV422P: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV422P10: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV422P12: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV444P: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV444P10: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV444P12: +#if CONFIG_AV1_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_GRAY8: +#if CONFIG_AV1_NVDEC_HWACCEL + *fmtp++ = AV_PIX_FMT_CUDA; +#endif + break; + case AV_PIX_FMT_GRAY10: +#if CONFIG_AV1_NVDEC_HWACCEL + *fmtp++ = AV_PIX_FMT_CUDA; +#endif + break; + } + + *fmtp++ = pix_fmt; + *fmtp = AV_PIX_FMT_NONE; + + for (int i = 0; pix_fmts[i] != pix_fmt; i++) + if (pix_fmts[i] == avctx->pix_fmt) { + s->pix_fmt = pix_fmt; + return 1; + } + + ret = ff_get_format(avctx, pix_fmts); + + /** + * check if the HW accel is inited correctly. If not, return un-implemented. + * Since now the av1 decoder doesn't support native decode, if it will be + * implemented in the future, need remove this check. + */ + if (!avctx->hwaccel) { + av_log(avctx, AV_LOG_ERROR, "Your platform doesn't support" + " hardware accelerated AV1 decoding.\n"); + avctx->pix_fmt = AV_PIX_FMT_NONE; + return AVERROR(ENOSYS); + } + + s->pix_fmt = pix_fmt; + avctx->pix_fmt = ret; + + av_log(avctx, AV_LOG_DEBUG, "AV1 decode get format: %s.\n", + av_get_pix_fmt_name(avctx->pix_fmt)); + + return 0; +} + +static void av1_frame_unref(AV1Frame *f) +{ + ff_progress_frame_unref(&f->pf); + av_refstruct_unref(&f->hwaccel_picture_private); + av_refstruct_unref(&f->header_ref); + f->raw_frame_header = NULL; + f->spatial_id = f->temporal_id = 0; + memset(f->skip_mode_frame_idx, 0, + 2 * sizeof(uint8_t)); + memset(&f->film_grain, 0, sizeof(f->film_grain)); + f->coded_lossless = 0; +} + +static void av1_frame_replace(AV1Frame *dst, const AV1Frame *src) +{ + av_assert1(dst != src); + + av_refstruct_replace(&dst->header_ref, src->header_ref); + + dst->raw_frame_header = src->raw_frame_header; + + ff_progress_frame_replace(&dst->pf, &src->pf); + + av_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); + + dst->spatial_id = src->spatial_id; + dst->temporal_id = src->temporal_id; + memcpy(dst->gm_invalid, + src->gm_invalid, + AV1_NUM_REF_FRAMES * sizeof(uint8_t)); + memcpy(dst->gm_type, + src->gm_type, + AV1_NUM_REF_FRAMES * sizeof(uint8_t)); + memcpy(dst->gm_params, + src->gm_params, + AV1_NUM_REF_FRAMES * 6 * sizeof(int32_t)); + memcpy(dst->skip_mode_frame_idx, + src->skip_mode_frame_idx, + 2 * sizeof(uint8_t)); + memcpy(&dst->film_grain, + &src->film_grain, + sizeof(dst->film_grain)); + dst->coded_lossless = src->coded_lossless; + + dst->order_hint = src->order_hint; + memcpy(dst->ref_frame_sign_bias, src->ref_frame_sign_bias, + sizeof(dst->ref_frame_sign_bias)); + memcpy(dst->order_hints, src->order_hints, + sizeof(dst->order_hints)); + + dst->force_integer_mv = src->force_integer_mv; +} + +static av_cold int av1_decode_free(AVCodecContext *avctx) +{ + AV1DecContext *s = avctx->priv_data; + AV1RawMetadataITUTT35 itut_t35; + + for (int i = 0; i < FF_ARRAY_ELEMS(s->ref); i++) + av1_frame_unref(&s->ref[i]); + av1_frame_unref(&s->cur_frame); + av_buffer_unref(&s->seq_data_ref); + av_refstruct_unref(&s->seq_ref); + av_refstruct_unref(&s->header_ref); + av_refstruct_unref(&s->cll_ref); + av_refstruct_unref(&s->mdcv_ref); + av_freep(&s->tile_group_info); + + while (s->itut_t35_fifo && av_fifo_read(s->itut_t35_fifo, &itut_t35, 1) >= 0) + av_buffer_unref(&itut_t35.payload_ref); + av_fifo_freep2(&s->itut_t35_fifo); + + ff_cbs_fragment_free(&s->current_obu); + ff_cbs_close(&s->cbc); + ff_dovi_ctx_unref(&s->dovi); + + return 0; +} + +static int set_context_with_sequence(AVCodecContext *avctx, + const AV1RawSequenceHeader *seq) +{ + int width = seq->max_frame_width_minus_1 + 1; + int height = seq->max_frame_height_minus_1 + 1; + + avctx->profile = seq->seq_profile; + avctx->level = seq->seq_level_idx[0]; + + if (seq->color_config.color_description_present_flag) { + avctx->color_range = + seq->color_config.color_range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + avctx->color_primaries = seq->color_config.color_primaries; + avctx->colorspace = seq->color_config.matrix_coefficients; + avctx->color_trc = seq->color_config.transfer_characteristics; + } + + switch (seq->color_config.chroma_sample_position) { + case AV1_CSP_VERTICAL: + avctx->chroma_sample_location = AVCHROMA_LOC_LEFT; + break; + case AV1_CSP_COLOCATED: + avctx->chroma_sample_location = AVCHROMA_LOC_TOPLEFT; + break; + } + + if (avctx->width != width || avctx->height != height) { + int ret = ff_set_dimensions(avctx, width, height); + if (ret < 0) + return ret; + } + + if (seq->timing_info_present_flag) + avctx->framerate = ff_av1_framerate(1LL + seq->timing_info.num_ticks_per_picture_minus_1, + seq->timing_info.num_units_in_display_tick, + seq->timing_info.time_scale); + + if (avctx->pix_fmt == AV_PIX_FMT_NONE) + avctx->pix_fmt = get_sw_pixel_format(avctx, seq); + + return 0; +} + +static int update_context_with_frame_header(AVCodecContext *avctx, + const AV1RawFrameHeader *header) +{ + AVRational aspect_ratio; + int width = header->frame_width_minus_1 + 1; + int height = header->frame_height_minus_1 + 1; + int r_width = header->render_width_minus_1 + 1; + int r_height = header->render_height_minus_1 + 1; + int ret; + + if (avctx->width != width || avctx->height != height) { + ret = ff_set_dimensions(avctx, width, height); + if (ret < 0) + return ret; + } + + av_reduce(&aspect_ratio.num, &aspect_ratio.den, + (int64_t)height * r_width, + (int64_t)width * r_height, + INT_MAX); + + if (av_cmp_q(avctx->sample_aspect_ratio, aspect_ratio)) { + ret = ff_set_sar(avctx, aspect_ratio); + if (ret < 0) + return ret; + } + + return 0; +} + +static const CodedBitstreamUnitType decompose_unit_types[] = { + AV1_OBU_FRAME, + AV1_OBU_FRAME_HEADER, + AV1_OBU_METADATA, + AV1_OBU_REDUNDANT_FRAME_HEADER, + AV1_OBU_SEQUENCE_HEADER, + AV1_OBU_TEMPORAL_DELIMITER, + AV1_OBU_TILE_GROUP, +}; + +static av_cold int av1_decode_init(AVCodecContext *avctx) +{ + AV1DecContext *s = avctx->priv_data; + AV1RawSequenceHeader *seq; + const AVPacketSideData *sd; + int ret; + + s->avctx = avctx; + s->pkt = avctx->internal->in_pkt; + s->pix_fmt = AV_PIX_FMT_NONE; + + ret = ff_cbs_init(&s->cbc, AV_CODEC_ID_AV1, avctx); + if (ret < 0) + return ret; + + s->cbc->decompose_unit_types = decompose_unit_types; + s->cbc->nb_decompose_unit_types = FF_ARRAY_ELEMS(decompose_unit_types); + + s->itut_t35_fifo = av_fifo_alloc2(1, sizeof(AV1RawMetadataITUTT35), + AV_FIFO_FLAG_AUTO_GROW); + if (!s->itut_t35_fifo) + return AVERROR(ENOMEM); + + av_opt_set_int(s->cbc->priv_data, "operating_point", s->operating_point, 0); + + if (avctx->extradata && avctx->extradata_size) { + ret = ff_cbs_read_extradata_from_codec(s->cbc, + &s->current_obu, + avctx); + if (ret < 0) { + av_log(avctx, AV_LOG_WARNING, "Failed to read extradata.\n"); + goto end; + } + + seq = ((CodedBitstreamAV1Context *)(s->cbc->priv_data))->sequence_header; + if (!seq) { + if (!(avctx->extradata[0] & 0x80)) + av_log(avctx, AV_LOG_WARNING, "No sequence header available in extradata.\n"); + goto end; + } + + ret = set_context_with_sequence(avctx, seq); + if (ret < 0) { + av_log(avctx, AV_LOG_WARNING, "Failed to set decoder context.\n"); + goto end; + } + + end: + ff_cbs_fragment_reset(&s->current_obu); + } + + s->dovi.logctx = avctx; + s->dovi.cfg.dv_profile = 10; // default for AV1 + sd = ff_get_coded_side_data(avctx, AV_PKT_DATA_DOVI_CONF); + if (sd && sd->size >= sizeof(s->dovi.cfg)) + s->dovi.cfg = *(AVDOVIDecoderConfigurationRecord *) sd->data; + + return ret; +} + +static int av1_frame_alloc(AVCodecContext *avctx, AV1Frame *f) +{ + AV1DecContext *s = avctx->priv_data; + AV1RawFrameHeader *header= s->raw_frame_header; + AVFrame *frame; + int ret; + + ret = update_context_with_frame_header(avctx, header); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to update context with frame header\n"); + return ret; + } + + ret = ff_progress_frame_get_buffer(avctx, &f->pf, AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + goto fail; + + frame = f->f; + if (header->frame_type == AV1_FRAME_KEY) + frame->flags |= AV_FRAME_FLAG_KEY; + else + frame->flags &= ~AV_FRAME_FLAG_KEY; + + switch (header->frame_type) { + case AV1_FRAME_KEY: + case AV1_FRAME_INTRA_ONLY: + frame->pict_type = AV_PICTURE_TYPE_I; + break; + case AV1_FRAME_INTER: + frame->pict_type = AV_PICTURE_TYPE_P; + break; + case AV1_FRAME_SWITCH: + frame->pict_type = AV_PICTURE_TYPE_SP; + break; + } + + ret = ff_hwaccel_frame_priv_alloc(avctx, &f->hwaccel_picture_private); + if (ret < 0) + goto fail; + + return 0; + +fail: + av1_frame_unref(f); + return ret; +} + +static int export_itut_t35(AVCodecContext *avctx, AVFrame *frame, + const AV1RawMetadataITUTT35 *itut_t35) +{ + AV1DecContext *s = avctx->priv_data; + FFITUTT35 itut35 = { .country_code = itut_t35->itu_t_t35_country_code }; + FFITUTT35Aux aux = { .dovi = &s->dovi }; + int ret; + + ret = ff_itut_t35_parse_buffer(&itut35, itut_t35->payload, itut_t35->payload_size, + FF_ITUT_T35_FLAG_COUNTRY_CODE); + if (ret <= 0) + return ret; + + ret = ff_itut_t35_parse_payload_to_frame(&itut35, &aux, avctx, frame); + if (ret < 0) + return ret; + + return 0; +} + +static int export_metadata(AVCodecContext *avctx, AVFrame *frame) +{ + AV1DecContext *s = avctx->priv_data; + AV1RawMetadataITUTT35 itut_t35; + int ret = 0; + + if (s->mdcv) { + AVMasteringDisplayMetadata *mastering; + + ret = ff_decode_mastering_display_new(avctx, frame, &mastering); + if (ret < 0) + return ret; + + if (mastering) { + for (int i = 0; i < 3; i++) { + mastering->display_primaries[i][0] = av_make_q(s->mdcv->primary_chromaticity_x[i], 1 << 16); + mastering->display_primaries[i][1] = av_make_q(s->mdcv->primary_chromaticity_y[i], 1 << 16); + } + mastering->white_point[0] = av_make_q(s->mdcv->white_point_chromaticity_x, 1 << 16); + mastering->white_point[1] = av_make_q(s->mdcv->white_point_chromaticity_y, 1 << 16); + + mastering->max_luminance = av_make_q(s->mdcv->luminance_max, 1 << 8); + mastering->min_luminance = av_make_q(s->mdcv->luminance_min, 1 << 14); + + mastering->has_primaries = 1; + mastering->has_luminance = 1; + } + } + + if (s->cll) { + AVContentLightMetadata *light; + + ret = ff_decode_content_light_new(avctx, frame, &light); + if (ret < 0) + return ret; + + if (light) { + light->MaxCLL = s->cll->max_cll; + light->MaxFALL = s->cll->max_fall; + } + } + + while (av_fifo_read(s->itut_t35_fifo, &itut_t35, 1) >= 0) { + if (ret >= 0) + ret = export_itut_t35(avctx, frame, &itut_t35); + av_buffer_unref(&itut_t35.payload_ref); + } + + return ret; +} + +static int export_film_grain(AVCodecContext *avctx, AVFrame *frame) +{ + AV1DecContext *s = avctx->priv_data; + const AV1RawFilmGrainParams *film_grain = &s->cur_frame.film_grain; + const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(frame->format); + AVFilmGrainParams *fgp; + AVFilmGrainAOMParams *aom; + + av_assert0(pixdesc); + if (!film_grain->apply_grain) + return 0; + + fgp = av_film_grain_params_create_side_data(frame); + if (!fgp) + return AVERROR(ENOMEM); + + fgp->type = AV_FILM_GRAIN_PARAMS_AV1; + fgp->seed = film_grain->grain_seed; + fgp->width = frame->width; + fgp->height = frame->height; + fgp->color_range = frame->color_range; + fgp->color_primaries = frame->color_primaries; + fgp->color_trc = frame->color_trc; + fgp->color_space = frame->colorspace; + fgp->subsampling_x = pixdesc->log2_chroma_w; + fgp->subsampling_y = pixdesc->log2_chroma_h; + + aom = &fgp->codec.aom; + aom->chroma_scaling_from_luma = film_grain->chroma_scaling_from_luma; + aom->scaling_shift = film_grain->grain_scaling_minus_8 + 8; + aom->ar_coeff_lag = film_grain->ar_coeff_lag; + aom->ar_coeff_shift = film_grain->ar_coeff_shift_minus_6 + 6; + aom->grain_scale_shift = film_grain->grain_scale_shift; + aom->overlap_flag = film_grain->overlap_flag; + aom->limit_output_range = film_grain->clip_to_restricted_range; + + aom->num_y_points = film_grain->num_y_points; + for (int i = 0; i < film_grain->num_y_points; i++) { + aom->y_points[i][0] = film_grain->point_y_value[i]; + aom->y_points[i][1] = film_grain->point_y_scaling[i]; + } + aom->num_uv_points[0] = film_grain->num_cb_points; + for (int i = 0; i < film_grain->num_cb_points; i++) { + aom->uv_points[0][i][0] = film_grain->point_cb_value[i]; + aom->uv_points[0][i][1] = film_grain->point_cb_scaling[i]; + } + aom->num_uv_points[1] = film_grain->num_cr_points; + for (int i = 0; i < film_grain->num_cr_points; i++) { + aom->uv_points[1][i][0] = film_grain->point_cr_value[i]; + aom->uv_points[1][i][1] = film_grain->point_cr_scaling[i]; + } + + for (int i = 0; i < 24; i++) { + aom->ar_coeffs_y[i] = film_grain->ar_coeffs_y_plus_128[i] - 128; + } + for (int i = 0; i < 25; i++) { + aom->ar_coeffs_uv[0][i] = film_grain->ar_coeffs_cb_plus_128[i] - 128; + aom->ar_coeffs_uv[1][i] = film_grain->ar_coeffs_cr_plus_128[i] - 128; + } + + aom->uv_mult[0] = film_grain->cb_mult; + aom->uv_mult[1] = film_grain->cr_mult; + aom->uv_mult_luma[0] = film_grain->cb_luma_mult; + aom->uv_mult_luma[1] = film_grain->cr_luma_mult; + aom->uv_offset[0] = film_grain->cb_offset; + aom->uv_offset[1] = film_grain->cr_offset; + + return 0; +} + +static int set_output_frame(AVCodecContext *avctx, AVFrame *frame) +{ + AV1DecContext *s = avctx->priv_data; + const AVFrame *srcframe = s->cur_frame.f; + AVPacket *pkt = s->pkt; + int ret; + + // TODO: all layers + if (s->operating_point_idc && + av_log2(s->operating_point_idc >> 8) > s->cur_frame.spatial_id) + return 0; + + ret = av_frame_ref(frame, srcframe); + if (ret < 0) + return ret; + + ret = export_metadata(avctx, frame); + if (ret < 0) { + av_frame_unref(frame); + return ret; + } + + if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN) { + ret = export_film_grain(avctx, frame); + if (ret < 0) { + av_frame_unref(frame); + return ret; + } + } + + frame->pts = pkt->pts; + frame->pkt_dts = pkt->dts; + + av_packet_unref(pkt); + + return 0; +} + +static void update_reference_list(AVCodecContext *avctx) +{ + AV1DecContext *s = avctx->priv_data; + const AV1RawFrameHeader *header = s->raw_frame_header; + + for (int i = 0; i < AV1_NUM_REF_FRAMES; i++) { + if (header->refresh_frame_flags & (1 << i)) + av1_frame_replace(&s->ref[i], &s->cur_frame); + } +} + +static int get_current_frame(AVCodecContext *avctx) +{ + AV1DecContext *s = avctx->priv_data; + int ret; + + av1_frame_unref(&s->cur_frame); + + s->cur_frame.header_ref = av_refstruct_ref(s->header_ref); + + s->cur_frame.raw_frame_header = s->raw_frame_header; + + ret = init_tile_data(s); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to init tile data.\n"); + return ret; + } + + if ((avctx->skip_frame >= AVDISCARD_NONINTRA && + (s->raw_frame_header->frame_type != AV1_FRAME_KEY && + s->raw_frame_header->frame_type != AV1_FRAME_INTRA_ONLY)) || + (avctx->skip_frame >= AVDISCARD_NONKEY && + s->raw_frame_header->frame_type != AV1_FRAME_KEY) || + avctx->skip_frame >= AVDISCARD_ALL) + return 0; + + if (s->pix_fmt == AV_PIX_FMT_NONE) { + ret = get_pixel_format(avctx); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to get pixel format.\n"); + return ret; + } + + if (!ret && FF_HW_HAS_CB(avctx, decode_params)) { + ret = FF_HW_CALL(avctx, decode_params, AV1_OBU_SEQUENCE_HEADER, + s->seq_data_ref->data, s->seq_data_ref->size); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "HW accel decode params fail.\n"); + return ret; + } + } + } + + ret = av1_frame_alloc(avctx, &s->cur_frame); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, + "Failed to allocate space for current frame.\n"); + return ret; + } + + global_motion_params(s); + skip_mode_params(s); + coded_lossless_param(s); + order_hint_info(s); + load_grain_params(s); + + s->cur_frame.force_integer_mv = + s->raw_frame_header->force_integer_mv || + s->raw_frame_header->frame_type == AV1_FRAME_KEY || + s->raw_frame_header->frame_type == AV1_FRAME_INTRA_ONLY; + + return ret; +} + +static int av1_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) +{ + AV1DecContext *s = avctx->priv_data; + AV1RawTileGroup *raw_tile_group = NULL; + int i = 0, ret; + + for (i = s->nb_unit; i < s->current_obu.nb_units; i++) { + CodedBitstreamUnit *unit = &s->current_obu.units[i]; + AV1RawOBU *obu = unit->content; + const AV1RawOBUHeader *header; + + av_log(avctx, AV_LOG_DEBUG, "OBU idx:%d, type:%d, content available:%d.\n", i, unit->type, !!obu); + + if (unit->type == AV1_OBU_TILE_LIST) { + av_log(avctx, AV_LOG_ERROR, "Large scale tile decoding is unsupported.\n"); + ret = AVERROR_PATCHWELCOME; + goto end; + } + + if (!obu) + continue; + + header = &obu->header; + + switch (unit->type) { + case AV1_OBU_SEQUENCE_HEADER: + ret = av_buffer_replace(&s->seq_data_ref, unit->data_ref); + if (ret < 0) + goto end; + + s->seq_data_ref->data = unit->data; + s->seq_data_ref->size = unit->data_size; + av_refstruct_replace(&s->seq_ref, unit->content_ref); + + s->raw_seq = &obu->obu.sequence_header; + s->raw_frame_header = NULL; + raw_tile_group = NULL; + + ret = set_context_with_sequence(avctx, s->raw_seq); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to set context.\n"); + s->raw_seq = NULL; + goto end; + } + + s->operating_point_idc = s->raw_seq->operating_point_idc[s->operating_point]; + + s->pix_fmt = AV_PIX_FMT_NONE; + + if (FF_HW_HAS_CB(avctx, decode_params)) { + ret = FF_HW_CALL(avctx, decode_params, AV1_OBU_SEQUENCE_HEADER, + s->seq_data_ref->data, s->seq_data_ref->size); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "HW accel decode params fail.\n"); + return ret; + } + } + + break; + case AV1_OBU_REDUNDANT_FRAME_HEADER: + if (s->raw_frame_header) + break; + av_fallthrough; + case AV1_OBU_FRAME: + case AV1_OBU_FRAME_HEADER: + if (!s->raw_seq) { + av_log(avctx, AV_LOG_ERROR, "Missing Sequence Header.\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + av_refstruct_replace(&s->header_ref, unit->content_ref); + + raw_tile_group = NULL; + + if (unit->type == AV1_OBU_FRAME) + s->raw_frame_header = &obu->obu.frame.header; + else + s->raw_frame_header = &obu->obu.frame_header; + + if (s->raw_frame_header->show_existing_frame) { + av1_frame_replace(&s->cur_frame, + &s->ref[s->raw_frame_header->frame_to_show_map_idx]); + + update_reference_list(avctx); + + if (s->cur_frame.f) { + ret = set_output_frame(avctx, frame); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Set output frame error.\n"); + goto end; + } + } + + s->raw_frame_header = NULL; + i++; + ret = 0; + + goto end; + } + + ret = get_current_frame(avctx); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Get current frame error\n"); + goto end; + } + + s->cur_frame.spatial_id = header->spatial_id; + s->cur_frame.temporal_id = header->temporal_id; + + if (avctx->hwaccel && s->cur_frame.f) { + ret = FF_HW_CALL(avctx, start_frame, s->pkt->buf, + unit->data, unit->data_size); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "HW accel start frame fail.\n"); + goto end; + } + } + if (unit->type != AV1_OBU_FRAME) + break; + av_fallthrough; + case AV1_OBU_TILE_GROUP: + if (!s->raw_frame_header) { + av_log(avctx, AV_LOG_ERROR, "Missing Frame Header.\n"); + ret = AVERROR_INVALIDDATA; + goto end; + } + + if (unit->type == AV1_OBU_FRAME) + raw_tile_group = &obu->obu.frame.tile_group; + else + raw_tile_group = &obu->obu.tile_group; + + ret = get_tiles_info(avctx, raw_tile_group); + if (ret < 0) + goto end; + + if (avctx->hwaccel && s->cur_frame.f) { + ret = FF_HW_CALL(avctx, decode_slice, raw_tile_group->tile_data.data, + raw_tile_group->tile_data.data_size); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, + "HW accel decode slice fail.\n"); + goto end; + } + } + break; + case AV1_OBU_TEMPORAL_DELIMITER: + s->raw_frame_header = NULL; + raw_tile_group = NULL; + break; + case AV1_OBU_TILE_LIST: + case AV1_OBU_PADDING: + break; + case AV1_OBU_METADATA: + switch (obu->obu.metadata.metadata_type) { + case AV1_METADATA_TYPE_HDR_CLL: + av_refstruct_replace(&s->cll_ref, unit->content_ref); + s->cll = &obu->obu.metadata.metadata.hdr_cll; + break; + case AV1_METADATA_TYPE_HDR_MDCV: + av_refstruct_replace(&s->mdcv_ref, unit->content_ref); + s->mdcv = &obu->obu.metadata.metadata.hdr_mdcv; + break; + case AV1_METADATA_TYPE_ITUT_T35: { + AV1RawMetadataITUTT35 itut_t35; + memcpy(&itut_t35, &obu->obu.metadata.metadata.itut_t35, sizeof(itut_t35)); + itut_t35.payload_ref = av_buffer_ref(obu->obu.metadata.metadata.itut_t35.payload_ref); + if (!itut_t35.payload_ref) { + ret = AVERROR(ENOMEM); + goto end; + } + ret = av_fifo_write(s->itut_t35_fifo, &itut_t35, 1); + if (ret < 0) { + av_buffer_unref(&itut_t35.payload_ref); + goto end; + } + break; + } + default: + break; + } + break; + default: + av_log(avctx, AV_LOG_DEBUG, + "Unknown obu type: %d (%zu bits).\n", + unit->type, unit->data_size); + } + + if (raw_tile_group && (s->tile_num == raw_tile_group->tg_end + 1)) { + int show_frame = s->raw_frame_header->show_frame; + // Set nb_unit to point at the next OBU, to indicate which + // OBUs have been processed for this current frame. (If this + // frame gets output, we set nb_unit to this value later too.) + s->nb_unit = i + 1; + if (avctx->hwaccel && s->cur_frame.f) { + ret = FF_HW_SIMPLE_CALL(avctx, end_frame); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "HW accel end frame fail.\n"); + goto end; + } + } + + update_reference_list(avctx); + + // Set start_unit to indicate the first OBU of the next frame. + s->start_unit = s->nb_unit; + raw_tile_group = NULL; + s->raw_frame_header = NULL; + + if (show_frame) { + // cur_frame.f needn't exist due to skip_frame. + if (s->cur_frame.f) { + ret = set_output_frame(avctx, frame); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Set output frame error\n"); + goto end; + } + } + i++; + ret = 0; + goto end; + } + } + } + + ret = AVERROR(EAGAIN); +end: + av_assert0(i <= s->current_obu.nb_units); + s->nb_unit = i; + + if ((ret < 0 && ret != AVERROR(EAGAIN)) || s->current_obu.nb_units == i) { + if (ret < 0) + s->raw_frame_header = NULL; + av_packet_unref(s->pkt); + ff_cbs_fragment_reset(&s->current_obu); + s->nb_unit = s->start_unit = 0; + } + if (!ret && !frame->buf[0]) + ret = AVERROR(EAGAIN); + + return ret; +} + +static int av1_receive_frame(AVCodecContext *avctx, AVFrame *frame) +{ + AV1DecContext *s = avctx->priv_data; + int ret; + + do { + if (!s->current_obu.nb_units) { + ret = ff_decode_get_packet(avctx, s->pkt); + if (ret < 0) + return ret; + + ret = ff_cbs_read_packet(s->cbc, &s->current_obu, s->pkt); + if (ret < 0) { + ff_cbs_fragment_reset(&s->current_obu); + av_packet_unref(s->pkt); + av_log(avctx, AV_LOG_ERROR, "Failed to read packet.\n"); + return ret; + } + + s->nb_unit = s->start_unit = 0; + av_log(avctx, AV_LOG_DEBUG, "Total OBUs on this packet: %d.\n", + s->current_obu.nb_units); + } + + ret = av1_receive_frame_internal(avctx, frame); + } while (ret == AVERROR(EAGAIN)); + + return ret; +} + +static av_cold void av1_decode_flush(AVCodecContext *avctx) +{ + AV1DecContext *s = avctx->priv_data; + AV1RawMetadataITUTT35 itut_t35; + + for (int i = 0; i < FF_ARRAY_ELEMS(s->ref); i++) + av1_frame_unref(&s->ref[i]); + + av1_frame_unref(&s->cur_frame); + s->operating_point_idc = 0; + s->nb_unit = s->start_unit = 0; + s->raw_frame_header = NULL; + s->raw_seq = NULL; + s->cll = NULL; + s->mdcv = NULL; + while (av_fifo_read(s->itut_t35_fifo, &itut_t35, 1) >= 0) + av_buffer_unref(&itut_t35.payload_ref); + + ff_cbs_fragment_reset(&s->current_obu); + ff_cbs_flush(s->cbc); + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); +} + +#define OFFSET(x) offsetof(AV1DecContext, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption av1_options[] = { + { "operating_point", "Select an operating point of the scalable bitstream", + OFFSET(operating_point), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, AV1_MAX_OPERATING_POINTS - 1, VD }, + { NULL } +}; + +static const AVClass av1_class = { + .class_name = "AV1 decoder", + .item_name = av_default_item_name, + .option = av1_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_av1_decoder = { + .p.name = "av1", + CODEC_LONG_NAME("Alliance for Open Media AV1"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .priv_data_size = sizeof(AV1DecContext), + .init = av1_decode_init, + .close = av1_decode_free, + FF_CODEC_RECEIVE_FRAME_CB(av1_receive_frame), + .p.capabilities = AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | + FF_CODEC_CAP_USES_PROGRESSFRAMES, + .flush = av1_decode_flush, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_av1_profiles), + .p.priv_class = &av1_class, + .hw_configs = (const AVCodecHWConfigInternal *const []) { +#if CONFIG_AV1_DXVA2_HWACCEL + HWACCEL_DXVA2(av1), +#endif +#if CONFIG_AV1_D3D11VA_HWACCEL + HWACCEL_D3D11VA(av1), +#endif +#if CONFIG_AV1_D3D11VA2_HWACCEL + HWACCEL_D3D11VA2(av1), +#endif +#if CONFIG_AV1_D3D12VA_HWACCEL + HWACCEL_D3D12VA(av1), +#endif +#if CONFIG_AV1_NVDEC_HWACCEL + HWACCEL_NVDEC(av1), +#endif +#if CONFIG_AV1_VAAPI_HWACCEL + HWACCEL_VAAPI(av1), +#endif +#if CONFIG_AV1_VDPAU_HWACCEL + HWACCEL_VDPAU(av1), +#endif +#if CONFIG_AV1_VIDEOTOOLBOX_HWACCEL + HWACCEL_VIDEOTOOLBOX(av1), +#endif +#if CONFIG_AV1_VULKAN_HWACCEL + HWACCEL_VULKAN(av1), +#endif + + NULL + }, +}; diff --git a/thirdparty/ffmpeg/libavcodec/h264_slice.c b/thirdparty/ffmpeg/libavcodec/h264_slice.c index 7e2c46029..f6fc26130 100644 --- a/thirdparty/ffmpeg/libavcodec/h264_slice.c +++ b/thirdparty/ffmpeg/libavcodec/h264_slice.c @@ -790,6 +790,7 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) (CONFIG_H264_D3D11VA_HWACCEL * 2) + \ CONFIG_H264_D3D12VA_HWACCEL + \ CONFIG_H264_NVDEC_HWACCEL + \ + CONFIG_H264_V4L2REQUEST_HWACCEL + \ CONFIG_H264_VAAPI_HWACCEL + \ CONFIG_H264_VIDEOTOOLBOX_HWACCEL + \ CONFIG_H264_VDPAU_HWACCEL + \ @@ -818,6 +819,9 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) #endif #if CONFIG_H264_NVDEC_HWACCEL *fmt++ = AV_PIX_FMT_CUDA; +#endif +#if CONFIG_H264_V4L2REQUEST_HWACCEL + *fmt++ = AV_PIX_FMT_DRM_PRIME; #endif if (CHROMA444(h)) { if (h->avctx->colorspace == AVCOL_SPC_RGB) { @@ -874,6 +878,9 @@ static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) #if CONFIG_H264_VIDEOTOOLBOX_HWACCEL if (h->avctx->colorspace != AVCOL_SPC_RGB) *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_H264_V4L2REQUEST_HWACCEL + *fmt++ = AV_PIX_FMT_DRM_PRIME; #endif if (CHROMA444(h)) { if (h->avctx->colorspace == AVCOL_SPC_RGB) @@ -1702,7 +1709,7 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl, unsigned int slice_type, tmp, i; int field_pic_flag, bottom_field_flag; int first_slice = sl == h->slice_ctx && !h->current_slice; - int picture_structure; + int picture_structure, pos; if (first_slice) av_assert0(!h->setup_finished); @@ -1793,6 +1800,7 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl, sl->poc_lsb = 0; sl->delta_poc_bottom = 0; + pos = get_bits_left(&sl->gb); if (sps->poc_type == 0) { sl->poc_lsb = get_bits(&sl->gb, sps->log2_max_poc_lsb); @@ -1807,6 +1815,7 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl, if (pps->pic_order_present == 1 && picture_structure == PICT_FRAME) sl->delta_poc[1] = get_se_golomb(&sl->gb); } + sl->pic_order_cnt_bit_size = pos - get_bits_left(&sl->gb); sl->redundant_pic_count = 0; if (pps->redundant_pic_cnt_present) @@ -1846,9 +1855,11 @@ static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl, sl->explicit_ref_marking = 0; if (nal->ref_idc) { + pos = get_bits_left(&sl->gb); ret = ff_h264_decode_ref_pic_marking(sl, &sl->gb, nal, h->avctx); if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE)) return AVERROR_INVALIDDATA; + sl->ref_pic_marking_bit_size = pos - get_bits_left(&sl->gb); } if (sl->slice_type_nos != AV_PICTURE_TYPE_I && pps->cabac) { diff --git a/thirdparty/ffmpeg/libavcodec/h264_slice.c.orig b/thirdparty/ffmpeg/libavcodec/h264_slice.c.orig new file mode 100644 index 000000000..7e2c46029 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/h264_slice.c.orig @@ -0,0 +1,2850 @@ +/* + * H.26L/H.264/AVC/JVT/14496-10/... decoder + * Copyright (c) 2003 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * H.264 / AVC / MPEG-4 part10 codec. + * @author Michael Niedermayer + */ + +#include "config_components.h" + +#include "libavutil/avassert.h" +#include "libavutil/mem.h" +#include "libavutil/pixdesc.h" +#include "libavutil/timecode.h" +#include "decode.h" +#include "cabac.h" +#include "cabac_functions.h" +#include "error_resilience.h" +#include "avcodec.h" +#include "h264.h" +#include "h264dec.h" +#include "h264data.h" +#include "h264chroma.h" +#include "h264_ps.h" +#include "golomb.h" +#include "mathops.h" +#include "mpegutils.h" +#include "rectangle.h" +#include "libavutil/refstruct.h" +#include "thread.h" +#include "threadframe.h" + +static const uint8_t field_scan[16+1] = { + 0 + 0 * 4, 0 + 1 * 4, 1 + 0 * 4, 0 + 2 * 4, + 0 + 3 * 4, 1 + 1 * 4, 1 + 2 * 4, 1 + 3 * 4, + 2 + 0 * 4, 2 + 1 * 4, 2 + 2 * 4, 2 + 3 * 4, + 3 + 0 * 4, 3 + 1 * 4, 3 + 2 * 4, 3 + 3 * 4, +}; + +static const uint8_t field_scan8x8[64+1] = { + 0 + 0 * 8, 0 + 1 * 8, 0 + 2 * 8, 1 + 0 * 8, + 1 + 1 * 8, 0 + 3 * 8, 0 + 4 * 8, 1 + 2 * 8, + 2 + 0 * 8, 1 + 3 * 8, 0 + 5 * 8, 0 + 6 * 8, + 0 + 7 * 8, 1 + 4 * 8, 2 + 1 * 8, 3 + 0 * 8, + 2 + 2 * 8, 1 + 5 * 8, 1 + 6 * 8, 1 + 7 * 8, + 2 + 3 * 8, 3 + 1 * 8, 4 + 0 * 8, 3 + 2 * 8, + 2 + 4 * 8, 2 + 5 * 8, 2 + 6 * 8, 2 + 7 * 8, + 3 + 3 * 8, 4 + 1 * 8, 5 + 0 * 8, 4 + 2 * 8, + 3 + 4 * 8, 3 + 5 * 8, 3 + 6 * 8, 3 + 7 * 8, + 4 + 3 * 8, 5 + 1 * 8, 6 + 0 * 8, 5 + 2 * 8, + 4 + 4 * 8, 4 + 5 * 8, 4 + 6 * 8, 4 + 7 * 8, + 5 + 3 * 8, 6 + 1 * 8, 6 + 2 * 8, 5 + 4 * 8, + 5 + 5 * 8, 5 + 6 * 8, 5 + 7 * 8, 6 + 3 * 8, + 7 + 0 * 8, 7 + 1 * 8, 6 + 4 * 8, 6 + 5 * 8, + 6 + 6 * 8, 6 + 7 * 8, 7 + 2 * 8, 7 + 3 * 8, + 7 + 4 * 8, 7 + 5 * 8, 7 + 6 * 8, 7 + 7 * 8, +}; + +static const uint8_t field_scan8x8_cavlc[64+1] = { + 0 + 0 * 8, 1 + 1 * 8, 2 + 0 * 8, 0 + 7 * 8, + 2 + 2 * 8, 2 + 3 * 8, 2 + 4 * 8, 3 + 3 * 8, + 3 + 4 * 8, 4 + 3 * 8, 4 + 4 * 8, 5 + 3 * 8, + 5 + 5 * 8, 7 + 0 * 8, 6 + 6 * 8, 7 + 4 * 8, + 0 + 1 * 8, 0 + 3 * 8, 1 + 3 * 8, 1 + 4 * 8, + 1 + 5 * 8, 3 + 1 * 8, 2 + 5 * 8, 4 + 1 * 8, + 3 + 5 * 8, 5 + 1 * 8, 4 + 5 * 8, 6 + 1 * 8, + 5 + 6 * 8, 7 + 1 * 8, 6 + 7 * 8, 7 + 5 * 8, + 0 + 2 * 8, 0 + 4 * 8, 0 + 5 * 8, 2 + 1 * 8, + 1 + 6 * 8, 4 + 0 * 8, 2 + 6 * 8, 5 + 0 * 8, + 3 + 6 * 8, 6 + 0 * 8, 4 + 6 * 8, 6 + 2 * 8, + 5 + 7 * 8, 6 + 4 * 8, 7 + 2 * 8, 7 + 6 * 8, + 1 + 0 * 8, 1 + 2 * 8, 0 + 6 * 8, 3 + 0 * 8, + 1 + 7 * 8, 3 + 2 * 8, 2 + 7 * 8, 4 + 2 * 8, + 3 + 7 * 8, 5 + 2 * 8, 4 + 7 * 8, 5 + 4 * 8, + 6 + 3 * 8, 6 + 5 * 8, 7 + 3 * 8, 7 + 7 * 8, +}; + +// zigzag_scan8x8_cavlc[i] = zigzag_scan8x8[(i/4) + 16*(i%4)] +static const uint8_t zigzag_scan8x8_cavlc[64+1] = { + 0 + 0 * 8, 1 + 1 * 8, 1 + 2 * 8, 2 + 2 * 8, + 4 + 1 * 8, 0 + 5 * 8, 3 + 3 * 8, 7 + 0 * 8, + 3 + 4 * 8, 1 + 7 * 8, 5 + 3 * 8, 6 + 3 * 8, + 2 + 7 * 8, 6 + 4 * 8, 5 + 6 * 8, 7 + 5 * 8, + 1 + 0 * 8, 2 + 0 * 8, 0 + 3 * 8, 3 + 1 * 8, + 3 + 2 * 8, 0 + 6 * 8, 4 + 2 * 8, 6 + 1 * 8, + 2 + 5 * 8, 2 + 6 * 8, 6 + 2 * 8, 5 + 4 * 8, + 3 + 7 * 8, 7 + 3 * 8, 4 + 7 * 8, 7 + 6 * 8, + 0 + 1 * 8, 3 + 0 * 8, 0 + 4 * 8, 4 + 0 * 8, + 2 + 3 * 8, 1 + 5 * 8, 5 + 1 * 8, 5 + 2 * 8, + 1 + 6 * 8, 3 + 5 * 8, 7 + 1 * 8, 4 + 5 * 8, + 4 + 6 * 8, 7 + 4 * 8, 5 + 7 * 8, 6 + 7 * 8, + 0 + 2 * 8, 2 + 1 * 8, 1 + 3 * 8, 5 + 0 * 8, + 1 + 4 * 8, 2 + 4 * 8, 6 + 0 * 8, 4 + 3 * 8, + 0 + 7 * 8, 4 + 4 * 8, 7 + 2 * 8, 3 + 6 * 8, + 5 + 5 * 8, 6 + 5 * 8, 6 + 6 * 8, 7 + 7 * 8, +}; + +static void release_unused_pictures(H264Context *h, int remove_current) +{ + int i; + + /* release non reference frames */ + for (i = 0; i < H264_MAX_PICTURE_COUNT; i++) { + if (h->DPB[i].f->buf[0] && !h->DPB[i].reference && + (remove_current || &h->DPB[i] != h->cur_pic_ptr)) { + ff_h264_unref_picture(&h->DPB[i]); + } + } +} + +static int alloc_scratch_buffers(H264SliceContext *sl, int linesize) +{ + const H264Context *h = sl->h264; + int alloc_size = FFALIGN(FFABS(linesize) + 32, 32); + + av_fast_malloc(&sl->bipred_scratchpad, &sl->bipred_scratchpad_allocated, 16 * 6 * alloc_size); + // edge emu needs blocksize + filter length - 1 + // (= 21x21 for H.264) + av_fast_malloc(&sl->edge_emu_buffer, &sl->edge_emu_buffer_allocated, alloc_size * 2 * 21); + + av_fast_mallocz(&sl->top_borders[0], &sl->top_borders_allocated[0], + h->mb_width * 16 * 3 * sizeof(uint8_t) * 2); + av_fast_mallocz(&sl->top_borders[1], &sl->top_borders_allocated[1], + h->mb_width * 16 * 3 * sizeof(uint8_t) * 2); + + if (!sl->bipred_scratchpad || !sl->edge_emu_buffer || + !sl->top_borders[0] || !sl->top_borders[1]) { + av_freep(&sl->bipred_scratchpad); + av_freep(&sl->edge_emu_buffer); + av_freep(&sl->top_borders[0]); + av_freep(&sl->top_borders[1]); + + sl->bipred_scratchpad_allocated = 0; + sl->edge_emu_buffer_allocated = 0; + sl->top_borders_allocated[0] = 0; + sl->top_borders_allocated[1] = 0; + return AVERROR(ENOMEM); + } + + return 0; +} + +static int init_table_pools(H264Context *h) +{ + const int big_mb_num = h->mb_stride * (h->mb_height + 1) + 1; + const int mb_array_size = h->mb_stride * h->mb_height; + const int b4_stride = h->mb_width * 4 + 1; + const int b4_array_size = b4_stride * h->mb_height * 4; + + h->qscale_table_pool = av_refstruct_pool_alloc(big_mb_num + h->mb_stride, 0); + h->mb_type_pool = av_refstruct_pool_alloc((big_mb_num + h->mb_stride) * + sizeof(uint32_t), 0); + h->motion_val_pool = av_refstruct_pool_alloc(2 * (b4_array_size + 4) * + sizeof(int16_t), 0); + h->ref_index_pool = av_refstruct_pool_alloc(4 * mb_array_size, 0); + + if (!h->qscale_table_pool || !h->mb_type_pool || !h->motion_val_pool || + !h->ref_index_pool) { + av_refstruct_pool_uninit(&h->qscale_table_pool); + av_refstruct_pool_uninit(&h->mb_type_pool); + av_refstruct_pool_uninit(&h->motion_val_pool); + av_refstruct_pool_uninit(&h->ref_index_pool); + return AVERROR(ENOMEM); + } + + return 0; +} + +static int alloc_picture(H264Context *h, H264Picture *pic) +{ + int i, ret = 0; + + av_assert0(!pic->f->data[0]); + + if (h->sei.common.itut_t35.lcevc) { + ret = ff_frame_new_side_data_from_buf(h->avctx, pic->f, AV_FRAME_DATA_LCEVC, &h->sei.common.itut_t35.lcevc); + if (ret < 0) + return ret; + } + + pic->tf.f = pic->f; + ret = ff_thread_get_ext_buffer(h->avctx, &pic->tf, + pic->reference ? AV_GET_BUFFER_FLAG_REF : 0); + if (ret < 0) + goto fail; + + if (pic->needs_fg) { + pic->f_grain->format = pic->f->format; + pic->f_grain->width = pic->f->width; + pic->f_grain->height = pic->f->height; + ret = ff_thread_get_buffer(h->avctx, pic->f_grain, 0); + if (ret < 0) + goto fail; + } + + ret = ff_hwaccel_frame_priv_alloc(h->avctx, &pic->hwaccel_picture_private); + if (ret < 0) + goto fail; + + if (h->decode_error_flags_pool) { + pic->decode_error_flags = av_refstruct_pool_get(h->decode_error_flags_pool); + if (!pic->decode_error_flags) + goto fail; + atomic_init(pic->decode_error_flags, 0); + } + + if (CONFIG_GRAY && !h->avctx->hwaccel && h->flags & AV_CODEC_FLAG_GRAY && pic->f->data[2]) { + int h_chroma_shift, v_chroma_shift; + av_pix_fmt_get_chroma_sub_sample(pic->f->format, + &h_chroma_shift, &v_chroma_shift); + + for(i=0; if->height, v_chroma_shift); i++) { + memset(pic->f->data[1] + pic->f->linesize[1]*i, + 0x80, AV_CEIL_RSHIFT(pic->f->width, h_chroma_shift)); + memset(pic->f->data[2] + pic->f->linesize[2]*i, + 0x80, AV_CEIL_RSHIFT(pic->f->width, h_chroma_shift)); + } + } + + if (!h->qscale_table_pool) { + ret = init_table_pools(h); + if (ret < 0) + goto fail; + } + + pic->qscale_table_base = av_refstruct_pool_get(h->qscale_table_pool); + pic->mb_type_base = av_refstruct_pool_get(h->mb_type_pool); + if (!pic->qscale_table_base || !pic->mb_type_base) + goto fail; + + pic->mb_type = pic->mb_type_base + 2 * h->mb_stride + 1; + pic->qscale_table = pic->qscale_table_base + 2 * h->mb_stride + 1; + + for (i = 0; i < 2; i++) { + pic->motion_val_base[i] = av_refstruct_pool_get(h->motion_val_pool); + pic->ref_index[i] = av_refstruct_pool_get(h->ref_index_pool); + if (!pic->motion_val_base[i] || !pic->ref_index[i]) + goto fail; + + pic->motion_val[i] = pic->motion_val_base[i] + 4; + } + + pic->pps = av_refstruct_ref_c(h->ps.pps); + + pic->mb_width = h->mb_width; + pic->mb_height = h->mb_height; + pic->mb_stride = h->mb_stride; + + return 0; +fail: + ff_h264_unref_picture(pic); + return (ret < 0) ? ret : AVERROR(ENOMEM); +} + +static int find_unused_picture(const H264Context *h) +{ + int i; + + for (i = 0; i < H264_MAX_PICTURE_COUNT; i++) { + if (!h->DPB[i].f->buf[0]) + return i; + } + return AVERROR_INVALIDDATA; +} + + +#define IN_RANGE(a, b, size) (((void*)(a) >= (void*)(b)) && ((void*)(a) < (void*)((b) + (size)))) + +#define REBASE_PICTURE(pic, new_ctx, old_ctx) \ + (((pic) && (pic) >= (old_ctx)->DPB && \ + (pic) < (old_ctx)->DPB + H264_MAX_PICTURE_COUNT) ? \ + &(new_ctx)->DPB[(pic) - (old_ctx)->DPB] : NULL) + +static void copy_picture_range(H264Picture **to, H264Picture *const *from, int count, + H264Context *new_base, const H264Context *old_base) +{ + int i; + + for (i = 0; i < count; i++) { + av_assert1(!from[i] || + IN_RANGE(from[i], old_base, 1) || + IN_RANGE(from[i], old_base->DPB, H264_MAX_PICTURE_COUNT)); + to[i] = REBASE_PICTURE(from[i], new_base, old_base); + } +} + +static void color_frame(AVFrame *frame, const int c[4]) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + + av_assert0(desc->flags & AV_PIX_FMT_FLAG_PLANAR); + + for (int p = 0; p < desc->nb_components; p++) { + uint8_t *dst = frame->data[p]; + int is_chroma = p == 1 || p == 2; + int bytes = is_chroma ? AV_CEIL_RSHIFT(frame->width, desc->log2_chroma_w) : frame->width; + int height = is_chroma ? AV_CEIL_RSHIFT(frame->height, desc->log2_chroma_h) : frame->height; + if (desc->comp[0].depth >= 9) { + if (bytes >= 1) + ((uint16_t*)dst)[0] = c[p]; + if (bytes >= 2) + av_memcpy_backptr(dst + 2, 2, 2 * (bytes - 1)); + dst += frame->linesize[p]; + for (int y = 1; y < height; y++) { + memcpy(dst, frame->data[p], 2*bytes); + dst += frame->linesize[p]; + } + } else { + for (int y = 0; y < height; y++) { + memset(dst, c[p], bytes); + dst += frame->linesize[p]; + } + } + } +} + +static int h264_slice_header_init(H264Context *h); + +int ff_h264_update_thread_context(AVCodecContext *dst, + const AVCodecContext *src) +{ + H264Context *h = dst->priv_data, *h1 = src->priv_data; + int inited = h->context_initialized, err = 0; + int need_reinit = 0; + int i, ret; + + if (dst == src) + return 0; + + if (inited && !h1->ps.sps) + return AVERROR_INVALIDDATA; + + if (inited && + (h->width != h1->width || + h->height != h1->height || + h->mb_width != h1->mb_width || + h->mb_height != h1->mb_height || + !h->ps.sps || + h->ps.sps->bit_depth_luma != h1->ps.sps->bit_depth_luma || + h->ps.sps->chroma_format_idc != h1->ps.sps->chroma_format_idc || + h->ps.sps->vui.matrix_coeffs != h1->ps.sps->vui.matrix_coeffs)) { + need_reinit = 1; + } + + /* copy block_offset since frame_start may not be called */ + memcpy(h->block_offset, h1->block_offset, sizeof(h->block_offset)); + + // SPS/PPS + for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.sps_list); i++) + av_refstruct_replace(&h->ps.sps_list[i], h1->ps.sps_list[i]); + for (int i = 0; i < FF_ARRAY_ELEMS(h->ps.pps_list); i++) + av_refstruct_replace(&h->ps.pps_list[i], h1->ps.pps_list[i]); + + av_refstruct_replace(&h->ps.pps, h1->ps.pps); + h->ps.sps = h1->ps.sps; + + if (need_reinit || !inited) { + h->width = h1->width; + h->height = h1->height; + h->mb_height = h1->mb_height; + h->mb_width = h1->mb_width; + h->mb_num = h1->mb_num; + h->mb_stride = h1->mb_stride; + h->b_stride = h1->b_stride; + h->x264_build = h1->x264_build; + + if (h->context_initialized || h1->context_initialized) { + if ((err = h264_slice_header_init(h)) < 0) { + av_log(h->avctx, AV_LOG_ERROR, "h264_slice_header_init() failed"); + return err; + } + } + + /* copy block_offset since frame_start may not be called */ + memcpy(h->block_offset, h1->block_offset, sizeof(h->block_offset)); + } + + h->width_from_caller = h1->width_from_caller; + h->height_from_caller = h1->height_from_caller; + h->first_field = h1->first_field; + h->picture_structure = h1->picture_structure; + h->mb_aff_frame = h1->mb_aff_frame; + h->droppable = h1->droppable; + + for (i = 0; i < H264_MAX_PICTURE_COUNT; i++) { + ret = ff_h264_replace_picture(&h->DPB[i], &h1->DPB[i]); + if (ret < 0) + return ret; + } + + h->cur_pic_ptr = REBASE_PICTURE(h1->cur_pic_ptr, h, h1); + ret = ff_h264_replace_picture(&h->cur_pic, &h1->cur_pic); + if (ret < 0) + return ret; + + h->enable_er = h1->enable_er; + h->workaround_bugs = h1->workaround_bugs; + h->droppable = h1->droppable; + + // extradata/NAL handling + h->is_avc = h1->is_avc; + h->nal_length_size = h1->nal_length_size; + + memcpy(&h->poc, &h1->poc, sizeof(h->poc)); + + memcpy(h->short_ref, h1->short_ref, sizeof(h->short_ref)); + memcpy(h->long_ref, h1->long_ref, sizeof(h->long_ref)); + memcpy(h->delayed_pic, h1->delayed_pic, sizeof(h->delayed_pic)); + memcpy(h->last_pocs, h1->last_pocs, sizeof(h->last_pocs)); + + h->next_output_pic = h1->next_output_pic; + h->next_outputed_poc = h1->next_outputed_poc; + h->poc_offset = h1->poc_offset; + + memcpy(h->mmco, h1->mmco, sizeof(h->mmco)); + h->nb_mmco = h1->nb_mmco; + h->mmco_reset = h1->mmco_reset; + h->explicit_ref_marking = h1->explicit_ref_marking; + h->long_ref_count = h1->long_ref_count; + h->short_ref_count = h1->short_ref_count; + + copy_picture_range(h->short_ref, h1->short_ref, 32, h, h1); + copy_picture_range(h->long_ref, h1->long_ref, 32, h, h1); + copy_picture_range(h->delayed_pic, h1->delayed_pic, + FF_ARRAY_ELEMS(h->delayed_pic), h, h1); + + h->frame_recovered = h1->frame_recovered; + + ret = ff_h2645_sei_ctx_replace(&h->sei.common, &h1->sei.common); + if (ret < 0) + return ret; + + h->sei.common.unregistered.x264_build = h1->sei.common.unregistered.x264_build; + + if (!h->cur_pic_ptr) + return 0; + + if (!h->droppable) { + err = ff_h264_execute_ref_pic_marking(h); + h->poc.prev_poc_msb = h->poc.poc_msb; + h->poc.prev_poc_lsb = h->poc.poc_lsb; + } + h->poc.prev_frame_num_offset = h->poc.frame_num_offset; + h->poc.prev_frame_num = h->poc.frame_num; + + h->recovery_frame = h1->recovery_frame; + h->non_gray = h1->non_gray; + + return err; +} + +int ff_h264_update_thread_context_for_user(AVCodecContext *dst, + const AVCodecContext *src) +{ + H264Context *h = dst->priv_data; + const H264Context *h1 = src->priv_data; + + h->is_avc = h1->is_avc; + h->nal_length_size = h1->nal_length_size; + + return 0; +} + +static int h264_frame_start(H264Context *h) +{ + H264Picture *pic; + int i, ret; + const int pixel_shift = h->pixel_shift; + + if (!ff_thread_can_start_frame(h->avctx)) { + av_log(h->avctx, AV_LOG_ERROR, "Attempt to start a frame outside SETUP state\n"); + return AVERROR_BUG; + } + + release_unused_pictures(h, 1); + h->cur_pic_ptr = NULL; + + i = find_unused_picture(h); + if (i < 0) { + av_log(h->avctx, AV_LOG_ERROR, "no frame buffer available\n"); + return i; + } + pic = &h->DPB[i]; + + pic->reference = h->droppable ? 0 : h->picture_structure; + pic->field_picture = h->picture_structure != PICT_FRAME; + pic->frame_num = h->poc.frame_num; + /* + * Zero key_frame here; IDR markings per slice in frame or fields are ORed + * in later. + * See decode_nal_units(). + */ + pic->f->flags &= ~AV_FRAME_FLAG_KEY; + pic->mmco_reset = 0; + pic->recovered = 0; + pic->invalid_gap = 0; + pic->sei_recovery_frame_cnt = h->sei.recovery_point.recovery_frame_cnt; + + pic->f->pict_type = h->slice_ctx[0].slice_type; + + pic->f->crop_left = h->crop_left; + pic->f->crop_right = h->crop_right; + pic->f->crop_top = h->crop_top; + pic->f->crop_bottom = h->crop_bottom; + + pic->needs_fg = + h->sei.common.film_grain_characteristics && + h->sei.common.film_grain_characteristics->present && + !h->avctx->hwaccel && + !(h->avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN); + + if ((ret = alloc_picture(h, pic)) < 0) + return ret; + + h->cur_pic_ptr = pic; + ff_h264_unref_picture(&h->cur_pic); + if (CONFIG_ERROR_RESILIENCE) { + ff_h264_set_erpic(&h->er.cur_pic, NULL); + } + + if ((ret = ff_h264_ref_picture(&h->cur_pic, h->cur_pic_ptr)) < 0) + return ret; + + for (i = 0; i < h->nb_slice_ctx; i++) { + h->slice_ctx[i].linesize = h->cur_pic_ptr->f->linesize[0]; + h->slice_ctx[i].uvlinesize = h->cur_pic_ptr->f->linesize[1]; + } + + if (CONFIG_ERROR_RESILIENCE && h->enable_er) { + ff_er_frame_start(&h->er); + ff_h264_set_erpic(&h->er.last_pic, NULL); + ff_h264_set_erpic(&h->er.next_pic, NULL); + } + + for (i = 0; i < 16; i++) { + h->block_offset[i] = (4 * ((scan8[i] - scan8[0]) & 7) << pixel_shift) + 4 * pic->f->linesize[0] * ((scan8[i] - scan8[0]) >> 3); + h->block_offset[48 + i] = (4 * ((scan8[i] - scan8[0]) & 7) << pixel_shift) + 8 * pic->f->linesize[0] * ((scan8[i] - scan8[0]) >> 3); + } + for (i = 0; i < 16; i++) { + h->block_offset[16 + i] = + h->block_offset[32 + i] = (4 * ((scan8[i] - scan8[0]) & 7) << pixel_shift) + 4 * pic->f->linesize[1] * ((scan8[i] - scan8[0]) >> 3); + h->block_offset[48 + 16 + i] = + h->block_offset[48 + 32 + i] = (4 * ((scan8[i] - scan8[0]) & 7) << pixel_shift) + 8 * pic->f->linesize[1] * ((scan8[i] - scan8[0]) >> 3); + } + + /* We mark the current picture as non-reference after allocating it, so + * that if we break out due to an error it can be released automatically + * in the next ff_mpv_frame_start(). + */ + h->cur_pic_ptr->reference = 0; + + h->cur_pic_ptr->field_poc[0] = h->cur_pic_ptr->field_poc[1] = INT_MAX; + + h->next_output_pic = NULL; + + h->postpone_filter = 0; + + h->mb_aff_frame = h->ps.sps->mb_aff && (h->picture_structure == PICT_FRAME); + + if (h->sei.common.unregistered.x264_build >= 0) + h->x264_build = h->sei.common.unregistered.x264_build; + + assert(h->cur_pic_ptr->long_ref == 0); + + return 0; +} + +static av_always_inline void backup_mb_border(const H264Context *h, H264SliceContext *sl, + const uint8_t *src_y, + const uint8_t *src_cb, const uint8_t *src_cr, + int linesize, int uvlinesize, + int simple) +{ + uint8_t *top_border; + int top_idx = 1; + const int pixel_shift = h->pixel_shift; + int chroma444 = CHROMA444(h); + int chroma422 = CHROMA422(h); + + src_y -= linesize; + src_cb -= uvlinesize; + src_cr -= uvlinesize; + + if (!simple && FRAME_MBAFF(h)) { + if (sl->mb_y & 1) { + if (!MB_MBAFF(sl)) { + top_border = sl->top_borders[0][sl->mb_x]; + AV_COPY128(top_border, src_y + 15 * linesize); + if (pixel_shift) + AV_COPY128(top_border + 16, src_y + 15 * linesize + 16); + if (simple || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) { + if (chroma444) { + if (pixel_shift) { + AV_COPY128(top_border + 32, src_cb + 15 * uvlinesize); + AV_COPY128(top_border + 48, src_cb + 15 * uvlinesize + 16); + AV_COPY128(top_border + 64, src_cr + 15 * uvlinesize); + AV_COPY128(top_border + 80, src_cr + 15 * uvlinesize + 16); + } else { + AV_COPY128(top_border + 16, src_cb + 15 * uvlinesize); + AV_COPY128(top_border + 32, src_cr + 15 * uvlinesize); + } + } else if (chroma422) { + if (pixel_shift) { + AV_COPY128(top_border + 32, src_cb + 15 * uvlinesize); + AV_COPY128(top_border + 48, src_cr + 15 * uvlinesize); + } else { + AV_COPY64(top_border + 16, src_cb + 15 * uvlinesize); + AV_COPY64(top_border + 24, src_cr + 15 * uvlinesize); + } + } else { + if (pixel_shift) { + AV_COPY128(top_border + 32, src_cb + 7 * uvlinesize); + AV_COPY128(top_border + 48, src_cr + 7 * uvlinesize); + } else { + AV_COPY64(top_border + 16, src_cb + 7 * uvlinesize); + AV_COPY64(top_border + 24, src_cr + 7 * uvlinesize); + } + } + } + } + } else if (MB_MBAFF(sl)) { + top_idx = 0; + } else + return; + } + + top_border = sl->top_borders[top_idx][sl->mb_x]; + /* There are two lines saved, the line above the top macroblock + * of a pair, and the line above the bottom macroblock. */ + AV_COPY128(top_border, src_y + 16 * linesize); + if (pixel_shift) + AV_COPY128(top_border + 16, src_y + 16 * linesize + 16); + + if (simple || !CONFIG_GRAY || !(h->flags & AV_CODEC_FLAG_GRAY)) { + if (chroma444) { + if (pixel_shift) { + AV_COPY128(top_border + 32, src_cb + 16 * linesize); + AV_COPY128(top_border + 48, src_cb + 16 * linesize + 16); + AV_COPY128(top_border + 64, src_cr + 16 * linesize); + AV_COPY128(top_border + 80, src_cr + 16 * linesize + 16); + } else { + AV_COPY128(top_border + 16, src_cb + 16 * linesize); + AV_COPY128(top_border + 32, src_cr + 16 * linesize); + } + } else if (chroma422) { + if (pixel_shift) { + AV_COPY128(top_border + 32, src_cb + 16 * uvlinesize); + AV_COPY128(top_border + 48, src_cr + 16 * uvlinesize); + } else { + AV_COPY64(top_border + 16, src_cb + 16 * uvlinesize); + AV_COPY64(top_border + 24, src_cr + 16 * uvlinesize); + } + } else { + if (pixel_shift) { + AV_COPY128(top_border + 32, src_cb + 8 * uvlinesize); + AV_COPY128(top_border + 48, src_cr + 8 * uvlinesize); + } else { + AV_COPY64(top_border + 16, src_cb + 8 * uvlinesize); + AV_COPY64(top_border + 24, src_cr + 8 * uvlinesize); + } + } + } +} + +/** + * Initialize implicit_weight table. + * @param field 0/1 initialize the weight for interlaced MBAFF + * -1 initializes the rest + */ +static void implicit_weight_table(const H264Context *h, H264SliceContext *sl, int field) +{ + int ref0, ref1, i, cur_poc, ref_start, ref_count0, ref_count1; + + for (i = 0; i < 2; i++) { + sl->pwt.luma_weight_flag[i] = 0; + sl->pwt.chroma_weight_flag[i] = 0; + } + + if (field < 0) { + if (h->picture_structure == PICT_FRAME) { + cur_poc = h->cur_pic_ptr->poc; + } else { + cur_poc = h->cur_pic_ptr->field_poc[h->picture_structure - 1]; + } + if (sl->ref_count[0] == 1 && sl->ref_count[1] == 1 && !FRAME_MBAFF(h) && + sl->ref_list[0][0].poc + (int64_t)sl->ref_list[1][0].poc == 2LL * cur_poc) { + sl->pwt.use_weight = 0; + sl->pwt.use_weight_chroma = 0; + return; + } + ref_start = 0; + ref_count0 = sl->ref_count[0]; + ref_count1 = sl->ref_count[1]; + } else { + cur_poc = h->cur_pic_ptr->field_poc[field]; + ref_start = 16; + ref_count0 = 16 + 2 * sl->ref_count[0]; + ref_count1 = 16 + 2 * sl->ref_count[1]; + } + + sl->pwt.use_weight = 2; + sl->pwt.use_weight_chroma = 2; + sl->pwt.luma_log2_weight_denom = 5; + sl->pwt.chroma_log2_weight_denom = 5; + + for (ref0 = ref_start; ref0 < ref_count0; ref0++) { + int64_t poc0 = sl->ref_list[0][ref0].poc; + for (ref1 = ref_start; ref1 < ref_count1; ref1++) { + int w = 32; + if (!sl->ref_list[0][ref0].parent->long_ref && !sl->ref_list[1][ref1].parent->long_ref) { + int poc1 = sl->ref_list[1][ref1].poc; + int td = av_clip_int8(poc1 - poc0); + if (td) { + int tb = av_clip_int8(cur_poc - poc0); + int tx = (16384 + (FFABS(td) >> 1)) / td; + int dist_scale_factor = (tb * tx + 32) >> 8; + if (dist_scale_factor >= -64 && dist_scale_factor <= 128) + w = 64 - dist_scale_factor; + } + } + if (field < 0) { + sl->pwt.implicit_weight[ref0][ref1][0] = + sl->pwt.implicit_weight[ref0][ref1][1] = w; + } else { + sl->pwt.implicit_weight[ref0][ref1][field] = w; + } + } + } +} + +/** + * initialize scan tables + */ +static void init_scan_tables(H264Context *h) +{ + int i; + for (i = 0; i < 16; i++) { +#define TRANSPOSE(x) ((x) >> 2) | (((x) << 2) & 0xF) + h->zigzag_scan[i] = TRANSPOSE(ff_zigzag_scan[i]); + h->field_scan[i] = TRANSPOSE(field_scan[i]); +#undef TRANSPOSE + } + for (i = 0; i < 64; i++) { +#define TRANSPOSE(x) ((x) >> 3) | (((x) & 7) << 3) + h->zigzag_scan8x8[i] = TRANSPOSE(ff_zigzag_direct[i]); + h->zigzag_scan8x8_cavlc[i] = TRANSPOSE(zigzag_scan8x8_cavlc[i]); + h->field_scan8x8[i] = TRANSPOSE(field_scan8x8[i]); + h->field_scan8x8_cavlc[i] = TRANSPOSE(field_scan8x8_cavlc[i]); +#undef TRANSPOSE + } + if (h->ps.sps->transform_bypass) { // FIXME same ugly + memcpy(h->zigzag_scan_q0 , ff_zigzag_scan , sizeof(h->zigzag_scan_q0 )); + memcpy(h->zigzag_scan8x8_q0 , ff_zigzag_direct , sizeof(h->zigzag_scan8x8_q0 )); + memcpy(h->zigzag_scan8x8_cavlc_q0 , zigzag_scan8x8_cavlc , sizeof(h->zigzag_scan8x8_cavlc_q0)); + memcpy(h->field_scan_q0 , field_scan , sizeof(h->field_scan_q0 )); + memcpy(h->field_scan8x8_q0 , field_scan8x8 , sizeof(h->field_scan8x8_q0 )); + memcpy(h->field_scan8x8_cavlc_q0 , field_scan8x8_cavlc , sizeof(h->field_scan8x8_cavlc_q0 )); + } else { + memcpy(h->zigzag_scan_q0 , h->zigzag_scan , sizeof(h->zigzag_scan_q0 )); + memcpy(h->zigzag_scan8x8_q0 , h->zigzag_scan8x8 , sizeof(h->zigzag_scan8x8_q0 )); + memcpy(h->zigzag_scan8x8_cavlc_q0 , h->zigzag_scan8x8_cavlc , sizeof(h->zigzag_scan8x8_cavlc_q0)); + memcpy(h->field_scan_q0 , h->field_scan , sizeof(h->field_scan_q0 )); + memcpy(h->field_scan8x8_q0 , h->field_scan8x8 , sizeof(h->field_scan8x8_q0 )); + memcpy(h->field_scan8x8_cavlc_q0 , h->field_scan8x8_cavlc , sizeof(h->field_scan8x8_cavlc_q0 )); + } +} + +static enum AVPixelFormat get_pixel_format(H264Context *h, int force_callback) +{ +#define HWACCEL_MAX (CONFIG_H264_DXVA2_HWACCEL + \ + (CONFIG_H264_D3D11VA_HWACCEL * 2) + \ + CONFIG_H264_D3D12VA_HWACCEL + \ + CONFIG_H264_NVDEC_HWACCEL + \ + CONFIG_H264_VAAPI_HWACCEL + \ + CONFIG_H264_VIDEOTOOLBOX_HWACCEL + \ + CONFIG_H264_VDPAU_HWACCEL + \ + CONFIG_H264_VULKAN_HWACCEL) + enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmt = pix_fmts; + + switch (h->ps.sps->bit_depth_luma) { + case 9: + if (CHROMA444(h)) { + if (h->avctx->colorspace == AVCOL_SPC_RGB) { + *fmt++ = AV_PIX_FMT_GBRP9; + } else + *fmt++ = AV_PIX_FMT_YUV444P9; + } else if (CHROMA422(h)) + *fmt++ = AV_PIX_FMT_YUV422P9; + else + *fmt++ = AV_PIX_FMT_YUV420P9; + break; + case 10: +#if CONFIG_H264_VIDEOTOOLBOX_HWACCEL + if (h->avctx->colorspace != AVCOL_SPC_RGB) + *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_H264_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_H264_NVDEC_HWACCEL + *fmt++ = AV_PIX_FMT_CUDA; +#endif + if (CHROMA444(h)) { + if (h->avctx->colorspace == AVCOL_SPC_RGB) { + *fmt++ = AV_PIX_FMT_GBRP10; + } else + *fmt++ = AV_PIX_FMT_YUV444P10; + } else if (CHROMA422(h)) + *fmt++ = AV_PIX_FMT_YUV422P10; + else { +#if CONFIG_H264_VAAPI_HWACCEL + // Just add as candidate. Whether VAProfileH264High10 usable or + // not is decided by vaapi_decode_make_config() defined in FFmpeg + // and vaQueryCodingProfile() defined in libva. + *fmt++ = AV_PIX_FMT_VAAPI; +#endif + *fmt++ = AV_PIX_FMT_YUV420P10; + } + break; + case 12: +#if CONFIG_H264_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif + if (CHROMA444(h)) { + if (h->avctx->colorspace == AVCOL_SPC_RGB) { + *fmt++ = AV_PIX_FMT_GBRP12; + } else + *fmt++ = AV_PIX_FMT_YUV444P12; + } else if (CHROMA422(h)) + *fmt++ = AV_PIX_FMT_YUV422P12; + else + *fmt++ = AV_PIX_FMT_YUV420P12; + break; + case 14: + if (CHROMA444(h)) { + if (h->avctx->colorspace == AVCOL_SPC_RGB) { + *fmt++ = AV_PIX_FMT_GBRP14; + } else + *fmt++ = AV_PIX_FMT_YUV444P14; + } else if (CHROMA422(h)) + *fmt++ = AV_PIX_FMT_YUV422P14; + else + *fmt++ = AV_PIX_FMT_YUV420P14; + break; + case 8: +#if CONFIG_H264_VDPAU_HWACCEL + *fmt++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_H264_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_H264_NVDEC_HWACCEL + *fmt++ = AV_PIX_FMT_CUDA; +#endif +#if CONFIG_H264_VIDEOTOOLBOX_HWACCEL + if (h->avctx->colorspace != AVCOL_SPC_RGB) + *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif + if (CHROMA444(h)) { + if (h->avctx->colorspace == AVCOL_SPC_RGB) + *fmt++ = AV_PIX_FMT_GBRP; + else if (h->avctx->color_range == AVCOL_RANGE_JPEG) + *fmt++ = AV_PIX_FMT_YUVJ444P; + else + *fmt++ = AV_PIX_FMT_YUV444P; + } else if (CHROMA422(h)) { + if (h->avctx->color_range == AVCOL_RANGE_JPEG) + *fmt++ = AV_PIX_FMT_YUVJ422P; + else + *fmt++ = AV_PIX_FMT_YUV422P; + } else { +#if CONFIG_H264_DXVA2_HWACCEL + *fmt++ = AV_PIX_FMT_DXVA2_VLD; +#endif +#if CONFIG_H264_D3D11VA_HWACCEL + *fmt++ = AV_PIX_FMT_D3D11VA_VLD; + *fmt++ = AV_PIX_FMT_D3D11; +#endif +#if CONFIG_H264_D3D12VA_HWACCEL + *fmt++ = AV_PIX_FMT_D3D12; +#endif +#if CONFIG_H264_VAAPI_HWACCEL + *fmt++ = AV_PIX_FMT_VAAPI; +#endif + if (h->avctx->color_range == AVCOL_RANGE_JPEG) + *fmt++ = AV_PIX_FMT_YUVJ420P; + else + *fmt++ = AV_PIX_FMT_YUV420P; + } + break; + default: + av_log(h->avctx, AV_LOG_ERROR, + "Unsupported bit depth %d\n", h->ps.sps->bit_depth_luma); + return AVERROR_INVALIDDATA; + } + + *fmt = AV_PIX_FMT_NONE; + + for (int i = 0; pix_fmts[i] != AV_PIX_FMT_NONE; i++) + if (pix_fmts[i] == h->avctx->pix_fmt && !force_callback) + return pix_fmts[i]; + return ff_get_format(h->avctx, pix_fmts); +} + +/* export coded and cropped frame dimensions to AVCodecContext */ +static void init_dimensions(H264Context *h) +{ + const SPS *sps = h->ps.sps; + int cr = sps->crop_right; + int cl = sps->crop_left; + int ct = sps->crop_top; + int cb = sps->crop_bottom; + int width = h->width - (cr + cl); + int height = h->height - (ct + cb); + av_assert0(sps->crop_right + sps->crop_left < (unsigned)h->width); + av_assert0(sps->crop_top + sps->crop_bottom < (unsigned)h->height); + + /* handle container cropping */ + if (h->width_from_caller > 0 && h->height_from_caller > 0 && + !sps->crop_top && !sps->crop_left && + FFALIGN(h->width_from_caller, 16) == FFALIGN(width, 16) && + FFALIGN(h->height_from_caller, 16) == FFALIGN(height, 16) && + h->width_from_caller <= width && + h->height_from_caller <= height) { + width = h->width_from_caller; + height = h->height_from_caller; + cl = 0; + ct = 0; + cr = h->width - width; + cb = h->height - height; + } else { + h->width_from_caller = 0; + h->height_from_caller = 0; + } + + h->avctx->coded_width = h->width; + h->avctx->coded_height = h->height; + h->avctx->width = width; + h->avctx->height = height; + h->crop_right = cr; + h->crop_left = cl; + h->crop_top = ct; + h->crop_bottom = cb; +} + +static int h264_slice_header_init(H264Context *h) +{ + const SPS *sps = h->ps.sps; + int i, ret; + + if (!sps) { + ret = AVERROR_INVALIDDATA; + goto fail; + } + + ff_set_sar(h->avctx, sps->vui.sar); + av_pix_fmt_get_chroma_sub_sample(h->avctx->pix_fmt, + &h->chroma_x_shift, &h->chroma_y_shift); + + if (sps->timing_info_present_flag) { + int64_t den = sps->time_scale; + if (h->x264_build < 44U) + den *= 2; + av_reduce(&h->avctx->framerate.den, &h->avctx->framerate.num, + sps->num_units_in_tick * 2, den, 1 << 30); + } + + ff_h264_free_tables(h); + + h->first_field = 0; + h->prev_interlaced_frame = 1; + + init_scan_tables(h); + ret = ff_h264_alloc_tables(h); + if (ret < 0) { + av_log(h->avctx, AV_LOG_ERROR, "Could not allocate memory\n"); + goto fail; + } + + if (sps->bit_depth_luma < 8 || sps->bit_depth_luma > 14 || + sps->bit_depth_luma == 11 || sps->bit_depth_luma == 13 + ) { + av_log(h->avctx, AV_LOG_ERROR, "Unsupported bit depth %d\n", + sps->bit_depth_luma); + ret = AVERROR_INVALIDDATA; + goto fail; + } + + h->cur_bit_depth_luma = + h->avctx->bits_per_raw_sample = sps->bit_depth_luma; + h->cur_chroma_format_idc = sps->chroma_format_idc; + h->pixel_shift = sps->bit_depth_luma > 8; + h->chroma_format_idc = sps->chroma_format_idc; + h->bit_depth_luma = sps->bit_depth_luma; + + ff_h264dsp_init(&h->h264dsp, sps->bit_depth_luma, + sps->chroma_format_idc); + ff_h264chroma_init(&h->h264chroma, sps->bit_depth_chroma); + ff_h264qpel_init(&h->h264qpel, sps->bit_depth_luma); + ff_h264_pred_init(&h->hpc, AV_CODEC_ID_H264, sps->bit_depth_luma, + sps->chroma_format_idc); + ff_videodsp_init(&h->vdsp, sps->bit_depth_luma); + + if (!HAVE_THREADS || !(h->avctx->active_thread_type & FF_THREAD_SLICE)) { + ff_h264_slice_context_init(h, &h->slice_ctx[0]); + } else { + for (i = 0; i < h->nb_slice_ctx; i++) { + H264SliceContext *sl = &h->slice_ctx[i]; + + sl->h264 = h; + sl->intra4x4_pred_mode = h->intra4x4_pred_mode + i * 8 * 2 * h->mb_stride; + sl->mvd_table[0] = h->mvd_table[0] + i * 8 * 2 * h->mb_stride; + sl->mvd_table[1] = h->mvd_table[1] + i * 8 * 2 * h->mb_stride; + + ff_h264_slice_context_init(h, sl); + } + } + + h->context_initialized = 1; + + return 0; +fail: + ff_h264_free_tables(h); + h->context_initialized = 0; + return ret; +} + +static enum AVPixelFormat non_j_pixfmt(enum AVPixelFormat a) +{ + switch (a) { + case AV_PIX_FMT_YUVJ420P: return AV_PIX_FMT_YUV420P; + case AV_PIX_FMT_YUVJ422P: return AV_PIX_FMT_YUV422P; + case AV_PIX_FMT_YUVJ444P: return AV_PIX_FMT_YUV444P; + default: + return a; + } +} + +static int h264_init_ps(H264Context *h, const H264SliceContext *sl, int first_slice) +{ + const SPS *sps; + int needs_reinit = 0, must_reinit, ret; + + if (first_slice) + av_refstruct_replace(&h->ps.pps, h->ps.pps_list[sl->pps_id]); + + if (h->ps.sps != h->ps.pps->sps) { + h->ps.sps = h->ps.pps->sps; + + if (h->mb_width != h->ps.sps->mb_width || + h->mb_height != h->ps.sps->mb_height || + h->cur_bit_depth_luma != h->ps.sps->bit_depth_luma || + h->cur_chroma_format_idc != h->ps.sps->chroma_format_idc + ) + needs_reinit = 1; + + if (h->bit_depth_luma != h->ps.sps->bit_depth_luma || + h->chroma_format_idc != h->ps.sps->chroma_format_idc) + needs_reinit = 1; + } + sps = h->ps.sps; + + must_reinit = (h->context_initialized && + ( 16*sps->mb_width != h->avctx->coded_width + || 16*sps->mb_height != h->avctx->coded_height + || h->cur_bit_depth_luma != sps->bit_depth_luma + || h->cur_chroma_format_idc != sps->chroma_format_idc + || h->mb_width != sps->mb_width + || h->mb_height != sps->mb_height + )); + if (h->avctx->pix_fmt == AV_PIX_FMT_NONE + || (non_j_pixfmt(h->avctx->pix_fmt) != non_j_pixfmt(get_pixel_format(h, 0)))) + must_reinit = 1; + + if (first_slice && av_cmp_q(sps->vui.sar, h->avctx->sample_aspect_ratio)) + must_reinit = 1; + + if (!h->setup_finished) { + h->avctx->profile = ff_h264_get_profile(sps); + h->avctx->level = sps->level_idc; + h->avctx->refs = sps->ref_frame_count; + + h->mb_width = sps->mb_width; + h->mb_height = sps->mb_height; + h->mb_num = h->mb_width * h->mb_height; + h->mb_stride = h->mb_width + 1; + + h->b_stride = h->mb_width * 4; + + h->chroma_y_shift = sps->chroma_format_idc <= 1; // 400 uses yuv420p + + h->width = 16 * h->mb_width; + h->height = 16 * h->mb_height; + + init_dimensions(h); + + if (sps->vui.video_signal_type_present_flag) { + h->avctx->color_range = sps->vui.video_full_range_flag > 0 ? AVCOL_RANGE_JPEG + : AVCOL_RANGE_MPEG; + if (sps->vui.colour_description_present_flag) { + if (h->avctx->colorspace != sps->vui.matrix_coeffs) + needs_reinit = 1; + h->avctx->color_primaries = sps->vui.colour_primaries; + h->avctx->color_trc = sps->vui.transfer_characteristics; + h->avctx->colorspace = sps->vui.matrix_coeffs; + } + } + + if (h->sei.common.alternative_transfer.present && + av_color_transfer_name(h->sei.common.alternative_transfer.preferred_transfer_characteristics) && + h->sei.common.alternative_transfer.preferred_transfer_characteristics != AVCOL_TRC_UNSPECIFIED) { + h->avctx->color_trc = h->sei.common.alternative_transfer.preferred_transfer_characteristics; + } + } + h->avctx->chroma_sample_location = sps->vui.chroma_location; + + if (!h->context_initialized || must_reinit || needs_reinit) { + int flush_changes = h->context_initialized; + h->context_initialized = 0; + if (sl != h->slice_ctx) { + av_log(h->avctx, AV_LOG_ERROR, + "changing width %d -> %d / height %d -> %d on " + "slice %d\n", + h->width, h->avctx->coded_width, + h->height, h->avctx->coded_height, + h->current_slice + 1); + return AVERROR_INVALIDDATA; + } + + av_assert1(first_slice); + + if (flush_changes) + ff_h264_flush_change(h); + + if ((ret = get_pixel_format(h, must_reinit || needs_reinit)) < 0) + return ret; + h->avctx->pix_fmt = ret; + + av_log(h->avctx, AV_LOG_VERBOSE, "Reinit context to %dx%d, " + "pix_fmt: %s\n", h->width, h->height, av_get_pix_fmt_name(h->avctx->pix_fmt)); + + if ((ret = h264_slice_header_init(h)) < 0) { + av_log(h->avctx, AV_LOG_ERROR, + "h264_slice_header_init() failed\n"); + return ret; + } + } + + return 0; +} + +static int h264_export_frame_props(H264Context *h) +{ + const SPS *sps = h->ps.sps; + H264Picture *cur = h->cur_pic_ptr; + AVFrame *out = cur->f; + int interlaced_frame = 0, top_field_first = 0; + int ret; + + out->flags &= ~AV_FRAME_FLAG_INTERLACED; + out->repeat_pict = 0; + + /* Signal interlacing information externally. */ + /* Prioritize picture timing SEI information over used + * decoding process if it exists. */ + if (h->sei.picture_timing.present) { + int ret = ff_h264_sei_process_picture_timing(&h->sei.picture_timing, sps, + h->avctx); + if (ret < 0) { + av_log(h->avctx, AV_LOG_ERROR, "Error processing a picture timing SEI\n"); + if (h->avctx->err_recognition & AV_EF_EXPLODE) + return ret; + h->sei.picture_timing.present = 0; + } + } + + if (sps->pic_struct_present_flag && h->sei.picture_timing.present) { + const H264SEIPictureTiming *pt = &h->sei.picture_timing; + switch (pt->pic_struct) { + case H264_SEI_PIC_STRUCT_FRAME: + break; + case H264_SEI_PIC_STRUCT_TOP_FIELD: + case H264_SEI_PIC_STRUCT_BOTTOM_FIELD: + interlaced_frame = 1; + break; + case H264_SEI_PIC_STRUCT_TOP_BOTTOM: + case H264_SEI_PIC_STRUCT_BOTTOM_TOP: + if (FIELD_OR_MBAFF_PICTURE(h)) + interlaced_frame = 1; + else + // try to flag soft telecine progressive + interlaced_frame = !!h->prev_interlaced_frame; + break; + case H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: + case H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: + /* Signal the possibility of telecined film externally + * (pic_struct 5,6). From these hints, let the applications + * decide if they apply deinterlacing. */ + out->repeat_pict = 1; + break; + case H264_SEI_PIC_STRUCT_FRAME_DOUBLING: + out->repeat_pict = 2; + break; + case H264_SEI_PIC_STRUCT_FRAME_TRIPLING: + out->repeat_pict = 4; + break; + } + + if ((pt->ct_type & 3) && + pt->pic_struct <= H264_SEI_PIC_STRUCT_BOTTOM_TOP) + interlaced_frame = ((pt->ct_type & (1 << 1)) != 0); + } else { + /* Derive interlacing flag from used decoding process. */ + interlaced_frame = !!FIELD_OR_MBAFF_PICTURE(h); + } + h->prev_interlaced_frame = interlaced_frame; + + if (cur->field_poc[0] != cur->field_poc[1]) { + /* Derive top_field_first from field pocs. */ + top_field_first = (cur->field_poc[0] < cur->field_poc[1]); + } else { + if (sps->pic_struct_present_flag && h->sei.picture_timing.present) { + /* Use picture timing SEI information. Even if it is a + * information of a past frame, better than nothing. */ + if (h->sei.picture_timing.pic_struct == H264_SEI_PIC_STRUCT_TOP_BOTTOM || + h->sei.picture_timing.pic_struct == H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP) + top_field_first = 1; + } else if (interlaced_frame) { + /* Default to top field first when pic_struct_present_flag + * is not set but interlaced frame detected */ + top_field_first = 1; + } // else + /* Most likely progressive */ + } + + out->flags |= (AV_FRAME_FLAG_INTERLACED * interlaced_frame) | + (AV_FRAME_FLAG_TOP_FIELD_FIRST * top_field_first); + + ret = ff_h2645_sei_to_frame(out, &h->sei.common, AV_CODEC_ID_H264, h->avctx, + &sps->vui, sps->bit_depth_luma, sps->bit_depth_chroma, + cur->poc + (unsigned)(h->poc_offset << 5)); + if (ret < 0) + return ret; + + if (h->sei.picture_timing.timecode_cnt > 0) { + uint32_t *tc_sd; + char tcbuf[AV_TIMECODE_STR_SIZE]; + AVFrameSideData *tcside; + ret = ff_frame_new_side_data(h->avctx, out, AV_FRAME_DATA_S12M_TIMECODE, + sizeof(uint32_t)*4, &tcside); + if (ret < 0) + return ret; + + if (tcside) { + tc_sd = (uint32_t*)tcside->data; + tc_sd[0] = h->sei.picture_timing.timecode_cnt; + + for (int i = 0; i < tc_sd[0]; i++) { + int drop = h->sei.picture_timing.timecode[i].dropframe; + int hh = h->sei.picture_timing.timecode[i].hours; + int mm = h->sei.picture_timing.timecode[i].minutes; + int ss = h->sei.picture_timing.timecode[i].seconds; + int ff = h->sei.picture_timing.timecode[i].frame; + + tc_sd[i + 1] = av_timecode_get_smpte(h->avctx->framerate, drop, hh, mm, ss, ff); + av_timecode_make_smpte_tc_string2(tcbuf, h->avctx->framerate, tc_sd[i + 1], 0, 0); + av_dict_set(&out->metadata, "timecode", tcbuf, 0); + } + } + h->sei.picture_timing.timecode_cnt = 0; + } + + return 0; +} + +static int h264_select_output_frame(H264Context *h) +{ + const SPS *sps = h->ps.sps; + H264Picture *out = h->cur_pic_ptr; + H264Picture *cur = h->cur_pic_ptr; + int i, pics, out_of_order, out_idx; + + cur->mmco_reset = h->mmco_reset; + h->mmco_reset = 0; + + if (sps->bitstream_restriction_flag || + h->avctx->strict_std_compliance >= FF_COMPLIANCE_STRICT) { + h->avctx->has_b_frames = FFMAX(h->avctx->has_b_frames, sps->num_reorder_frames); + } + + for (i = 0; 1; i++) { + if(i == H264_MAX_DPB_FRAMES || cur->poc < h->last_pocs[i]){ + if(i) + h->last_pocs[i-1] = cur->poc; + break; + } else if(i) { + h->last_pocs[i-1]= h->last_pocs[i]; + } + } + out_of_order = H264_MAX_DPB_FRAMES - i; + if( cur->f->pict_type == AV_PICTURE_TYPE_B + || (h->last_pocs[H264_MAX_DPB_FRAMES-2] > INT_MIN && h->last_pocs[H264_MAX_DPB_FRAMES-1] - (int64_t)h->last_pocs[H264_MAX_DPB_FRAMES-2] > 2)) + out_of_order = FFMAX(out_of_order, 1); + if (out_of_order == H264_MAX_DPB_FRAMES) { + av_log(h->avctx, AV_LOG_VERBOSE, "Invalid POC %d<%d\n", cur->poc, h->last_pocs[0]); + for (i = 1; i < H264_MAX_DPB_FRAMES; i++) + h->last_pocs[i] = INT_MIN; + h->last_pocs[0] = cur->poc; + cur->mmco_reset = 1; + } else if(h->avctx->has_b_frames < out_of_order && !sps->bitstream_restriction_flag){ + int loglevel = h->avctx->frame_num > 1 ? AV_LOG_WARNING : AV_LOG_VERBOSE; + av_log(h->avctx, loglevel, "Increasing reorder buffer to %d\n", out_of_order); + h->avctx->has_b_frames = out_of_order; + } + + pics = 0; + while (h->delayed_pic[pics]) + pics++; + + av_assert0(pics <= H264_MAX_DPB_FRAMES); + + h->delayed_pic[pics++] = cur; + if (cur->reference == 0) + cur->reference = DELAYED_PIC_REF; + + out = h->delayed_pic[0]; + out_idx = 0; + for (i = 1; h->delayed_pic[i] && + !(h->delayed_pic[i]->f->flags & AV_FRAME_FLAG_KEY) && + !h->delayed_pic[i]->mmco_reset; + i++) + if (h->delayed_pic[i]->poc < out->poc) { + out = h->delayed_pic[i]; + out_idx = i; + } + if (h->avctx->has_b_frames == 0 && + ((h->delayed_pic[0]->f->flags & AV_FRAME_FLAG_KEY) || h->delayed_pic[0]->mmco_reset)) + h->next_outputed_poc = INT_MIN; + out_of_order = out->poc < h->next_outputed_poc; + + if (out_of_order || pics > h->avctx->has_b_frames) { + out->reference &= ~DELAYED_PIC_REF; + for (i = out_idx; h->delayed_pic[i]; i++) + h->delayed_pic[i] = h->delayed_pic[i + 1]; + } + if (!out_of_order && pics > h->avctx->has_b_frames) { + h->next_output_pic = out; + if (out_idx == 0 && h->delayed_pic[0] && ((h->delayed_pic[0]->f->flags & AV_FRAME_FLAG_KEY) || h->delayed_pic[0]->mmco_reset)) { + h->next_outputed_poc = INT_MIN; + } else + h->next_outputed_poc = out->poc; + + // We have reached an recovery point and all frames after it in + // display order are "recovered". + h->frame_recovered |= out->recovered; + + out->recovered |= h->frame_recovered & FRAME_RECOVERED_SEI; + + if (!out->recovered) { + if (!(h->avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT) && + !(h->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL)) { + h->next_output_pic = NULL; + } else { + out->f->flags |= AV_FRAME_FLAG_CORRUPT; + } + } + } else { + av_log(h->avctx, AV_LOG_DEBUG, "no picture %s\n", out_of_order ? "ooo" : ""); + } + + return 0; +} + +/* This function is called right after decoding the slice header for a first + * slice in a field (or a frame). It decides whether we are decoding a new frame + * or a second field in a pair and does the necessary setup. + */ +static int h264_field_start(H264Context *h, const H264SliceContext *sl, + const H2645NAL *nal, int first_slice) +{ + int i; + const SPS *sps; + + int last_pic_structure, last_pic_droppable, ret; + + ret = h264_init_ps(h, sl, first_slice); + if (ret < 0) + return ret; + + sps = h->ps.sps; + + if (sps->bitstream_restriction_flag && + h->avctx->has_b_frames < sps->num_reorder_frames) { + h->avctx->has_b_frames = sps->num_reorder_frames; + } + + last_pic_droppable = h->droppable; + last_pic_structure = h->picture_structure; + h->droppable = (nal->ref_idc == 0); + h->picture_structure = sl->picture_structure; + + h->poc.frame_num = sl->frame_num; + h->poc.poc_lsb = sl->poc_lsb; + h->poc.delta_poc_bottom = sl->delta_poc_bottom; + h->poc.delta_poc[0] = sl->delta_poc[0]; + h->poc.delta_poc[1] = sl->delta_poc[1]; + + if (nal->type == H264_NAL_IDR_SLICE) + h->poc_offset = sl->idr_pic_id; + else if (h->picture_intra_only) + h->poc_offset = 0; + + /* Shorten frame num gaps so we don't have to allocate reference + * frames just to throw them away */ + if (h->poc.frame_num != h->poc.prev_frame_num) { + int unwrap_prev_frame_num = h->poc.prev_frame_num; + int max_frame_num = 1 << sps->log2_max_frame_num; + + if (unwrap_prev_frame_num > h->poc.frame_num) + unwrap_prev_frame_num -= max_frame_num; + + if ((h->poc.frame_num - unwrap_prev_frame_num) > sps->ref_frame_count) { + unwrap_prev_frame_num = (h->poc.frame_num - sps->ref_frame_count) - 1; + if (unwrap_prev_frame_num < 0) + unwrap_prev_frame_num += max_frame_num; + + h->poc.prev_frame_num = unwrap_prev_frame_num; + } + } + + /* See if we have a decoded first field looking for a pair... + * Here, we're using that to see if we should mark previously + * decode frames as "finished". + * We have to do that before the "dummy" in-between frame allocation, + * since that can modify h->cur_pic_ptr. */ + if (h->first_field) { + int last_field = last_pic_structure == PICT_BOTTOM_FIELD; + av_assert0(h->cur_pic_ptr); + av_assert0(h->cur_pic_ptr->f->buf[0]); + assert(h->cur_pic_ptr->reference != DELAYED_PIC_REF); + + /* Mark old field/frame as completed */ + if (h->cur_pic_ptr->tf.owner[last_field] == h->avctx) { + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, last_field); + } + + /* figure out if we have a complementary field pair */ + if (!FIELD_PICTURE(h) || h->picture_structure == last_pic_structure) { + /* Previous field is unmatched. Don't display it, but let it + * remain for reference if marked as such. */ + if (last_pic_structure != PICT_FRAME) { + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, + last_pic_structure == PICT_TOP_FIELD); + } + } else { + if (h->cur_pic_ptr->frame_num != h->poc.frame_num) { + /* This and previous field were reference, but had + * different frame_nums. Consider this field first in + * pair. Throw away previous field except for reference + * purposes. */ + if (last_pic_structure != PICT_FRAME) { + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, + last_pic_structure == PICT_TOP_FIELD); + } + } else { + /* Second field in complementary pair */ + if (!((last_pic_structure == PICT_TOP_FIELD && + h->picture_structure == PICT_BOTTOM_FIELD) || + (last_pic_structure == PICT_BOTTOM_FIELD && + h->picture_structure == PICT_TOP_FIELD))) { + av_log(h->avctx, AV_LOG_ERROR, + "Invalid field mode combination %d/%d\n", + last_pic_structure, h->picture_structure); + h->picture_structure = last_pic_structure; + h->droppable = last_pic_droppable; + return AVERROR_INVALIDDATA; + } else if (last_pic_droppable != h->droppable) { + avpriv_request_sample(h->avctx, + "Found reference and non-reference fields in the same frame, which"); + h->picture_structure = last_pic_structure; + h->droppable = last_pic_droppable; + return AVERROR_PATCHWELCOME; + } + } + } + } + + while (h->poc.frame_num != h->poc.prev_frame_num && !h->first_field && + h->poc.frame_num != (h->poc.prev_frame_num + 1) % (1 << sps->log2_max_frame_num)) { + const H264Picture *prev = h->short_ref_count ? h->short_ref[0] : NULL; + av_log(h->avctx, AV_LOG_DEBUG, "Frame num gap %d %d\n", + h->poc.frame_num, h->poc.prev_frame_num); + if (!sps->gaps_in_frame_num_allowed_flag) + for(i=0; ilast_pocs); i++) + h->last_pocs[i] = INT_MIN; + ret = h264_frame_start(h); + if (ret < 0) { + h->first_field = 0; + return ret; + } + + h->poc.prev_frame_num++; + h->poc.prev_frame_num %= 1 << sps->log2_max_frame_num; + h->cur_pic_ptr->frame_num = h->poc.prev_frame_num; + h->cur_pic_ptr->invalid_gap = !sps->gaps_in_frame_num_allowed_flag; + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, 0); + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, 1); + + h->explicit_ref_marking = 0; + ret = ff_h264_execute_ref_pic_marking(h); + if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE)) + return ret; + /* Error concealment: If a ref is missing, copy the previous ref + * in its place. + * FIXME: Avoiding a memcpy would be nice, but ref handling makes + * many assumptions about there being no actual duplicates. + * FIXME: This does not copy padding for out-of-frame motion + * vectors. Given we are concealing a lost frame, this probably + * is not noticeable by comparison, but it should be fixed. */ + if (h->short_ref_count) { + int c[4] = { + 1<<(h->ps.sps->bit_depth_luma-1), + 1<<(h->ps.sps->bit_depth_chroma-1), + 1<<(h->ps.sps->bit_depth_chroma-1), + -1 + }; + + if (prev && + h->short_ref[0]->f->width == prev->f->width && + h->short_ref[0]->f->height == prev->f->height && + h->short_ref[0]->f->format == prev->f->format) { + ff_thread_await_progress(&prev->tf, INT_MAX, 0); + if (prev->field_picture) + ff_thread_await_progress(&prev->tf, INT_MAX, 1); + ff_thread_release_ext_buffer(&h->short_ref[0]->tf); + h->short_ref[0]->tf.f = h->short_ref[0]->f; + ret = ff_thread_ref_frame(&h->short_ref[0]->tf, &prev->tf); + if (ret < 0) + return ret; + h->short_ref[0]->poc = prev->poc + 2U; + h->short_ref[0]->gray = prev->gray; + ff_thread_report_progress(&h->short_ref[0]->tf, INT_MAX, 0); + if (h->short_ref[0]->field_picture) + ff_thread_report_progress(&h->short_ref[0]->tf, INT_MAX, 1); + } else if (!h->frame_recovered) { + if (!h->avctx->hwaccel) + color_frame(h->short_ref[0]->f, c); + h->short_ref[0]->gray = 1; + } + h->short_ref[0]->frame_num = h->poc.prev_frame_num; + } + } + + /* See if we have a decoded first field looking for a pair... + * We're using that to see whether to continue decoding in that + * frame, or to allocate a new one. */ + if (h->first_field) { + av_assert0(h->cur_pic_ptr); + av_assert0(h->cur_pic_ptr->f->buf[0]); + assert(h->cur_pic_ptr->reference != DELAYED_PIC_REF); + + /* figure out if we have a complementary field pair */ + if (!FIELD_PICTURE(h) || h->picture_structure == last_pic_structure) { + /* Previous field is unmatched. Don't display it, but let it + * remain for reference if marked as such. */ + h->missing_fields ++; + h->cur_pic_ptr = NULL; + h->first_field = FIELD_PICTURE(h); + } else { + h->missing_fields = 0; + if (h->cur_pic_ptr->frame_num != h->poc.frame_num) { + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, + h->picture_structure==PICT_BOTTOM_FIELD); + /* This and the previous field had different frame_nums. + * Consider this field first in pair. Throw away previous + * one except for reference purposes. */ + h->first_field = 1; + h->cur_pic_ptr = NULL; + } else if (h->cur_pic_ptr->reference & DELAYED_PIC_REF) { + /* This frame was already output, we cannot draw into it + * anymore. + */ + h->first_field = 1; + h->cur_pic_ptr = NULL; + } else { + /* Second field in complementary pair */ + h->first_field = 0; + } + } + } else { + /* Frame or first field in a potentially complementary pair */ + h->first_field = FIELD_PICTURE(h); + } + + if (!FIELD_PICTURE(h) || h->first_field) { + if (h264_frame_start(h) < 0) { + h->first_field = 0; + return AVERROR_INVALIDDATA; + } + } else { + int field = h->picture_structure == PICT_BOTTOM_FIELD; + release_unused_pictures(h, 0); + h->cur_pic_ptr->tf.owner[field] = h->avctx; + } + /* Some macroblocks can be accessed before they're available in case + * of lost slices, MBAFF or threading. */ + if (FIELD_PICTURE(h)) { + for(i = (h->picture_structure == PICT_BOTTOM_FIELD); imb_height; i++) + memset(h->slice_table + i*h->mb_stride, -1, (h->mb_stride - (i+1==h->mb_height)) * sizeof(*h->slice_table)); + } else { + memset(h->slice_table, -1, + (h->mb_height * h->mb_stride - 1) * sizeof(*h->slice_table)); + } + + ret = ff_h264_init_poc(h->cur_pic_ptr->field_poc, &h->cur_pic_ptr->poc, + h->ps.sps, &h->poc, h->picture_structure, nal->ref_idc); + if (ret < 0) + return ret; + + memcpy(h->mmco, sl->mmco, sl->nb_mmco * sizeof(*h->mmco)); + h->nb_mmco = sl->nb_mmco; + h->explicit_ref_marking = sl->explicit_ref_marking; + + h->picture_idr = nal->type == H264_NAL_IDR_SLICE; + + if (h->sei.recovery_point.recovery_frame_cnt >= 0) { + const int sei_recovery_frame_cnt = h->sei.recovery_point.recovery_frame_cnt; + + if (h->poc.frame_num != sei_recovery_frame_cnt || sl->slice_type_nos != AV_PICTURE_TYPE_I) + h->valid_recovery_point = 1; + + if ( h->recovery_frame < 0 + || av_zero_extend(h->recovery_frame - h->poc.frame_num, h->ps.sps->log2_max_frame_num) > sei_recovery_frame_cnt) { + h->recovery_frame = av_zero_extend(h->poc.frame_num + sei_recovery_frame_cnt, h->ps.sps->log2_max_frame_num); + + if (!h->valid_recovery_point) + h->recovery_frame = h->poc.frame_num; + } + } + + h->cur_pic_ptr->f->flags |= AV_FRAME_FLAG_KEY * !!(nal->type == H264_NAL_IDR_SLICE); + + if (nal->type == H264_NAL_IDR_SLICE) { + h->cur_pic_ptr->recovered |= FRAME_RECOVERED_IDR; + // If we have an IDR, all frames after it in decoded order are + // "recovered". + h->frame_recovered |= FRAME_RECOVERED_IDR; + } + + if (h->recovery_frame == h->poc.frame_num && nal->ref_idc) { + h->recovery_frame = -1; + h->cur_pic_ptr->recovered |= FRAME_RECOVERED_SEI; + } + +#if 1 + h->cur_pic_ptr->recovered |= h->frame_recovered; +#else + h->cur_pic_ptr->recovered |= !!(h->frame_recovered & FRAME_RECOVERED_IDR); +#endif + + /* Set the frame properties/side data. Only done for the second field in + * field coded frames, since some SEI information is present for each field + * and is merged by the SEI parsing code. */ + if (!FIELD_PICTURE(h) || !h->first_field || h->missing_fields > 1) { + ret = h264_export_frame_props(h); + if (ret < 0) + return ret; + + ret = h264_select_output_frame(h); + if (ret < 0) + return ret; + } + + return 0; +} + +static int h264_slice_header_parse(const H264Context *h, H264SliceContext *sl, + const H2645NAL *nal) +{ + const SPS *sps; + const PPS *pps; + int ret; + unsigned int slice_type, tmp, i; + int field_pic_flag, bottom_field_flag; + int first_slice = sl == h->slice_ctx && !h->current_slice; + int picture_structure; + + if (first_slice) + av_assert0(!h->setup_finished); + + sl->first_mb_addr = get_ue_golomb_long(&sl->gb); + + slice_type = get_ue_golomb_31(&sl->gb); + if (slice_type > 9) { + av_log(h->avctx, AV_LOG_ERROR, + "slice type %d too large at %d\n", + slice_type, sl->first_mb_addr); + return AVERROR_INVALIDDATA; + } + if (slice_type > 4) { + slice_type -= 5; + sl->slice_type_fixed = 1; + } else + sl->slice_type_fixed = 0; + + slice_type = ff_h264_golomb_to_pict_type[slice_type]; + sl->slice_type = slice_type; + sl->slice_type_nos = slice_type & 3; + + if (nal->type == H264_NAL_IDR_SLICE && + sl->slice_type_nos != AV_PICTURE_TYPE_I) { + av_log(h->avctx, AV_LOG_ERROR, "A non-intra slice in an IDR NAL unit.\n"); + return AVERROR_INVALIDDATA; + } + + sl->pps_id = get_ue_golomb(&sl->gb); + if (sl->pps_id >= MAX_PPS_COUNT) { + av_log(h->avctx, AV_LOG_ERROR, "pps_id %u out of range\n", sl->pps_id); + return AVERROR_INVALIDDATA; + } + if (!h->ps.pps_list[sl->pps_id]) { + av_log(h->avctx, AV_LOG_ERROR, + "non-existing PPS %u referenced\n", + sl->pps_id); + return AVERROR_INVALIDDATA; + } + pps = h->ps.pps_list[sl->pps_id]; + sps = pps->sps; + + sl->frame_num = get_bits(&sl->gb, sps->log2_max_frame_num); + if (!first_slice) { + if (h->poc.frame_num != sl->frame_num) { + av_log(h->avctx, AV_LOG_ERROR, "Frame num change from %d to %d\n", + h->poc.frame_num, sl->frame_num); + return AVERROR_INVALIDDATA; + } + } + + sl->mb_mbaff = 0; + + if (sps->frame_mbs_only_flag) { + picture_structure = PICT_FRAME; + } else { + if (!sps->direct_8x8_inference_flag && slice_type == AV_PICTURE_TYPE_B) { + av_log(h->avctx, AV_LOG_ERROR, "This stream was generated by a broken encoder, invalid 8x8 inference\n"); + return -1; + } + field_pic_flag = get_bits1(&sl->gb); + if (field_pic_flag) { + bottom_field_flag = get_bits1(&sl->gb); + picture_structure = PICT_TOP_FIELD + bottom_field_flag; + } else { + picture_structure = PICT_FRAME; + } + } + sl->picture_structure = picture_structure; + sl->mb_field_decoding_flag = picture_structure != PICT_FRAME; + + if (picture_structure == PICT_FRAME) { + sl->curr_pic_num = sl->frame_num; + sl->max_pic_num = 1 << sps->log2_max_frame_num; + } else { + sl->curr_pic_num = 2 * sl->frame_num + 1; + sl->max_pic_num = 1 << (sps->log2_max_frame_num + 1); + } + + if (nal->type == H264_NAL_IDR_SLICE) { + unsigned idr_pic_id = get_ue_golomb_long(&sl->gb); + if (idr_pic_id < 65536) { + sl->idr_pic_id = idr_pic_id; + } else + av_log(h->avctx, AV_LOG_WARNING, "idr_pic_id is invalid\n"); + } + + sl->poc_lsb = 0; + sl->delta_poc_bottom = 0; + if (sps->poc_type == 0) { + sl->poc_lsb = get_bits(&sl->gb, sps->log2_max_poc_lsb); + + if (pps->pic_order_present == 1 && picture_structure == PICT_FRAME) + sl->delta_poc_bottom = get_se_golomb(&sl->gb); + } + + sl->delta_poc[0] = sl->delta_poc[1] = 0; + if (sps->poc_type == 1 && !sps->delta_pic_order_always_zero_flag) { + sl->delta_poc[0] = get_se_golomb(&sl->gb); + + if (pps->pic_order_present == 1 && picture_structure == PICT_FRAME) + sl->delta_poc[1] = get_se_golomb(&sl->gb); + } + + sl->redundant_pic_count = 0; + if (pps->redundant_pic_cnt_present) + sl->redundant_pic_count = get_ue_golomb(&sl->gb); + + if (sl->slice_type_nos == AV_PICTURE_TYPE_B) + sl->direct_spatial_mv_pred = get_bits1(&sl->gb); + + ret = ff_h264_parse_ref_count(&sl->list_count, sl->ref_count, + &sl->gb, pps, sl->slice_type_nos, + picture_structure, h->avctx); + if (ret < 0) + return ret; + + if (sl->slice_type_nos != AV_PICTURE_TYPE_I) { + ret = ff_h264_decode_ref_pic_list_reordering(sl, h->avctx); + if (ret < 0) { + sl->ref_count[1] = sl->ref_count[0] = 0; + return ret; + } + } + + sl->pwt.use_weight = 0; + for (i = 0; i < 2; i++) { + sl->pwt.luma_weight_flag[i] = 0; + sl->pwt.chroma_weight_flag[i] = 0; + } + if ((pps->weighted_pred && sl->slice_type_nos == AV_PICTURE_TYPE_P) || + (pps->weighted_bipred_idc == 1 && + sl->slice_type_nos == AV_PICTURE_TYPE_B)) { + ret = ff_h264_pred_weight_table(&sl->gb, sps, sl->ref_count, + sl->slice_type_nos, &sl->pwt, + picture_structure, h->avctx); + if (ret < 0) + return ret; + } + + sl->explicit_ref_marking = 0; + if (nal->ref_idc) { + ret = ff_h264_decode_ref_pic_marking(sl, &sl->gb, nal, h->avctx); + if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE)) + return AVERROR_INVALIDDATA; + } + + if (sl->slice_type_nos != AV_PICTURE_TYPE_I && pps->cabac) { + tmp = get_ue_golomb_31(&sl->gb); + if (tmp > 2) { + av_log(h->avctx, AV_LOG_ERROR, "cabac_init_idc %u overflow\n", tmp); + return AVERROR_INVALIDDATA; + } + sl->cabac_init_idc = tmp; + } + + sl->last_qscale_diff = 0; + tmp = pps->init_qp + (unsigned)get_se_golomb(&sl->gb); + if (tmp > 51 + 6 * (sps->bit_depth_luma - 8)) { + av_log(h->avctx, AV_LOG_ERROR, "QP %u out of range\n", tmp); + return AVERROR_INVALIDDATA; + } + sl->qscale = tmp; + sl->chroma_qp[0] = get_chroma_qp(pps, 0, sl->qscale); + sl->chroma_qp[1] = get_chroma_qp(pps, 1, sl->qscale); + // FIXME qscale / qp ... stuff + if (sl->slice_type == AV_PICTURE_TYPE_SP) + get_bits1(&sl->gb); /* sp_for_switch_flag */ + if (sl->slice_type == AV_PICTURE_TYPE_SP || + sl->slice_type == AV_PICTURE_TYPE_SI) + get_se_golomb(&sl->gb); /* slice_qs_delta */ + + sl->deblocking_filter = 1; + sl->slice_alpha_c0_offset = 0; + sl->slice_beta_offset = 0; + if (pps->deblocking_filter_parameters_present) { + tmp = get_ue_golomb_31(&sl->gb); + if (tmp > 2) { + av_log(h->avctx, AV_LOG_ERROR, + "deblocking_filter_idc %u out of range\n", tmp); + return AVERROR_INVALIDDATA; + } + sl->deblocking_filter = tmp; + if (sl->deblocking_filter < 2) + sl->deblocking_filter ^= 1; // 1<->0 + + if (sl->deblocking_filter) { + int slice_alpha_c0_offset_div2 = get_se_golomb(&sl->gb); + int slice_beta_offset_div2 = get_se_golomb(&sl->gb); + if (slice_alpha_c0_offset_div2 > 6 || + slice_alpha_c0_offset_div2 < -6 || + slice_beta_offset_div2 > 6 || + slice_beta_offset_div2 < -6) { + av_log(h->avctx, AV_LOG_ERROR, + "deblocking filter parameters %d %d out of range\n", + slice_alpha_c0_offset_div2, slice_beta_offset_div2); + return AVERROR_INVALIDDATA; + } + sl->slice_alpha_c0_offset = slice_alpha_c0_offset_div2 * 2; + sl->slice_beta_offset = slice_beta_offset_div2 * 2; + } + } + + return 0; +} + +/* do all the per-slice initialization needed before we can start decoding the + * actual MBs */ +static int h264_slice_init(H264Context *h, H264SliceContext *sl, + const H2645NAL *nal) +{ + int i, j, ret = 0; + + if (h->picture_idr && nal->type != H264_NAL_IDR_SLICE) { + av_log(h->avctx, AV_LOG_ERROR, "Invalid mix of IDR and non-IDR slices\n"); + return AVERROR_INVALIDDATA; + } + + av_assert1(h->mb_num == h->mb_width * h->mb_height); + if (sl->first_mb_addr << FIELD_OR_MBAFF_PICTURE(h) >= h->mb_num || + sl->first_mb_addr >= h->mb_num) { + av_log(h->avctx, AV_LOG_ERROR, "first_mb_in_slice overflow\n"); + return AVERROR_INVALIDDATA; + } + sl->resync_mb_x = sl->mb_x = sl->first_mb_addr % h->mb_width; + sl->resync_mb_y = sl->mb_y = (sl->first_mb_addr / h->mb_width) << + FIELD_OR_MBAFF_PICTURE(h); + if (h->picture_structure == PICT_BOTTOM_FIELD) + sl->resync_mb_y = sl->mb_y = sl->mb_y + 1; + av_assert1(sl->mb_y < h->mb_height); + + ret = ff_h264_build_ref_list(h, sl); + if (ret < 0) + return ret; + + if (h->ps.pps->weighted_bipred_idc == 2 && + sl->slice_type_nos == AV_PICTURE_TYPE_B) { + implicit_weight_table(h, sl, -1); + if (FRAME_MBAFF(h)) { + implicit_weight_table(h, sl, 0); + implicit_weight_table(h, sl, 1); + } + } + + if (sl->slice_type_nos == AV_PICTURE_TYPE_B && !sl->direct_spatial_mv_pred) + ff_h264_direct_dist_scale_factor(h, sl); + ff_h264_direct_ref_list_init(h, sl); + + if (h->avctx->skip_loop_filter >= AVDISCARD_ALL || + (h->avctx->skip_loop_filter >= AVDISCARD_NONKEY && + h->nal_unit_type != H264_NAL_IDR_SLICE) || + (h->avctx->skip_loop_filter >= AVDISCARD_NONINTRA && + sl->slice_type_nos != AV_PICTURE_TYPE_I) || + (h->avctx->skip_loop_filter >= AVDISCARD_BIDIR && + sl->slice_type_nos == AV_PICTURE_TYPE_B) || + (h->avctx->skip_loop_filter >= AVDISCARD_NONREF && + nal->ref_idc == 0)) + sl->deblocking_filter = 0; + + if (sl->deblocking_filter == 1 && h->nb_slice_ctx > 1) { + if (h->avctx->flags2 & AV_CODEC_FLAG2_FAST) { + /* Cheat slightly for speed: + * Do not bother to deblock across slices. */ + sl->deblocking_filter = 2; + } else { + h->postpone_filter = 1; + } + } + sl->qp_thresh = 15 - + FFMIN(sl->slice_alpha_c0_offset, sl->slice_beta_offset) - + FFMAX3(0, + h->ps.pps->chroma_qp_index_offset[0], + h->ps.pps->chroma_qp_index_offset[1]) + + 6 * (h->ps.sps->bit_depth_luma - 8); + + // slice_table is uint16_t initialized to 0xFFFF as a sentinel. + if (h->current_slice >= 0xFFFE) { + av_log(h->avctx, AV_LOG_ERROR, "Too many slices (%d)\n", h->current_slice + 1); + return AVERROR_PATCHWELCOME; + } + + sl->slice_num = ++h->current_slice; + + if (sl->slice_num) + h->slice_row[(sl->slice_num-1)&(MAX_SLICES-1)]= sl->resync_mb_y; + if ( h->slice_row[sl->slice_num&(MAX_SLICES-1)] + 3 >= sl->resync_mb_y + && h->slice_row[sl->slice_num&(MAX_SLICES-1)] <= sl->resync_mb_y + && sl->slice_num >= MAX_SLICES) { + //in case of ASO this check needs to be updated depending on how we decide to assign slice numbers in this case + av_log(h->avctx, AV_LOG_WARNING, "Possibly too many slices (%d >= %d), increase MAX_SLICES and recompile if there are artifacts\n", sl->slice_num, MAX_SLICES); + } + + for (j = 0; j < 2; j++) { + int id_list[16]; + int *ref2frm = h->ref2frm[sl->slice_num & (MAX_SLICES - 1)][j]; + for (i = 0; i < 16; i++) { + id_list[i] = 60; + if (j < sl->list_count && i < sl->ref_count[j] && + sl->ref_list[j][i].parent->f->buf[0]) { + int k; + const AVBuffer *buf = sl->ref_list[j][i].parent->f->buf[0]->buffer; + for (k = 0; k < h->short_ref_count; k++) + if (h->short_ref[k]->f->buf[0]->buffer == buf) { + id_list[i] = k; + break; + } + for (k = 0; k < h->long_ref_count; k++) + if (h->long_ref[k] && h->long_ref[k]->f->buf[0]->buffer == buf) { + id_list[i] = h->short_ref_count + k; + break; + } + } + } + + ref2frm[0] = + ref2frm[1] = -1; + for (i = 0; i < 16; i++) + ref2frm[i + 2] = 4 * id_list[i] + (sl->ref_list[j][i].reference & 3); + ref2frm[18 + 0] = + ref2frm[18 + 1] = -1; + for (i = 16; i < 48; i++) + ref2frm[i + 4] = 4 * id_list[(i - 16) >> 1] + + (sl->ref_list[j][i].reference & 3); + } + + if (sl->slice_type_nos == AV_PICTURE_TYPE_I) { + h->cur_pic_ptr->gray = 0; + h->non_gray = 1; + } else { + int gray = 0; + for (j = 0; j < sl->list_count; j++) { + for (i = 0; i < sl->ref_count[j]; i++) { + gray |= sl->ref_list[j][i].parent->gray; + } + } + h->cur_pic_ptr->gray = gray; + } + + if (h->avctx->debug & FF_DEBUG_PICT_INFO) { + av_log(h->avctx, AV_LOG_DEBUG, + "slice:%d %c mb:%d %c%s%s frame:%d poc:%d/%d ref:%d/%d qp:%d loop:%d:%d:%d weight:%d%s %s\n", + sl->slice_num, + (h->picture_structure == PICT_FRAME ? 'F' : h->picture_structure == PICT_TOP_FIELD ? 'T' : 'B'), + sl->mb_y * h->mb_width + sl->mb_x, + av_get_picture_type_char(sl->slice_type), + sl->slice_type_fixed ? " fix" : "", + nal->type == H264_NAL_IDR_SLICE ? " IDR" : "", + h->poc.frame_num, + h->cur_pic_ptr->field_poc[0], + h->cur_pic_ptr->field_poc[1], + sl->ref_count[0], sl->ref_count[1], + sl->qscale, + sl->deblocking_filter, + sl->slice_alpha_c0_offset, sl->slice_beta_offset, + sl->pwt.use_weight, + sl->pwt.use_weight == 1 && sl->pwt.use_weight_chroma ? "c" : "", + sl->slice_type == AV_PICTURE_TYPE_B ? (sl->direct_spatial_mv_pred ? "SPAT" : "TEMP") : ""); + } + + return 0; +} + +int ff_h264_queue_decode_slice(H264Context *h, const H2645NAL *nal) +{ + H264SliceContext *sl = h->slice_ctx + h->nb_slice_ctx_queued; + int first_slice = sl == h->slice_ctx && !h->current_slice; + int ret; + + sl->gb = nal->gb; + + ret = h264_slice_header_parse(h, sl, nal); + if (ret < 0) + return ret; + + // discard redundant pictures + if (sl->redundant_pic_count > 0) { + sl->ref_count[0] = sl->ref_count[1] = 0; + return 0; + } + + if (sl->first_mb_addr == 0 || !h->current_slice) { + if (h->setup_finished) { + av_log(h->avctx, AV_LOG_ERROR, "Too many fields\n"); + return AVERROR_INVALIDDATA; + } + } + + if (sl->first_mb_addr == 0) { // FIXME better field boundary detection + if (h->current_slice) { + // this slice starts a new field + // first decode any pending queued slices + if (h->nb_slice_ctx_queued) { + H264SliceContext tmp_ctx; + + ret = ff_h264_execute_decode_slices(h); + if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE)) + return ret; + + memcpy(&tmp_ctx, h->slice_ctx, sizeof(tmp_ctx)); + memcpy(h->slice_ctx, sl, sizeof(tmp_ctx)); + memcpy(sl, &tmp_ctx, sizeof(tmp_ctx)); + sl = h->slice_ctx; + } + + if (h->cur_pic_ptr && FIELD_PICTURE(h) && h->first_field) { + ret = ff_h264_field_end(h, h->slice_ctx, 1); + if (ret < 0) + return ret; + } else if (h->cur_pic_ptr && !FIELD_PICTURE(h) && !h->first_field && h->nal_unit_type == H264_NAL_IDR_SLICE) { + av_log(h->avctx, AV_LOG_WARNING, "Broken frame packetizing\n"); + ret = ff_h264_field_end(h, h->slice_ctx, 1); + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, 0); + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, 1); + h->cur_pic_ptr = NULL; + if (ret < 0) + return ret; + } else + return AVERROR_INVALIDDATA; + } + + if (!h->first_field) { + if (h->cur_pic_ptr && !h->droppable) { + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, + h->picture_structure == PICT_BOTTOM_FIELD); + } + h->cur_pic_ptr = NULL; + } + } + + if (!h->current_slice) + av_assert0(sl == h->slice_ctx); + + if (h->current_slice == 0 && !h->first_field) { + if ( + (h->avctx->skip_frame >= AVDISCARD_NONREF && !h->nal_ref_idc) || + (h->avctx->skip_frame >= AVDISCARD_BIDIR && sl->slice_type_nos == AV_PICTURE_TYPE_B) || + (h->avctx->skip_frame >= AVDISCARD_NONINTRA && sl->slice_type_nos != AV_PICTURE_TYPE_I) || + (h->avctx->skip_frame >= AVDISCARD_NONKEY && h->nal_unit_type != H264_NAL_IDR_SLICE && h->sei.recovery_point.recovery_frame_cnt < 0) || + h->avctx->skip_frame >= AVDISCARD_ALL) { + return 0; + } + } + + if (!first_slice) { + const PPS *pps = h->ps.pps_list[sl->pps_id]; + + if (h->ps.pps->sps_id != pps->sps_id || + h->ps.pps->transform_8x8_mode != pps->transform_8x8_mode /*|| + (h->setup_finished && h->ps.pps != pps)*/) { + av_log(h->avctx, AV_LOG_ERROR, "PPS changed between slices\n"); + return AVERROR_INVALIDDATA; + } + if (h->ps.sps != pps->sps) { + av_log(h->avctx, AV_LOG_ERROR, + "SPS changed in the middle of the frame\n"); + return AVERROR_INVALIDDATA; + } + } + + if (h->current_slice == 0) { + ret = h264_field_start(h, sl, nal, first_slice); + if (ret < 0) + return ret; + } else { + if (h->picture_structure != sl->picture_structure || + h->droppable != (nal->ref_idc == 0)) { + av_log(h->avctx, AV_LOG_ERROR, + "Changing field mode (%d -> %d) between slices is not allowed\n", + h->picture_structure, sl->picture_structure); + return AVERROR_INVALIDDATA; + } else if (!h->cur_pic_ptr) { + av_log(h->avctx, AV_LOG_ERROR, + "unset cur_pic_ptr on slice %d\n", + h->current_slice + 1); + return AVERROR_INVALIDDATA; + } + } + + ret = h264_slice_init(h, sl, nal); + if (ret < 0) + return ret; + + h->nb_slice_ctx_queued++; + + return 0; +} + +int ff_h264_get_slice_type(const H264SliceContext *sl) +{ + switch (sl->slice_type) { + case AV_PICTURE_TYPE_P: + return 0; + case AV_PICTURE_TYPE_B: + return 1; + case AV_PICTURE_TYPE_I: + return 2; + case AV_PICTURE_TYPE_SP: + return 3; + case AV_PICTURE_TYPE_SI: + return 4; + default: + return AVERROR_INVALIDDATA; + } +} + +static av_always_inline void fill_filter_caches_inter(const H264Context *h, + H264SliceContext *sl, + int mb_type, int top_xy, + const int left_xy[LEFT_MBS], + int top_type, + const int left_type[LEFT_MBS], + int mb_xy, int list) +{ + int b_stride = h->b_stride; + int16_t(*mv_dst)[2] = &sl->mv_cache[list][scan8[0]]; + int8_t *ref_cache = &sl->ref_cache[list][scan8[0]]; + if (IS_INTER(mb_type) || IS_DIRECT(mb_type)) { + if (USES_LIST(top_type, list)) { + const int b_xy = h->mb2b_xy[top_xy] + 3 * b_stride; + const int b8_xy = 4 * top_xy + 2; + const int *ref2frm = &h->ref2frm[h->slice_table[top_xy] & (MAX_SLICES - 1)][list][(MB_MBAFF(sl) ? 20 : 2)]; + AV_COPY128(mv_dst - 1 * 8, h->cur_pic.motion_val[list][b_xy + 0]); + ref_cache[0 - 1 * 8] = + ref_cache[1 - 1 * 8] = ref2frm[h->cur_pic.ref_index[list][b8_xy + 0]]; + ref_cache[2 - 1 * 8] = + ref_cache[3 - 1 * 8] = ref2frm[h->cur_pic.ref_index[list][b8_xy + 1]]; + } else { + AV_ZERO128(mv_dst - 1 * 8); + AV_WN32A(&ref_cache[0 - 1 * 8], ((LIST_NOT_USED) & 0xFF) * 0x01010101u); + } + + if (!IS_INTERLACED(mb_type ^ left_type[LTOP])) { + if (USES_LIST(left_type[LTOP], list)) { + const int b_xy = h->mb2b_xy[left_xy[LTOP]] + 3; + const int b8_xy = 4 * left_xy[LTOP] + 1; + const int *ref2frm = &h->ref2frm[h->slice_table[left_xy[LTOP]] & (MAX_SLICES - 1)][list][(MB_MBAFF(sl) ? 20 : 2)]; + AV_COPY32(mv_dst - 1 + 0, h->cur_pic.motion_val[list][b_xy + b_stride * 0]); + AV_COPY32(mv_dst - 1 + 8, h->cur_pic.motion_val[list][b_xy + b_stride * 1]); + AV_COPY32(mv_dst - 1 + 16, h->cur_pic.motion_val[list][b_xy + b_stride * 2]); + AV_COPY32(mv_dst - 1 + 24, h->cur_pic.motion_val[list][b_xy + b_stride * 3]); + ref_cache[-1 + 0] = + ref_cache[-1 + 8] = ref2frm[h->cur_pic.ref_index[list][b8_xy + 2 * 0]]; + ref_cache[-1 + 16] = + ref_cache[-1 + 24] = ref2frm[h->cur_pic.ref_index[list][b8_xy + 2 * 1]]; + } else { + AV_ZERO32(mv_dst - 1 + 0); + AV_ZERO32(mv_dst - 1 + 8); + AV_ZERO32(mv_dst - 1 + 16); + AV_ZERO32(mv_dst - 1 + 24); + ref_cache[-1 + 0] = + ref_cache[-1 + 8] = + ref_cache[-1 + 16] = + ref_cache[-1 + 24] = LIST_NOT_USED; + } + } + } + + if (!USES_LIST(mb_type, list)) { + fill_rectangle(mv_dst, 4, 4, 8, pack16to32(0, 0), 4); + AV_WN32A(&ref_cache[0 * 8], ((LIST_NOT_USED) & 0xFF) * 0x01010101u); + AV_WN32A(&ref_cache[1 * 8], ((LIST_NOT_USED) & 0xFF) * 0x01010101u); + AV_WN32A(&ref_cache[2 * 8], ((LIST_NOT_USED) & 0xFF) * 0x01010101u); + AV_WN32A(&ref_cache[3 * 8], ((LIST_NOT_USED) & 0xFF) * 0x01010101u); + return; + } + + { + const int8_t *ref = &h->cur_pic.ref_index[list][4 * mb_xy]; + const int *ref2frm = &h->ref2frm[sl->slice_num & (MAX_SLICES - 1)][list][(MB_MBAFF(sl) ? 20 : 2)]; + uint32_t ref01 = (pack16to32(ref2frm[ref[0]], ref2frm[ref[1]]) & 0x00FF00FF) * 0x0101; + uint32_t ref23 = (pack16to32(ref2frm[ref[2]], ref2frm[ref[3]]) & 0x00FF00FF) * 0x0101; + AV_WN32A(&ref_cache[0 * 8], ref01); + AV_WN32A(&ref_cache[1 * 8], ref01); + AV_WN32A(&ref_cache[2 * 8], ref23); + AV_WN32A(&ref_cache[3 * 8], ref23); + } + + { + int16_t(*mv_src)[2] = &h->cur_pic.motion_val[list][4 * sl->mb_x + 4 * sl->mb_y * b_stride]; + AV_COPY128(mv_dst + 8 * 0, mv_src + 0 * b_stride); + AV_COPY128(mv_dst + 8 * 1, mv_src + 1 * b_stride); + AV_COPY128(mv_dst + 8 * 2, mv_src + 2 * b_stride); + AV_COPY128(mv_dst + 8 * 3, mv_src + 3 * b_stride); + } +} + +/** + * @return non zero if the loop filter can be skipped + */ +static int fill_filter_caches(const H264Context *h, H264SliceContext *sl, int mb_type) +{ + const int mb_xy = sl->mb_xy; + int top_xy, left_xy[LEFT_MBS]; + int top_type, left_type[LEFT_MBS]; + const uint8_t *nnz; + uint8_t *nnz_cache; + + top_xy = mb_xy - (h->mb_stride << MB_FIELD(sl)); + + left_xy[LBOT] = left_xy[LTOP] = mb_xy - 1; + if (FRAME_MBAFF(h)) { + const int left_mb_field_flag = IS_INTERLACED(h->cur_pic.mb_type[mb_xy - 1]); + const int curr_mb_field_flag = IS_INTERLACED(mb_type); + if (sl->mb_y & 1) { + if (left_mb_field_flag != curr_mb_field_flag) + left_xy[LTOP] -= h->mb_stride; + } else { + if (curr_mb_field_flag) + top_xy += h->mb_stride & + (((h->cur_pic.mb_type[top_xy] >> 7) & 1) - 1); + if (left_mb_field_flag != curr_mb_field_flag) + left_xy[LBOT] += h->mb_stride; + } + } + + sl->top_mb_xy = top_xy; + sl->left_mb_xy[LTOP] = left_xy[LTOP]; + sl->left_mb_xy[LBOT] = left_xy[LBOT]; + { + /* For sufficiently low qp, filtering wouldn't do anything. + * This is a conservative estimate: could also check beta_offset + * and more accurate chroma_qp. */ + int qp_thresh = sl->qp_thresh; // FIXME strictly we should store qp_thresh for each mb of a slice + int qp = h->cur_pic.qscale_table[mb_xy]; + if (qp <= qp_thresh && + (left_xy[LTOP] < 0 || + ((qp + h->cur_pic.qscale_table[left_xy[LTOP]] + 1) >> 1) <= qp_thresh) && + (top_xy < 0 || + ((qp + h->cur_pic.qscale_table[top_xy] + 1) >> 1) <= qp_thresh)) { + if (!FRAME_MBAFF(h)) + return 1; + if ((left_xy[LTOP] < 0 || + ((qp + h->cur_pic.qscale_table[left_xy[LBOT]] + 1) >> 1) <= qp_thresh) && + (top_xy < h->mb_stride || + ((qp + h->cur_pic.qscale_table[top_xy - h->mb_stride] + 1) >> 1) <= qp_thresh)) + return 1; + } + } + + top_type = h->cur_pic.mb_type[top_xy]; + left_type[LTOP] = h->cur_pic.mb_type[left_xy[LTOP]]; + left_type[LBOT] = h->cur_pic.mb_type[left_xy[LBOT]]; + if (sl->deblocking_filter == 2) { + if (h->slice_table[top_xy] != sl->slice_num) + top_type = 0; + if (h->slice_table[left_xy[LBOT]] != sl->slice_num) + left_type[LTOP] = left_type[LBOT] = 0; + } else { + if (h->slice_table[top_xy] == 0xFFFF) + top_type = 0; + if (h->slice_table[left_xy[LBOT]] == 0xFFFF) + left_type[LTOP] = left_type[LBOT] = 0; + } + sl->top_type = top_type; + sl->left_type[LTOP] = left_type[LTOP]; + sl->left_type[LBOT] = left_type[LBOT]; + + if (IS_INTRA(mb_type)) + return 0; + + fill_filter_caches_inter(h, sl, mb_type, top_xy, left_xy, + top_type, left_type, mb_xy, 0); + if (sl->list_count == 2) + fill_filter_caches_inter(h, sl, mb_type, top_xy, left_xy, + top_type, left_type, mb_xy, 1); + + nnz = h->non_zero_count[mb_xy]; + nnz_cache = sl->non_zero_count_cache; + AV_COPY32(&nnz_cache[4 + 8 * 1], &nnz[0]); + AV_COPY32(&nnz_cache[4 + 8 * 2], &nnz[4]); + AV_COPY32(&nnz_cache[4 + 8 * 3], &nnz[8]); + AV_COPY32(&nnz_cache[4 + 8 * 4], &nnz[12]); + sl->cbp = h->cbp_table[mb_xy]; + + if (top_type) { + nnz = h->non_zero_count[top_xy]; + AV_COPY32(&nnz_cache[4 + 8 * 0], &nnz[3 * 4]); + } + + if (left_type[LTOP]) { + nnz = h->non_zero_count[left_xy[LTOP]]; + nnz_cache[3 + 8 * 1] = nnz[3 + 0 * 4]; + nnz_cache[3 + 8 * 2] = nnz[3 + 1 * 4]; + nnz_cache[3 + 8 * 3] = nnz[3 + 2 * 4]; + nnz_cache[3 + 8 * 4] = nnz[3 + 3 * 4]; + } + + /* CAVLC 8x8dct requires NNZ values for residual decoding that differ + * from what the loop filter needs */ + if (!CABAC(h) && h->ps.pps->transform_8x8_mode) { + if (IS_8x8DCT(top_type)) { + nnz_cache[4 + 8 * 0] = + nnz_cache[5 + 8 * 0] = (h->cbp_table[top_xy] & 0x4000) >> 12; + nnz_cache[6 + 8 * 0] = + nnz_cache[7 + 8 * 0] = (h->cbp_table[top_xy] & 0x8000) >> 12; + } + if (IS_8x8DCT(left_type[LTOP])) { + nnz_cache[3 + 8 * 1] = + nnz_cache[3 + 8 * 2] = (h->cbp_table[left_xy[LTOP]] & 0x2000) >> 12; // FIXME check MBAFF + } + if (IS_8x8DCT(left_type[LBOT])) { + nnz_cache[3 + 8 * 3] = + nnz_cache[3 + 8 * 4] = (h->cbp_table[left_xy[LBOT]] & 0x8000) >> 12; // FIXME check MBAFF + } + + if (IS_8x8DCT(mb_type)) { + nnz_cache[scan8[0]] = + nnz_cache[scan8[1]] = + nnz_cache[scan8[2]] = + nnz_cache[scan8[3]] = (sl->cbp & 0x1000) >> 12; + + nnz_cache[scan8[0 + 4]] = + nnz_cache[scan8[1 + 4]] = + nnz_cache[scan8[2 + 4]] = + nnz_cache[scan8[3 + 4]] = (sl->cbp & 0x2000) >> 12; + + nnz_cache[scan8[0 + 8]] = + nnz_cache[scan8[1 + 8]] = + nnz_cache[scan8[2 + 8]] = + nnz_cache[scan8[3 + 8]] = (sl->cbp & 0x4000) >> 12; + + nnz_cache[scan8[0 + 12]] = + nnz_cache[scan8[1 + 12]] = + nnz_cache[scan8[2 + 12]] = + nnz_cache[scan8[3 + 12]] = (sl->cbp & 0x8000) >> 12; + } + } + + return 0; +} + +static void loop_filter(const H264Context *h, H264SliceContext *sl, int start_x, int end_x) +{ + uint8_t *dest_y, *dest_cb, *dest_cr; + int linesize, uvlinesize, mb_x, mb_y; + const int end_mb_y = sl->mb_y + FRAME_MBAFF(h); + const int old_slice_type = sl->slice_type; + const int pixel_shift = h->pixel_shift; + const int block_h = 16 >> h->chroma_y_shift; + + if (h->postpone_filter) + return; + + if (sl->deblocking_filter) { + for (mb_x = start_x; mb_x < end_x; mb_x++) + for (mb_y = end_mb_y - FRAME_MBAFF(h); mb_y <= end_mb_y; mb_y++) { + int mb_xy, mb_type; + mb_xy = sl->mb_xy = mb_x + mb_y * h->mb_stride; + mb_type = h->cur_pic.mb_type[mb_xy]; + + if (FRAME_MBAFF(h)) + sl->mb_mbaff = + sl->mb_field_decoding_flag = !!IS_INTERLACED(mb_type); + + sl->mb_x = mb_x; + sl->mb_y = mb_y; + dest_y = h->cur_pic.f->data[0] + + ((mb_x << pixel_shift) + mb_y * sl->linesize) * 16; + dest_cb = h->cur_pic.f->data[1] + + (mb_x << pixel_shift) * (8 << CHROMA444(h)) + + mb_y * sl->uvlinesize * block_h; + dest_cr = h->cur_pic.f->data[2] + + (mb_x << pixel_shift) * (8 << CHROMA444(h)) + + mb_y * sl->uvlinesize * block_h; + // FIXME simplify above + + if (MB_FIELD(sl)) { + linesize = sl->mb_linesize = sl->linesize * 2; + uvlinesize = sl->mb_uvlinesize = sl->uvlinesize * 2; + if (mb_y & 1) { // FIXME move out of this function? + dest_y -= sl->linesize * 15; + dest_cb -= sl->uvlinesize * (block_h - 1); + dest_cr -= sl->uvlinesize * (block_h - 1); + } + } else { + linesize = sl->mb_linesize = sl->linesize; + uvlinesize = sl->mb_uvlinesize = sl->uvlinesize; + } + backup_mb_border(h, sl, dest_y, dest_cb, dest_cr, linesize, + uvlinesize, 0); + if (fill_filter_caches(h, sl, mb_type)) + continue; + sl->chroma_qp[0] = get_chroma_qp(h->ps.pps, 0, h->cur_pic.qscale_table[mb_xy]); + sl->chroma_qp[1] = get_chroma_qp(h->ps.pps, 1, h->cur_pic.qscale_table[mb_xy]); + + if (FRAME_MBAFF(h)) { + ff_h264_filter_mb(h, sl, mb_x, mb_y, dest_y, dest_cb, dest_cr, + linesize, uvlinesize); + } else { + ff_h264_filter_mb_fast(h, sl, mb_x, mb_y, dest_y, dest_cb, + dest_cr, linesize, uvlinesize); + } + } + } + sl->slice_type = old_slice_type; + sl->mb_x = end_x; + sl->mb_y = end_mb_y - FRAME_MBAFF(h); + sl->chroma_qp[0] = get_chroma_qp(h->ps.pps, 0, sl->qscale); + sl->chroma_qp[1] = get_chroma_qp(h->ps.pps, 1, sl->qscale); +} + +static void predict_field_decoding_flag(const H264Context *h, H264SliceContext *sl) +{ + const int mb_xy = sl->mb_x + sl->mb_y * h->mb_stride; + int mb_type = (h->slice_table[mb_xy - 1] == sl->slice_num) ? + h->cur_pic.mb_type[mb_xy - 1] : + (h->slice_table[mb_xy - h->mb_stride] == sl->slice_num) ? + h->cur_pic.mb_type[mb_xy - h->mb_stride] : 0; + sl->mb_mbaff = sl->mb_field_decoding_flag = IS_INTERLACED(mb_type) ? 1 : 0; +} + +/** + * Draw edges and report progress for the last MB row. + */ +static void decode_finish_row(const H264Context *h, H264SliceContext *sl) +{ + int top = 16 * (sl->mb_y >> FIELD_PICTURE(h)); + int pic_height = 16 * h->mb_height >> FIELD_PICTURE(h); + int height = 16 << FRAME_MBAFF(h); + int deblock_border = (16 + 4) << FRAME_MBAFF(h); + + if (sl->deblocking_filter) { + if ((top + height) >= pic_height) + height += deblock_border; + top -= deblock_border; + } + + if (top >= pic_height || (top + height) < 0) + return; + + height = FFMIN(height, pic_height - top); + if (top < 0) { + height = top + height; + top = 0; + } + + ff_h264_draw_horiz_band(h, sl, top, height); + + if (h->droppable || h->er.error_occurred) + return; + + ff_thread_report_progress(&h->cur_pic_ptr->tf, top + height - 1, + h->picture_structure == PICT_BOTTOM_FIELD); +} + +static void er_add_slice(H264SliceContext *sl, + int startx, int starty, + int endx, int endy, int status) +{ + if (!sl->h264->enable_er) + return; + + if (CONFIG_ERROR_RESILIENCE) { + ff_er_add_slice(sl->er, startx, starty, endx, endy, status); + } +} + +static int decode_slice(struct AVCodecContext *avctx, void *arg) +{ + H264SliceContext *sl = arg; + const H264Context *h = sl->h264; + int lf_x_start = sl->mb_x; + int orig_deblock = sl->deblocking_filter; + int ret; + + sl->linesize = h->cur_pic_ptr->f->linesize[0]; + sl->uvlinesize = h->cur_pic_ptr->f->linesize[1]; + + ret = alloc_scratch_buffers(sl, sl->linesize); + if (ret < 0) + return ret; + + sl->mb_skip_run = -1; + + av_assert0(h->block_offset[15] == (4 * ((scan8[15] - scan8[0]) & 7) << h->pixel_shift) + 4 * sl->linesize * ((scan8[15] - scan8[0]) >> 3)); + + if (h->postpone_filter) + sl->deblocking_filter = 0; + + sl->is_complex = FRAME_MBAFF(h) || h->picture_structure != PICT_FRAME || + (CONFIG_GRAY && (h->flags & AV_CODEC_FLAG_GRAY)); + + if (!(h->avctx->active_thread_type & FF_THREAD_SLICE) && h->picture_structure == PICT_FRAME && sl->er->error_status_table) { + const int start_i = av_clip(sl->resync_mb_x + sl->resync_mb_y * h->mb_width, 0, h->mb_num - 1); + if (start_i) { + int prev_status = sl->er->error_status_table[sl->er->mb_index2xy[start_i - 1]]; + prev_status &= ~ VP_START; + if (prev_status != (ER_MV_END | ER_DC_END | ER_AC_END)) + sl->er->error_occurred = 1; + } + } + + if (h->ps.pps->cabac) { + /* realign */ + align_get_bits(&sl->gb); + + /* init cabac */ + ret = ff_init_cabac_decoder(&sl->cabac, + sl->gb.buffer + get_bits_count(&sl->gb) / 8, + (get_bits_left(&sl->gb) + 7) / 8); + if (ret < 0) + return ret; + + ff_h264_init_cabac_states(h, sl); + + for (;;) { + int ret, eos; + if (sl->mb_x + sl->mb_y * h->mb_width >= sl->next_slice_idx) { + av_log(h->avctx, AV_LOG_ERROR, "Slice overlaps with next at %d\n", + sl->next_slice_idx); + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, sl->mb_x, + sl->mb_y, ER_MB_ERROR); + return AVERROR_INVALIDDATA; + } + + ret = ff_h264_decode_mb_cabac(h, sl); + + if (ret >= 0) + ff_h264_hl_decode_mb(h, sl); + + // FIXME optimal? or let mb_decode decode 16x32 ? + if (ret >= 0 && FRAME_MBAFF(h)) { + sl->mb_y++; + + ret = ff_h264_decode_mb_cabac(h, sl); + + if (ret >= 0) + ff_h264_hl_decode_mb(h, sl); + sl->mb_y--; + } + eos = get_cabac_terminate(&sl->cabac); + + if ((h->workaround_bugs & FF_BUG_TRUNCATED) && + sl->cabac.bytestream > sl->cabac.bytestream_end + 2) { + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, sl->mb_x - 1, + sl->mb_y, ER_MB_END); + if (sl->mb_x >= lf_x_start) + loop_filter(h, sl, lf_x_start, sl->mb_x + 1); + goto finish; + } + if (sl->cabac.bytestream > sl->cabac.bytestream_end + 2 ) + av_log(h->avctx, AV_LOG_DEBUG, "bytestream overread %td\n", sl->cabac.bytestream_end - sl->cabac.bytestream); + if (ret < 0 || sl->cabac.bytestream > sl->cabac.bytestream_end + 4) { + av_log(h->avctx, AV_LOG_ERROR, + "error while decoding MB %d %d, bytestream %td\n", + sl->mb_x, sl->mb_y, + sl->cabac.bytestream_end - sl->cabac.bytestream); + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, sl->mb_x, + sl->mb_y, ER_MB_ERROR); + return AVERROR_INVALIDDATA; + } + + if (++sl->mb_x >= h->mb_width) { + loop_filter(h, sl, lf_x_start, sl->mb_x); + sl->mb_x = lf_x_start = 0; + decode_finish_row(h, sl); + ++sl->mb_y; + if (FIELD_OR_MBAFF_PICTURE(h)) { + ++sl->mb_y; + if (FRAME_MBAFF(h) && sl->mb_y < h->mb_height) + predict_field_decoding_flag(h, sl); + } + } + + if (eos || sl->mb_y >= h->mb_height) { + ff_tlog(h->avctx, "slice end %d %d\n", + get_bits_count(&sl->gb), sl->gb.size_in_bits); + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, sl->mb_x - 1, + sl->mb_y, ER_MB_END); + if (sl->mb_x > lf_x_start) + loop_filter(h, sl, lf_x_start, sl->mb_x); + goto finish; + } + } + } else { + for (;;) { + int ret; + + if (sl->mb_x + sl->mb_y * h->mb_width >= sl->next_slice_idx) { + av_log(h->avctx, AV_LOG_ERROR, "Slice overlaps with next at %d\n", + sl->next_slice_idx); + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, sl->mb_x, + sl->mb_y, ER_MB_ERROR); + return AVERROR_INVALIDDATA; + } + + ret = ff_h264_decode_mb_cavlc(h, sl); + + if (ret >= 0) + ff_h264_hl_decode_mb(h, sl); + + // FIXME optimal? or let mb_decode decode 16x32 ? + if (ret >= 0 && FRAME_MBAFF(h)) { + sl->mb_y++; + ret = ff_h264_decode_mb_cavlc(h, sl); + + if (ret >= 0) + ff_h264_hl_decode_mb(h, sl); + sl->mb_y--; + } + + if (ret < 0) { + av_log(h->avctx, AV_LOG_ERROR, + "error while decoding MB %d %d\n", sl->mb_x, sl->mb_y); + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, sl->mb_x, + sl->mb_y, ER_MB_ERROR); + return ret; + } + + if (++sl->mb_x >= h->mb_width) { + loop_filter(h, sl, lf_x_start, sl->mb_x); + sl->mb_x = lf_x_start = 0; + decode_finish_row(h, sl); + ++sl->mb_y; + if (FIELD_OR_MBAFF_PICTURE(h)) { + ++sl->mb_y; + if (FRAME_MBAFF(h) && sl->mb_y < h->mb_height) + predict_field_decoding_flag(h, sl); + } + if (sl->mb_y >= h->mb_height) { + ff_tlog(h->avctx, "slice end %d %d\n", + get_bits_count(&sl->gb), sl->gb.size_in_bits); + + if ( get_bits_left(&sl->gb) == 0 + || get_bits_left(&sl->gb) > 0 && !(h->avctx->err_recognition & AV_EF_AGGRESSIVE)) { + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, + sl->mb_x - 1, sl->mb_y, ER_MB_END); + + goto finish; + } else { + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, + sl->mb_x, sl->mb_y, ER_MB_END); + + return AVERROR_INVALIDDATA; + } + } + } + + if (get_bits_left(&sl->gb) <= 0 && sl->mb_skip_run <= 0) { + ff_tlog(h->avctx, "slice end %d %d\n", + get_bits_count(&sl->gb), sl->gb.size_in_bits); + + if (get_bits_left(&sl->gb) == 0) { + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, + sl->mb_x - 1, sl->mb_y, ER_MB_END); + if (sl->mb_x > lf_x_start) + loop_filter(h, sl, lf_x_start, sl->mb_x); + + goto finish; + } else { + er_add_slice(sl, sl->resync_mb_x, sl->resync_mb_y, sl->mb_x, + sl->mb_y, ER_MB_ERROR); + + return AVERROR_INVALIDDATA; + } + } + } + } + +finish: + sl->deblocking_filter = orig_deblock; + return 0; +} + +/** + * Call decode_slice() for each context. + * + * @param h h264 master context + */ +int ff_h264_execute_decode_slices(H264Context *h) +{ + AVCodecContext *const avctx = h->avctx; + H264SliceContext *sl; + int context_count = h->nb_slice_ctx_queued; + int ret = 0; + int i, j; + + h->slice_ctx[0].next_slice_idx = INT_MAX; + + if (h->avctx->hwaccel || context_count < 1) + return 0; + + av_assert0(context_count && h->slice_ctx[context_count - 1].mb_y < h->mb_height); + + if (context_count == 1) { + + h->slice_ctx[0].next_slice_idx = h->mb_width * h->mb_height; + h->postpone_filter = 0; + + ret = decode_slice(avctx, &h->slice_ctx[0]); + h->mb_y = h->slice_ctx[0].mb_y; + if (ret < 0) + goto finish; + } else { + av_assert0(context_count > 0); + for (i = 0; i < context_count; i++) { + int next_slice_idx = h->mb_width * h->mb_height; + int slice_idx; + + sl = &h->slice_ctx[i]; + + /* make sure none of those slices overlap */ + slice_idx = sl->mb_y * h->mb_width + sl->mb_x; + for (j = 0; j < context_count; j++) { + H264SliceContext *sl2 = &h->slice_ctx[j]; + int slice_idx2 = sl2->mb_y * h->mb_width + sl2->mb_x; + + if (i == j || slice_idx2 < slice_idx) + continue; + next_slice_idx = FFMIN(next_slice_idx, slice_idx2); + } + sl->next_slice_idx = next_slice_idx; + } + + avctx->execute(avctx, decode_slice, h->slice_ctx, + NULL, context_count, sizeof(h->slice_ctx[0])); + + /* pull back stuff from slices to master context */ + sl = &h->slice_ctx[context_count - 1]; + h->mb_y = sl->mb_y; + + if (h->postpone_filter) { + h->postpone_filter = 0; + + for (i = 0; i < context_count; i++) { + int y_end, x_end; + + sl = &h->slice_ctx[i]; + y_end = FFMIN(sl->mb_y + 1, h->mb_height); + x_end = (sl->mb_y >= h->mb_height) ? h->mb_width : sl->mb_x; + + for (j = sl->resync_mb_y; j < y_end; j += 1 + FIELD_OR_MBAFF_PICTURE(h)) { + sl->mb_y = j; + loop_filter(h, sl, j > sl->resync_mb_y ? 0 : sl->resync_mb_x, + j == y_end - 1 ? x_end : h->mb_width); + } + } + } + } + +finish: + h->nb_slice_ctx_queued = 0; + return ret; +} diff --git a/thirdparty/ffmpeg/libavcodec/h264dec.c b/thirdparty/ffmpeg/libavcodec/h264dec.c index ca65df2b8..f49c5ffde 100644 --- a/thirdparty/ffmpeg/libavcodec/h264dec.c +++ b/thirdparty/ffmpeg/libavcodec/h264dec.c @@ -1145,6 +1145,9 @@ const FFCodec ff_h264_decoder = { #endif #if CONFIG_H264_VULKAN_HWACCEL HWACCEL_VULKAN(h264), +#endif +#if CONFIG_H264_V4L2REQUEST_HWACCEL + HWACCEL_V4L2REQUEST(h264), #endif NULL }, diff --git a/thirdparty/ffmpeg/libavcodec/h264dec.c.orig b/thirdparty/ffmpeg/libavcodec/h264dec.c.orig new file mode 100644 index 000000000..ca65df2b8 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/h264dec.c.orig @@ -0,0 +1,1158 @@ +/* + * H.26L/H.264/AVC/JVT/14496-10/... decoder + * Copyright (c) 2003 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * H.264 / AVC / MPEG-4 part10 codec. + * @author Michael Niedermayer + */ + +#define UNCHECKED_BITSTREAM_READER 1 + +#include "config_components.h" + +#include "libavutil/attributes.h" +#include "libavutil/avassert.h" +#include "libavutil/emms.h" +#include "libavutil/imgutils.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/thread.h" +#include "libavutil/video_enc_params.h" + +#include "codec_internal.h" +#include "internal.h" +#include "error_resilience.h" +#include "avcodec.h" +#include "h264.h" +#include "h264dec.h" +#include "h2645_parse.h" +#include "h264data.h" +#include "h264_ps.h" +#include "golomb.h" +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "mpegutils.h" +#include "profiles.h" +#include "rectangle.h" +#include "libavutil/refstruct.h" +#include "thread.h" +#include "threadframe.h" + +const uint16_t ff_h264_mb_sizes[4] = { 256, 384, 512, 768 }; + +int avpriv_h264_has_num_reorder_frames(AVCodecContext *avctx) +{ + H264Context *h = avctx->priv_data; + return h && h->ps.sps ? h->ps.sps->num_reorder_frames : 0; +} + +static void h264_er_decode_mb(void *opaque, int ref, int mv_dir, int mv_type, + int (*mv)[2][4][2], + int mb_x, int mb_y, int mb_intra, int mb_skipped) +{ + const H264Context *h = opaque; + H264SliceContext *sl = &h->slice_ctx[0]; + + sl->mb_x = mb_x; + sl->mb_y = mb_y; + sl->mb_xy = mb_x + mb_y * h->mb_stride; + memset(sl->non_zero_count_cache, 0, sizeof(sl->non_zero_count_cache)); + av_assert1(ref >= 0); + /* FIXME: It is possible albeit uncommon that slice references + * differ between slices. We take the easy approach and ignore + * it for now. If this turns out to have any relevance in + * practice then correct remapping should be added. */ + if (ref >= sl->ref_count[0]) + ref = 0; + if (!sl->ref_list[0][ref].data[0]) { + av_log(h->avctx, AV_LOG_DEBUG, "Reference not available for error concealing\n"); + ref = 0; + } + if ((sl->ref_list[0][ref].reference&3) != 3) { + av_log(h->avctx, AV_LOG_DEBUG, "Reference invalid\n"); + return; + } + fill_rectangle(&h->cur_pic.ref_index[0][4 * sl->mb_xy], + 2, 2, 2, ref, 1); + fill_rectangle(&sl->ref_cache[0][scan8[0]], 4, 4, 8, ref, 1); + fill_rectangle(sl->mv_cache[0][scan8[0]], 4, 4, 8, + pack16to32((*mv)[0][0][0], (*mv)[0][0][1]), 4); + sl->mb_mbaff = + sl->mb_field_decoding_flag = 0; + ff_h264_hl_decode_mb(h, &h->slice_ctx[0]); +} + +void ff_h264_draw_horiz_band(const H264Context *h, H264SliceContext *sl, + int y, int height) +{ + AVCodecContext *avctx = h->avctx; + const AVFrame *src = h->cur_pic.f; + const AVPixFmtDescriptor *desc; + int offset[AV_NUM_DATA_POINTERS]; + int vshift; + const int field_pic = h->picture_structure != PICT_FRAME; + + if (!avctx->draw_horiz_band) + return; + + if (field_pic && h->first_field && !(avctx->slice_flags & SLICE_FLAG_ALLOW_FIELD)) + return; + + if (field_pic) { + height <<= 1; + y <<= 1; + } + + height = FFMIN(height, avctx->height - y); + + desc = av_pix_fmt_desc_get(avctx->pix_fmt); + vshift = desc->log2_chroma_h; + + offset[0] = y * src->linesize[0]; + offset[1] = + offset[2] = (y >> vshift) * src->linesize[1]; + for (int i = 3; i < AV_NUM_DATA_POINTERS; i++) + offset[i] = 0; + + emms_c(); + + avctx->draw_horiz_band(avctx, src, offset, + y, h->picture_structure, height); +} + +void ff_h264_free_tables(H264Context *h) +{ + int i; + + av_freep(&h->intra4x4_pred_mode); + av_freep(&h->chroma_pred_mode_table); + av_freep(&h->cbp_table); + av_freep(&h->mvd_table[0]); + av_freep(&h->mvd_table[1]); + av_freep(&h->direct_table); + av_freep(&h->non_zero_count); + av_freep(&h->slice_table_base); + h->slice_table = NULL; + av_freep(&h->list_counts); + + av_freep(&h->mb2b_xy); + av_freep(&h->mb2br_xy); + + av_refstruct_pool_uninit(&h->qscale_table_pool); + av_refstruct_pool_uninit(&h->mb_type_pool); + av_refstruct_pool_uninit(&h->motion_val_pool); + av_refstruct_pool_uninit(&h->ref_index_pool); + +#if CONFIG_ERROR_RESILIENCE + av_freep(&h->er.mb_index2xy); + av_freep(&h->er.error_status_table); + av_freep(&h->er.er_temp_buffer); + av_freep(&h->dc_val_base); +#endif + + for (i = 0; i < h->nb_slice_ctx; i++) { + H264SliceContext *sl = &h->slice_ctx[i]; + + av_freep(&sl->bipred_scratchpad); + av_freep(&sl->edge_emu_buffer); + av_freep(&sl->top_borders[0]); + av_freep(&sl->top_borders[1]); + + sl->bipred_scratchpad_allocated = 0; + sl->edge_emu_buffer_allocated = 0; + sl->top_borders_allocated[0] = 0; + sl->top_borders_allocated[1] = 0; + } +} + +int ff_h264_alloc_tables(H264Context *h) +{ + ERContext *const er = &h->er; + const int big_mb_num = h->mb_stride * (h->mb_height + 1); + const int row_mb_num = 2*h->mb_stride*FFMAX(h->nb_slice_ctx, 1); + const int st_size = big_mb_num + h->mb_stride; + int x, y; + + if (!FF_ALLOCZ_TYPED_ARRAY(h->intra4x4_pred_mode, row_mb_num * 8) || + !FF_ALLOCZ_TYPED_ARRAY(h->non_zero_count, big_mb_num) || + !FF_ALLOCZ_TYPED_ARRAY(h->slice_table_base, st_size) || + !FF_ALLOCZ_TYPED_ARRAY(h->cbp_table, big_mb_num) || + !FF_ALLOCZ_TYPED_ARRAY(h->chroma_pred_mode_table, big_mb_num) || + !FF_ALLOCZ_TYPED_ARRAY(h->mvd_table[0], row_mb_num * 8) || + !FF_ALLOCZ_TYPED_ARRAY(h->mvd_table[1], row_mb_num * 8) || + !FF_ALLOCZ_TYPED_ARRAY(h->direct_table, big_mb_num * 4) || + !FF_ALLOCZ_TYPED_ARRAY(h->list_counts, big_mb_num) || + !FF_ALLOCZ_TYPED_ARRAY(h->mb2b_xy, big_mb_num) || + !FF_ALLOCZ_TYPED_ARRAY(h->mb2br_xy, big_mb_num)) + return AVERROR(ENOMEM); + h->slice_ctx[0].intra4x4_pred_mode = h->intra4x4_pred_mode; + h->slice_ctx[0].mvd_table[0] = h->mvd_table[0]; + h->slice_ctx[0].mvd_table[1] = h->mvd_table[1]; + memset(h->slice_table_base, -1, + st_size * sizeof(*h->slice_table_base)); + h->slice_table = h->slice_table_base + h->mb_stride * 2 + 1; + for (y = 0; y < h->mb_height; y++) + for (x = 0; x < h->mb_width; x++) { + const int mb_xy = x + y * h->mb_stride; + const int b_xy = 4 * x + 4 * y * h->b_stride; + + h->mb2b_xy[mb_xy] = b_xy; + h->mb2br_xy[mb_xy] = 8 * (FMO ? mb_xy : (mb_xy % (2 * h->mb_stride))); + } + + if (CONFIG_ERROR_RESILIENCE) { + int y_size = (2 * h->mb_width + 1) * (2 * h->mb_height + 1); + int yc_size = y_size + 2 * big_mb_num; + + /* init ER */ + er->avctx = h->avctx; + er->decode_mb = h264_er_decode_mb; + er->opaque = h; + er->quarter_sample = 1; + + er->mb_num = h->mb_num; + er->mb_width = h->mb_width; + er->mb_height = h->mb_height; + er->mb_stride = h->mb_stride; + er->b8_stride = h->mb_width * 2 + 1; + + // error resilience code looks cleaner with this + if (!FF_ALLOCZ_TYPED_ARRAY(er->mb_index2xy, h->mb_num + 1) || + !FF_ALLOCZ_TYPED_ARRAY(h->dc_val_base, yc_size)) + return AVERROR(ENOMEM); // ff_h264_free_tables will clean up for us + + for (y = 0; y < h->mb_height; y++) + for (x = 0; x < h->mb_width; x++) + er->mb_index2xy[x + y * h->mb_width] = x + y * h->mb_stride; + + er->mb_index2xy[h->mb_height * h->mb_width] = (h->mb_height - 1) * + h->mb_stride + h->mb_width; + er->dc_val[0] = h->dc_val_base + h->mb_width * 2 + 2; + er->dc_val[1] = h->dc_val_base + y_size + h->mb_stride + 1; + er->dc_val[2] = er->dc_val[1] + big_mb_num; + for (int i = 0; i < yc_size; i++) + h->dc_val_base[i] = 1024; + + return ff_er_init(er); + } + + return 0; +} + +/** + * Init slice context + */ +void ff_h264_slice_context_init(H264Context *h, H264SliceContext *sl) +{ + sl->ref_cache[0][scan8[5] + 1] = + sl->ref_cache[0][scan8[7] + 1] = + sl->ref_cache[0][scan8[13] + 1] = + sl->ref_cache[1][scan8[5] + 1] = + sl->ref_cache[1][scan8[7] + 1] = + sl->ref_cache[1][scan8[13] + 1] = PART_NOT_AVAILABLE; + + sl->er = &h->er; +} + +static int h264_init_pic(H264Picture *pic) +{ + pic->f = av_frame_alloc(); + if (!pic->f) + return AVERROR(ENOMEM); + + pic->f_grain = av_frame_alloc(); + if (!pic->f_grain) + return AVERROR(ENOMEM); + + return 0; +} + +static int h264_init_context(AVCodecContext *avctx, H264Context *h) +{ + int i, ret; + + h->avctx = avctx; + h->cur_chroma_format_idc = -1; + + h->width_from_caller = avctx->width; + h->height_from_caller = avctx->height; + + h->workaround_bugs = avctx->workaround_bugs; + h->flags = avctx->flags; + h->poc.prev_poc_msb = 1 << 16; + h->recovery_frame = -1; + h->frame_recovered = 0; + h->poc.prev_frame_num = -1; + h->sei.common.frame_packing.arrangement_cancel_flag = -1; + h->sei.common.unregistered.x264_build = -1; + + h->next_outputed_poc = INT_MIN; + for (i = 0; i < FF_ARRAY_ELEMS(h->last_pocs); i++) + h->last_pocs[i] = INT_MIN; + + ff_h264_sei_uninit(&h->sei); + + if (avctx->active_thread_type & FF_THREAD_FRAME) { + h->decode_error_flags_pool = av_refstruct_pool_alloc(sizeof(atomic_int), 0); + if (!h->decode_error_flags_pool) + return AVERROR(ENOMEM); + } + + h->nb_slice_ctx = (avctx->active_thread_type & FF_THREAD_SLICE) ? avctx->thread_count : 1; + h->slice_ctx = av_calloc(h->nb_slice_ctx, sizeof(*h->slice_ctx)); + if (!h->slice_ctx) { + h->nb_slice_ctx = 0; + return AVERROR(ENOMEM); + } + + for (i = 0; i < H264_MAX_PICTURE_COUNT; i++) { + if ((ret = h264_init_pic(&h->DPB[i])) < 0) + return ret; + } + + if ((ret = h264_init_pic(&h->cur_pic)) < 0) + return ret; + + if ((ret = h264_init_pic(&h->last_pic_for_ec)) < 0) + return ret; + + for (i = 0; i < h->nb_slice_ctx; i++) + h->slice_ctx[i].h264 = h; + + return 0; +} + +static void h264_free_pic(H264Context *h, H264Picture *pic) +{ + ff_h264_unref_picture(pic); + av_frame_free(&pic->f); + av_frame_free(&pic->f_grain); +} + +static av_cold int h264_decode_end(AVCodecContext *avctx) +{ + H264Context *h = avctx->priv_data; + int i; + + ff_h264_remove_all_refs(h); + ff_h264_free_tables(h); + + for (i = 0; i < H264_MAX_PICTURE_COUNT; i++) { + h264_free_pic(h, &h->DPB[i]); + } + memset(h->delayed_pic, 0, sizeof(h->delayed_pic)); + + h->cur_pic_ptr = NULL; + + av_refstruct_pool_uninit(&h->decode_error_flags_pool); + + av_freep(&h->slice_ctx); + h->nb_slice_ctx = 0; + + ff_h264_sei_uninit(&h->sei); + ff_h264_ps_uninit(&h->ps); + + ff_h2645_packet_uninit(&h->pkt); + + h264_free_pic(h, &h->cur_pic); + h264_free_pic(h, &h->last_pic_for_ec); + + return 0; +} + +static AVOnce h264_vlc_init = AV_ONCE_INIT; + +static av_cold int h264_decode_init(AVCodecContext *avctx) +{ + H264Context *h = avctx->priv_data; + int ret; + + ret = h264_init_context(avctx, h); + if (ret < 0) + return ret; + + ret = ff_thread_once(&h264_vlc_init, ff_h264_decode_init_vlc); + if (ret != 0) { + av_log(avctx, AV_LOG_ERROR, "pthread_once has failed."); + return AVERROR_UNKNOWN; + } + + if (!avctx->internal->is_copy) { + if (avctx->extradata_size > 0 && avctx->extradata) { + ret = ff_h264_decode_extradata(avctx->extradata, avctx->extradata_size, + &h->ps, &h->is_avc, &h->nal_length_size, + avctx->err_recognition, avctx); + if (ret < 0) { + int explode = avctx->err_recognition & AV_EF_EXPLODE; + av_log(avctx, explode ? AV_LOG_ERROR: AV_LOG_WARNING, + "Error decoding the extradata\n"); + if (explode) { + return ret; + } + ret = 0; + } + } + } + + if (h->ps.sps && h->ps.sps->bitstream_restriction_flag && + h->avctx->has_b_frames < h->ps.sps->num_reorder_frames) { + h->avctx->has_b_frames = h->ps.sps->num_reorder_frames; + } + + ff_h264_flush_change(h); + + if (h->enable_er < 0 && (avctx->active_thread_type & FF_THREAD_SLICE)) + h->enable_er = 0; + + if (h->enable_er && (avctx->active_thread_type & FF_THREAD_SLICE)) { + av_log(avctx, AV_LOG_WARNING, + "Error resilience with slice threads is enabled. It is unsafe and unsupported and may crash. " + "Use it at your own risk\n"); + } + + return 0; +} + +/** + * instantaneous decoder refresh. + */ +static void idr(H264Context *h) +{ + int i; + ff_h264_remove_all_refs(h); + h->poc.prev_frame_num = + h->poc.prev_frame_num_offset = 0; + h->poc.prev_poc_msb = 1<<16; + h->poc.prev_poc_lsb = -1; + for (i = 0; i < FF_ARRAY_ELEMS(h->last_pocs); i++) + h->last_pocs[i] = INT_MIN; +} + +/* forget old pics after a seek */ +void ff_h264_flush_change(H264Context *h) +{ + int i, j; + + h->next_outputed_poc = INT_MIN; + h->prev_interlaced_frame = 1; + idr(h); + + h->poc.prev_frame_num = -1; + if (h->cur_pic_ptr) { + h->cur_pic_ptr->reference = 0; + for (j=i=0; h->delayed_pic[i]; i++) + if (h->delayed_pic[i] != h->cur_pic_ptr) + h->delayed_pic[j++] = h->delayed_pic[i]; + h->delayed_pic[j] = NULL; + } + ff_h264_unref_picture(&h->last_pic_for_ec); + + h->first_field = 0; + h->recovery_frame = -1; + h->frame_recovered = 0; + h->current_slice = 0; + h->mmco_reset = 1; +} + +static av_cold void h264_decode_flush(AVCodecContext *avctx) +{ + H264Context *h = avctx->priv_data; + int i; + + memset(h->delayed_pic, 0, sizeof(h->delayed_pic)); + + ff_h264_flush_change(h); + ff_h264_sei_uninit(&h->sei); + + for (i = 0; i < H264_MAX_PICTURE_COUNT; i++) + ff_h264_unref_picture(&h->DPB[i]); + h->cur_pic_ptr = NULL; + ff_h264_unref_picture(&h->cur_pic); + + h->mb_y = 0; + h->non_gray = 0; + + ff_h264_free_tables(h); + h->context_initialized = 0; + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); +} + +static int get_last_needed_nal(H264Context *h) +{ + int nals_needed = 0; + int slice_type = 0; + int picture_intra_only = 1; + int first_slice = 0; + int i, ret; + + for (i = 0; i < h->pkt.nb_nals; i++) { + H2645NAL *nal = &h->pkt.nals[i]; + GetBitContext gb; + + /* packets can sometimes contain multiple PPS/SPS, + * e.g. two PAFF field pictures in one packet, or a demuxer + * which splits NALs strangely if so, when frame threading we + * can't start the next thread until we've read all of them */ + switch (nal->type) { + case H264_NAL_SPS: + case H264_NAL_PPS: + nals_needed = i; + break; + case H264_NAL_DPA: + case H264_NAL_IDR_SLICE: + case H264_NAL_SLICE: + ret = init_get_bits8(&gb, nal->data + 1, nal->size - 1); + if (ret < 0) { + av_log(h->avctx, AV_LOG_ERROR, "Invalid zero-sized VCL NAL unit\n"); + if (h->avctx->err_recognition & AV_EF_EXPLODE) + return ret; + + break; + } + if (!get_ue_golomb_long(&gb) || // first_mb_in_slice + !first_slice || + first_slice != nal->type) + nals_needed = i; + slice_type = get_ue_golomb_31(&gb); + if (slice_type > 9) + slice_type = 0; + if (slice_type > 4) + slice_type -= 5; + + slice_type = ff_h264_golomb_to_pict_type[slice_type]; + picture_intra_only &= (slice_type & 3) == AV_PICTURE_TYPE_I; + if (!first_slice) + first_slice = nal->type; + } + } + + h->picture_intra_only = picture_intra_only; + + return nals_needed; +} + +static void debug_green_metadata(const H264SEIGreenMetaData *gm, void *logctx) +{ + av_log(logctx, AV_LOG_DEBUG, "Green Metadata Info SEI message\n"); + av_log(logctx, AV_LOG_DEBUG, " green_metadata_type: %d\n", gm->green_metadata_type); + + if (gm->green_metadata_type == 0) { + av_log(logctx, AV_LOG_DEBUG, " green_metadata_period_type: %d\n", gm->period_type); + + if (gm->period_type == 2) + av_log(logctx, AV_LOG_DEBUG, " green_metadata_num_seconds: %d\n", gm->num_seconds); + else if (gm->period_type == 3) + av_log(logctx, AV_LOG_DEBUG, " green_metadata_num_pictures: %d\n", gm->num_pictures); + + av_log(logctx, AV_LOG_DEBUG, " SEI GREEN Complexity Metrics: %f %f %f %f\n", + (float)gm->percent_non_zero_macroblocks/255, + (float)gm->percent_intra_coded_macroblocks/255, + (float)gm->percent_six_tap_filtering/255, + (float)gm->percent_alpha_point_deblocking_instance/255); + + } else if (gm->green_metadata_type == 1) { + av_log(logctx, AV_LOG_DEBUG, " xsd_metric_type: %d\n", gm->xsd_metric_type); + + if (gm->xsd_metric_type == 0) + av_log(logctx, AV_LOG_DEBUG, " xsd_metric_value: %f\n", + (float)gm->xsd_metric_value/100); + } +} + +static int decode_nal_units(H264Context *h, AVBufferRef *buf_ref, + const uint8_t *buf, int buf_size) +{ + AVCodecContext *const avctx = h->avctx; + int nals_needed = 0; ///< number of NALs that need decoding before the next frame thread starts + int idr_cleared=0; + int i, ret = 0; + + h->has_slice = 0; + h->nal_unit_type= 0; + + if (!(avctx->flags2 & AV_CODEC_FLAG2_CHUNKS)) { + h->current_slice = 0; + if (!h->first_field) { + h->cur_pic_ptr = NULL; + ff_h264_sei_uninit(&h->sei); + } + } + + if (h->nal_length_size == 4) { + if (buf_size > 8 && AV_RB32(buf) == 1 && AV_RB32(buf+5) > (unsigned)buf_size) { + h->is_avc = 0; + }else if(buf_size > 3 && AV_RB32(buf) > 1 && AV_RB32(buf) <= (unsigned)buf_size) + h->is_avc = 1; + } + + ret = ff_h2645_packet_split(&h->pkt, buf, buf_size, avctx, h->nal_length_size, + avctx->codec_id, !!h->is_avc * H2645_FLAG_IS_NALFF); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, + "Error splitting the input into NAL units.\n"); + return ret; + } + + if (avctx->active_thread_type & FF_THREAD_FRAME) + nals_needed = get_last_needed_nal(h); + if (nals_needed < 0) + return nals_needed; + + for (i = 0; i < h->pkt.nb_nals; i++) { + H2645NAL *nal = &h->pkt.nals[i]; + int max_slice_ctx, err; + + if (avctx->skip_frame >= AVDISCARD_NONREF && + nal->ref_idc == 0 && nal->type != H264_NAL_SEI) + continue; + + // FIXME these should stop being context-global variables + h->nal_ref_idc = nal->ref_idc; + h->nal_unit_type = nal->type; + + err = 0; + switch (nal->type) { + case H264_NAL_IDR_SLICE: + if ((nal->data[1] & 0xFC) == 0x98) { + av_log(h->avctx, AV_LOG_ERROR, "Invalid inter IDR frame\n"); + h->next_outputed_poc = INT_MIN; + ret = -1; + goto end; + } + if(!idr_cleared) { + idr(h); // FIXME ensure we don't lose some frames if there is reordering + } + idr_cleared = 1; + h->has_recovery_point = 1; + av_fallthrough; + case H264_NAL_SLICE: + h->has_slice = 1; + + if ((err = ff_h264_queue_decode_slice(h, nal))) { + H264SliceContext *sl = h->slice_ctx + h->nb_slice_ctx_queued; + sl->ref_count[0] = sl->ref_count[1] = 0; + break; + } + + if (h->current_slice == 1) { + if (avctx->active_thread_type & FF_THREAD_FRAME && + i >= nals_needed && !h->setup_finished && h->cur_pic_ptr) { + ff_thread_finish_setup(avctx); + h->setup_finished = 1; + } + + if (h->avctx->hwaccel && + (ret = FF_HW_CALL(h->avctx, start_frame, buf_ref, + buf, buf_size)) < 0) + goto end; + } + + max_slice_ctx = avctx->hwaccel ? 1 : h->nb_slice_ctx; + if (h->nb_slice_ctx_queued == max_slice_ctx) { + if (h->avctx->hwaccel) { + ret = FF_HW_CALL(avctx, decode_slice, nal->raw_data, nal->raw_size); + h->nb_slice_ctx_queued = 0; + } else + ret = ff_h264_execute_decode_slices(h); + if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE)) + goto end; + } + break; + case H264_NAL_DPA: + case H264_NAL_DPB: + case H264_NAL_DPC: + avpriv_request_sample(avctx, "data partitioning"); + break; + case H264_NAL_SEI: + if (h->setup_finished) { + avpriv_request_sample(avctx, "Late SEI"); + break; + } + ret = ff_h264_sei_decode(&h->sei, &nal->gb, &h->ps, avctx); + h->has_recovery_point = h->has_recovery_point || h->sei.recovery_point.recovery_frame_cnt != -1; + if (avctx->debug & FF_DEBUG_GREEN_MD) + debug_green_metadata(&h->sei.green_metadata, h->avctx); + if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE)) + goto end; + break; + case H264_NAL_SPS: { + GetBitContext tmp_gb = nal->gb; + if (FF_HW_HAS_CB(avctx, decode_params)) { + ret = FF_HW_CALL(avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); + if (ret < 0) + goto end; + } + if (ff_h264_decode_seq_parameter_set(&tmp_gb, avctx, &h->ps, 0) >= 0) + break; + av_log(h->avctx, AV_LOG_DEBUG, + "SPS decoding failure, trying again with the complete NAL\n"); + init_get_bits8(&tmp_gb, nal->raw_data + 1, nal->raw_size - 1); + if (ff_h264_decode_seq_parameter_set(&tmp_gb, avctx, &h->ps, 0) >= 0) + break; + ff_h264_decode_seq_parameter_set(&nal->gb, avctx, &h->ps, 1); + break; + } + case H264_NAL_PPS: + if (FF_HW_HAS_CB(avctx, decode_params)) { + ret = FF_HW_CALL(avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); + if (ret < 0) + goto end; + } + ret = ff_h264_decode_picture_parameter_set(&nal->gb, avctx, &h->ps, + nal->size_bits); + if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE)) + goto end; + break; + case H264_NAL_AUD: + case H264_NAL_END_SEQUENCE: + case H264_NAL_END_STREAM: + case H264_NAL_FILLER_DATA: + case H264_NAL_SPS_EXT: + case H264_NAL_AUXILIARY_SLICE: + break; + default: + av_log(avctx, AV_LOG_DEBUG, "Unknown NAL code: %d (%d bits)\n", + nal->type, nal->size_bits); + } + + if (err < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE)) { + av_log(h->avctx, AV_LOG_ERROR, "decode_slice_header error\n"); + ret = err; + goto end; + } + } + + ret = ff_h264_execute_decode_slices(h); + if (ret < 0 && (h->avctx->err_recognition & AV_EF_EXPLODE)) + goto end; + + // set decode_error_flags to allow users to detect concealed decoding errors + if ((ret < 0 || h->er.error_occurred) && h->cur_pic_ptr) { + if (h->cur_pic_ptr->decode_error_flags) { + /* Frame-threading in use */ + atomic_int *decode_error = h->cur_pic_ptr->decode_error_flags; + /* Using atomics here is not supposed to provide synchronisation; + * they are merely used to allow to set decode_error from both + * decoding threads in case of coded slices. */ + atomic_fetch_or_explicit(decode_error, FF_DECODE_ERROR_DECODE_SLICES, + memory_order_relaxed); + } else + h->cur_pic_ptr->f->decode_error_flags |= FF_DECODE_ERROR_DECODE_SLICES; + } + + ret = 0; +end: + +#if CONFIG_ERROR_RESILIENCE + /* + * FIXME: Error handling code does not seem to support interlaced + * when slices span multiple rows + * The ff_er_add_slice calls don't work right for bottom + * fields; they cause massive erroneous error concealing + * Error marking covers both fields (top and bottom). + * This causes a mismatched s->error_count + * and a bad error table. Further, the error count goes to + * INT_MAX when called for bottom field, because mb_y is + * past end by one (callers fault) and resync_mb_y != 0 + * causes problems for the first MB line, too. + */ + if (!FIELD_PICTURE(h) && h->current_slice && h->enable_er) { + + H264SliceContext *sl = h->slice_ctx; + int use_last_pic = h->last_pic_for_ec.f->buf[0] && !sl->ref_count[0]; + int decode_error_flags = 0; + + ff_h264_set_erpic(&h->er.cur_pic, h->cur_pic_ptr); + + if (use_last_pic) { + ff_h264_set_erpic(&h->er.last_pic, &h->last_pic_for_ec); + sl->ref_list[0][0].parent = &h->last_pic_for_ec; + memcpy(sl->ref_list[0][0].data, h->last_pic_for_ec.f->data, sizeof(sl->ref_list[0][0].data)); + memcpy(sl->ref_list[0][0].linesize, h->last_pic_for_ec.f->linesize, sizeof(sl->ref_list[0][0].linesize)); + sl->ref_list[0][0].reference = h->last_pic_for_ec.reference; + } else if (sl->ref_count[0]) { + ff_h264_set_erpic(&h->er.last_pic, sl->ref_list[0][0].parent); + } else + ff_h264_set_erpic(&h->er.last_pic, NULL); + + if (sl->ref_count[1]) + ff_h264_set_erpic(&h->er.next_pic, sl->ref_list[1][0].parent); + + ff_er_frame_end(&h->er, &decode_error_flags); + if (decode_error_flags) { + if (h->cur_pic_ptr->decode_error_flags) { + atomic_int *decode_error = h->cur_pic_ptr->decode_error_flags; + atomic_fetch_or_explicit(decode_error, decode_error_flags, + memory_order_relaxed); + } else + h->cur_pic_ptr->f->decode_error_flags |= decode_error_flags; + } + if (use_last_pic) + memset(&sl->ref_list[0][0], 0, sizeof(sl->ref_list[0][0])); + } +#endif /* CONFIG_ERROR_RESILIENCE */ + /* clean up */ + if (h->cur_pic_ptr && !h->droppable && h->has_slice) { + ff_thread_report_progress(&h->cur_pic_ptr->tf, INT_MAX, + h->picture_structure == PICT_BOTTOM_FIELD); + } + + return (ret < 0) ? ret : buf_size; +} + +static int h264_export_enc_params(AVFrame *f, const H264Picture *p) +{ + AVVideoEncParams *par; + unsigned int nb_mb = p->mb_height * p->mb_width; + unsigned int x, y; + + par = av_video_enc_params_create_side_data(f, AV_VIDEO_ENC_PARAMS_H264, nb_mb); + if (!par) + return AVERROR(ENOMEM); + + par->qp = p->pps->init_qp; + + par->delta_qp[1][0] = p->pps->chroma_qp_index_offset[0]; + par->delta_qp[1][1] = p->pps->chroma_qp_index_offset[0]; + par->delta_qp[2][0] = p->pps->chroma_qp_index_offset[1]; + par->delta_qp[2][1] = p->pps->chroma_qp_index_offset[1]; + + for (y = 0; y < p->mb_height; y++) + for (x = 0; x < p->mb_width; x++) { + const unsigned int block_idx = y * p->mb_width + x; + const unsigned int mb_xy = y * p->mb_stride + x; + AVVideoBlockParams *b = av_video_enc_params_block(par, block_idx); + + b->src_x = x * 16; + b->src_y = y * 16; + b->w = 16; + b->h = 16; + + b->delta_qp = p->qscale_table[mb_xy] - par->qp; + } + + return 0; +} + +static int output_frame(H264Context *h, AVFrame *dst, H264Picture *srcp) +{ + int ret; + + ret = av_frame_ref(dst, srcp->needs_fg ? srcp->f_grain : srcp->f); + if (ret < 0) + return ret; + + if (srcp->needs_fg && (ret = av_frame_copy_props(dst, srcp->f)) < 0) + return ret; + + if (srcp->decode_error_flags) { + atomic_int *decode_error = srcp->decode_error_flags; + /* The following is not supposed to provide synchronisation at all: + * given that srcp has already finished decoding, decode_error + * has already been set to its final value. */ + dst->decode_error_flags |= atomic_load_explicit(decode_error, memory_order_relaxed); + } + + av_dict_set(&dst->metadata, "stereo_mode", ff_h264_sei_stereo_mode(&h->sei.common.frame_packing), 0); + + if (srcp->sei_recovery_frame_cnt == 0) + dst->flags |= AV_FRAME_FLAG_KEY; + + if (h->avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) { + ret = h264_export_enc_params(dst, srcp); + if (ret < 0) + goto fail; + } + + if (!(h->avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN)) + av_frame_remove_side_data(dst, AV_FRAME_DATA_FILM_GRAIN_PARAMS); + + return 0; +fail: + av_frame_unref(dst); + return ret; +} + +static int is_avcc_extradata(const uint8_t *buf, int buf_size) +{ + int cnt= buf[5]&0x1f; + const uint8_t *p= buf+6; + if (!cnt) + return 0; + while(cnt--){ + int nalsize= AV_RB16(p) + 2; + if(nalsize > buf_size - (p-buf) || (p[2] & 0x9F) != 7) + return 0; + p += nalsize; + } + cnt = *(p++); + if(!cnt) + return 0; + while(cnt--){ + int nalsize= AV_RB16(p) + 2; + if(nalsize > buf_size - (p-buf) || (p[2] & 0x9F) != 8) + return 0; + p += nalsize; + } + return 1; +} + +static int finalize_frame(H264Context *h, AVFrame *dst, H264Picture *out, int *got_frame) +{ + int ret; + + if (((h->avctx->flags & AV_CODEC_FLAG_OUTPUT_CORRUPT) || + (h->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL) || + out->recovered)) { + + if (h->skip_gray > 0 && + h->non_gray && out->gray && + !(h->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL) + ) + return 0; + + if (!h->avctx->hwaccel && + (out->field_poc[0] == INT_MAX || + out->field_poc[1] == INT_MAX) + ) { + int p; + AVFrame *f = out->f; + int field = out->field_poc[0] == INT_MAX; + uint8_t *dst_data[4]; + int linesizes[4]; + const uint8_t *src_data[4]; + + av_log(h->avctx, AV_LOG_DEBUG, "Duplicating field %d to fill missing\n", field); + + for (p = 0; p<4; p++) { + dst_data[p] = f->data[p] + (field^1)*f->linesize[p]; + src_data[p] = f->data[p] + field *f->linesize[p]; + linesizes[p] = 2*f->linesize[p]; + } + + av_image_copy(dst_data, linesizes, src_data, linesizes, + f->format, f->width, f->height>>1); + } + + ret = output_frame(h, dst, out); + if (ret < 0) + return ret; + + *got_frame = 1; + + if (CONFIG_MPEGVIDEODEC) { + ff_print_debug_info2(h->avctx, dst, + out->mb_type, + out->qscale_table, + out->motion_val, + out->mb_width, out->mb_height, out->mb_stride, 1); + } + } + + return 0; +} + +static int send_next_delayed_frame(H264Context *h, AVFrame *dst_frame, + int *got_frame, int buf_index) +{ + int ret, i, out_idx; + H264Picture *out; + + h->cur_pic_ptr = NULL; + h->first_field = 0; + + while (h->delayed_pic[0]) { + out = h->delayed_pic[0]; + out_idx = 0; + for (i = 1; + h->delayed_pic[i] && + !(h->delayed_pic[i]->f->flags & AV_FRAME_FLAG_KEY) && + !h->delayed_pic[i]->mmco_reset; + i++) + if (h->delayed_pic[i]->poc < out->poc) { + out = h->delayed_pic[i]; + out_idx = i; + } + + for (i = out_idx; h->delayed_pic[i]; i++) + h->delayed_pic[i] = h->delayed_pic[i + 1]; + + if (out) { + h->frame_recovered |= out->recovered; + out->recovered |= h->frame_recovered & FRAME_RECOVERED_SEI; + + out->reference &= ~DELAYED_PIC_REF; + ret = finalize_frame(h, dst_frame, out, got_frame); + if (ret < 0) + return ret; + if (*got_frame) + break; + } + } + + return buf_index; +} + +static int h264_decode_frame(AVCodecContext *avctx, AVFrame *pict, + int *got_frame, AVPacket *avpkt) +{ + const uint8_t *buf = avpkt->data; + int buf_size = avpkt->size; + H264Context *h = avctx->priv_data; + int buf_index; + int ret; + + h->flags = avctx->flags; + h->setup_finished = 0; + h->nb_slice_ctx_queued = 0; + + ff_h264_unref_picture(&h->last_pic_for_ec); + + /* end of stream, output what is still in the buffers */ + if (buf_size == 0) + return send_next_delayed_frame(h, pict, got_frame, 0); + + if (av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA, NULL)) { + size_t side_size; + uint8_t *side = av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA, &side_size); + ff_h264_decode_extradata(side, side_size, + &h->ps, &h->is_avc, &h->nal_length_size, + avctx->err_recognition, avctx); + } + if (h->is_avc && buf_size >= 9 && buf[0]==1 && buf[2]==0 && (buf[4]&0xFC)==0xFC) { + if (is_avcc_extradata(buf, buf_size)) + return ff_h264_decode_extradata(buf, buf_size, + &h->ps, &h->is_avc, &h->nal_length_size, + avctx->err_recognition, avctx); + } + + buf_index = decode_nal_units(h, avpkt->buf, buf, buf_size); + if (buf_index < 0) + return AVERROR_INVALIDDATA; + + if (!h->cur_pic_ptr && h->nal_unit_type == H264_NAL_END_SEQUENCE) { + av_assert0(buf_index <= buf_size); + return send_next_delayed_frame(h, pict, got_frame, buf_index); + } + + if (!(avctx->flags2 & AV_CODEC_FLAG2_CHUNKS) && (!h->cur_pic_ptr || !h->has_slice)) { + if (avctx->skip_frame >= AVDISCARD_NONREF || + buf_size >= 4 && !memcmp("Q264", buf, 4)) + return buf_size; + av_log(avctx, AV_LOG_ERROR, "no frame!\n"); + return AVERROR_INVALIDDATA; + } + + if (!(avctx->flags2 & AV_CODEC_FLAG2_CHUNKS) || + (h->mb_y >= h->mb_height && h->mb_height)) { + if ((ret = ff_h264_field_end(h, &h->slice_ctx[0], 0)) < 0) + return ret; + + /* Wait for second field. */ + if (h->next_output_pic) { + ret = finalize_frame(h, pict, h->next_output_pic, got_frame); + if (ret < 0) + return ret; + } + } + + av_assert0(pict->buf[0] || !*got_frame); + + ff_h264_unref_picture(&h->last_pic_for_ec); + + return buf_size; +} + +#define OFFSET(x) offsetof(H264Context, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +#define VDX VD | AV_OPT_FLAG_EXPORT +static const AVOption h264_options[] = { + { "is_avc", "is avc", OFFSET(is_avc), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, VDX }, + { "nal_length_size", "nal_length_size", OFFSET(nal_length_size), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 4, VDX }, + { "enable_er", "Enable error resilience on damaged frames (unsafe)", OFFSET(enable_er), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, VD }, + { "x264_build", "Assume this x264 version if no x264 version found in any SEI", OFFSET(x264_build), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, VD }, + { "skip_gray", "Do not return gray gap frames", OFFSET(skip_gray), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, VD }, + { "noref_gray", "Avoid using gray gap frames as references", OFFSET(noref_gray), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, VD }, + { NULL }, +}; + +static const AVClass h264_class = { + .class_name = "H264 Decoder", + .item_name = av_default_item_name, + .option = h264_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_h264_decoder = { + .p.name = "h264", + CODEC_LONG_NAME("H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .priv_data_size = sizeof(H264Context), + .init = h264_decode_init, + .close = h264_decode_end, + FF_CODEC_DECODE_CB(h264_decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS | + AV_CODEC_CAP_FRAME_THREADS, + .hw_configs = (const AVCodecHWConfigInternal *const []) { +#if CONFIG_H264_DXVA2_HWACCEL + HWACCEL_DXVA2(h264), +#endif +#if CONFIG_H264_D3D11VA_HWACCEL + HWACCEL_D3D11VA(h264), +#endif +#if CONFIG_H264_D3D11VA2_HWACCEL + HWACCEL_D3D11VA2(h264), +#endif +#if CONFIG_H264_D3D12VA_HWACCEL + HWACCEL_D3D12VA(h264), +#endif +#if CONFIG_H264_NVDEC_HWACCEL + HWACCEL_NVDEC(h264), +#endif +#if CONFIG_H264_VAAPI_HWACCEL + HWACCEL_VAAPI(h264), +#endif +#if CONFIG_H264_VDPAU_HWACCEL + HWACCEL_VDPAU(h264), +#endif +#if CONFIG_H264_VIDEOTOOLBOX_HWACCEL + HWACCEL_VIDEOTOOLBOX(h264), +#endif +#if CONFIG_H264_VULKAN_HWACCEL + HWACCEL_VULKAN(h264), +#endif + NULL + }, + .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING | + FF_CODEC_CAP_INIT_CLEANUP, + .flush = h264_decode_flush, + UPDATE_THREAD_CONTEXT(ff_h264_update_thread_context), + UPDATE_THREAD_CONTEXT_FOR_USER(ff_h264_update_thread_context_for_user), + .p.profiles = NULL_IF_CONFIG_SMALL(ff_h264_profiles), + .p.priv_class = &h264_class, +}; diff --git a/thirdparty/ffmpeg/libavcodec/h264dec.h b/thirdparty/ffmpeg/libavcodec/h264dec.h index 74fd09dfa..33d788627 100644 --- a/thirdparty/ffmpeg/libavcodec/h264dec.h +++ b/thirdparty/ffmpeg/libavcodec/h264dec.h @@ -322,6 +322,7 @@ typedef struct H264SliceContext { MMCO mmco[H264_MAX_MMCO_COUNT]; int nb_mmco; int explicit_ref_marking; + int ref_pic_marking_bit_size; int frame_num; int idr_pic_id; @@ -330,6 +331,7 @@ typedef struct H264SliceContext { int delta_poc[2]; int curr_pic_num; int max_pic_num; + int pic_order_cnt_bit_size; } H264SliceContext; /** diff --git a/thirdparty/ffmpeg/libavcodec/hevc/hevcdec.c b/thirdparty/ffmpeg/libavcodec/hevc/hevcdec.c index 18e22f697..3e2d2728b 100644 --- a/thirdparty/ffmpeg/libavcodec/hevc/hevcdec.c +++ b/thirdparty/ffmpeg/libavcodec/hevc/hevcdec.c @@ -566,6 +566,7 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) CONFIG_HEVC_D3D11VA_HWACCEL * 2 + \ CONFIG_HEVC_D3D12VA_HWACCEL + \ CONFIG_HEVC_NVDEC_HWACCEL + \ + CONFIG_HEVC_V4L2REQUEST_HWACCEL + \ CONFIG_HEVC_VAAPI_HWACCEL + \ CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + \ CONFIG_HEVC_VDPAU_HWACCEL + \ @@ -604,6 +605,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #endif #if CONFIG_HEVC_VULKAN_HWACCEL *fmt++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_HEVC_V4L2REQUEST_HWACCEL + *fmt++ = AV_PIX_FMT_DRM_PRIME; #endif break; case AV_PIX_FMT_YUV420P10: @@ -631,6 +635,9 @@ static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) #endif #if CONFIG_HEVC_NVDEC_HWACCEL *fmt++ = AV_PIX_FMT_CUDA; +#endif +#if CONFIG_HEVC_V4L2REQUEST_HWACCEL + *fmt++ = AV_PIX_FMT_DRM_PRIME; #endif break; case AV_PIX_FMT_YUV444P: @@ -4273,6 +4280,9 @@ const FFCodec ff_hevc_decoder = { #endif #if CONFIG_HEVC_VULKAN_HWACCEL HWACCEL_VULKAN(hevc), +#endif +#if CONFIG_HEVC_V4L2REQUEST_HWACCEL + HWACCEL_V4L2REQUEST(hevc), #endif NULL }, diff --git a/thirdparty/ffmpeg/libavcodec/hevc/hevcdec.c.orig b/thirdparty/ffmpeg/libavcodec/hevc/hevcdec.c.orig new file mode 100644 index 000000000..18e22f697 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/hevc/hevcdec.c.orig @@ -0,0 +1,4279 @@ +/* + * HEVC video Decoder + * + * Copyright (C) 2012 - 2013 Guillaume Martres + * Copyright (C) 2012 - 2013 Mickael Raulet + * Copyright (C) 2012 - 2013 Gildas Cocherel + * Copyright (C) 2012 - 2013 Wassim Hamidouche + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_components.h" + +#include "libavutil/attributes.h" +#include "libavutil/avstring.h" +#include "libavutil/common.h" +#include "libavutil/container_fifo.h" +#include "libavutil/film_grain_params.h" +#include "libavutil/internal.h" +#include "libavutil/md5.h" +#include "libavutil/mem.h" +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" +#include "libavutil/stereo3d.h" +#include "libavutil/tdrdi.h" +#include "libavutil/timecode.h" +#include "libavutil/refstruct.h" + +#include "libavcodec/aom_film_grain.h" +#include "libavcodec/bswapdsp.h" +#include "libavcodec/cabac_functions.h" +#include "libavcodec/codec_internal.h" +#include "libavcodec/decode.h" +#include "libavcodec/golomb.h" +#include "libavcodec/h274.h" +#include "libavcodec/hwaccel_internal.h" +#include "libavcodec/hwconfig.h" +#include "libavcodec/internal.h" +#include "libavcodec/profiles.h" +#include "libavcodec/progressframe.h" +#include "libavcodec/thread.h" +#include "libavcodec/threadprogress.h" + +#include "hevc.h" +#include "parse.h" +#include "hevcdec.h" + +static const uint8_t hevc_pel_weight[65] = { [2] = 0, [4] = 1, [6] = 2, [8] = 3, [12] = 4, [16] = 5, [24] = 6, [32] = 7, [48] = 8, [64] = 9 }; + +/** + * NOTE: Each function hls_foo correspond to the function foo in the + * specification (HLS stands for High Level Syntax). + */ + +/** + * Section 5.7 + */ + +/* free everything allocated by pic_arrays_init() */ +static void pic_arrays_free(HEVCLayerContext *l) +{ + av_freep(&l->sao); + av_freep(&l->deblock); + + av_freep(&l->skip_flag); + av_freep(&l->tab_ct_depth); + + av_freep(&l->tab_ipm); + av_freep(&l->cbf_luma); + av_freep(&l->is_pcm); + + av_freep(&l->qp_y_tab); + av_freep(&l->tab_slice_address); + av_freep(&l->filter_slice_edges); + + av_freep(&l->horizontal_bs); + av_freep(&l->vertical_bs); + + for (int i = 0; i < 3; i++) { + av_freep(&l->sao_pixel_buffer_h[i]); + av_freep(&l->sao_pixel_buffer_v[i]); + } + + av_refstruct_pool_uninit(&l->tab_mvf_pool); + av_refstruct_pool_uninit(&l->rpl_tab_pool); +} + +/* allocate arrays that depend on frame dimensions */ +static int pic_arrays_init(HEVCLayerContext *l, const HEVCSPS *sps) +{ + int log2_min_cb_size = sps->log2_min_cb_size; + int width = sps->width; + int height = sps->height; + int pic_size_in_ctb = ((width >> log2_min_cb_size) + 1) * + ((height >> log2_min_cb_size) + 1); + int ctb_count = sps->ctb_width * sps->ctb_height; + int min_pu_size = sps->min_pu_width * sps->min_pu_height; + + l->bs_width = (width >> 2) + 1; + l->bs_height = (height >> 2) + 1; + + l->sao = av_calloc(ctb_count, sizeof(*l->sao)); + l->deblock = av_calloc(ctb_count, sizeof(*l->deblock)); + if (!l->sao || !l->deblock) + goto fail; + + l->skip_flag = av_malloc_array(sps->min_cb_height, sps->min_cb_width); + l->tab_ct_depth = av_malloc_array(sps->min_cb_height, sps->min_cb_width); + if (!l->skip_flag || !l->tab_ct_depth) + goto fail; + + l->cbf_luma = av_malloc_array(sps->min_tb_width, sps->min_tb_height); + l->tab_ipm = av_mallocz(min_pu_size); + l->is_pcm = av_malloc_array(sps->min_pu_width + 1, sps->min_pu_height + 1); + if (!l->tab_ipm || !l->cbf_luma || !l->is_pcm) + goto fail; + + l->filter_slice_edges = av_mallocz(ctb_count); + l->tab_slice_address = av_malloc_array(pic_size_in_ctb, + sizeof(*l->tab_slice_address)); + l->qp_y_tab = av_calloc(pic_size_in_ctb, + sizeof(*l->qp_y_tab)); + if (!l->qp_y_tab || !l->filter_slice_edges || !l->tab_slice_address) + goto fail; + + l->horizontal_bs = av_calloc(l->bs_width, l->bs_height); + l->vertical_bs = av_calloc(l->bs_width, l->bs_height); + if (!l->horizontal_bs || !l->vertical_bs) + goto fail; + + l->tab_mvf_pool = av_refstruct_pool_alloc(min_pu_size * sizeof(MvField), 0); + l->rpl_tab_pool = av_refstruct_pool_alloc(ctb_count * sizeof(RefPicListTab), 0); + if (!l->tab_mvf_pool || !l->rpl_tab_pool) + goto fail; + + if (sps->sao_enabled) { + int c_count = (sps->chroma_format_idc != 0) ? 3 : 1; + + for (int c_idx = 0; c_idx < c_count; c_idx++) { + int w = sps->width >> sps->hshift[c_idx]; + int h = sps->height >> sps->vshift[c_idx]; + l->sao_pixel_buffer_h[c_idx] = + av_mallocz((w * 2 * sps->ctb_height) << + sps->pixel_shift); + l->sao_pixel_buffer_v[c_idx] = + av_mallocz((h * 2 * sps->ctb_width) << + sps->pixel_shift); + if (!l->sao_pixel_buffer_h[c_idx] || + !l->sao_pixel_buffer_v[c_idx]) + goto fail; + } + } + + return 0; + +fail: + pic_arrays_free(l); + return AVERROR(ENOMEM); +} + +static int pred_weight_table(SliceHeader *sh, void *logctx, + const HEVCSPS *sps, GetBitContext *gb) +{ + int i = 0; + int j = 0; + int luma_log2_weight_denom; + unsigned luma_weight_flags, chroma_weight_flags; + + luma_log2_weight_denom = get_ue_golomb_long(gb); + if (luma_log2_weight_denom < 0 || luma_log2_weight_denom > 7) { + av_log(logctx, AV_LOG_ERROR, "luma_log2_weight_denom %d is invalid\n", luma_log2_weight_denom); + return AVERROR_INVALIDDATA; + } + sh->luma_log2_weight_denom = av_clip_uintp2(luma_log2_weight_denom, 3); + if (sps->chroma_format_idc != 0) { + int64_t chroma_log2_weight_denom = luma_log2_weight_denom + (int64_t)get_se_golomb(gb); + if (chroma_log2_weight_denom < 0 || chroma_log2_weight_denom > 7) { + av_log(logctx, AV_LOG_ERROR, "chroma_log2_weight_denom %"PRId64" is invalid\n", chroma_log2_weight_denom); + return AVERROR_INVALIDDATA; + } + sh->chroma_log2_weight_denom = chroma_log2_weight_denom; + } + + luma_weight_flags = get_bits(gb, sh->nb_refs[L0]); + chroma_weight_flags = sps->chroma_format_idc != 0 ? get_bits(gb, sh->nb_refs[L0]) : 0; + for (i = 0; i < sh->nb_refs[L0]; i++) { + unsigned flag_bit = 1 << (sh->nb_refs[L0] - 1 - i); + + if (luma_weight_flags & flag_bit) { + int delta_luma_weight_l0 = get_se_golomb(gb); + if ((int8_t)delta_luma_weight_l0 != delta_luma_weight_l0) + return AVERROR_INVALIDDATA; + sh->luma_weight_l0[i] = (1 << sh->luma_log2_weight_denom) + delta_luma_weight_l0; + sh->luma_offset_l0[i] = get_se_golomb(gb); + } else { + sh->luma_weight_l0[i] = 1 << sh->luma_log2_weight_denom; + sh->luma_offset_l0[i] = 0; + } + if (chroma_weight_flags & flag_bit) { + for (j = 0; j < 2; j++) { + int delta_chroma_weight_l0 = get_se_golomb(gb); + int delta_chroma_offset_l0 = get_se_golomb(gb); + + if ( (int8_t)delta_chroma_weight_l0 != delta_chroma_weight_l0 + || delta_chroma_offset_l0 < -(1<<17) || delta_chroma_offset_l0 > (1<<17)) { + return AVERROR_INVALIDDATA; + } + + sh->chroma_weight_l0[i][j] = (1 << sh->chroma_log2_weight_denom) + delta_chroma_weight_l0; + sh->chroma_offset_l0[i][j] = av_clip((delta_chroma_offset_l0 - ((128 * sh->chroma_weight_l0[i][j]) + >> sh->chroma_log2_weight_denom) + 128), -128, 127); + } + } else { + sh->chroma_weight_l0[i][0] = 1 << sh->chroma_log2_weight_denom; + sh->chroma_offset_l0[i][0] = 0; + sh->chroma_weight_l0[i][1] = 1 << sh->chroma_log2_weight_denom; + sh->chroma_offset_l0[i][1] = 0; + } + } + if (sh->slice_type == HEVC_SLICE_B) { + luma_weight_flags = get_bits(gb, sh->nb_refs[L1]); + chroma_weight_flags = sps->chroma_format_idc != 0 ? get_bits(gb, sh->nb_refs[L1]) : 0; + for (i = 0; i < sh->nb_refs[L1]; i++) { + unsigned flag_bit = 1 << (sh->nb_refs[L1] - 1 - i); + + if (luma_weight_flags & flag_bit) { + int delta_luma_weight_l1 = get_se_golomb(gb); + if ((int8_t)delta_luma_weight_l1 != delta_luma_weight_l1) + return AVERROR_INVALIDDATA; + sh->luma_weight_l1[i] = (1 << sh->luma_log2_weight_denom) + delta_luma_weight_l1; + sh->luma_offset_l1[i] = get_se_golomb(gb); + } else { + sh->luma_weight_l1[i] = 1 << sh->luma_log2_weight_denom; + sh->luma_offset_l1[i] = 0; + } + if (chroma_weight_flags & flag_bit) { + for (j = 0; j < 2; j++) { + int delta_chroma_weight_l1 = get_se_golomb(gb); + int delta_chroma_offset_l1 = get_se_golomb(gb); + + if ( (int8_t)delta_chroma_weight_l1 != delta_chroma_weight_l1 + || delta_chroma_offset_l1 < -(1<<17) || delta_chroma_offset_l1 > (1<<17)) { + return AVERROR_INVALIDDATA; + } + + sh->chroma_weight_l1[i][j] = (1 << sh->chroma_log2_weight_denom) + delta_chroma_weight_l1; + sh->chroma_offset_l1[i][j] = av_clip((delta_chroma_offset_l1 - ((128 * sh->chroma_weight_l1[i][j]) + >> sh->chroma_log2_weight_denom) + 128), -128, 127); + } + } else { + sh->chroma_weight_l1[i][0] = 1 << sh->chroma_log2_weight_denom; + sh->chroma_offset_l1[i][0] = 0; + sh->chroma_weight_l1[i][1] = 1 << sh->chroma_log2_weight_denom; + sh->chroma_offset_l1[i][1] = 0; + } + } + } + return 0; +} + +static int decode_lt_rps(const HEVCSPS *sps, LongTermRPS *rps, + GetBitContext *gb, int cur_poc, int poc_lsb) +{ + int max_poc_lsb = 1 << sps->log2_max_poc_lsb; + int prev_delta_msb = 0; + unsigned int nb_sps = 0, nb_sh; + int i; + + rps->nb_refs = 0; + if (!sps->long_term_ref_pics_present) + return 0; + + if (sps->num_long_term_ref_pics_sps > 0) + nb_sps = get_ue_golomb_long(gb); + nb_sh = get_ue_golomb_long(gb); + + if (nb_sps > sps->num_long_term_ref_pics_sps) + return AVERROR_INVALIDDATA; + if (nb_sh + (uint64_t)nb_sps > FF_ARRAY_ELEMS(rps->poc)) + return AVERROR_INVALIDDATA; + + rps->nb_refs = nb_sh + nb_sps; + + for (i = 0; i < rps->nb_refs; i++) { + + if (i < nb_sps) { + uint8_t lt_idx_sps = 0; + + if (sps->num_long_term_ref_pics_sps > 1) + lt_idx_sps = get_bits(gb, av_ceil_log2(sps->num_long_term_ref_pics_sps)); + + rps->poc[i] = sps->lt_ref_pic_poc_lsb_sps[lt_idx_sps]; + rps->used[i] = !!(sps->used_by_curr_pic_lt & (1U << lt_idx_sps)); + } else { + rps->poc[i] = get_bits(gb, sps->log2_max_poc_lsb); + rps->used[i] = get_bits1(gb); + } + + rps->poc_msb_present[i] = get_bits1(gb); + if (rps->poc_msb_present[i]) { + int64_t delta = get_ue_golomb_long(gb); + int64_t poc; + + if (i && i != nb_sps) + delta += prev_delta_msb; + + poc = rps->poc[i] + cur_poc - delta * max_poc_lsb - poc_lsb; + if (poc != (int32_t)poc) + return AVERROR_INVALIDDATA; + rps->poc[i] = poc; + prev_delta_msb = delta; + } + } + + return 0; +} + +static void export_stream_params(HEVCContext *s, const HEVCSPS *sps) +{ + AVCodecContext *avctx = s->avctx; + const HEVCVPS *vps = sps->vps; + const HEVCWindow *ow = &sps->output_window; + unsigned int num = 0, den = 0; + + avctx->pix_fmt = sps->pix_fmt; + avctx->coded_width = sps->width; + avctx->coded_height = sps->height; + avctx->width = sps->width - ow->left_offset - ow->right_offset; + avctx->height = sps->height - ow->top_offset - ow->bottom_offset; + avctx->has_b_frames = sps->temporal_layer[sps->max_sub_layers - 1].num_reorder_pics; + avctx->profile = sps->ptl.general_ptl.profile_idc; + avctx->level = sps->ptl.general_ptl.level_idc; + + ff_set_sar(avctx, sps->vui.common.sar); + + if (sps->vui.common.video_signal_type_present_flag) + avctx->color_range = sps->vui.common.video_full_range_flag ? AVCOL_RANGE_JPEG + : AVCOL_RANGE_MPEG; + else + avctx->color_range = AVCOL_RANGE_MPEG; + + if (sps->vui.common.colour_description_present_flag) { + avctx->color_primaries = sps->vui.common.colour_primaries; + avctx->color_trc = sps->vui.common.transfer_characteristics; + avctx->colorspace = sps->vui.common.matrix_coeffs; + } else { + avctx->color_primaries = AVCOL_PRI_UNSPECIFIED; + avctx->color_trc = AVCOL_TRC_UNSPECIFIED; + avctx->colorspace = AVCOL_SPC_UNSPECIFIED; + } + + avctx->chroma_sample_location = AVCHROMA_LOC_UNSPECIFIED; + if (sps->chroma_format_idc == 1) { + if (sps->vui.common.chroma_loc_info_present_flag) { + if (sps->vui.common.chroma_sample_loc_type_top_field <= 5) + avctx->chroma_sample_location = sps->vui.common.chroma_sample_loc_type_top_field + 1; + } else + avctx->chroma_sample_location = AVCHROMA_LOC_LEFT; + } + + if (vps->vps_timing_info_present_flag) { + num = vps->vps_num_units_in_tick; + den = vps->vps_time_scale; + } else if (sps->vui.vui_timing_info_present_flag) { + num = sps->vui.vui_num_units_in_tick; + den = sps->vui.vui_time_scale; + } + + if (num > 0 && den > 0) + av_reduce(&avctx->framerate.den, &avctx->framerate.num, + num, den, 1 << 30); +} + +static int export_stream_params_from_sei(HEVCContext *s) +{ + AVCodecContext *avctx = s->avctx; + + if (s->sei.common.alternative_transfer.present && + av_color_transfer_name(s->sei.common.alternative_transfer.preferred_transfer_characteristics) && + s->sei.common.alternative_transfer.preferred_transfer_characteristics != AVCOL_TRC_UNSPECIFIED) { + avctx->color_trc = s->sei.common.alternative_transfer.preferred_transfer_characteristics; + } + + return 0; +} + +static int export_multilayer(HEVCContext *s, const HEVCVPS *vps) +{ + const HEVCSEITDRDI *tdrdi = &s->sei.tdrdi; + + av_freep(&s->view_ids_available); + s->nb_view_ids_available = 0; + av_freep(&s->view_pos_available); + s->nb_view_pos_available = 0; + + // don't export anything in the trivial case (1 layer, view id=0) + if (vps->nb_layers < 2 && !vps->view_id[0]) + return 0; + + s->view_ids_available = av_calloc(vps->nb_layers, sizeof(*s->view_ids_available)); + if (!s->view_ids_available) + return AVERROR(ENOMEM); + + if (tdrdi->num_ref_displays) { + s->view_pos_available = av_calloc(vps->nb_layers, sizeof(*s->view_pos_available)); + if (!s->view_pos_available) + return AVERROR(ENOMEM); + } + + for (int i = 0; i < vps->nb_layers; i++) { + s->view_ids_available[i] = vps->view_id[i]; + + if (s->view_pos_available) { + s->view_pos_available[i] = vps->view_id[i] == tdrdi->left_view_id[0] ? + AV_STEREO3D_VIEW_LEFT : + vps->view_id[i] == tdrdi->right_view_id[0] ? + AV_STEREO3D_VIEW_RIGHT : AV_STEREO3D_VIEW_UNSPEC; + } + } + s->nb_view_ids_available = vps->nb_layers; + s->nb_view_pos_available = s->view_pos_available ? vps->nb_layers : 0; + + return 0; +} + +int ff_hevc_is_alpha_video(const HEVCContext *s) +{ + const HEVCVPS *vps = s->vps; + int ret = 0; + + if (vps->nb_layers != 2 || !vps->layer_id_in_nuh[1]) + return 0; + + /* decode_vps_ext() guarantees that SCALABILITY_AUXILIARY with AuxId other + * than alpha cannot reach here. + */ + ret = (s->vps->scalability_mask_flag & HEVC_SCALABILITY_AUXILIARY); + + av_log(s->avctx, AV_LOG_DEBUG, "Multi layer video, %s alpha video\n", + ret ? "is" : "not"); + + return ret; +} + +static int setup_multilayer(HEVCContext *s, const HEVCVPS *vps) +{ + unsigned layers_active_output = 0, highest_layer; + + s->layers_active_output = 1; + s->layers_active_decode = 1; + + if (ff_hevc_is_alpha_video(s)) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->avctx->pix_fmt); + + if (!(desc->flags & AV_PIX_FMT_FLAG_ALPHA)) + return 0; + + s->layers_active_decode = (1 << vps->nb_layers) - 1; + s->layers_active_output = 1; + + return 0; + } + + // nothing requested - decode base layer only + if (!s->nb_view_ids) + return 0; + + if (s->nb_view_ids == 1 && s->view_ids[0] == -1) { + layers_active_output = (1 << vps->nb_layers) - 1; + } else { + for (int i = 0; i < s->nb_view_ids; i++) { + int view_id = s->view_ids[i]; + int layer_idx = -1; + + if (view_id < 0) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid view ID requested: %d\n", view_id); + return AVERROR(EINVAL); + } + + for (int j = 0; j < vps->nb_layers; j++) { + if (vps->view_id[j] == view_id) { + layer_idx = j; + break; + } + } + if (layer_idx < 0) { + av_log(s->avctx, AV_LOG_ERROR, + "View ID %d not present in VPS\n", view_id); + return AVERROR(EINVAL); + } + layers_active_output |= 1 << layer_idx; + } + } + + if (!layers_active_output) { + av_log(s->avctx, AV_LOG_ERROR, "No layers selected\n"); + return AVERROR_BUG; + } + + highest_layer = ff_log2(layers_active_output); + if (highest_layer >= FF_ARRAY_ELEMS(s->layers)) { + av_log(s->avctx, AV_LOG_ERROR, + "Too many layers requested: %u\n", layers_active_output); + return AVERROR(EINVAL); + } + + /* Assume a higher layer depends on all the lower ones. + * This is enforced in VPS parsing currently, this logic will need + * to be changed if we want to support more complex dependency structures. + */ + s->layers_active_decode = (1 << (highest_layer + 1)) - 1; + s->layers_active_output = layers_active_output; + + av_log(s->avctx, AV_LOG_DEBUG, "decode/output layers: %x/%x\n", + s->layers_active_decode, s->layers_active_output); + + return 0; +} + +static enum AVPixelFormat map_to_alpha_format(HEVCContext *s, + enum AVPixelFormat pix_fmt) +{ + switch (pix_fmt) { + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUVJ420P: + return AV_PIX_FMT_YUVA420P; + case AV_PIX_FMT_YUV420P10: + return AV_PIX_FMT_YUVA420P10; + case AV_PIX_FMT_YUV444P: + return AV_PIX_FMT_YUVA444P; + case AV_PIX_FMT_YUV422P: + return AV_PIX_FMT_YUVA422P; + case AV_PIX_FMT_YUV422P10LE: + return AV_PIX_FMT_YUVA422P10LE; + case AV_PIX_FMT_YUV444P10: + return AV_PIX_FMT_YUVA444P10; + case AV_PIX_FMT_YUV444P12: + return AV_PIX_FMT_YUVA444P12; + case AV_PIX_FMT_YUV422P12: + return AV_PIX_FMT_YUVA422P12; + default: + av_log(s->avctx, AV_LOG_WARNING, "No alpha pixel format map for %s\n", + av_get_pix_fmt_name(pix_fmt)); + return AV_PIX_FMT_NONE; + } +} + +static enum AVPixelFormat get_format(HEVCContext *s, const HEVCSPS *sps) +{ +#define HWACCEL_MAX (CONFIG_HEVC_DXVA2_HWACCEL + \ + CONFIG_HEVC_D3D11VA_HWACCEL * 2 + \ + CONFIG_HEVC_D3D12VA_HWACCEL + \ + CONFIG_HEVC_NVDEC_HWACCEL + \ + CONFIG_HEVC_VAAPI_HWACCEL + \ + CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + \ + CONFIG_HEVC_VDPAU_HWACCEL + \ + CONFIG_HEVC_VULKAN_HWACCEL) + enum AVPixelFormat pix_fmts[HWACCEL_MAX + 3], *fmt = pix_fmts; + enum AVPixelFormat alpha_fmt = AV_PIX_FMT_NONE; + int ret; + + if (ff_hevc_is_alpha_video(s)) + alpha_fmt = map_to_alpha_format(s, sps->pix_fmt); + + switch (sps->pix_fmt) { + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUVJ420P: +#if CONFIG_HEVC_DXVA2_HWACCEL + *fmt++ = AV_PIX_FMT_DXVA2_VLD; +#endif +#if CONFIG_HEVC_D3D11VA_HWACCEL + *fmt++ = AV_PIX_FMT_D3D11VA_VLD; + *fmt++ = AV_PIX_FMT_D3D11; +#endif +#if CONFIG_HEVC_D3D12VA_HWACCEL + *fmt++ = AV_PIX_FMT_D3D12; +#endif +#if CONFIG_HEVC_VAAPI_HWACCEL + *fmt++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_HEVC_VDPAU_HWACCEL + *fmt++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_HEVC_NVDEC_HWACCEL + *fmt++ = AV_PIX_FMT_CUDA; +#endif +#if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV420P10: +#if CONFIG_HEVC_DXVA2_HWACCEL + *fmt++ = AV_PIX_FMT_DXVA2_VLD; +#endif +#if CONFIG_HEVC_D3D11VA_HWACCEL + *fmt++ = AV_PIX_FMT_D3D11VA_VLD; + *fmt++ = AV_PIX_FMT_D3D11; +#endif +#if CONFIG_HEVC_D3D12VA_HWACCEL + *fmt++ = AV_PIX_FMT_D3D12; +#endif +#if CONFIG_HEVC_VAAPI_HWACCEL + *fmt++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_HEVC_VDPAU_HWACCEL + *fmt++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_HEVC_NVDEC_HWACCEL + *fmt++ = AV_PIX_FMT_CUDA; +#endif + break; + case AV_PIX_FMT_YUV444P: +#if CONFIG_HEVC_VAAPI_HWACCEL + *fmt++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_HEVC_VDPAU_HWACCEL + *fmt++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_HEVC_NVDEC_HWACCEL + *fmt++ = AV_PIX_FMT_CUDA; +#endif +#if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV422P: + case AV_PIX_FMT_YUV422P10LE: +#if CONFIG_HEVC_VAAPI_HWACCEL + *fmt++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_HEVC_NVDEC_HWACCEL + *fmt++ = AV_PIX_FMT_CUDA; +#endif + break; + case AV_PIX_FMT_YUV444P10: +#if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + *fmt++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif + av_fallthrough; + case AV_PIX_FMT_YUV420P12: + case AV_PIX_FMT_YUV444P12: +#if CONFIG_HEVC_VAAPI_HWACCEL + *fmt++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_HEVC_VDPAU_HWACCEL + *fmt++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_HEVC_NVDEC_HWACCEL + *fmt++ = AV_PIX_FMT_CUDA; +#endif + break; + case AV_PIX_FMT_YUV422P12: +#if CONFIG_HEVC_VAAPI_HWACCEL + *fmt++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + *fmt++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_HEVC_NVDEC_HWACCEL + *fmt++ = AV_PIX_FMT_CUDA; +#endif + break; + } + + if (alpha_fmt != AV_PIX_FMT_NONE) + *fmt++ = alpha_fmt; + *fmt++ = sps->pix_fmt; + *fmt = AV_PIX_FMT_NONE; + + // export multilayer information from active VPS to the caller, + // so it is available in get_format() + ret = export_multilayer(s, sps->vps); + if (ret < 0) + return ret; + + ret = ff_get_format(s->avctx, pix_fmts); + if (ret < 0) + return ret; + s->avctx->pix_fmt = ret; + + // set up multilayer decoding, if requested by caller + ret = setup_multilayer(s, sps->vps); + if (ret < 0) + return ret; + + return 0; +} + +static int set_sps(HEVCContext *s, HEVCLayerContext *l, const HEVCSPS *sps) +{ + int ret; + + pic_arrays_free(l); + av_refstruct_unref(&l->sps); + av_refstruct_unref(&s->vps); + + if (!sps) + return 0; + + ret = pic_arrays_init(l, sps); + if (ret < 0) + goto fail; + + ff_hevc_pred_init(&s->hpc, sps->bit_depth); + ff_hevc_dsp_init (&s->hevcdsp, sps->bit_depth); + ff_videodsp_init (&s->vdsp, sps->bit_depth); + + l->sps = av_refstruct_ref_c(sps); + s->vps = av_refstruct_ref_c(sps->vps); + + return 0; + +fail: + pic_arrays_free(l); + av_refstruct_unref(&l->sps); + return ret; +} + +static int hls_slice_header(SliceHeader *sh, const HEVCContext *s, GetBitContext *gb) +{ + const HEVCPPS *pps; + const HEVCSPS *sps; + const HEVCVPS *vps; + unsigned pps_id, layer_idx; + int i, ret; + + // Coded parameters + sh->first_slice_in_pic_flag = get_bits1(gb); + + sh->no_output_of_prior_pics_flag = 0; + if (IS_IRAP(s)) + sh->no_output_of_prior_pics_flag = get_bits1(gb); + + pps_id = get_ue_golomb_long(gb); + if (pps_id >= HEVC_MAX_PPS_COUNT || !s->ps.pps_list[pps_id]) { + av_log(s->avctx, AV_LOG_ERROR, "PPS id out of range: %d\n", pps_id); + return AVERROR_INVALIDDATA; + } + if (!sh->first_slice_in_pic_flag && s->ps.pps_list[pps_id] != s->pps) { + av_log(s->avctx, AV_LOG_ERROR, "PPS changed between slices.\n"); + return AVERROR_INVALIDDATA; + } + sh->pps_id = pps_id; + + pps = s->ps.pps_list[pps_id]; + sps = pps->sps; + vps = sps->vps; + layer_idx = vps->layer_idx[s->nuh_layer_id]; + + if (s->nal_unit_type == HEVC_NAL_CRA_NUT && s->last_eos == 1) + sh->no_output_of_prior_pics_flag = 1; + + sh->dependent_slice_segment_flag = 0; + if (!sh->first_slice_in_pic_flag) { + int slice_address_length; + + if (pps->dependent_slice_segments_enabled_flag) + sh->dependent_slice_segment_flag = get_bits1(gb); + if (sh->dependent_slice_segment_flag && !s->slice_initialized) { + av_log(s->avctx, AV_LOG_ERROR, "Independent slice segment missing.\n"); + return AVERROR_INVALIDDATA; + } + + slice_address_length = av_ceil_log2(sps->ctb_width * + sps->ctb_height); + sh->slice_segment_addr = get_bitsz(gb, slice_address_length); + if (sh->slice_segment_addr >= sps->ctb_width * sps->ctb_height) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid slice segment address: %u.\n", + sh->slice_segment_addr); + return AVERROR_INVALIDDATA; + } + + if (!sh->dependent_slice_segment_flag) { + sh->slice_addr = sh->slice_segment_addr; + } + } else { + sh->slice_segment_addr = sh->slice_addr = 0; + } + + if (!sh->dependent_slice_segment_flag) { + for (i = 0; i < pps->num_extra_slice_header_bits; i++) + skip_bits(gb, 1); // slice_reserved_undetermined_flag[] + + sh->slice_type = get_ue_golomb_long(gb); + if (!(sh->slice_type == HEVC_SLICE_I || + sh->slice_type == HEVC_SLICE_P || + sh->slice_type == HEVC_SLICE_B)) { + av_log(s->avctx, AV_LOG_ERROR, "Unknown slice type: %d.\n", + sh->slice_type); + return AVERROR_INVALIDDATA; + } + if (IS_IRAP(s) && sh->slice_type != HEVC_SLICE_I && + !pps->pps_curr_pic_ref_enabled_flag && + s->nuh_layer_id == 0) { + av_log(s->avctx, AV_LOG_ERROR, "Inter slices in an IRAP frame.\n"); + return AVERROR_INVALIDDATA; + } + + // when flag is not present, picture is inferred to be output + sh->pic_output_flag = 1; + if (pps->output_flag_present_flag) + sh->pic_output_flag = get_bits1(gb); + + if (sps->separate_colour_plane) + sh->colour_plane_id = get_bits(gb, 2); + + if (!IS_IDR(s) || + (s->nuh_layer_id > 0 && + !(vps->poc_lsb_not_present & (1 << layer_idx)))) { + int poc; + + sh->pic_order_cnt_lsb = get_bits(gb, sps->log2_max_poc_lsb); + poc = ff_hevc_compute_poc(sps, s->poc_tid0, sh->pic_order_cnt_lsb, s->nal_unit_type); + if (!sh->first_slice_in_pic_flag && poc != sh->poc) { + av_log(s->avctx, AV_LOG_WARNING, + "Ignoring POC change between slices: %d -> %d\n", poc, sh->poc); + if (s->avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + poc = sh->poc; + } + sh->poc = poc; + } + + if (!IS_IDR(s)) { + int pos; + + sh->short_term_ref_pic_set_sps_flag = get_bits1(gb); + pos = get_bits_left(gb); + if (!sh->short_term_ref_pic_set_sps_flag) { + ret = ff_hevc_decode_short_term_rps(gb, s->avctx, &sh->slice_rps, sps, 1); + if (ret < 0) + return ret; + + sh->short_term_rps = &sh->slice_rps; + } else { + int numbits, rps_idx; + + if (!sps->nb_st_rps) { + av_log(s->avctx, AV_LOG_ERROR, "No ref lists in the SPS.\n"); + return AVERROR_INVALIDDATA; + } + + numbits = av_ceil_log2(sps->nb_st_rps); + rps_idx = numbits > 0 ? get_bits(gb, numbits) : 0; + sh->short_term_rps = &sps->st_rps[rps_idx]; + } + sh->short_term_ref_pic_set_size = pos - get_bits_left(gb); + + pos = get_bits_left(gb); + ret = decode_lt_rps(sps, &sh->long_term_rps, gb, sh->poc, sh->pic_order_cnt_lsb); + if (ret < 0) { + av_log(s->avctx, AV_LOG_WARNING, "Invalid long term RPS.\n"); + if (s->avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + sh->long_term_ref_pic_set_size = pos - get_bits_left(gb); + + if (sps->temporal_mvp_enabled) + sh->slice_temporal_mvp_enabled_flag = get_bits1(gb); + else + sh->slice_temporal_mvp_enabled_flag = 0; + } else { + sh->poc = 0; + sh->pic_order_cnt_lsb = 0; + sh->short_term_ref_pic_set_sps_flag = 0; + sh->short_term_ref_pic_set_size = 0; + sh->short_term_rps = NULL; + sh->long_term_ref_pic_set_size = 0; + sh->long_term_rps.nb_refs = 0; + sh->slice_temporal_mvp_enabled_flag = 0; + } + + sh->inter_layer_pred = 0; + if (s->nuh_layer_id > 0) { + int num_direct_ref_layers = vps->num_direct_ref_layers[layer_idx]; + + if (vps->default_ref_layers_active) + sh->inter_layer_pred = !!num_direct_ref_layers; + else if (num_direct_ref_layers) { + sh->inter_layer_pred = get_bits1(gb); + + if (sh->inter_layer_pred && num_direct_ref_layers > 1) { + av_log(s->avctx, AV_LOG_ERROR, + "NumDirectRefLayers>1 not supported\n"); + return AVERROR_PATCHWELCOME; + } + } + } + + if (sps->sao_enabled) { + sh->slice_sample_adaptive_offset_flag[0] = get_bits1(gb); + if (sps->chroma_format_idc) { + sh->slice_sample_adaptive_offset_flag[1] = + sh->slice_sample_adaptive_offset_flag[2] = get_bits1(gb); + } + } else { + sh->slice_sample_adaptive_offset_flag[0] = 0; + sh->slice_sample_adaptive_offset_flag[1] = 0; + sh->slice_sample_adaptive_offset_flag[2] = 0; + } + + sh->nb_refs[L0] = sh->nb_refs[L1] = 0; + if (sh->slice_type == HEVC_SLICE_P || sh->slice_type == HEVC_SLICE_B) { + int nb_refs; + + sh->nb_refs[L0] = pps->num_ref_idx_l0_default_active; + if (sh->slice_type == HEVC_SLICE_B) + sh->nb_refs[L1] = pps->num_ref_idx_l1_default_active; + + if (get_bits1(gb)) { // num_ref_idx_active_override_flag + sh->nb_refs[L0] = get_ue_golomb_31(gb) + 1; + if (sh->slice_type == HEVC_SLICE_B) + sh->nb_refs[L1] = get_ue_golomb_31(gb) + 1; + } + if (sh->nb_refs[L0] >= HEVC_MAX_REFS || sh->nb_refs[L1] >= HEVC_MAX_REFS) { + av_log(s->avctx, AV_LOG_ERROR, "Too many refs: %d/%d.\n", + sh->nb_refs[L0], sh->nb_refs[L1]); + return AVERROR_INVALIDDATA; + } + + sh->rpl_modification_flag[0] = 0; + sh->rpl_modification_flag[1] = 0; + nb_refs = ff_hevc_frame_nb_refs(sh, pps, layer_idx); + if (!nb_refs) { + av_log(s->avctx, AV_LOG_ERROR, "Zero refs for a frame with P or B slices.\n"); + return AVERROR_INVALIDDATA; + } + + if (pps->lists_modification_present_flag && nb_refs > 1) { + sh->rpl_modification_flag[0] = get_bits1(gb); + if (sh->rpl_modification_flag[0]) { + for (i = 0; i < sh->nb_refs[L0]; i++) + sh->list_entry_lx[0][i] = get_bits(gb, av_ceil_log2(nb_refs)); + } + + if (sh->slice_type == HEVC_SLICE_B) { + sh->rpl_modification_flag[1] = get_bits1(gb); + if (sh->rpl_modification_flag[1] == 1) + for (i = 0; i < sh->nb_refs[L1]; i++) + sh->list_entry_lx[1][i] = get_bits(gb, av_ceil_log2(nb_refs)); + } + } + + if (sh->slice_type == HEVC_SLICE_B) + sh->mvd_l1_zero_flag = get_bits1(gb); + + if (pps->cabac_init_present_flag) + sh->cabac_init_flag = get_bits1(gb); + else + sh->cabac_init_flag = 0; + + sh->collocated_ref_idx = 0; + if (sh->slice_temporal_mvp_enabled_flag) { + sh->collocated_list = L0; + if (sh->slice_type == HEVC_SLICE_B) + sh->collocated_list = !get_bits1(gb); + + if (sh->nb_refs[sh->collocated_list] > 1) { + sh->collocated_ref_idx = get_ue_golomb_long(gb); + if (sh->collocated_ref_idx >= sh->nb_refs[sh->collocated_list]) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid collocated_ref_idx: %d.\n", + sh->collocated_ref_idx); + return AVERROR_INVALIDDATA; + } + } + } + + if ((pps->weighted_pred_flag && sh->slice_type == HEVC_SLICE_P) || + (pps->weighted_bipred_flag && sh->slice_type == HEVC_SLICE_B)) { + ret = pred_weight_table(sh, s->avctx, sps, gb); + if (ret < 0) + return ret; + } + + sh->max_num_merge_cand = 5 - get_ue_golomb_long(gb); + if (sh->max_num_merge_cand < 1 || sh->max_num_merge_cand > 5) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid number of merging MVP candidates: %d.\n", + sh->max_num_merge_cand); + return AVERROR_INVALIDDATA; + } + + // Syntax in 7.3.6.1 + if (sps->motion_vector_resolution_control_idc == 2) + sh->use_integer_mv_flag = get_bits1(gb); + else + // Inferred to be equal to motion_vector_resolution_control_idc if not present + sh->use_integer_mv_flag = sps->motion_vector_resolution_control_idc; + + } + + sh->slice_qp_delta = get_se_golomb(gb); + + if (pps->pic_slice_level_chroma_qp_offsets_present_flag) { + sh->slice_cb_qp_offset = get_se_golomb(gb); + sh->slice_cr_qp_offset = get_se_golomb(gb); + if (sh->slice_cb_qp_offset < -12 || sh->slice_cb_qp_offset > 12 || + sh->slice_cr_qp_offset < -12 || sh->slice_cr_qp_offset > 12) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid slice cx qp offset.\n"); + return AVERROR_INVALIDDATA; + } + } else { + sh->slice_cb_qp_offset = 0; + sh->slice_cr_qp_offset = 0; + } + + if (pps->pps_slice_act_qp_offsets_present_flag) { + sh->slice_act_y_qp_offset = get_se_golomb(gb); + sh->slice_act_cb_qp_offset = get_se_golomb(gb); + sh->slice_act_cr_qp_offset = get_se_golomb(gb); + } + + if (pps->chroma_qp_offset_list_enabled_flag) + sh->cu_chroma_qp_offset_enabled_flag = get_bits1(gb); + else + sh->cu_chroma_qp_offset_enabled_flag = 0; + + if (pps->deblocking_filter_control_present_flag) { + int deblocking_filter_override_flag = 0; + + if (pps->deblocking_filter_override_enabled_flag) + deblocking_filter_override_flag = get_bits1(gb); + + if (deblocking_filter_override_flag) { + sh->disable_deblocking_filter_flag = get_bits1(gb); + if (!sh->disable_deblocking_filter_flag) { + int beta_offset_div2 = get_se_golomb(gb); + int tc_offset_div2 = get_se_golomb(gb) ; + if (beta_offset_div2 < -6 || beta_offset_div2 > 6 || + tc_offset_div2 < -6 || tc_offset_div2 > 6) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid deblock filter offsets: %d, %d\n", + beta_offset_div2, tc_offset_div2); + return AVERROR_INVALIDDATA; + } + sh->beta_offset = beta_offset_div2 * 2; + sh->tc_offset = tc_offset_div2 * 2; + } + } else { + sh->disable_deblocking_filter_flag = pps->disable_dbf; + sh->beta_offset = pps->beta_offset; + sh->tc_offset = pps->tc_offset; + } + } else { + sh->disable_deblocking_filter_flag = 0; + sh->beta_offset = 0; + sh->tc_offset = 0; + } + + if (pps->seq_loop_filter_across_slices_enabled_flag && + (sh->slice_sample_adaptive_offset_flag[0] || + sh->slice_sample_adaptive_offset_flag[1] || + !sh->disable_deblocking_filter_flag)) { + sh->slice_loop_filter_across_slices_enabled_flag = get_bits1(gb); + } else { + sh->slice_loop_filter_across_slices_enabled_flag = pps->seq_loop_filter_across_slices_enabled_flag; + } + } + + sh->num_entry_point_offsets = 0; + if (pps->tiles_enabled_flag || pps->entropy_coding_sync_enabled_flag) { + unsigned num_entry_point_offsets = get_ue_golomb_long(gb); + // It would be possible to bound this tighter but this here is simpler + if (num_entry_point_offsets > get_bits_left(gb) || num_entry_point_offsets > UINT16_MAX) { + av_log(s->avctx, AV_LOG_ERROR, "num_entry_point_offsets %d is invalid\n", num_entry_point_offsets); + return AVERROR_INVALIDDATA; + } + + sh->num_entry_point_offsets = num_entry_point_offsets; + if (sh->num_entry_point_offsets > 0) { + int offset_len = get_ue_golomb_long(gb) + 1; + + if (offset_len < 1 || offset_len > 32) { + sh->num_entry_point_offsets = 0; + av_log(s->avctx, AV_LOG_ERROR, "offset_len %d is invalid\n", offset_len); + return AVERROR_INVALIDDATA; + } + + av_freep(&sh->entry_point_offset); + av_freep(&sh->offset); + av_freep(&sh->size); + sh->entry_point_offset = av_malloc_array(sh->num_entry_point_offsets, sizeof(unsigned)); + sh->offset = av_malloc_array(sh->num_entry_point_offsets + 1, sizeof(int)); + sh->size = av_malloc_array(sh->num_entry_point_offsets + 1, sizeof(int)); + if (!sh->entry_point_offset || !sh->offset || !sh->size) { + sh->num_entry_point_offsets = 0; + av_log(s->avctx, AV_LOG_ERROR, "Failed to allocate memory\n"); + return AVERROR(ENOMEM); + } + for (i = 0; i < sh->num_entry_point_offsets; i++) { + unsigned val = get_bits_long(gb, offset_len); + sh->entry_point_offset[i] = val + 1; // +1; // +1 to get the size + } + } + } + + if (pps->slice_header_extension_present_flag) { + unsigned int length = get_ue_golomb_long(gb); + if (length*8LL > get_bits_left(gb)) { + av_log(s->avctx, AV_LOG_ERROR, "too many slice_header_extension_data_bytes\n"); + return AVERROR_INVALIDDATA; + } + for (i = 0; i < length; i++) + skip_bits(gb, 8); // slice_header_extension_data_byte + } + + ret = get_bits1(gb); + if (!ret && get_bits_left(gb) >= 0) { + av_log(s->avctx, AV_LOG_ERROR, "alignment_bit_equal_to_one=0\n"); + return AVERROR_INVALIDDATA; + } + sh->data_offset = align_get_bits(gb) - gb->buffer; + + if (get_bits_left(gb) < 0) { + av_log(s->avctx, AV_LOG_ERROR, + "Overread slice header by %d bits\n", -get_bits_left(gb)); + return AVERROR_INVALIDDATA; + } + + // Inferred parameters + sh->slice_qp = 26U + pps->pic_init_qp_minus26 + sh->slice_qp_delta; + if (sh->slice_qp > 51 || + sh->slice_qp < -sps->qp_bd_offset) { + av_log(s->avctx, AV_LOG_ERROR, + "The slice_qp %d is outside the valid range " + "[%d, 51].\n", + sh->slice_qp, + -sps->qp_bd_offset); + return AVERROR_INVALIDDATA; + } + + sh->slice_ctb_addr_rs = sh->slice_segment_addr; + + if (sh->dependent_slice_segment_flag && + (!sh->slice_ctb_addr_rs || !pps->ctb_addr_rs_to_ts[sh->slice_ctb_addr_rs])) { + av_log(s->avctx, AV_LOG_ERROR, "Impossible slice segment.\n"); + return AVERROR_INVALIDDATA; + } + + return 0; +} + +#define CTB(tab, x, y) ((tab)[(y) * sps->ctb_width + (x)]) + +#define SET_SAO(elem, value) \ +do { \ + if (!sao_merge_up_flag && !sao_merge_left_flag) \ + sao->elem = value; \ + else if (sao_merge_left_flag) \ + sao->elem = CTB(l->sao, rx-1, ry).elem; \ + else if (sao_merge_up_flag) \ + sao->elem = CTB(l->sao, rx, ry-1).elem; \ + else \ + sao->elem = 0; \ +} while (0) + +static void hls_sao_param(HEVCLocalContext *lc, const HEVCLayerContext *l, + const HEVCPPS *pps, const HEVCSPS *sps, + int rx, int ry) +{ + const HEVCContext *const s = lc->parent; + int sao_merge_left_flag = 0; + int sao_merge_up_flag = 0; + SAOParams *sao = &CTB(l->sao, rx, ry); + int c_idx, i; + + if (s->sh.slice_sample_adaptive_offset_flag[0] || + s->sh.slice_sample_adaptive_offset_flag[1]) { + if (rx > 0) { + if (lc->ctb_left_flag) + sao_merge_left_flag = ff_hevc_sao_merge_flag_decode(lc); + } + if (ry > 0 && !sao_merge_left_flag) { + if (lc->ctb_up_flag) + sao_merge_up_flag = ff_hevc_sao_merge_flag_decode(lc); + } + } + + for (c_idx = 0; c_idx < (sps->chroma_format_idc ? 3 : 1); c_idx++) { + int log2_sao_offset_scale = c_idx == 0 ? pps->log2_sao_offset_scale_luma : + pps->log2_sao_offset_scale_chroma; + + if (!s->sh.slice_sample_adaptive_offset_flag[c_idx]) { + sao->type_idx[c_idx] = SAO_NOT_APPLIED; + continue; + } + + if (c_idx == 2) { + sao->type_idx[2] = sao->type_idx[1]; + sao->eo_class[2] = sao->eo_class[1]; + } else { + SET_SAO(type_idx[c_idx], ff_hevc_sao_type_idx_decode(lc)); + } + + if (sao->type_idx[c_idx] == SAO_NOT_APPLIED) + continue; + + for (i = 0; i < 4; i++) + SET_SAO(offset_abs[c_idx][i], ff_hevc_sao_offset_abs_decode(lc, sps->bit_depth)); + + if (sao->type_idx[c_idx] == SAO_BAND) { + for (i = 0; i < 4; i++) { + if (sao->offset_abs[c_idx][i]) { + SET_SAO(offset_sign[c_idx][i], + ff_hevc_sao_offset_sign_decode(lc)); + } else { + sao->offset_sign[c_idx][i] = 0; + } + } + SET_SAO(band_position[c_idx], ff_hevc_sao_band_position_decode(lc)); + } else if (c_idx != 2) { + SET_SAO(eo_class[c_idx], ff_hevc_sao_eo_class_decode(lc)); + } + + // Inferred parameters + sao->offset_val[c_idx][0] = 0; + for (i = 0; i < 4; i++) { + sao->offset_val[c_idx][i + 1] = sao->offset_abs[c_idx][i]; + if (sao->type_idx[c_idx] == SAO_EDGE) { + if (i > 1) + sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1]; + } else if (sao->offset_sign[c_idx][i]) { + sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1]; + } + sao->offset_val[c_idx][i + 1] *= 1 << log2_sao_offset_scale; + } + } +} + +#undef SET_SAO +#undef CTB + +static int hls_cross_component_pred(HEVCLocalContext *lc, int idx) +{ + int log2_res_scale_abs_plus1 = ff_hevc_log2_res_scale_abs(lc, idx); + + if (log2_res_scale_abs_plus1 != 0) { + int res_scale_sign_flag = ff_hevc_res_scale_sign_flag(lc, idx); + lc->tu.res_scale_val = (1 << (log2_res_scale_abs_plus1 - 1)) * + (1 - 2 * res_scale_sign_flag); + } else { + lc->tu.res_scale_val = 0; + } + + + return 0; +} + +static int hls_transform_unit(HEVCLocalContext *lc, + const HEVCLayerContext *l, + const HEVCPPS *pps, const HEVCSPS *sps, + int x0, int y0, + int xBase, int yBase, int cb_xBase, int cb_yBase, + int log2_cb_size, int log2_trafo_size, + int blk_idx, int cbf_luma, int *cbf_cb, int *cbf_cr) +{ + const HEVCContext *const s = lc->parent; + const int log2_trafo_size_c = log2_trafo_size - sps->hshift[1]; + int i; + + if (lc->cu.pred_mode == MODE_INTRA) { + int trafo_size = 1 << log2_trafo_size; + ff_hevc_set_neighbour_available(lc, x0, y0, trafo_size, trafo_size, sps->log2_ctb_size); + + s->hpc.intra_pred[log2_trafo_size - 2](lc, pps, x0, y0, 0); + } + + if (cbf_luma || cbf_cb[0] || cbf_cr[0] || + (sps->chroma_format_idc == 2 && (cbf_cb[1] || cbf_cr[1]))) { + int scan_idx = SCAN_DIAG; + int scan_idx_c = SCAN_DIAG; + int cbf_chroma = cbf_cb[0] || cbf_cr[0] || + (sps->chroma_format_idc == 2 && + (cbf_cb[1] || cbf_cr[1])); + + if (pps->cu_qp_delta_enabled_flag && !lc->tu.is_cu_qp_delta_coded) { + lc->tu.cu_qp_delta = ff_hevc_cu_qp_delta_abs(lc); + if (lc->tu.cu_qp_delta != 0) + if (ff_hevc_cu_qp_delta_sign_flag(lc) == 1) + lc->tu.cu_qp_delta = -lc->tu.cu_qp_delta; + lc->tu.is_cu_qp_delta_coded = 1; + + if (lc->tu.cu_qp_delta < -(26 + sps->qp_bd_offset / 2) || + lc->tu.cu_qp_delta > (25 + sps->qp_bd_offset / 2)) { + av_log(s->avctx, AV_LOG_ERROR, + "The cu_qp_delta %d is outside the valid range " + "[%d, %d].\n", + lc->tu.cu_qp_delta, + -(26 + sps->qp_bd_offset / 2), + (25 + sps->qp_bd_offset / 2)); + return AVERROR_INVALIDDATA; + } + + ff_hevc_set_qPy(lc, l, pps, cb_xBase, cb_yBase, log2_cb_size); + } + + if (s->sh.cu_chroma_qp_offset_enabled_flag && cbf_chroma && + !lc->cu.cu_transquant_bypass_flag && !lc->tu.is_cu_chroma_qp_offset_coded) { + int cu_chroma_qp_offset_flag = ff_hevc_cu_chroma_qp_offset_flag(lc); + if (cu_chroma_qp_offset_flag) { + int cu_chroma_qp_offset_idx = 0; + if (pps->chroma_qp_offset_list_len_minus1 > 0) { + cu_chroma_qp_offset_idx = ff_hevc_cu_chroma_qp_offset_idx(lc, pps->chroma_qp_offset_list_len_minus1); + av_log(s->avctx, AV_LOG_ERROR, + "cu_chroma_qp_offset_idx not yet tested.\n"); + } + lc->tu.cu_qp_offset_cb = pps->cb_qp_offset_list[cu_chroma_qp_offset_idx]; + lc->tu.cu_qp_offset_cr = pps->cr_qp_offset_list[cu_chroma_qp_offset_idx]; + } else { + lc->tu.cu_qp_offset_cb = 0; + lc->tu.cu_qp_offset_cr = 0; + } + lc->tu.is_cu_chroma_qp_offset_coded = 1; + } + + if (lc->cu.pred_mode == MODE_INTRA && log2_trafo_size < 4) { + if (lc->tu.intra_pred_mode >= 6 && + lc->tu.intra_pred_mode <= 14) { + scan_idx = SCAN_VERT; + } else if (lc->tu.intra_pred_mode >= 22 && + lc->tu.intra_pred_mode <= 30) { + scan_idx = SCAN_HORIZ; + } + + if (lc->tu.intra_pred_mode_c >= 6 && + lc->tu.intra_pred_mode_c <= 14) { + scan_idx_c = SCAN_VERT; + } else if (lc->tu.intra_pred_mode_c >= 22 && + lc->tu.intra_pred_mode_c <= 30) { + scan_idx_c = SCAN_HORIZ; + } + } + + lc->tu.cross_pf = 0; + + if (cbf_luma) + ff_hevc_hls_residual_coding(lc, pps, x0, y0, log2_trafo_size, scan_idx, 0); + if (sps->chroma_format_idc && (log2_trafo_size > 2 || sps->chroma_format_idc == 3)) { + int trafo_size_h = 1 << (log2_trafo_size_c + sps->hshift[1]); + int trafo_size_v = 1 << (log2_trafo_size_c + sps->vshift[1]); + lc->tu.cross_pf = (pps->cross_component_prediction_enabled_flag && cbf_luma && + (lc->cu.pred_mode == MODE_INTER || + (lc->tu.chroma_mode_c == 4))); + + if (lc->tu.cross_pf) { + hls_cross_component_pred(lc, 0); + } + for (i = 0; i < (sps->chroma_format_idc == 2 ? 2 : 1); i++) { + if (lc->cu.pred_mode == MODE_INTRA) { + ff_hevc_set_neighbour_available(lc, x0, y0 + (i << log2_trafo_size_c), + trafo_size_h, trafo_size_v, sps->log2_ctb_size); + s->hpc.intra_pred[log2_trafo_size_c - 2](lc, pps, x0, y0 + (i << log2_trafo_size_c), 1); + } + if (cbf_cb[i]) + ff_hevc_hls_residual_coding(lc, pps, x0, y0 + (i << log2_trafo_size_c), + log2_trafo_size_c, scan_idx_c, 1); + else + if (lc->tu.cross_pf) { + ptrdiff_t stride = s->cur_frame->f->linesize[1]; + int hshift = sps->hshift[1]; + int vshift = sps->vshift[1]; + const int16_t *coeffs_y = (int16_t*)lc->edge_emu_buffer; + int16_t *coeffs = (int16_t*)lc->edge_emu_buffer2; + int size = 1 << log2_trafo_size_c; + + uint8_t *dst = &s->cur_frame->f->data[1][(y0 >> vshift) * stride + + ((x0 >> hshift) << sps->pixel_shift)]; + for (i = 0; i < (size * size); i++) { + coeffs[i] = ((lc->tu.res_scale_val * coeffs_y[i]) >> 3); + } + s->hevcdsp.add_residual[log2_trafo_size_c-2](dst, coeffs, stride); + } + } + + if (lc->tu.cross_pf) { + hls_cross_component_pred(lc, 1); + } + for (i = 0; i < (sps->chroma_format_idc == 2 ? 2 : 1); i++) { + if (lc->cu.pred_mode == MODE_INTRA) { + ff_hevc_set_neighbour_available(lc, x0, y0 + (i << log2_trafo_size_c), + trafo_size_h, trafo_size_v, sps->log2_ctb_size); + s->hpc.intra_pred[log2_trafo_size_c - 2](lc, pps, x0, y0 + (i << log2_trafo_size_c), 2); + } + if (cbf_cr[i]) + ff_hevc_hls_residual_coding(lc, pps, x0, y0 + (i << log2_trafo_size_c), + log2_trafo_size_c, scan_idx_c, 2); + else + if (lc->tu.cross_pf) { + ptrdiff_t stride = s->cur_frame->f->linesize[2]; + int hshift = sps->hshift[2]; + int vshift = sps->vshift[2]; + const int16_t *coeffs_y = (int16_t*)lc->edge_emu_buffer; + int16_t *coeffs = (int16_t*)lc->edge_emu_buffer2; + int size = 1 << log2_trafo_size_c; + + uint8_t *dst = &s->cur_frame->f->data[2][(y0 >> vshift) * stride + + ((x0 >> hshift) << sps->pixel_shift)]; + for (i = 0; i < (size * size); i++) { + coeffs[i] = ((lc->tu.res_scale_val * coeffs_y[i]) >> 3); + } + s->hevcdsp.add_residual[log2_trafo_size_c-2](dst, coeffs, stride); + } + } + } else if (sps->chroma_format_idc && blk_idx == 3) { + int trafo_size_h = 1 << (log2_trafo_size + 1); + int trafo_size_v = 1 << (log2_trafo_size + sps->vshift[1]); + for (i = 0; i < (sps->chroma_format_idc == 2 ? 2 : 1); i++) { + if (lc->cu.pred_mode == MODE_INTRA) { + ff_hevc_set_neighbour_available(lc, xBase, yBase + (i << log2_trafo_size), + trafo_size_h, trafo_size_v, sps->log2_ctb_size); + s->hpc.intra_pred[log2_trafo_size - 2](lc, pps, xBase, yBase + (i << log2_trafo_size), 1); + } + if (cbf_cb[i]) + ff_hevc_hls_residual_coding(lc, pps, xBase, yBase + (i << log2_trafo_size), + log2_trafo_size, scan_idx_c, 1); + } + for (i = 0; i < (sps->chroma_format_idc == 2 ? 2 : 1); i++) { + if (lc->cu.pred_mode == MODE_INTRA) { + ff_hevc_set_neighbour_available(lc, xBase, yBase + (i << log2_trafo_size), + trafo_size_h, trafo_size_v, sps->log2_ctb_size); + s->hpc.intra_pred[log2_trafo_size - 2](lc, pps, xBase, yBase + (i << log2_trafo_size), 2); + } + if (cbf_cr[i]) + ff_hevc_hls_residual_coding(lc, pps, xBase, yBase + (i << log2_trafo_size), + log2_trafo_size, scan_idx_c, 2); + } + } + } else if (sps->chroma_format_idc && lc->cu.pred_mode == MODE_INTRA) { + if (log2_trafo_size > 2 || sps->chroma_format_idc == 3) { + int trafo_size_h = 1 << (log2_trafo_size_c + sps->hshift[1]); + int trafo_size_v = 1 << (log2_trafo_size_c + sps->vshift[1]); + ff_hevc_set_neighbour_available(lc, x0, y0, trafo_size_h, trafo_size_v, + sps->log2_ctb_size); + s->hpc.intra_pred[log2_trafo_size_c - 2](lc, pps, x0, y0, 1); + s->hpc.intra_pred[log2_trafo_size_c - 2](lc, pps, x0, y0, 2); + if (sps->chroma_format_idc == 2) { + ff_hevc_set_neighbour_available(lc, x0, y0 + (1 << log2_trafo_size_c), + trafo_size_h, trafo_size_v, sps->log2_ctb_size); + s->hpc.intra_pred[log2_trafo_size_c - 2](lc, pps, x0, y0 + (1 << log2_trafo_size_c), 1); + s->hpc.intra_pred[log2_trafo_size_c - 2](lc, pps, x0, y0 + (1 << log2_trafo_size_c), 2); + } + } else if (blk_idx == 3) { + int trafo_size_h = 1 << (log2_trafo_size + 1); + int trafo_size_v = 1 << (log2_trafo_size + sps->vshift[1]); + ff_hevc_set_neighbour_available(lc, xBase, yBase, + trafo_size_h, trafo_size_v, sps->log2_ctb_size); + s->hpc.intra_pred[log2_trafo_size - 2](lc, pps, xBase, yBase, 1); + s->hpc.intra_pred[log2_trafo_size - 2](lc, pps, xBase, yBase, 2); + if (sps->chroma_format_idc == 2) { + ff_hevc_set_neighbour_available(lc, xBase, yBase + (1 << log2_trafo_size), + trafo_size_h, trafo_size_v, sps->log2_ctb_size); + s->hpc.intra_pred[log2_trafo_size - 2](lc, pps, xBase, yBase + (1 << log2_trafo_size), 1); + s->hpc.intra_pred[log2_trafo_size - 2](lc, pps, xBase, yBase + (1 << log2_trafo_size), 2); + } + } + } + + return 0; +} + +static void set_deblocking_bypass(uint8_t *is_pcm, const HEVCSPS *sps, + int x0, int y0, int log2_cb_size) +{ + int cb_size = 1 << log2_cb_size; + int log2_min_pu_size = sps->log2_min_pu_size; + + int min_pu_width = sps->min_pu_width; + int x_end = FFMIN(x0 + cb_size, sps->width); + int y_end = FFMIN(y0 + cb_size, sps->height); + int i, j; + + for (j = (y0 >> log2_min_pu_size); j < (y_end >> log2_min_pu_size); j++) + for (i = (x0 >> log2_min_pu_size); i < (x_end >> log2_min_pu_size); i++) + is_pcm[i + j * min_pu_width] = 2; +} + +static int hls_transform_tree(HEVCLocalContext *lc, + const HEVCLayerContext *l, + const HEVCPPS *pps, const HEVCSPS *sps, + int x0, int y0, + int xBase, int yBase, int cb_xBase, int cb_yBase, + int log2_cb_size, int log2_trafo_size, + int trafo_depth, int blk_idx, + const int *base_cbf_cb, const int *base_cbf_cr) +{ + const HEVCContext *const s = lc->parent; + uint8_t split_transform_flag; + int cbf_cb[2]; + int cbf_cr[2]; + int ret; + + cbf_cb[0] = base_cbf_cb[0]; + cbf_cb[1] = base_cbf_cb[1]; + cbf_cr[0] = base_cbf_cr[0]; + cbf_cr[1] = base_cbf_cr[1]; + + if (lc->cu.intra_split_flag) { + if (trafo_depth == 1) { + lc->tu.intra_pred_mode = lc->pu.intra_pred_mode[blk_idx]; + if (sps->chroma_format_idc == 3) { + lc->tu.intra_pred_mode_c = lc->pu.intra_pred_mode_c[blk_idx]; + lc->tu.chroma_mode_c = lc->pu.chroma_mode_c[blk_idx]; + } else { + lc->tu.intra_pred_mode_c = lc->pu.intra_pred_mode_c[0]; + lc->tu.chroma_mode_c = lc->pu.chroma_mode_c[0]; + } + } + } else { + lc->tu.intra_pred_mode = lc->pu.intra_pred_mode[0]; + lc->tu.intra_pred_mode_c = lc->pu.intra_pred_mode_c[0]; + lc->tu.chroma_mode_c = lc->pu.chroma_mode_c[0]; + } + + if (log2_trafo_size <= sps->log2_max_trafo_size && + log2_trafo_size > sps->log2_min_tb_size && + trafo_depth < lc->cu.max_trafo_depth && + !(lc->cu.intra_split_flag && trafo_depth == 0)) { + split_transform_flag = ff_hevc_split_transform_flag_decode(lc, log2_trafo_size); + } else { + int inter_split = sps->max_transform_hierarchy_depth_inter == 0 && + lc->cu.pred_mode == MODE_INTER && + lc->cu.part_mode != PART_2Nx2N && + trafo_depth == 0; + + split_transform_flag = log2_trafo_size > sps->log2_max_trafo_size || + (lc->cu.intra_split_flag && trafo_depth == 0) || + inter_split; + } + + if (sps->chroma_format_idc && (log2_trafo_size > 2 || sps->chroma_format_idc == 3)) { + if (trafo_depth == 0 || cbf_cb[0]) { + cbf_cb[0] = ff_hevc_cbf_cb_cr_decode(lc, trafo_depth); + if (sps->chroma_format_idc == 2 && (!split_transform_flag || log2_trafo_size == 3)) { + cbf_cb[1] = ff_hevc_cbf_cb_cr_decode(lc, trafo_depth); + } + } + + if (trafo_depth == 0 || cbf_cr[0]) { + cbf_cr[0] = ff_hevc_cbf_cb_cr_decode(lc, trafo_depth); + if (sps->chroma_format_idc == 2 && (!split_transform_flag || log2_trafo_size == 3)) { + cbf_cr[1] = ff_hevc_cbf_cb_cr_decode(lc, trafo_depth); + } + } + } + + if (split_transform_flag) { + const int trafo_size_split = 1 << (log2_trafo_size - 1); + const int x1 = x0 + trafo_size_split; + const int y1 = y0 + trafo_size_split; + +#define SUBDIVIDE(x, y, idx) \ +do { \ + ret = hls_transform_tree(lc, l, pps, sps, \ + x, y, x0, y0, cb_xBase, cb_yBase, log2_cb_size, \ + log2_trafo_size - 1, trafo_depth + 1, idx, \ + cbf_cb, cbf_cr); \ + if (ret < 0) \ + return ret; \ +} while (0) + + SUBDIVIDE(x0, y0, 0); + SUBDIVIDE(x1, y0, 1); + SUBDIVIDE(x0, y1, 2); + SUBDIVIDE(x1, y1, 3); + +#undef SUBDIVIDE + } else { + int min_tu_size = 1 << sps->log2_min_tb_size; + int log2_min_tu_size = sps->log2_min_tb_size; + int min_tu_width = sps->min_tb_width; + int cbf_luma = 1; + + if (lc->cu.pred_mode == MODE_INTRA || trafo_depth != 0 || + cbf_cb[0] || cbf_cr[0] || + (sps->chroma_format_idc == 2 && (cbf_cb[1] || cbf_cr[1]))) { + cbf_luma = ff_hevc_cbf_luma_decode(lc, trafo_depth); + } + + ret = hls_transform_unit(lc, l, pps, sps, + x0, y0, xBase, yBase, cb_xBase, cb_yBase, + log2_cb_size, log2_trafo_size, + blk_idx, cbf_luma, cbf_cb, cbf_cr); + if (ret < 0) + return ret; + // TODO: store cbf_luma somewhere else + if (cbf_luma) { + int i, j; + for (i = 0; i < (1 << log2_trafo_size); i += min_tu_size) + for (j = 0; j < (1 << log2_trafo_size); j += min_tu_size) { + int x_tu = (x0 + j) >> log2_min_tu_size; + int y_tu = (y0 + i) >> log2_min_tu_size; + l->cbf_luma[y_tu * min_tu_width + x_tu] = 1; + } + } + if (!s->sh.disable_deblocking_filter_flag) { + ff_hevc_deblocking_boundary_strengths(lc, l, pps, x0, y0, log2_trafo_size); + if (pps->transquant_bypass_enable_flag && + lc->cu.cu_transquant_bypass_flag) + set_deblocking_bypass(l->is_pcm, sps, x0, y0, log2_trafo_size); + } + } + return 0; +} + +static int hls_pcm_sample(HEVCLocalContext *lc, const HEVCLayerContext *l, + const HEVCPPS *pps, int x0, int y0, int log2_cb_size) +{ + const HEVCContext *const s = lc->parent; + const HEVCSPS *const sps = pps->sps; + GetBitContext gb; + int cb_size = 1 << log2_cb_size; + ptrdiff_t stride0 = s->cur_frame->f->linesize[0]; + uint8_t *dst0 = &s->cur_frame->f->data[0][y0 * stride0 + (x0 << sps->pixel_shift)]; + + int length = cb_size * cb_size * sps->pcm.bit_depth + (sps->chroma_format_idc != 0 ? + (((cb_size >> sps->hshift[1]) * (cb_size >> sps->vshift[1])) + + ((cb_size >> sps->hshift[2]) * (cb_size >> sps->vshift[2]))) * + sps->pcm.bit_depth_chroma : 0); + const uint8_t *pcm = skip_bytes(&lc->cc, (length + 7) >> 3); + int ret; + + if (!s->sh.disable_deblocking_filter_flag) + ff_hevc_deblocking_boundary_strengths(lc, l, pps, x0, y0, log2_cb_size); + + ret = init_get_bits(&gb, pcm, length); + if (ret < 0) + return ret; + + s->hevcdsp.put_pcm(dst0, stride0, cb_size, cb_size, &gb, sps->pcm.bit_depth); + if (sps->chroma_format_idc) { + ptrdiff_t stride1 = s->cur_frame->f->linesize[1]; + ptrdiff_t stride2 = s->cur_frame->f->linesize[2]; + uint8_t *dst1 = &s->cur_frame->f->data[1][(y0 >> sps->vshift[1]) * stride1 + ((x0 >> sps->hshift[1]) << sps->pixel_shift)]; + uint8_t *dst2 = &s->cur_frame->f->data[2][(y0 >> sps->vshift[2]) * stride2 + ((x0 >> sps->hshift[2]) << sps->pixel_shift)]; + + s->hevcdsp.put_pcm(dst1, stride1, + cb_size >> sps->hshift[1], + cb_size >> sps->vshift[1], + &gb, sps->pcm.bit_depth_chroma); + s->hevcdsp.put_pcm(dst2, stride2, + cb_size >> sps->hshift[2], + cb_size >> sps->vshift[2], + &gb, sps->pcm.bit_depth_chroma); + } + + return 0; +} + +/** + * 8.5.3.2.2.1 Luma sample unidirectional interpolation process + * + * @param s HEVC decoding context + * @param dst target buffer for block data at block position + * @param dststride stride of the dst buffer + * @param ref reference picture buffer at origin (0, 0) + * @param mv motion vector (relative to block position) to get pixel data from + * @param x_off horizontal position of block from origin (0, 0) + * @param y_off vertical position of block from origin (0, 0) + * @param block_w width of block + * @param block_h height of block + * @param luma_weight weighting factor applied to the luma prediction + * @param luma_offset additive offset applied to the luma prediction value + */ + +static void luma_mc_uni(HEVCLocalContext *lc, + const HEVCPPS *pps, const HEVCSPS *sps, + uint8_t *dst, ptrdiff_t dststride, + const AVFrame *ref, const Mv *mv, int x_off, int y_off, + int block_w, int block_h, int luma_weight, int luma_offset) +{ + const HEVCContext *const s = lc->parent; + const uint8_t *src = ref->data[0]; + ptrdiff_t srcstride = ref->linesize[0]; + int pic_width = sps->width; + int pic_height = sps->height; + int mx = mv->x & 3; + int my = mv->y & 3; + int weight_flag = (s->sh.slice_type == HEVC_SLICE_P && pps->weighted_pred_flag) || + (s->sh.slice_type == HEVC_SLICE_B && pps->weighted_bipred_flag); + int idx = hevc_pel_weight[block_w]; + + x_off += mv->x >> 2; + y_off += mv->y >> 2; + src += y_off * srcstride + (x_off * (1 << sps->pixel_shift)); + + if (x_off < QPEL_EXTRA_BEFORE || y_off < QPEL_EXTRA_AFTER || + x_off >= pic_width - block_w - QPEL_EXTRA_AFTER || + y_off >= pic_height - block_h - QPEL_EXTRA_AFTER || + ref == s->cur_frame->f) { + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << sps->pixel_shift; + int offset = QPEL_EXTRA_BEFORE * srcstride + (QPEL_EXTRA_BEFORE << sps->pixel_shift); + int buf_offset = QPEL_EXTRA_BEFORE * edge_emu_stride + (QPEL_EXTRA_BEFORE << sps->pixel_shift); + + s->vdsp.emulated_edge_mc(lc->edge_emu_buffer, src - offset, + edge_emu_stride, srcstride, + block_w + QPEL_EXTRA, + block_h + QPEL_EXTRA, + x_off - QPEL_EXTRA_BEFORE, y_off - QPEL_EXTRA_BEFORE, + pic_width, pic_height); + src = lc->edge_emu_buffer + buf_offset; + srcstride = edge_emu_stride; + } + + if (!weight_flag) + s->hevcdsp.put_hevc_qpel_uni[idx][!!my][!!mx](dst, dststride, src, srcstride, + block_h, mx, my, block_w); + else + s->hevcdsp.put_hevc_qpel_uni_w[idx][!!my][!!mx](dst, dststride, src, srcstride, + block_h, s->sh.luma_log2_weight_denom, + luma_weight, luma_offset, mx, my, block_w); +} + +/** + * 8.5.3.2.2.1 Luma sample bidirectional interpolation process + * + * @param s HEVC decoding context + * @param dst target buffer for block data at block position + * @param dststride stride of the dst buffer + * @param ref0 reference picture0 buffer at origin (0, 0) + * @param mv0 motion vector0 (relative to block position) to get pixel data from + * @param x_off horizontal position of block from origin (0, 0) + * @param y_off vertical position of block from origin (0, 0) + * @param block_w width of block + * @param block_h height of block + * @param ref1 reference picture1 buffer at origin (0, 0) + * @param mv1 motion vector1 (relative to block position) to get pixel data from + * @param current_mv current motion vector structure + */ +static void luma_mc_bi(HEVCLocalContext *lc, + const HEVCPPS *pps, const HEVCSPS *sps, + uint8_t *dst, ptrdiff_t dststride, + const AVFrame *ref0, const Mv *mv0, int x_off, int y_off, + int block_w, int block_h, const AVFrame *ref1, + const Mv *mv1, struct MvField *current_mv) +{ + const HEVCContext *const s = lc->parent; + ptrdiff_t src0stride = ref0->linesize[0]; + ptrdiff_t src1stride = ref1->linesize[0]; + int pic_width = sps->width; + int pic_height = sps->height; + int mx0 = mv0->x & 3; + int my0 = mv0->y & 3; + int mx1 = mv1->x & 3; + int my1 = mv1->y & 3; + int weight_flag = (s->sh.slice_type == HEVC_SLICE_P && pps->weighted_pred_flag) || + (s->sh.slice_type == HEVC_SLICE_B && pps->weighted_bipred_flag); + int x_off0 = x_off + (mv0->x >> 2); + int y_off0 = y_off + (mv0->y >> 2); + int x_off1 = x_off + (mv1->x >> 2); + int y_off1 = y_off + (mv1->y >> 2); + int idx = hevc_pel_weight[block_w]; + + const uint8_t *src0 = ref0->data[0] + y_off0 * src0stride + (int)((unsigned)x_off0 << sps->pixel_shift); + const uint8_t *src1 = ref1->data[0] + y_off1 * src1stride + (int)((unsigned)x_off1 << sps->pixel_shift); + + if (x_off0 < QPEL_EXTRA_BEFORE || y_off0 < QPEL_EXTRA_AFTER || + x_off0 >= pic_width - block_w - QPEL_EXTRA_AFTER || + y_off0 >= pic_height - block_h - QPEL_EXTRA_AFTER) { + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << sps->pixel_shift; + int offset = QPEL_EXTRA_BEFORE * src0stride + (QPEL_EXTRA_BEFORE << sps->pixel_shift); + int buf_offset = QPEL_EXTRA_BEFORE * edge_emu_stride + (QPEL_EXTRA_BEFORE << sps->pixel_shift); + + s->vdsp.emulated_edge_mc(lc->edge_emu_buffer, src0 - offset, + edge_emu_stride, src0stride, + block_w + QPEL_EXTRA, + block_h + QPEL_EXTRA, + x_off0 - QPEL_EXTRA_BEFORE, y_off0 - QPEL_EXTRA_BEFORE, + pic_width, pic_height); + src0 = lc->edge_emu_buffer + buf_offset; + src0stride = edge_emu_stride; + } + + if (x_off1 < QPEL_EXTRA_BEFORE || y_off1 < QPEL_EXTRA_AFTER || + x_off1 >= pic_width - block_w - QPEL_EXTRA_AFTER || + y_off1 >= pic_height - block_h - QPEL_EXTRA_AFTER) { + const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << sps->pixel_shift; + int offset = QPEL_EXTRA_BEFORE * src1stride + (QPEL_EXTRA_BEFORE << sps->pixel_shift); + int buf_offset = QPEL_EXTRA_BEFORE * edge_emu_stride + (QPEL_EXTRA_BEFORE << sps->pixel_shift); + + s->vdsp.emulated_edge_mc(lc->edge_emu_buffer2, src1 - offset, + edge_emu_stride, src1stride, + block_w + QPEL_EXTRA, + block_h + QPEL_EXTRA, + x_off1 - QPEL_EXTRA_BEFORE, y_off1 - QPEL_EXTRA_BEFORE, + pic_width, pic_height); + src1 = lc->edge_emu_buffer2 + buf_offset; + src1stride = edge_emu_stride; + } + + s->hevcdsp.put_hevc_qpel[idx][!!my0][!!mx0](lc->tmp, src0, src0stride, + block_h, mx0, my0, block_w); + if (!weight_flag) + s->hevcdsp.put_hevc_qpel_bi[idx][!!my1][!!mx1](dst, dststride, src1, src1stride, lc->tmp, + block_h, mx1, my1, block_w); + else + s->hevcdsp.put_hevc_qpel_bi_w[idx][!!my1][!!mx1](dst, dststride, src1, src1stride, lc->tmp, + block_h, s->sh.luma_log2_weight_denom, + s->sh.luma_weight_l0[current_mv->ref_idx[0]], + s->sh.luma_weight_l1[current_mv->ref_idx[1]], + s->sh.luma_offset_l0[current_mv->ref_idx[0]], + s->sh.luma_offset_l1[current_mv->ref_idx[1]], + mx1, my1, block_w); + +} + +/** + * 8.5.3.2.2.2 Chroma sample uniprediction interpolation process + * + * @param s HEVC decoding context + * @param dst1 target buffer for block data at block position (U plane) + * @param dst2 target buffer for block data at block position (V plane) + * @param dststride stride of the dst1 and dst2 buffers + * @param ref reference picture buffer at origin (0, 0) + * @param mv motion vector (relative to block position) to get pixel data from + * @param x_off horizontal position of block from origin (0, 0) + * @param y_off vertical position of block from origin (0, 0) + * @param block_w width of block + * @param block_h height of block + * @param chroma_weight weighting factor applied to the chroma prediction + * @param chroma_offset additive offset applied to the chroma prediction value + */ + +static void chroma_mc_uni(HEVCLocalContext *lc, + const HEVCPPS *pps, const HEVCSPS *sps, + uint8_t *dst0, + ptrdiff_t dststride, const uint8_t *src0, ptrdiff_t srcstride, int reflist, + int x_off, int y_off, int block_w, int block_h, + const struct MvField *current_mv, int chroma_weight, int chroma_offset) +{ + const HEVCContext *const s = lc->parent; + int pic_width = sps->width >> sps->hshift[1]; + int pic_height = sps->height >> sps->vshift[1]; + const Mv *mv = ¤t_mv->mv[reflist]; + int weight_flag = (s->sh.slice_type == HEVC_SLICE_P && pps->weighted_pred_flag) || + (s->sh.slice_type == HEVC_SLICE_B && pps->weighted_bipred_flag); + int idx = hevc_pel_weight[block_w]; + int hshift = sps->hshift[1]; + int vshift = sps->vshift[1]; + intptr_t mx = av_zero_extend(mv->x, 2 + hshift); + intptr_t my = av_zero_extend(mv->y, 2 + vshift); + intptr_t _mx = mx << (1 - hshift); + intptr_t _my = my << (1 - vshift); + int emu = src0 == s->cur_frame->f->data[1] || src0 == s->cur_frame->f->data[2]; + + x_off += mv->x >> (2 + hshift); + y_off += mv->y >> (2 + vshift); + src0 += y_off * srcstride + (x_off * (1 << sps->pixel_shift)); + + if (x_off < EPEL_EXTRA_BEFORE || y_off < EPEL_EXTRA_AFTER || + x_off >= pic_width - block_w - EPEL_EXTRA_AFTER || + y_off >= pic_height - block_h - EPEL_EXTRA_AFTER || + emu) { + const int edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << sps->pixel_shift; + int offset0 = EPEL_EXTRA_BEFORE * (srcstride + (1 << sps->pixel_shift)); + int buf_offset0 = EPEL_EXTRA_BEFORE * + (edge_emu_stride + (1 << sps->pixel_shift)); + s->vdsp.emulated_edge_mc(lc->edge_emu_buffer, src0 - offset0, + edge_emu_stride, srcstride, + block_w + EPEL_EXTRA, block_h + EPEL_EXTRA, + x_off - EPEL_EXTRA_BEFORE, + y_off - EPEL_EXTRA_BEFORE, + pic_width, pic_height); + + src0 = lc->edge_emu_buffer + buf_offset0; + srcstride = edge_emu_stride; + } + if (!weight_flag) + s->hevcdsp.put_hevc_epel_uni[idx][!!my][!!mx](dst0, dststride, src0, srcstride, + block_h, _mx, _my, block_w); + else + s->hevcdsp.put_hevc_epel_uni_w[idx][!!my][!!mx](dst0, dststride, src0, srcstride, + block_h, s->sh.chroma_log2_weight_denom, + chroma_weight, chroma_offset, _mx, _my, block_w); +} + +/** + * 8.5.3.2.2.2 Chroma sample bidirectional interpolation process + * + * @param s HEVC decoding context + * @param dst target buffer for block data at block position + * @param dststride stride of the dst buffer + * @param ref0 reference picture0 buffer at origin (0, 0) + * @param mv0 motion vector0 (relative to block position) to get pixel data from + * @param x_off horizontal position of block from origin (0, 0) + * @param y_off vertical position of block from origin (0, 0) + * @param block_w width of block + * @param block_h height of block + * @param ref1 reference picture1 buffer at origin (0, 0) + * @param mv1 motion vector1 (relative to block position) to get pixel data from + * @param current_mv current motion vector structure + * @param cidx chroma component(cb, cr) + */ +static void chroma_mc_bi(HEVCLocalContext *lc, + const HEVCPPS *pps, const HEVCSPS *sps, + uint8_t *dst0, ptrdiff_t dststride, + const AVFrame *ref0, const AVFrame *ref1, + int x_off, int y_off, int block_w, int block_h, const MvField *current_mv, int cidx) +{ + const HEVCContext *const s = lc->parent; + const uint8_t *src1 = ref0->data[cidx+1]; + const uint8_t *src2 = ref1->data[cidx+1]; + ptrdiff_t src1stride = ref0->linesize[cidx+1]; + ptrdiff_t src2stride = ref1->linesize[cidx+1]; + int weight_flag = (s->sh.slice_type == HEVC_SLICE_P && pps->weighted_pred_flag) || + (s->sh.slice_type == HEVC_SLICE_B && pps->weighted_bipred_flag); + int pic_width = sps->width >> sps->hshift[1]; + int pic_height = sps->height >> sps->vshift[1]; + const Mv *const mv0 = ¤t_mv->mv[0]; + const Mv *const mv1 = ¤t_mv->mv[1]; + int hshift = sps->hshift[1]; + int vshift = sps->vshift[1]; + + intptr_t mx0 = av_zero_extend(mv0->x, 2 + hshift); + intptr_t my0 = av_zero_extend(mv0->y, 2 + vshift); + intptr_t mx1 = av_zero_extend(mv1->x, 2 + hshift); + intptr_t my1 = av_zero_extend(mv1->y, 2 + vshift); + intptr_t _mx0 = mx0 << (1 - hshift); + intptr_t _my0 = my0 << (1 - vshift); + intptr_t _mx1 = mx1 << (1 - hshift); + intptr_t _my1 = my1 << (1 - vshift); + + int x_off0 = x_off + (mv0->x >> (2 + hshift)); + int y_off0 = y_off + (mv0->y >> (2 + vshift)); + int x_off1 = x_off + (mv1->x >> (2 + hshift)); + int y_off1 = y_off + (mv1->y >> (2 + vshift)); + int idx = hevc_pel_weight[block_w]; + src1 += y_off0 * src1stride + (int)((unsigned)x_off0 << sps->pixel_shift); + src2 += y_off1 * src2stride + (int)((unsigned)x_off1 << sps->pixel_shift); + + if (x_off0 < EPEL_EXTRA_BEFORE || y_off0 < EPEL_EXTRA_AFTER || + x_off0 >= pic_width - block_w - EPEL_EXTRA_AFTER || + y_off0 >= pic_height - block_h - EPEL_EXTRA_AFTER) { + const int edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << sps->pixel_shift; + int offset1 = EPEL_EXTRA_BEFORE * (src1stride + (1 << sps->pixel_shift)); + int buf_offset1 = EPEL_EXTRA_BEFORE * + (edge_emu_stride + (1 << sps->pixel_shift)); + + s->vdsp.emulated_edge_mc(lc->edge_emu_buffer, src1 - offset1, + edge_emu_stride, src1stride, + block_w + EPEL_EXTRA, block_h + EPEL_EXTRA, + x_off0 - EPEL_EXTRA_BEFORE, + y_off0 - EPEL_EXTRA_BEFORE, + pic_width, pic_height); + + src1 = lc->edge_emu_buffer + buf_offset1; + src1stride = edge_emu_stride; + } + + if (x_off1 < EPEL_EXTRA_BEFORE || y_off1 < EPEL_EXTRA_AFTER || + x_off1 >= pic_width - block_w - EPEL_EXTRA_AFTER || + y_off1 >= pic_height - block_h - EPEL_EXTRA_AFTER) { + const int edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << sps->pixel_shift; + int offset1 = EPEL_EXTRA_BEFORE * (src2stride + (1 << sps->pixel_shift)); + int buf_offset1 = EPEL_EXTRA_BEFORE * + (edge_emu_stride + (1 << sps->pixel_shift)); + + s->vdsp.emulated_edge_mc(lc->edge_emu_buffer2, src2 - offset1, + edge_emu_stride, src2stride, + block_w + EPEL_EXTRA, block_h + EPEL_EXTRA, + x_off1 - EPEL_EXTRA_BEFORE, + y_off1 - EPEL_EXTRA_BEFORE, + pic_width, pic_height); + + src2 = lc->edge_emu_buffer2 + buf_offset1; + src2stride = edge_emu_stride; + } + + s->hevcdsp.put_hevc_epel[idx][!!my0][!!mx0](lc->tmp, src1, src1stride, + block_h, _mx0, _my0, block_w); + if (!weight_flag) + s->hevcdsp.put_hevc_epel_bi[idx][!!my1][!!mx1](dst0, s->cur_frame->f->linesize[cidx+1], + src2, src2stride, lc->tmp, + block_h, _mx1, _my1, block_w); + else + s->hevcdsp.put_hevc_epel_bi_w[idx][!!my1][!!mx1](dst0, s->cur_frame->f->linesize[cidx+1], + src2, src2stride, lc->tmp, + block_h, + s->sh.chroma_log2_weight_denom, + s->sh.chroma_weight_l0[current_mv->ref_idx[0]][cidx], + s->sh.chroma_weight_l1[current_mv->ref_idx[1]][cidx], + s->sh.chroma_offset_l0[current_mv->ref_idx[0]][cidx], + s->sh.chroma_offset_l1[current_mv->ref_idx[1]][cidx], + _mx1, _my1, block_w); +} + +static void hevc_await_progress(const HEVCContext *s, const HEVCFrame *ref, + const Mv *mv, int y0, int height) +{ + if (s->avctx->active_thread_type == FF_THREAD_FRAME ) { + int y = FFMAX(0, (mv->y >> 2) + y0 + height + 9); + + ff_progress_frame_await(&ref->tf, y); + } +} + +static void hevc_luma_mv_mvp_mode(HEVCLocalContext *lc, + const HEVCPPS *pps, const HEVCSPS *sps, + int x0, int y0, int nPbW, + int nPbH, int log2_cb_size, int part_idx, + int merge_idx, MvField *mv) +{ + const HEVCContext *const s = lc->parent; + enum InterPredIdc inter_pred_idc = PRED_L0; + int mvp_flag; + + ff_hevc_set_neighbour_available(lc, x0, y0, nPbW, nPbH, sps->log2_ctb_size); + mv->pred_flag = 0; + if (s->sh.slice_type == HEVC_SLICE_B) + inter_pred_idc = ff_hevc_inter_pred_idc_decode(lc, nPbW, nPbH); + + if (inter_pred_idc != PRED_L1) { + if (s->sh.nb_refs[L0]) + mv->ref_idx[0]= ff_hevc_ref_idx_lx_decode(lc, s->sh.nb_refs[L0]); + + mv->pred_flag = PF_L0; + ff_hevc_hls_mvd_coding(lc, x0, y0, 0); + mvp_flag = ff_hevc_mvp_lx_flag_decode(lc); + ff_hevc_luma_mv_mvp_mode(lc, pps, x0, y0, nPbW, nPbH, log2_cb_size, + part_idx, merge_idx, mv, mvp_flag, 0); + mv->mv[0].x += lc->pu.mvd.x; + mv->mv[0].y += lc->pu.mvd.y; + } + + if (inter_pred_idc != PRED_L0) { + if (s->sh.nb_refs[L1]) + mv->ref_idx[1]= ff_hevc_ref_idx_lx_decode(lc, s->sh.nb_refs[L1]); + + if (s->sh.mvd_l1_zero_flag == 1 && inter_pred_idc == PRED_BI) { + AV_ZERO32(&lc->pu.mvd); + } else { + ff_hevc_hls_mvd_coding(lc, x0, y0, 1); + } + + mv->pred_flag += PF_L1; + mvp_flag = ff_hevc_mvp_lx_flag_decode(lc); + ff_hevc_luma_mv_mvp_mode(lc, pps, x0, y0, nPbW, nPbH, log2_cb_size, + part_idx, merge_idx, mv, mvp_flag, 1); + mv->mv[1].x += lc->pu.mvd.x; + mv->mv[1].y += lc->pu.mvd.y; + } +} + +static void hls_prediction_unit(HEVCLocalContext *lc, + const HEVCLayerContext *l, + const HEVCPPS *pps, const HEVCSPS *sps, + int x0, int y0, int nPbW, int nPbH, + int log2_cb_size, int partIdx, int idx) +{ +#define POS(c_idx, x, y) \ + s->cur_frame->f->data[c_idx] ? \ + &s->cur_frame->f->data[c_idx][((y) >> sps->vshift[c_idx]) * linesize[c_idx] + \ + (((x) >> sps->hshift[c_idx]) << sps->pixel_shift)] : NULL + const HEVCContext *const s = lc->parent; + int merge_idx = 0; + struct MvField current_mv = {{{ 0 }}}; + + int min_pu_width = sps->min_pu_width; + + MvField *tab_mvf = s->cur_frame->tab_mvf; + const RefPicList *refPicList = s->cur_frame->refPicList; + const HEVCFrame *ref0 = NULL, *ref1 = NULL; + const int *linesize = s->cur_frame->f->linesize; + uint8_t *dst0 = s->cur_frame->f->data[0] + y0 * linesize[0] + (x0 << sps->pixel_shift); + uint8_t *dst1 = POS(1, x0, y0); + uint8_t *dst2 = POS(2, x0, y0); + int log2_min_cb_size = sps->log2_min_cb_size; + int min_cb_width = sps->min_cb_width; + int x_cb = x0 >> log2_min_cb_size; + int y_cb = y0 >> log2_min_cb_size; + int x_pu, y_pu; + int i, j; + + int skip_flag = SAMPLE_CTB(l->skip_flag, x_cb, y_cb); + + if (!skip_flag) + lc->pu.merge_flag = ff_hevc_merge_flag_decode(lc); + + if (skip_flag || lc->pu.merge_flag) { + if (s->sh.max_num_merge_cand > 1) + merge_idx = ff_hevc_merge_idx_decode(lc); + else + merge_idx = 0; + + ff_hevc_luma_mv_merge_mode(lc, pps, x0, y0, nPbW, nPbH, log2_cb_size, + partIdx, merge_idx, ¤t_mv); + } else { + hevc_luma_mv_mvp_mode(lc, pps, sps, x0, y0, nPbW, nPbH, log2_cb_size, + partIdx, merge_idx, ¤t_mv); + } + + x_pu = x0 >> sps->log2_min_pu_size; + y_pu = y0 >> sps->log2_min_pu_size; + + for (j = 0; j < nPbH >> sps->log2_min_pu_size; j++) + for (i = 0; i < nPbW >> sps->log2_min_pu_size; i++) + tab_mvf[(y_pu + j) * min_pu_width + x_pu + i] = current_mv; + + if (current_mv.pred_flag & PF_L0) { + ref0 = refPicList[0].ref[current_mv.ref_idx[0]]; + if (!ref0 || !ref0->f) + return; + hevc_await_progress(s, ref0, ¤t_mv.mv[0], y0, nPbH); + } + if (current_mv.pred_flag & PF_L1) { + ref1 = refPicList[1].ref[current_mv.ref_idx[1]]; + if (!ref1 || !ref1->f) + return; + hevc_await_progress(s, ref1, ¤t_mv.mv[1], y0, nPbH); + } + + if (current_mv.pred_flag == PF_L0) { + int x0_c = x0 >> sps->hshift[1]; + int y0_c = y0 >> sps->vshift[1]; + int nPbW_c = nPbW >> sps->hshift[1]; + int nPbH_c = nPbH >> sps->vshift[1]; + + luma_mc_uni(lc, pps, sps, dst0, linesize[0], ref0->f, + ¤t_mv.mv[0], x0, y0, nPbW, nPbH, + s->sh.luma_weight_l0[current_mv.ref_idx[0]], + s->sh.luma_offset_l0[current_mv.ref_idx[0]]); + + if (sps->chroma_format_idc) { + chroma_mc_uni(lc, pps, sps, dst1, linesize[1], ref0->f->data[1], ref0->f->linesize[1], + 0, x0_c, y0_c, nPbW_c, nPbH_c, ¤t_mv, + s->sh.chroma_weight_l0[current_mv.ref_idx[0]][0], s->sh.chroma_offset_l0[current_mv.ref_idx[0]][0]); + chroma_mc_uni(lc, pps, sps, dst2, linesize[2], ref0->f->data[2], ref0->f->linesize[2], + 0, x0_c, y0_c, nPbW_c, nPbH_c, ¤t_mv, + s->sh.chroma_weight_l0[current_mv.ref_idx[0]][1], s->sh.chroma_offset_l0[current_mv.ref_idx[0]][1]); + } + } else if (current_mv.pred_flag == PF_L1) { + int x0_c = x0 >> sps->hshift[1]; + int y0_c = y0 >> sps->vshift[1]; + int nPbW_c = nPbW >> sps->hshift[1]; + int nPbH_c = nPbH >> sps->vshift[1]; + + luma_mc_uni(lc, pps, sps, dst0, linesize[0], ref1->f, + ¤t_mv.mv[1], x0, y0, nPbW, nPbH, + s->sh.luma_weight_l1[current_mv.ref_idx[1]], + s->sh.luma_offset_l1[current_mv.ref_idx[1]]); + + if (sps->chroma_format_idc) { + chroma_mc_uni(lc, pps, sps, dst1, linesize[1], ref1->f->data[1], ref1->f->linesize[1], + 1, x0_c, y0_c, nPbW_c, nPbH_c, ¤t_mv, + s->sh.chroma_weight_l1[current_mv.ref_idx[1]][0], s->sh.chroma_offset_l1[current_mv.ref_idx[1]][0]); + + chroma_mc_uni(lc, pps, sps, dst2, linesize[2], ref1->f->data[2], ref1->f->linesize[2], + 1, x0_c, y0_c, nPbW_c, nPbH_c, ¤t_mv, + s->sh.chroma_weight_l1[current_mv.ref_idx[1]][1], s->sh.chroma_offset_l1[current_mv.ref_idx[1]][1]); + } + } else if (current_mv.pred_flag == PF_BI) { + int x0_c = x0 >> sps->hshift[1]; + int y0_c = y0 >> sps->vshift[1]; + int nPbW_c = nPbW >> sps->hshift[1]; + int nPbH_c = nPbH >> sps->vshift[1]; + + luma_mc_bi(lc, pps, sps, dst0, linesize[0], ref0->f, + ¤t_mv.mv[0], x0, y0, nPbW, nPbH, + ref1->f, ¤t_mv.mv[1], ¤t_mv); + + if (sps->chroma_format_idc) { + chroma_mc_bi(lc, pps, sps, dst1, linesize[1], ref0->f, ref1->f, + x0_c, y0_c, nPbW_c, nPbH_c, ¤t_mv, 0); + + chroma_mc_bi(lc, pps, sps, dst2, linesize[2], ref0->f, ref1->f, + x0_c, y0_c, nPbW_c, nPbH_c, ¤t_mv, 1); + } + } +} + +/** + * 8.4.1 + */ +static int luma_intra_pred_mode(HEVCLocalContext *lc, const HEVCLayerContext *l, + const HEVCSPS *sps, + int x0, int y0, int pu_size, + int prev_intra_luma_pred_flag) +{ + const HEVCContext *const s = lc->parent; + int x_pu = x0 >> sps->log2_min_pu_size; + int y_pu = y0 >> sps->log2_min_pu_size; + int min_pu_width = sps->min_pu_width; + int size_in_pus = pu_size >> sps->log2_min_pu_size; + int x0b = av_zero_extend(x0, sps->log2_ctb_size); + int y0b = av_zero_extend(y0, sps->log2_ctb_size); + + int cand_up = (lc->ctb_up_flag || y0b) ? + l->tab_ipm[(y_pu - 1) * min_pu_width + x_pu] : INTRA_DC; + int cand_left = (lc->ctb_left_flag || x0b) ? + l->tab_ipm[y_pu * min_pu_width + x_pu - 1] : INTRA_DC; + + int y_ctb = (y0 >> (sps->log2_ctb_size)) << (sps->log2_ctb_size); + + MvField *tab_mvf = s->cur_frame->tab_mvf; + int intra_pred_mode; + int candidate[3]; + int i, j; + + // intra_pred_mode prediction does not cross vertical CTB boundaries + if ((y0 - 1) < y_ctb) + cand_up = INTRA_DC; + + if (cand_left == cand_up) { + if (cand_left < 2) { + candidate[0] = INTRA_PLANAR; + candidate[1] = INTRA_DC; + candidate[2] = INTRA_ANGULAR_26; + } else { + candidate[0] = cand_left; + candidate[1] = 2 + ((cand_left - 2 - 1 + 32) & 31); + candidate[2] = 2 + ((cand_left - 2 + 1) & 31); + } + } else { + candidate[0] = cand_left; + candidate[1] = cand_up; + if (candidate[0] != INTRA_PLANAR && candidate[1] != INTRA_PLANAR) { + candidate[2] = INTRA_PLANAR; + } else if (candidate[0] != INTRA_DC && candidate[1] != INTRA_DC) { + candidate[2] = INTRA_DC; + } else { + candidate[2] = INTRA_ANGULAR_26; + } + } + + if (prev_intra_luma_pred_flag) { + intra_pred_mode = candidate[lc->pu.mpm_idx]; + } else { + if (candidate[0] > candidate[1]) + FFSWAP(uint8_t, candidate[0], candidate[1]); + if (candidate[0] > candidate[2]) + FFSWAP(uint8_t, candidate[0], candidate[2]); + if (candidate[1] > candidate[2]) + FFSWAP(uint8_t, candidate[1], candidate[2]); + + intra_pred_mode = lc->pu.rem_intra_luma_pred_mode; + for (i = 0; i < 3; i++) + if (intra_pred_mode >= candidate[i]) + intra_pred_mode++; + } + + /* write the intra prediction units into the mv array */ + if (!size_in_pus) + size_in_pus = 1; + for (i = 0; i < size_in_pus; i++) { + memset(&l->tab_ipm[(y_pu + i) * min_pu_width + x_pu], + intra_pred_mode, size_in_pus); + + for (j = 0; j < size_in_pus; j++) { + tab_mvf[(y_pu + j) * min_pu_width + x_pu + i].pred_flag = PF_INTRA; + } + } + + return intra_pred_mode; +} + +static av_always_inline void set_ct_depth(const HEVCSPS *sps, uint8_t *tab_ct_depth, + int x0, int y0, + int log2_cb_size, int ct_depth) +{ + int length = (1 << log2_cb_size) >> sps->log2_min_cb_size; + int x_cb = x0 >> sps->log2_min_cb_size; + int y_cb = y0 >> sps->log2_min_cb_size; + int y; + + for (y = 0; y < length; y++) + memset(&tab_ct_depth[(y_cb + y) * sps->min_cb_width + x_cb], + ct_depth, length); +} + +static const uint8_t tab_mode_idx[] = { + 0, 1, 2, 2, 2, 2, 3, 5, 7, 8, 10, 12, 13, 15, 17, 18, 19, 20, + 21, 22, 23, 23, 24, 24, 25, 25, 26, 27, 27, 28, 28, 29, 29, 30, 31}; + +static void intra_prediction_unit(HEVCLocalContext *lc, + const HEVCLayerContext *l, const HEVCSPS *sps, + int x0, int y0, + int log2_cb_size) +{ + static const uint8_t intra_chroma_table[4] = { 0, 26, 10, 1 }; + uint8_t prev_intra_luma_pred_flag[4]; + int split = lc->cu.part_mode == PART_NxN; + int pb_size = (1 << log2_cb_size) >> split; + int side = split + 1; + int chroma_mode; + int i, j; + + for (i = 0; i < side; i++) + for (j = 0; j < side; j++) + prev_intra_luma_pred_flag[2 * i + j] = ff_hevc_prev_intra_luma_pred_flag_decode(lc); + + for (i = 0; i < side; i++) { + for (j = 0; j < side; j++) { + if (prev_intra_luma_pred_flag[2 * i + j]) + lc->pu.mpm_idx = ff_hevc_mpm_idx_decode(lc); + else + lc->pu.rem_intra_luma_pred_mode = ff_hevc_rem_intra_luma_pred_mode_decode(lc); + + lc->pu.intra_pred_mode[2 * i + j] = + luma_intra_pred_mode(lc, l, sps, + x0 + pb_size * j, y0 + pb_size * i, pb_size, + prev_intra_luma_pred_flag[2 * i + j]); + } + } + + if (sps->chroma_format_idc == 3) { + for (i = 0; i < side; i++) { + for (j = 0; j < side; j++) { + lc->pu.chroma_mode_c[2 * i + j] = chroma_mode = ff_hevc_intra_chroma_pred_mode_decode(lc); + if (chroma_mode != 4) { + if (lc->pu.intra_pred_mode[2 * i + j] == intra_chroma_table[chroma_mode]) + lc->pu.intra_pred_mode_c[2 * i + j] = 34; + else + lc->pu.intra_pred_mode_c[2 * i + j] = intra_chroma_table[chroma_mode]; + } else { + lc->pu.intra_pred_mode_c[2 * i + j] = lc->pu.intra_pred_mode[2 * i + j]; + } + } + } + } else if (sps->chroma_format_idc == 2) { + int mode_idx; + lc->pu.chroma_mode_c[0] = chroma_mode = ff_hevc_intra_chroma_pred_mode_decode(lc); + if (chroma_mode != 4) { + if (lc->pu.intra_pred_mode[0] == intra_chroma_table[chroma_mode]) + mode_idx = 34; + else + mode_idx = intra_chroma_table[chroma_mode]; + } else { + mode_idx = lc->pu.intra_pred_mode[0]; + } + lc->pu.intra_pred_mode_c[0] = tab_mode_idx[mode_idx]; + } else if (sps->chroma_format_idc != 0) { + chroma_mode = ff_hevc_intra_chroma_pred_mode_decode(lc); + if (chroma_mode != 4) { + if (lc->pu.intra_pred_mode[0] == intra_chroma_table[chroma_mode]) + lc->pu.intra_pred_mode_c[0] = 34; + else + lc->pu.intra_pred_mode_c[0] = intra_chroma_table[chroma_mode]; + } else { + lc->pu.intra_pred_mode_c[0] = lc->pu.intra_pred_mode[0]; + } + } +} + +static void intra_prediction_unit_default_value(HEVCLocalContext *lc, + const HEVCLayerContext *l, + const HEVCSPS *sps, + int x0, int y0, + int log2_cb_size) +{ + const HEVCContext *const s = lc->parent; + int pb_size = 1 << log2_cb_size; + int size_in_pus = pb_size >> sps->log2_min_pu_size; + int min_pu_width = sps->min_pu_width; + MvField *tab_mvf = s->cur_frame->tab_mvf; + int x_pu = x0 >> sps->log2_min_pu_size; + int y_pu = y0 >> sps->log2_min_pu_size; + int j, k; + + if (size_in_pus == 0) + size_in_pus = 1; + for (j = 0; j < size_in_pus; j++) + memset(&l->tab_ipm[(y_pu + j) * min_pu_width + x_pu], INTRA_DC, size_in_pus); + if (lc->cu.pred_mode == MODE_INTRA) + for (j = 0; j < size_in_pus; j++) + for (k = 0; k < size_in_pus; k++) + tab_mvf[(y_pu + j) * min_pu_width + x_pu + k].pred_flag = PF_INTRA; +} + +static int hls_coding_unit(HEVCLocalContext *lc, const HEVCContext *s, + const HEVCLayerContext *l, + const HEVCPPS *pps, const HEVCSPS *sps, + int x0, int y0, int log2_cb_size) +{ + int cb_size = 1 << log2_cb_size; + int log2_min_cb_size = sps->log2_min_cb_size; + int length = cb_size >> log2_min_cb_size; + int min_cb_width = sps->min_cb_width; + int x_cb = x0 >> log2_min_cb_size; + int y_cb = y0 >> log2_min_cb_size; + int idx = log2_cb_size - 2; + int qp_block_mask = (1 << (sps->log2_ctb_size - pps->diff_cu_qp_delta_depth)) - 1; + int x, y, ret; + + lc->cu.x = x0; + lc->cu.y = y0; + lc->cu.pred_mode = MODE_INTRA; + lc->cu.part_mode = PART_2Nx2N; + lc->cu.intra_split_flag = 0; + + SAMPLE_CTB(l->skip_flag, x_cb, y_cb) = 0; + for (x = 0; x < 4; x++) + lc->pu.intra_pred_mode[x] = 1; + if (pps->transquant_bypass_enable_flag) { + lc->cu.cu_transquant_bypass_flag = ff_hevc_cu_transquant_bypass_flag_decode(lc); + if (lc->cu.cu_transquant_bypass_flag) + set_deblocking_bypass(l->is_pcm, sps, x0, y0, log2_cb_size); + } else + lc->cu.cu_transquant_bypass_flag = 0; + + if (s->sh.slice_type != HEVC_SLICE_I) { + const int x0b = av_zero_extend(x0, sps->log2_ctb_size); + const int y0b = av_zero_extend(y0, sps->log2_ctb_size); + uint8_t skip_flag = ff_hevc_skip_flag_decode(lc, l->skip_flag, + x0b, y0b, x_cb, y_cb, + min_cb_width); + + x = y_cb * min_cb_width + x_cb; + for (y = 0; y < length; y++) { + memset(&l->skip_flag[x], skip_flag, length); + x += min_cb_width; + } + lc->cu.pred_mode = skip_flag ? MODE_SKIP : MODE_INTER; + } else { + x = y_cb * min_cb_width + x_cb; + for (y = 0; y < length; y++) { + memset(&l->skip_flag[x], 0, length); + x += min_cb_width; + } + } + + if (SAMPLE_CTB(l->skip_flag, x_cb, y_cb)) { + hls_prediction_unit(lc, l, pps, sps, + x0, y0, cb_size, cb_size, log2_cb_size, 0, idx); + intra_prediction_unit_default_value(lc, l, sps, x0, y0, log2_cb_size); + + if (!s->sh.disable_deblocking_filter_flag) + ff_hevc_deblocking_boundary_strengths(lc, l, pps, x0, y0, log2_cb_size); + } else { + int pcm_flag = 0; + + if (s->sh.slice_type != HEVC_SLICE_I) + lc->cu.pred_mode = ff_hevc_pred_mode_decode(lc); + if (lc->cu.pred_mode != MODE_INTRA || + log2_cb_size == sps->log2_min_cb_size) { + lc->cu.part_mode = ff_hevc_part_mode_decode(lc, sps, log2_cb_size); + lc->cu.intra_split_flag = lc->cu.part_mode == PART_NxN && + lc->cu.pred_mode == MODE_INTRA; + } + + if (lc->cu.pred_mode == MODE_INTRA) { + if (lc->cu.part_mode == PART_2Nx2N && sps->pcm_enabled && + log2_cb_size >= sps->pcm.log2_min_pcm_cb_size && + log2_cb_size <= sps->pcm.log2_max_pcm_cb_size) { + pcm_flag = ff_hevc_pcm_flag_decode(lc); + } + if (pcm_flag) { + intra_prediction_unit_default_value(lc, l, sps, x0, y0, log2_cb_size); + ret = hls_pcm_sample(lc, l, pps, x0, y0, log2_cb_size); + if (sps->pcm_loop_filter_disabled) + set_deblocking_bypass(l->is_pcm, sps, x0, y0, log2_cb_size); + + if (ret < 0) + return ret; + } else { + intra_prediction_unit(lc, l, sps, x0, y0, log2_cb_size); + } + } else { + intra_prediction_unit_default_value(lc, l, sps, x0, y0, log2_cb_size); + switch (lc->cu.part_mode) { + case PART_2Nx2N: + hls_prediction_unit(lc, l, pps, sps, + x0, y0, cb_size, cb_size, log2_cb_size, 0, idx); + break; + case PART_2NxN: + hls_prediction_unit(lc, l, pps, sps, + x0, y0, cb_size, cb_size / 2, log2_cb_size, 0, idx); + hls_prediction_unit(lc, l, pps, sps, + x0, y0 + cb_size / 2, cb_size, cb_size / 2, log2_cb_size, 1, idx); + break; + case PART_Nx2N: + hls_prediction_unit(lc, l, pps, sps, + x0, y0, cb_size / 2, cb_size, log2_cb_size, 0, idx - 1); + hls_prediction_unit(lc, l, pps, sps, + x0 + cb_size / 2, y0, cb_size / 2, cb_size, log2_cb_size, 1, idx - 1); + break; + case PART_2NxnU: + hls_prediction_unit(lc, l, pps, sps, + x0, y0, cb_size, cb_size / 4, log2_cb_size, 0, idx); + hls_prediction_unit(lc, l, pps, sps, + x0, y0 + cb_size / 4, cb_size, cb_size * 3 / 4, log2_cb_size, 1, idx); + break; + case PART_2NxnD: + hls_prediction_unit(lc, l, pps, sps, + x0, y0, cb_size, cb_size * 3 / 4, log2_cb_size, 0, idx); + hls_prediction_unit(lc, l, pps, sps, + x0, y0 + cb_size * 3 / 4, cb_size, cb_size / 4, log2_cb_size, 1, idx); + break; + case PART_nLx2N: + hls_prediction_unit(lc, l, pps, sps, + x0, y0, cb_size / 4, cb_size, log2_cb_size, 0, idx - 2); + hls_prediction_unit(lc, l, pps, sps, + x0 + cb_size / 4, y0, cb_size * 3 / 4, cb_size, log2_cb_size, 1, idx - 2); + break; + case PART_nRx2N: + hls_prediction_unit(lc, l, pps, sps, + x0, y0, cb_size * 3 / 4, cb_size, log2_cb_size, 0, idx - 2); + hls_prediction_unit(lc, l, pps, sps, + x0 + cb_size * 3 / 4, y0, cb_size / 4, cb_size, log2_cb_size, 1, idx - 2); + break; + case PART_NxN: + hls_prediction_unit(lc, l, pps, sps, + x0, y0, cb_size / 2, cb_size / 2, log2_cb_size, 0, idx - 1); + hls_prediction_unit(lc, l, pps, sps, + x0 + cb_size / 2, y0, cb_size / 2, cb_size / 2, log2_cb_size, 1, idx - 1); + hls_prediction_unit(lc, l, pps, sps, + x0, y0 + cb_size / 2, cb_size / 2, cb_size / 2, log2_cb_size, 2, idx - 1); + hls_prediction_unit(lc, l, pps, sps, + x0 + cb_size / 2, y0 + cb_size / 2, cb_size / 2, cb_size / 2, log2_cb_size, 3, idx - 1); + break; + } + } + + if (!pcm_flag) { + int rqt_root_cbf = 1; + + if (lc->cu.pred_mode != MODE_INTRA && + !(lc->cu.part_mode == PART_2Nx2N && lc->pu.merge_flag)) { + rqt_root_cbf = ff_hevc_no_residual_syntax_flag_decode(lc); + } + if (rqt_root_cbf) { + const static int cbf[2] = { 0 }; + lc->cu.max_trafo_depth = lc->cu.pred_mode == MODE_INTRA ? + sps->max_transform_hierarchy_depth_intra + lc->cu.intra_split_flag : + sps->max_transform_hierarchy_depth_inter; + ret = hls_transform_tree(lc, l, pps, sps, x0, y0, x0, y0, x0, y0, + log2_cb_size, + log2_cb_size, 0, 0, cbf, cbf); + if (ret < 0) + return ret; + } else { + if (!s->sh.disable_deblocking_filter_flag) + ff_hevc_deblocking_boundary_strengths(lc, l, pps, x0, y0, log2_cb_size); + } + } + } + + if (pps->cu_qp_delta_enabled_flag && lc->tu.is_cu_qp_delta_coded == 0) + ff_hevc_set_qPy(lc, l, pps, x0, y0, log2_cb_size); + + x = y_cb * min_cb_width + x_cb; + for (y = 0; y < length; y++) { + memset(&l->qp_y_tab[x], lc->qp_y, length); + x += min_cb_width; + } + + if(((x0 + (1<qPy_pred = lc->qp_y; + } + + set_ct_depth(sps, l->tab_ct_depth, x0, y0, log2_cb_size, lc->ct_depth); + + return 0; +} + +static int hls_coding_quadtree(HEVCLocalContext *lc, + const HEVCLayerContext *l, + const HEVCPPS *pps, const HEVCSPS *sps, + int x0, int y0, + int log2_cb_size, int cb_depth) +{ + const HEVCContext *const s = lc->parent; + const int cb_size = 1 << log2_cb_size; + int ret; + int split_cu; + + lc->ct_depth = cb_depth; + if (x0 + cb_size <= sps->width && + y0 + cb_size <= sps->height && + log2_cb_size > sps->log2_min_cb_size) { + split_cu = ff_hevc_split_coding_unit_flag_decode(lc, l->tab_ct_depth, + sps, cb_depth, x0, y0); + } else { + split_cu = (log2_cb_size > sps->log2_min_cb_size); + } + if (pps->cu_qp_delta_enabled_flag && + log2_cb_size >= sps->log2_ctb_size - pps->diff_cu_qp_delta_depth) { + lc->tu.is_cu_qp_delta_coded = 0; + lc->tu.cu_qp_delta = 0; + } + + if (s->sh.cu_chroma_qp_offset_enabled_flag && + log2_cb_size >= sps->log2_ctb_size - pps->diff_cu_chroma_qp_offset_depth) { + lc->tu.is_cu_chroma_qp_offset_coded = 0; + } + + if (split_cu) { + int qp_block_mask = (1 << (sps->log2_ctb_size - pps->diff_cu_qp_delta_depth)) - 1; + const int cb_size_split = cb_size >> 1; + const int x1 = x0 + cb_size_split; + const int y1 = y0 + cb_size_split; + + int more_data = 0; + + more_data = hls_coding_quadtree(lc, l, pps, sps, + x0, y0, log2_cb_size - 1, cb_depth + 1); + if (more_data < 0) + return more_data; + + if (more_data && x1 < sps->width) { + more_data = hls_coding_quadtree(lc, l, pps, sps, + x1, y0, log2_cb_size - 1, cb_depth + 1); + if (more_data < 0) + return more_data; + } + if (more_data && y1 < sps->height) { + more_data = hls_coding_quadtree(lc, l, pps, sps, + x0, y1, log2_cb_size - 1, cb_depth + 1); + if (more_data < 0) + return more_data; + } + if (more_data && x1 < sps->width && + y1 < sps->height) { + more_data = hls_coding_quadtree(lc, l, pps, sps, + x1, y1, log2_cb_size - 1, cb_depth + 1); + if (more_data < 0) + return more_data; + } + + if(((x0 + (1<qPy_pred = lc->qp_y; + + if (more_data) + return ((x1 + cb_size_split) < sps->width || + (y1 + cb_size_split) < sps->height); + else + return 0; + } else { + ret = hls_coding_unit(lc, s, l, pps, sps, x0, y0, log2_cb_size); + if (ret < 0) + return ret; + if ((!((x0 + cb_size) % + (1 << (sps->log2_ctb_size))) || + (x0 + cb_size >= sps->width)) && + (!((y0 + cb_size) % + (1 << (sps->log2_ctb_size))) || + (y0 + cb_size >= sps->height))) { + int end_of_slice_flag = ff_hevc_end_of_slice_flag_decode(lc); + return !end_of_slice_flag; + } else { + return 1; + } + } + + return 0; +} + +static void hls_decode_neighbour(HEVCLocalContext *lc, + const HEVCLayerContext *l, + const HEVCPPS *pps, const HEVCSPS *sps, + int x_ctb, int y_ctb, int ctb_addr_ts) +{ + const HEVCContext *const s = lc->parent; + int ctb_size = 1 << sps->log2_ctb_size; + int ctb_addr_rs = pps->ctb_addr_ts_to_rs[ctb_addr_ts]; + int ctb_addr_in_slice = ctb_addr_rs - s->sh.slice_addr; + + l->tab_slice_address[ctb_addr_rs] = s->sh.slice_addr; + + if (pps->entropy_coding_sync_enabled_flag) { + if (x_ctb == 0 && (y_ctb & (ctb_size - 1)) == 0) + lc->first_qp_group = 1; + lc->end_of_tiles_x = sps->width; + } else if (pps->tiles_enabled_flag) { + if (ctb_addr_ts && pps->tile_id[ctb_addr_ts] != pps->tile_id[ctb_addr_ts - 1]) { + int idxX = pps->col_idxX[x_ctb >> sps->log2_ctb_size]; + lc->end_of_tiles_x = x_ctb + (pps->column_width[idxX] << sps->log2_ctb_size); + lc->first_qp_group = 1; + } + } else { + lc->end_of_tiles_x = sps->width; + } + + lc->end_of_tiles_y = FFMIN(y_ctb + ctb_size, sps->height); + + lc->boundary_flags = 0; + if (pps->tiles_enabled_flag) { + if (x_ctb > 0 && pps->tile_id[ctb_addr_ts] != pps->tile_id[pps->ctb_addr_rs_to_ts[ctb_addr_rs - 1]]) + lc->boundary_flags |= BOUNDARY_LEFT_TILE; + if (x_ctb > 0 && l->tab_slice_address[ctb_addr_rs] != l->tab_slice_address[ctb_addr_rs - 1]) + lc->boundary_flags |= BOUNDARY_LEFT_SLICE; + if (y_ctb > 0 && pps->tile_id[ctb_addr_ts] != pps->tile_id[pps->ctb_addr_rs_to_ts[ctb_addr_rs - sps->ctb_width]]) + lc->boundary_flags |= BOUNDARY_UPPER_TILE; + if (y_ctb > 0 && l->tab_slice_address[ctb_addr_rs] != l->tab_slice_address[ctb_addr_rs - sps->ctb_width]) + lc->boundary_flags |= BOUNDARY_UPPER_SLICE; + } else { + if (ctb_addr_in_slice <= 0) + lc->boundary_flags |= BOUNDARY_LEFT_SLICE; + if (ctb_addr_in_slice < sps->ctb_width) + lc->boundary_flags |= BOUNDARY_UPPER_SLICE; + } + + lc->ctb_left_flag = ((x_ctb > 0) && (ctb_addr_in_slice > 0) && !(lc->boundary_flags & BOUNDARY_LEFT_TILE)); + lc->ctb_up_flag = ((y_ctb > 0) && (ctb_addr_in_slice >= sps->ctb_width) && !(lc->boundary_flags & BOUNDARY_UPPER_TILE)); + lc->ctb_up_right_flag = ((y_ctb > 0) && (ctb_addr_in_slice+1 >= sps->ctb_width) && (pps->tile_id[ctb_addr_ts] == pps->tile_id[pps->ctb_addr_rs_to_ts[ctb_addr_rs+1 - sps->ctb_width]])); + lc->ctb_up_left_flag = ((x_ctb > 0) && (y_ctb > 0) && (ctb_addr_in_slice-1 >= sps->ctb_width) && (pps->tile_id[ctb_addr_ts] == pps->tile_id[pps->ctb_addr_rs_to_ts[ctb_addr_rs-1 - sps->ctb_width]])); +} + +static int hls_decode_entry(HEVCContext *s, GetBitContext *gb) +{ + HEVCLocalContext *const lc = &s->local_ctx[0]; + const HEVCLayerContext *const l = &s->layers[s->cur_layer]; + const HEVCPPS *const pps = s->pps; + const HEVCSPS *const sps = pps->sps; + const uint8_t *slice_data = gb->buffer + s->sh.data_offset; + const size_t slice_size = get_bits_bytesize(gb, 1) - s->sh.data_offset; + int ctb_size = 1 << sps->log2_ctb_size; + int more_data = 1; + int x_ctb = 0; + int y_ctb = 0; + int ctb_addr_ts = pps->ctb_addr_rs_to_ts[s->sh.slice_ctb_addr_rs]; + int ret; + + while (more_data && ctb_addr_ts < sps->ctb_size) { + int ctb_addr_rs = pps->ctb_addr_ts_to_rs[ctb_addr_ts]; + + x_ctb = (ctb_addr_rs % ((sps->width + ctb_size - 1) >> sps->log2_ctb_size)) << sps->log2_ctb_size; + y_ctb = (ctb_addr_rs / ((sps->width + ctb_size - 1) >> sps->log2_ctb_size)) << sps->log2_ctb_size; + hls_decode_neighbour(lc, l, pps, sps, x_ctb, y_ctb, ctb_addr_ts); + + ret = ff_hevc_cabac_init(lc, pps, ctb_addr_ts, slice_data, slice_size, 0); + if (ret < 0) { + l->tab_slice_address[ctb_addr_rs] = -1; + return ret; + } + + hls_sao_param(lc, l, pps, sps, + x_ctb >> sps->log2_ctb_size, y_ctb >> sps->log2_ctb_size); + + l->deblock[ctb_addr_rs].beta_offset = s->sh.beta_offset; + l->deblock[ctb_addr_rs].tc_offset = s->sh.tc_offset; + l->filter_slice_edges[ctb_addr_rs] = s->sh.slice_loop_filter_across_slices_enabled_flag; + + more_data = hls_coding_quadtree(lc, l, pps, sps, x_ctb, y_ctb, sps->log2_ctb_size, 0); + if (more_data < 0) { + l->tab_slice_address[ctb_addr_rs] = -1; + return more_data; + } + + + ctb_addr_ts++; + ff_hevc_save_states(lc, pps, ctb_addr_ts); + ff_hevc_hls_filters(lc, l, pps, x_ctb, y_ctb, ctb_size); + } + + if (x_ctb + ctb_size >= sps->width && + y_ctb + ctb_size >= sps->height) + ff_hevc_hls_filter(lc, l, pps, x_ctb, y_ctb, ctb_size); + + return ctb_addr_ts; +} + +static int hls_decode_entry_wpp(AVCodecContext *avctx, void *hevc_lclist, + int job, int thread) +{ + HEVCLocalContext *lc = &((HEVCLocalContext*)hevc_lclist)[thread]; + const HEVCContext *const s = lc->parent; + const HEVCLayerContext *const l = &s->layers[s->cur_layer]; + const HEVCPPS *const pps = s->pps; + const HEVCSPS *const sps = pps->sps; + int ctb_size = 1 << sps->log2_ctb_size; + int more_data = 1; + int ctb_row = job; + int ctb_addr_rs = s->sh.slice_ctb_addr_rs + ctb_row * ((sps->width + ctb_size - 1) >> sps->log2_ctb_size); + int ctb_addr_ts = pps->ctb_addr_rs_to_ts[ctb_addr_rs]; + + const uint8_t *data = s->data + s->sh.offset[ctb_row]; + const size_t data_size = s->sh.size[ctb_row]; + + int progress = 0; + + int ret; + + if (ctb_row) + ff_init_cabac_decoder(&lc->cc, data, data_size); + + while(more_data && ctb_addr_ts < sps->ctb_size) { + int x_ctb = (ctb_addr_rs % sps->ctb_width) << sps->log2_ctb_size; + int y_ctb = (ctb_addr_rs / sps->ctb_width) << sps->log2_ctb_size; + + hls_decode_neighbour(lc, l, pps, sps, x_ctb, y_ctb, ctb_addr_ts); + + if (ctb_row) + ff_thread_progress_await(&s->wpp_progress[ctb_row - 1], + progress + SHIFT_CTB_WPP + 1); + + /* atomic_load's prototype requires a pointer to non-const atomic variable + * (due to implementations via mutexes, where reads involve writes). + * Of course, casting const away here is nevertheless safe. */ + if (atomic_load((atomic_int*)&s->wpp_err)) { + ff_thread_progress_report(&s->wpp_progress[ctb_row], INT_MAX); + return 0; + } + + ret = ff_hevc_cabac_init(lc, pps, ctb_addr_ts, data, data_size, 1); + if (ret < 0) + goto error; + hls_sao_param(lc, l, pps, sps, + x_ctb >> sps->log2_ctb_size, y_ctb >> sps->log2_ctb_size); + + l->deblock[ctb_addr_rs].beta_offset = s->sh.beta_offset; + l->deblock[ctb_addr_rs].tc_offset = s->sh.tc_offset; + l->filter_slice_edges[ctb_addr_rs] = s->sh.slice_loop_filter_across_slices_enabled_flag; + + more_data = hls_coding_quadtree(lc, l, pps, sps, x_ctb, y_ctb, sps->log2_ctb_size, 0); + + if (more_data < 0) { + ret = more_data; + goto error; + } + + ctb_addr_ts++; + + ff_hevc_save_states(lc, pps, ctb_addr_ts); + ff_thread_progress_report(&s->wpp_progress[ctb_row], ++progress); + ff_hevc_hls_filters(lc, l, pps, x_ctb, y_ctb, ctb_size); + + if (!more_data && (x_ctb+ctb_size) < sps->width && ctb_row != s->sh.num_entry_point_offsets) { + /* Casting const away here is safe, because it is an atomic operation. */ + atomic_store((atomic_int*)&s->wpp_err, 1); + ff_thread_progress_report(&s->wpp_progress[ctb_row], INT_MAX); + return 0; + } + + if ((x_ctb+ctb_size) >= sps->width && (y_ctb+ctb_size) >= sps->height ) { + ff_hevc_hls_filter(lc, l, pps, x_ctb, y_ctb, ctb_size); + ff_thread_progress_report(&s->wpp_progress[ctb_row], INT_MAX); + return ctb_addr_ts; + } + ctb_addr_rs = pps->ctb_addr_ts_to_rs[ctb_addr_ts]; + x_ctb+=ctb_size; + + if(x_ctb >= sps->width) { + break; + } + } + ff_thread_progress_report(&s->wpp_progress[ctb_row], INT_MAX); + + return 0; +error: + l->tab_slice_address[ctb_addr_rs] = -1; + /* Casting const away here is safe, because it is an atomic operation. */ + atomic_store((atomic_int*)&s->wpp_err, 1); + ff_thread_progress_report(&s->wpp_progress[ctb_row], INT_MAX); + return ret; +} + +static int wpp_progress_init(HEVCContext *s, unsigned count) +{ + if (s->nb_wpp_progress < count) { + void *tmp = av_realloc_array(s->wpp_progress, count, + sizeof(*s->wpp_progress)); + if (!tmp) + return AVERROR(ENOMEM); + + s->wpp_progress = tmp; + memset(s->wpp_progress + s->nb_wpp_progress, 0, + (count - s->nb_wpp_progress) * sizeof(*s->wpp_progress)); + + for (int i = s->nb_wpp_progress; i < count; i++) { + int ret = ff_thread_progress_init(&s->wpp_progress[i], 1); + if (ret < 0) + return ret; + s->nb_wpp_progress = i + 1; + } + } + + for (int i = 0; i < count; i++) + ff_thread_progress_reset(&s->wpp_progress[i]); + + return 0; +} + +static int hls_slice_data_wpp(HEVCContext *s, const H2645NAL *nal) +{ + const HEVCPPS *const pps = s->pps; + const HEVCSPS *const sps = pps->sps; + const uint8_t *data = nal->data; + int length = nal->size; + int *ret; + int64_t offset; + int64_t startheader, cmpt = 0; + int j, res = 0; + + if (s->sh.slice_ctb_addr_rs + s->sh.num_entry_point_offsets * (int64_t)sps->ctb_width >= sps->ctb_width * (int64_t)sps->ctb_height) { + av_log(s->avctx, AV_LOG_ERROR, "WPP ctb addresses are wrong (%d %d %d %d)\n", + s->sh.slice_ctb_addr_rs, s->sh.num_entry_point_offsets, + sps->ctb_width, sps->ctb_height + ); + return AVERROR_INVALIDDATA; + } + + if (s->avctx->thread_count > s->nb_local_ctx) { + HEVCLocalContext *tmp = av_malloc_array(s->avctx->thread_count, sizeof(*s->local_ctx)); + + if (!tmp) + return AVERROR(ENOMEM); + + memcpy(tmp, s->local_ctx, sizeof(*s->local_ctx) * s->nb_local_ctx); + av_free(s->local_ctx); + s->local_ctx = tmp; + + for (unsigned i = s->nb_local_ctx; i < s->avctx->thread_count; i++) { + tmp = &s->local_ctx[i]; + + memset(tmp, 0, sizeof(*tmp)); + + tmp->logctx = s->avctx; + tmp->parent = s; + tmp->common_cabac_state = &s->cabac; + } + + s->nb_local_ctx = s->avctx->thread_count; + } + + offset = s->sh.data_offset; + + for (j = 0, cmpt = 0, startheader = offset + s->sh.entry_point_offset[0]; j < nal->skipped_bytes; j++) { + if (nal->skipped_bytes_pos[j] >= offset && nal->skipped_bytes_pos[j] < startheader) { + startheader--; + cmpt++; + } + } + + for (int i = 1; i < s->sh.num_entry_point_offsets; i++) { + offset += (s->sh.entry_point_offset[i - 1] - cmpt); + for (j = 0, cmpt = 0, startheader = offset + + s->sh.entry_point_offset[i]; j < nal->skipped_bytes; j++) { + if (nal->skipped_bytes_pos[j] >= offset && nal->skipped_bytes_pos[j] < startheader) { + startheader--; + cmpt++; + } + } + s->sh.size[i] = s->sh.entry_point_offset[i] - cmpt; + s->sh.offset[i] = offset; + + } + + offset += s->sh.entry_point_offset[s->sh.num_entry_point_offsets - 1] - cmpt; + if (length < offset) { + av_log(s->avctx, AV_LOG_ERROR, "entry_point_offset table is corrupted\n"); + return AVERROR_INVALIDDATA; + } + s->sh.size [s->sh.num_entry_point_offsets] = length - offset; + s->sh.offset[s->sh.num_entry_point_offsets] = offset; + + s->sh.offset[0] = s->sh.data_offset; + s->sh.size[0] = s->sh.offset[1] - s->sh.offset[0]; + + s->data = data; + + for (unsigned i = 1; i < s->nb_local_ctx; i++) { + s->local_ctx[i].first_qp_group = 1; + s->local_ctx[i].qp_y = s->local_ctx[0].qp_y; + } + + atomic_store(&s->wpp_err, 0); + res = wpp_progress_init(s, s->sh.num_entry_point_offsets + 1); + if (res < 0) + return res; + + ret = av_calloc(s->sh.num_entry_point_offsets + 1, sizeof(*ret)); + if (!ret) + return AVERROR(ENOMEM); + + if (pps->entropy_coding_sync_enabled_flag) + s->avctx->execute2(s->avctx, hls_decode_entry_wpp, s->local_ctx, ret, s->sh.num_entry_point_offsets + 1); + + for (int i = 0; i <= s->sh.num_entry_point_offsets; i++) + res += ret[i]; + + av_free(ret); + return res; +} + +static int decode_slice_data(HEVCContext *s, const HEVCLayerContext *l, + const H2645NAL *nal, GetBitContext *gb) +{ + const HEVCPPS *pps = s->pps; + int ret; + + if (!s->sh.first_slice_in_pic_flag) + s->slice_idx += !s->sh.dependent_slice_segment_flag; + + if (!s->sh.dependent_slice_segment_flag && s->sh.slice_type != HEVC_SLICE_I) { + ret = ff_hevc_slice_rpl(s); + if (ret < 0) { + av_log(s->avctx, AV_LOG_WARNING, + "Error constructing the reference lists for the current slice.\n"); + return ret; + } + } + + s->slice_initialized = 1; + + if (s->avctx->hwaccel) + return FF_HW_CALL(s->avctx, decode_slice, nal->raw_data, nal->raw_size); + + if (s->avctx->profile == AV_PROFILE_HEVC_SCC) { + av_log(s->avctx, AV_LOG_ERROR, + "SCC profile is not yet implemented in hevc native decoder.\n"); + return AVERROR_PATCHWELCOME; + } + + if (s->sh.dependent_slice_segment_flag) { + int ctb_addr_ts = pps->ctb_addr_rs_to_ts[s->sh.slice_ctb_addr_rs]; + int prev_rs = pps->ctb_addr_ts_to_rs[ctb_addr_ts - 1]; + if (l->tab_slice_address[prev_rs] != s->sh.slice_addr) { + av_log(s->avctx, AV_LOG_ERROR, "Previous slice segment missing\n"); + return AVERROR_INVALIDDATA; + } + } + + s->local_ctx[0].first_qp_group = !s->sh.dependent_slice_segment_flag; + + if (!pps->cu_qp_delta_enabled_flag) + s->local_ctx[0].qp_y = s->sh.slice_qp; + + s->local_ctx[0].tu.cu_qp_offset_cb = 0; + s->local_ctx[0].tu.cu_qp_offset_cr = 0; + + if (s->avctx->active_thread_type == FF_THREAD_SLICE && + s->sh.num_entry_point_offsets > 0 && + pps->num_tile_rows == 1 && pps->num_tile_columns == 1) + return hls_slice_data_wpp(s, nal); + + return hls_decode_entry(s, gb); +} + +static int set_side_data(HEVCContext *s) +{ + const HEVCSPS *sps = s->cur_frame->pps->sps; + AVFrame *out = s->cur_frame->f; + int ret; + + // Decrement the mastering display and content light level flag when IRAP + // frame has no_rasl_output_flag=1 so the side data persists for the entire + // coded video sequence. + if (IS_IRAP(s) && s->no_rasl_output_flag) { + if (s->sei.common.mastering_display.present > 0) + s->sei.common.mastering_display.present--; + + if (s->sei.common.content_light.present > 0) + s->sei.common.content_light.present--; + } + + ret = ff_h2645_sei_to_frame(out, &s->sei.common, AV_CODEC_ID_HEVC, s->avctx, + &sps->vui.common, + sps->bit_depth, sps->bit_depth_chroma, + s->cur_frame->poc /* no poc_offset in HEVC */); + if (ret < 0) + return ret; + + if (s->sei.timecode.present) { + uint32_t *tc_sd; + char tcbuf[AV_TIMECODE_STR_SIZE]; + AVFrameSideData *tcside; + ret = ff_frame_new_side_data(s->avctx, out, AV_FRAME_DATA_S12M_TIMECODE, + sizeof(uint32_t) * 4, &tcside); + if (ret < 0) + return ret; + + if (tcside) { + tc_sd = (uint32_t*)tcside->data; + tc_sd[0] = s->sei.timecode.num_clock_ts; + + for (int i = 0; i < tc_sd[0]; i++) { + int drop = s->sei.timecode.cnt_dropped_flag[i]; + int hh = s->sei.timecode.hours_value[i]; + int mm = s->sei.timecode.minutes_value[i]; + int ss = s->sei.timecode.seconds_value[i]; + int ff = s->sei.timecode.n_frames[i]; + + tc_sd[i + 1] = av_timecode_get_smpte(s->avctx->framerate, drop, hh, mm, ss, ff); + av_timecode_make_smpte_tc_string2(tcbuf, s->avctx->framerate, tc_sd[i + 1], 0, 0); + av_dict_set(&out->metadata, "timecode", tcbuf, 0); + } + } + + s->sei.timecode.num_clock_ts = 0; + } + + if (s->sei.common.itut_t35.hdr_plus) { + AVBufferRef *info_ref = av_buffer_ref(s->sei.common.itut_t35.hdr_plus); + if (!info_ref) + return AVERROR(ENOMEM); + + ret = ff_frame_new_side_data_from_buf(s->avctx, out, AV_FRAME_DATA_DYNAMIC_HDR_PLUS, &info_ref); + if (ret < 0) + return ret; + } + + if (s->rpu_buf) { + AVFrameSideData *rpu = av_frame_new_side_data_from_buf(out, AV_FRAME_DATA_DOVI_RPU_BUFFER, s->rpu_buf); + if (!rpu) + return AVERROR(ENOMEM); + + s->rpu_buf = NULL; + } + + if ((ret = ff_dovi_attach_side_data(&s->dovi_ctx, out)) < 0) + return ret; + + if (s->sei.common.itut_t35.hdr_vivid) { + if (!av_frame_side_data_add(&out->side_data, &out->nb_side_data, + AV_FRAME_DATA_DYNAMIC_HDR_VIVID, + &s->sei.common.itut_t35.hdr_vivid, + AV_FRAME_SIDE_DATA_FLAG_NEW_REF)) + return AVERROR(ENOMEM); + } + + return 0; +} + +static int find_finish_setup_nal(const HEVCContext *s) +{ + int nal_idx = 0; + + for (int i = nal_idx; i < s->pkt.nb_nals; i++) { + const H2645NAL *nal = &s->pkt.nals[i]; + const int layer_id = nal->nuh_layer_id; + GetBitContext gb = nal->gb; + + if (layer_id > HEVC_MAX_NUH_LAYER_ID || s->vps->layer_idx[layer_id] < 0 || + !(s->layers_active_decode & (1 << s->vps->layer_idx[layer_id]))) + continue; + + switch (nal->type) { + case HEVC_NAL_TRAIL_R: + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TSA_N: + case HEVC_NAL_TSA_R: + case HEVC_NAL_STSA_N: + case HEVC_NAL_STSA_R: + case HEVC_NAL_BLA_W_LP: + case HEVC_NAL_BLA_W_RADL: + case HEVC_NAL_BLA_N_LP: + case HEVC_NAL_IDR_W_RADL: + case HEVC_NAL_IDR_N_LP: + case HEVC_NAL_CRA_NUT: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RADL_R: + case HEVC_NAL_RASL_N: + case HEVC_NAL_RASL_R: + if (!get_bits1(&gb)) // first_slice_segment_in_pic_flag + continue; + av_fallthrough; + case HEVC_NAL_VPS: + case HEVC_NAL_SPS: + case HEVC_NAL_PPS: + nal_idx = i; + break; + } + } + + return nal_idx; +} + +static int hevc_frame_start(HEVCContext *s, HEVCLayerContext *l, + unsigned nal_idx) +{ + const HEVCPPS *const pps = s->ps.pps_list[s->sh.pps_id]; + const HEVCSPS *const sps = pps->sps; + int pic_size_in_ctb = ((sps->width >> sps->log2_min_cb_size) + 1) * + ((sps->height >> sps->log2_min_cb_size) + 1); + int new_sequence = (l == &s->layers[0]) && + (IS_IDR(s) || IS_BLA(s) || s->last_eos); + int prev_layers_active_decode = s->layers_active_decode; + int prev_layers_active_output = s->layers_active_output; + int ret; + + if (sps->vps != s->vps && l != &s->layers[0]) { + av_log(s->avctx, AV_LOG_ERROR, "VPS changed in a non-base layer\n"); + set_sps(s, l, NULL); + return AVERROR_INVALIDDATA; + } + + av_refstruct_replace(&s->pps, pps); + if (l->sps != sps) { + const HEVCSPS *sps_base = s->layers[0].sps; + enum AVPixelFormat pix_fmt = sps->pix_fmt; + + if (l != &s->layers[0]) { + if (!sps_base) { + av_log(s->avctx, AV_LOG_ERROR, + "Access unit starts with a non-base layer frame\n"); + return AVERROR_INVALIDDATA; + } + + // Files produced by Vision Pro lack VPS extension VUI, + // so the secondary layer has no range information. + // This check avoids failing in such a case. + if (sps_base->pix_fmt == AV_PIX_FMT_YUVJ420P && + sps->pix_fmt == AV_PIX_FMT_YUV420P && + !sps->vui.common.video_signal_type_present_flag) + pix_fmt = sps_base->pix_fmt; + + // Ignore range mismatch between base layer and alpha layer + if (ff_hevc_is_alpha_video(s) && + sps_base->pix_fmt == AV_PIX_FMT_YUV420P && + pix_fmt == AV_PIX_FMT_YUVJ420P) + pix_fmt = sps_base->pix_fmt; + + if (pix_fmt != sps_base->pix_fmt || + sps->width != sps_base->width || + sps->height != sps_base->height) { + av_log(s->avctx, AV_LOG_ERROR, + "Base/non-base layer SPS have unsupported parameter combination\n"); + return AVERROR(ENOSYS); + } + } + + ff_hevc_clear_refs(l); + + ret = set_sps(s, l, sps); + if (ret < 0) + return ret; + + if (l == &s->layers[0]) { + export_stream_params(s, sps); + + ret = get_format(s, sps); + if (ret < 0) { + set_sps(s, l, NULL); + return ret; + } + + new_sequence = 1; + } + } + + memset(l->horizontal_bs, 0, l->bs_width * l->bs_height); + memset(l->vertical_bs, 0, l->bs_width * l->bs_height); + memset(l->cbf_luma, 0, sps->min_tb_width * sps->min_tb_height); + memset(l->is_pcm, 0, (sps->min_pu_width + 1) * (sps->min_pu_height + 1)); + memset(l->tab_slice_address, -1, pic_size_in_ctb * sizeof(*l->tab_slice_address)); + + if (IS_IDR(s)) + ff_hevc_clear_refs(l); + + s->slice_idx = 0; + s->first_nal_type = s->nal_unit_type; + s->poc = s->sh.poc; + + if (IS_IRAP(s)) { + s->no_rasl_output_flag = IS_IDR(s) || IS_BLA(s) || + (s->nal_unit_type == HEVC_NAL_CRA_NUT && s->last_eos); + s->recovery_poc = HEVC_RECOVERY_END; + } + + if (s->recovery_poc != HEVC_RECOVERY_END && + s->sei.recovery_point.has_recovery_poc) { + if (s->recovery_poc == HEVC_RECOVERY_UNSPECIFIED) + s->recovery_poc = s->poc + s->sei.recovery_point.recovery_poc_cnt; + else if (s->poc >= s->recovery_poc) + s->recovery_poc = HEVC_RECOVERY_END; + } + + /* 8.3.1 */ + if (s->temporal_id == 0 && + s->nal_unit_type != HEVC_NAL_TRAIL_N && + s->nal_unit_type != HEVC_NAL_TSA_N && + s->nal_unit_type != HEVC_NAL_STSA_N && + s->nal_unit_type != HEVC_NAL_RADL_N && + s->nal_unit_type != HEVC_NAL_RADL_R && + s->nal_unit_type != HEVC_NAL_RASL_N && + s->nal_unit_type != HEVC_NAL_RASL_R) + s->poc_tid0 = s->poc; + + if (pps->tiles_enabled_flag) + s->local_ctx[0].end_of_tiles_x = pps->column_width[0] << sps->log2_ctb_size; + + if (new_sequence) { + ret = ff_hevc_output_frames(s, prev_layers_active_decode, prev_layers_active_output, + 0, 0, s->sh.no_output_of_prior_pics_flag); + if (ret < 0) + return ret; + } + + ret = export_stream_params_from_sei(s); + if (ret < 0) + return ret; + + ret = ff_hevc_set_new_ref(s, l, s->poc); + if (ret < 0) + goto fail; + + ret = ff_hevc_frame_rps(s, l); + if (ret < 0) { + av_log(s->avctx, AV_LOG_ERROR, "Error constructing the frame RPS.\n"); + goto fail; + } + + if (IS_IRAP(s)) + s->cur_frame->f->flags |= AV_FRAME_FLAG_KEY; + else + s->cur_frame->f->flags &= ~AV_FRAME_FLAG_KEY; + + s->cur_frame->needs_fg = ((s->sei.common.film_grain_characteristics && + s->sei.common.film_grain_characteristics->present) || + s->sei.common.itut_t35.aom_film_grain.enable) && + !(s->avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN) && + !s->avctx->hwaccel; + + ret = set_side_data(s); + if (ret < 0) + goto fail; + + if (s->cur_frame->needs_fg && + (s->sei.common.film_grain_characteristics && s->sei.common.film_grain_characteristics->present && + !ff_h274_film_grain_params_supported(s->sei.common.film_grain_characteristics->model_id, + s->cur_frame->f->format) || + !av_film_grain_params_select(s->cur_frame->f))) { + av_log_once(s->avctx, AV_LOG_WARNING, AV_LOG_DEBUG, &s->film_grain_warning_shown, + "Unsupported film grain parameters. Ignoring film grain.\n"); + s->cur_frame->needs_fg = 0; + } + + if (s->cur_frame->needs_fg) { + s->cur_frame->frame_grain->format = s->cur_frame->f->format; + s->cur_frame->frame_grain->width = s->cur_frame->f->width; + s->cur_frame->frame_grain->height = s->cur_frame->f->height; + if ((ret = ff_thread_get_buffer(s->avctx, s->cur_frame->frame_grain, 0)) < 0) + goto fail; + + ret = av_frame_copy_props(s->cur_frame->frame_grain, s->cur_frame->f); + if (ret < 0) + goto fail; + } + + s->cur_frame->f->pict_type = 3 - s->sh.slice_type; + + ret = ff_hevc_output_frames(s, s->layers_active_decode, s->layers_active_output, + sps->temporal_layer[sps->max_sub_layers - 1].num_reorder_pics, + sps->temporal_layer[sps->max_sub_layers - 1].max_dec_pic_buffering, 0); + if (ret < 0) + goto fail; + + if (s->avctx->hwaccel) { + AVCodecInternal *avci = s->avctx->internal; + AVPacket *avpkt = avci->in_pkt; + ret = FF_HW_CALL(s->avctx, start_frame, + avpkt->buf, NULL, 0); + if (ret < 0) + goto fail; + } + + // after starting the base-layer frame we know which layers will be decoded, + // so we can now figure out which NALUs to wait for before we can call + // ff_thread_finish_setup() + if (l == &s->layers[0]) + s->finish_setup_nal_idx = find_finish_setup_nal(s); + + if (nal_idx >= s->finish_setup_nal_idx) + ff_thread_finish_setup(s->avctx); + + return 0; + +fail: + if (l->cur_frame) + ff_hevc_unref_frame(l->cur_frame, ~0); + l->cur_frame = NULL; + s->cur_frame = s->collocated_ref = NULL; + return ret; +} + +static int verify_md5(HEVCContext *s, AVFrame *frame) +{ + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + char msg_buf[4 * (50 + 2 * 2 * 16 /* MD5-size */)]; + int pixel_shift; + int err = 0; + int i, j; + + if (!desc) + return AVERROR(EINVAL); + + pixel_shift = desc->comp[0].depth > 8; + + /* the checksums are LE, so we have to byteswap for >8bpp formats + * on BE arches */ +#if HAVE_BIGENDIAN + if (pixel_shift && !s->checksum_buf) { + av_fast_malloc(&s->checksum_buf, &s->checksum_buf_size, + FFMAX3(frame->linesize[0], frame->linesize[1], + frame->linesize[2])); + if (!s->checksum_buf) + return AVERROR(ENOMEM); + } +#endif + + msg_buf[0] = '\0'; + for (i = 0; frame->data[i]; i++) { + int width = s->avctx->coded_width; + int height = s->avctx->coded_height; + int w = (i == 1 || i == 2) ? (width >> desc->log2_chroma_w) : width; + int h = (i == 1 || i == 2) ? (height >> desc->log2_chroma_h) : height; + uint8_t md5[16]; + + av_md5_init(s->md5_ctx); + for (j = 0; j < h; j++) { + const uint8_t *src = frame->data[i] + j * frame->linesize[i]; +#if HAVE_BIGENDIAN + if (pixel_shift) { + s->bdsp.bswap16_buf((uint16_t *) s->checksum_buf, + (const uint16_t *) src, w); + src = s->checksum_buf; + } +#endif + av_md5_update(s->md5_ctx, src, w << pixel_shift); + } + av_md5_final(s->md5_ctx, md5); + +#define MD5_PRI "%016" PRIx64 "%016" PRIx64 +#define MD5_PRI_ARG(buf) AV_RB64(buf), AV_RB64((const uint8_t*)(buf) + 8) + + if (!memcmp(md5, s->sei.picture_hash.md5[i], 16)) { + av_strlcatf(msg_buf, sizeof(msg_buf), + "plane %d - correct " MD5_PRI "; ", + i, MD5_PRI_ARG(md5)); + } else { + av_strlcatf(msg_buf, sizeof(msg_buf), + "mismatching checksum of plane %d - " MD5_PRI " != " MD5_PRI "; ", + i, MD5_PRI_ARG(md5), MD5_PRI_ARG(s->sei.picture_hash.md5[i])); + err = AVERROR_INVALIDDATA; + } + } + + av_log(s->avctx, err < 0 ? AV_LOG_ERROR : AV_LOG_DEBUG, + "Verifying checksum for frame with POC %d: %s\n", + s->poc, msg_buf); + + return err; + } + +static int hevc_frame_end(HEVCContext *s, HEVCLayerContext *l) +{ + HEVCFrame *out = l->cur_frame; + const AVFilmGrainParams *fgp; + av_unused int ret; + + if (out->needs_fg) { + av_assert0(out->frame_grain->buf[0]); + fgp = av_film_grain_params_select(out->f); + switch (fgp->type) { + case AV_FILM_GRAIN_PARAMS_NONE: + av_assert0(0); + return AVERROR_BUG; + case AV_FILM_GRAIN_PARAMS_H274: + ret = ff_h274_apply_film_grain(out->frame_grain, out->f, fgp); + break; + case AV_FILM_GRAIN_PARAMS_AV1: + ret = ff_aom_apply_film_grain(out->frame_grain, out->f, fgp); + break; + } + av_assert1(ret >= 0); + } + + if (s->avctx->hwaccel) { + ret = FF_HW_SIMPLE_CALL(s->avctx, end_frame); + if (ret < 0) { + av_log(s->avctx, AV_LOG_ERROR, + "hardware accelerator failed to decode picture\n"); + return ret; + } + } else { + if (s->avctx->err_recognition & AV_EF_CRCCHECK && + s->sei.picture_hash.is_md5) { + ret = verify_md5(s, out->f); + if (ret < 0 && s->avctx->err_recognition & AV_EF_EXPLODE) + return ret; + } + } + s->sei.picture_hash.is_md5 = 0; + + av_log(s->avctx, AV_LOG_DEBUG, "Decoded frame with POC %zu/%d.\n", + l - s->layers, s->poc); + + return 0; +} + +static int decode_slice(HEVCContext *s, unsigned nal_idx, GetBitContext *gb) +{ + const int layer_idx = s->vps ? s->vps->layer_idx[s->nuh_layer_id] : 0; + HEVCLayerContext *l; + int ret; + + // skip layers not requested to be decoded + // layers_active_decode can only change while decoding a base-layer frame, + // so we can check it for non-base layers + if (layer_idx < 0 || + (s->nuh_layer_id > 0 && !(s->layers_active_decode & (1 << layer_idx)))) + return 0; + + ret = hls_slice_header(&s->sh, s, gb); + // Once hls_slice_header has been called, the context is inconsistent with the slice header + // until the context is reinitialized according to the contents of the new slice header + // at the start of decode_slice_data. + s->slice_initialized = 0; + if (ret < 0) { + return ret; + } + + if ((s->avctx->skip_frame >= AVDISCARD_BIDIR && s->sh.slice_type == HEVC_SLICE_B) || + (s->avctx->skip_frame >= AVDISCARD_NONINTRA && s->sh.slice_type != HEVC_SLICE_I) || + (s->avctx->skip_frame >= AVDISCARD_NONKEY && !IS_IRAP(s)) || + ((s->nal_unit_type == HEVC_NAL_RASL_R || s->nal_unit_type == HEVC_NAL_RASL_N) && + s->no_rasl_output_flag)) { + return 0; + } + + // switching to a new layer, mark previous layer's frame (if any) as done + if (s->cur_layer != layer_idx && + s->layers[s->cur_layer].cur_frame && + s->avctx->active_thread_type == FF_THREAD_FRAME) + ff_progress_frame_report(&s->layers[s->cur_layer].cur_frame->tf, INT_MAX); + + s->cur_layer = layer_idx; + l = &s->layers[s->cur_layer]; + + if (s->sh.first_slice_in_pic_flag) { + if (l->cur_frame) { + av_log(s->avctx, AV_LOG_ERROR, "Two slices reporting being the first in the same frame.\n"); + return AVERROR_INVALIDDATA; + } + + ret = hevc_frame_start(s, l, nal_idx); + if (ret < 0) + return ret; + } else if (!l->cur_frame) { + av_log(s->avctx, AV_LOG_ERROR, "First slice in a frame missing.\n"); + return AVERROR_INVALIDDATA; + } + + if (s->nal_unit_type != s->first_nal_type) { + av_log(s->avctx, AV_LOG_ERROR, + "Non-matching NAL types of the VCL NALUs: %d %d\n", + s->first_nal_type, s->nal_unit_type); + return AVERROR_INVALIDDATA; + } + + ret = decode_slice_data(s, l, &s->pkt.nals[nal_idx], gb); + if (ret < 0) + return ret; + + return 0; +} + +static int decode_nal_unit(HEVCContext *s, unsigned nal_idx) +{ + H2645NAL *nal = &s->pkt.nals[nal_idx]; + GetBitContext gb = nal->gb; + int ret; + + s->nal_unit_type = nal->type; + s->nuh_layer_id = nal->nuh_layer_id; + s->temporal_id = nal->temporal_id; + + if (FF_HW_HAS_CB(s->avctx, decode_params) && + (s->nal_unit_type == HEVC_NAL_VPS || + s->nal_unit_type == HEVC_NAL_SPS || + s->nal_unit_type == HEVC_NAL_PPS || + s->nal_unit_type == HEVC_NAL_SEI_PREFIX || + s->nal_unit_type == HEVC_NAL_SEI_SUFFIX)) { + ret = FF_HW_CALL(s->avctx, decode_params, + nal->type, nal->raw_data, nal->raw_size); + if (ret < 0) + goto fail; + } + + switch (s->nal_unit_type) { + case HEVC_NAL_VPS: + ret = ff_hevc_decode_nal_vps(&gb, s->avctx, &s->ps); + if (ret < 0) + goto fail; + break; + case HEVC_NAL_SPS: + ret = ff_hevc_decode_nal_sps(&gb, s->avctx, &s->ps, + nal->nuh_layer_id, s->apply_defdispwin); + if (ret < 0) + goto fail; + break; + case HEVC_NAL_PPS: + ret = ff_hevc_decode_nal_pps(&gb, s->avctx, &s->ps); + if (ret < 0) + goto fail; + break; + case HEVC_NAL_SEI_PREFIX: + case HEVC_NAL_SEI_SUFFIX: + ret = ff_hevc_decode_nal_sei(&gb, s->avctx, &s->sei, &s->ps, s->nal_unit_type); + if (ret < 0) + goto fail; + break; + case HEVC_NAL_TRAIL_R: + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TSA_N: + case HEVC_NAL_TSA_R: + case HEVC_NAL_STSA_N: + case HEVC_NAL_STSA_R: + case HEVC_NAL_BLA_W_LP: + case HEVC_NAL_BLA_W_RADL: + case HEVC_NAL_BLA_N_LP: + case HEVC_NAL_IDR_W_RADL: + case HEVC_NAL_IDR_N_LP: + case HEVC_NAL_CRA_NUT: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RADL_R: + case HEVC_NAL_RASL_N: + case HEVC_NAL_RASL_R: + ret = decode_slice(s, nal_idx, &gb); + if (ret < 0) + goto fail; + break; + case HEVC_NAL_EOS_NUT: + case HEVC_NAL_EOB_NUT: + case HEVC_NAL_AUD: + case HEVC_NAL_FD_NUT: + case HEVC_NAL_UNSPEC62: // Dolby Vision RPU + case HEVC_NAL_UNSPEC63: // Dolby Vision EL + break; + default: + av_log(s->avctx, AV_LOG_VERBOSE, + "Skipping NAL unit %d\n", s->nal_unit_type); + } + + return 0; +fail: + if (ret == AVERROR_INVALIDDATA && + !(s->avctx->err_recognition & AV_EF_EXPLODE)) { + av_log(s->avctx, AV_LOG_WARNING, + "Skipping invalid undecodable NALU: %d\n", s->nal_unit_type); + return 0; + } + return ret; +} + +static void decode_reset_recovery_point(HEVCContext *s) +{ + s->recovery_poc = HEVC_RECOVERY_UNSPECIFIED; + s->sei.recovery_point.has_recovery_poc = 0; +} + +static int decode_nal_units(HEVCContext *s, const uint8_t *buf, int length) +{ + int ret = 0; + int eos_at_start = 1; + int flags = (H2645_FLAG_IS_NALFF * !!s->is_nalff) | H2645_FLAG_SMALL_PADDING; + + s->cur_frame = s->collocated_ref = NULL; + s->last_eos = s->eos; + s->eos = 0; + s->slice_initialized = 0; + if (s->last_eos) + decode_reset_recovery_point(s); + + for (int i = 0; i < FF_ARRAY_ELEMS(s->layers); i++) { + HEVCLayerContext *l = &s->layers[i]; + l->cur_frame = NULL; + } + + /* split the input packet into NAL units, so we know the upper bound on the + * number of slices in the frame */ + ret = ff_h2645_packet_split(&s->pkt, buf, length, s->avctx, + s->nal_length_size, s->avctx->codec_id, flags); + if (ret < 0) { + av_log(s->avctx, AV_LOG_ERROR, + "Error splitting the input into NAL units.\n"); + return ret; + } + + for (int i = 0; i < s->pkt.nb_nals; i++) { + if (s->pkt.nals[i].type == HEVC_NAL_EOB_NUT || + s->pkt.nals[i].type == HEVC_NAL_EOS_NUT) { + if (eos_at_start) { + s->last_eos = 1; + decode_reset_recovery_point(s); + } else { + s->eos = 1; + } + } else { + eos_at_start = 0; + } + } + + /* + * Check for RPU delimiter. + * + * Dolby Vision RPUs masquerade as unregistered NALs of type 62. + * + * We have to do this check here an create the rpu buffer, since RPUs are appended + * to the end of an AU; they are normally the last non-EOB/EOS NAL in the AU. + */ + H2645NAL *rpu_nal = NULL; + for (int i = s->pkt.nb_nals - 1; i > 0 ; i--) { + if (s->pkt.nals[i].type == HEVC_NAL_UNSPEC62 && s->pkt.nals[i].size > 2 + && !s->pkt.nals[i].nuh_layer_id && !s->pkt.nals[i].temporal_id) { + rpu_nal = &s->pkt.nals[i]; + break; + } + } + + if (rpu_nal) { + if (s->rpu_buf) { + av_buffer_unref(&s->rpu_buf); + av_log(s->avctx, AV_LOG_WARNING, "Multiple Dolby Vision RPUs found in one AU. Skipping previous.\n"); + } + + s->rpu_buf = av_buffer_alloc(rpu_nal->raw_size - 2); + if (!s->rpu_buf) { + ret = AVERROR(ENOMEM); + goto fail; + } + memcpy(s->rpu_buf->data, rpu_nal->raw_data + 2, rpu_nal->raw_size - 2); + + ret = ff_dovi_rpu_parse(&s->dovi_ctx, rpu_nal->data + 2, rpu_nal->size - 2, + s->avctx->err_recognition); + if (ret < 0) { + av_buffer_unref(&s->rpu_buf); + av_log(s->avctx, AV_LOG_WARNING, "Error parsing DOVI NAL unit.\n"); + /* ignore */ + } + } + + /* decode the NAL units */ + for (int i = 0; i < s->pkt.nb_nals; i++) { + H2645NAL *nal = &s->pkt.nals[i]; + + if (s->avctx->skip_frame >= AVDISCARD_ALL || + (s->avctx->skip_frame >= AVDISCARD_NONREF && ff_hevc_nal_is_nonref(nal->type))) + continue; + + ret = decode_nal_unit(s, i); + if (ret < 0) { + av_log(s->avctx, AV_LOG_WARNING, + "Error parsing NAL unit #%d.\n", i); + goto fail; + } + } + +fail: + for (int i = 0; i < FF_ARRAY_ELEMS(s->layers); i++) { + HEVCLayerContext *l = &s->layers[i]; + + if (!l->cur_frame) + continue; + + if (ret >= 0) + ret = hevc_frame_end(s, l); + + if (s->avctx->active_thread_type == FF_THREAD_FRAME) + ff_progress_frame_report(&l->cur_frame->tf, INT_MAX); + } + + return ret; +} + +static int hevc_decode_extradata(HEVCContext *s, uint8_t *buf, int length, int first) +{ + int ret, i; + + ret = ff_hevc_decode_extradata(buf, length, &s->ps, &s->sei, &s->is_nalff, + &s->nal_length_size, s->avctx->err_recognition, + s->apply_defdispwin, s->avctx); + if (ret < 0) + return ret; + + /* export stream parameters from the first SPS */ + for (i = 0; i < FF_ARRAY_ELEMS(s->ps.sps_list); i++) { + if (first && s->ps.sps_list[i]) { + const HEVCSPS *sps = s->ps.sps_list[i]; + export_stream_params(s, sps); + + ret = export_multilayer(s, sps->vps); + if (ret < 0) + return ret; + + break; + } + } + + /* export stream parameters from SEI */ + ret = export_stream_params_from_sei(s); + if (ret < 0) + return ret; + + return 0; +} + +static int hevc_receive_frame(AVCodecContext *avctx, AVFrame *frame) +{ + HEVCContext *s = avctx->priv_data; + AVCodecInternal *avci = avctx->internal; + AVPacket *avpkt = avci->in_pkt; + + int ret; + uint8_t *sd; + size_t sd_size; + + s->pkt_dts = AV_NOPTS_VALUE; + + if (av_container_fifo_can_read(s->output_fifo)) + goto do_output; + + av_packet_unref(avpkt); + ret = ff_decode_get_packet(avctx, avpkt); + if (ret == AVERROR_EOF) { + ret = ff_hevc_output_frames(s, s->layers_active_decode, + s->layers_active_output, 0, 0, 0); + if (ret < 0) + return ret; + goto do_output; + } else if (ret < 0) + return ret; + + s->pkt_dts = avpkt->dts; + + sd = av_packet_get_side_data(avpkt, AV_PKT_DATA_NEW_EXTRADATA, &sd_size); + if (sd && sd_size > 0) { + ret = hevc_decode_extradata(s, sd, sd_size, 0); + if (ret < 0) + return ret; + } + + sd = av_packet_get_side_data(avpkt, AV_PKT_DATA_DOVI_CONF, &sd_size); + if (sd && sd_size >= sizeof(s->dovi_ctx.cfg)) { + int old = s->dovi_ctx.cfg.dv_profile; + s->dovi_ctx.cfg = *(AVDOVIDecoderConfigurationRecord *) sd; + if (old) + av_log(avctx, AV_LOG_DEBUG, + "New DOVI configuration record from input packet (profile %d -> %u).\n", + old, s->dovi_ctx.cfg.dv_profile); + } + + ret = decode_nal_units(s, avpkt->data, avpkt->size); + if (ret < 0) + return ret; + +do_output: + if (av_container_fifo_read(s->output_fifo, frame, 0) >= 0) { + if (!(avctx->export_side_data & AV_CODEC_EXPORT_DATA_FILM_GRAIN)) + av_frame_remove_side_data(frame, AV_FRAME_DATA_FILM_GRAIN_PARAMS); + + return 0; + } + + return avci->draining ? AVERROR_EOF : AVERROR(EAGAIN); +} + +static int hevc_ref_frame(HEVCFrame *dst, const HEVCFrame *src) +{ + int ret; + + ff_progress_frame_ref(&dst->tf, &src->tf); + + if (src->needs_fg) { + ret = av_frame_ref(dst->frame_grain, src->frame_grain); + if (ret < 0) { + ff_hevc_unref_frame(dst, ~0); + return ret; + } + dst->needs_fg = 1; + } + + dst->pps = av_refstruct_ref_c(src->pps); + dst->tab_mvf = av_refstruct_ref(src->tab_mvf); + dst->rpl_tab = av_refstruct_ref(src->rpl_tab); + dst->rpl = av_refstruct_ref(src->rpl); + dst->nb_rpl_elems = src->nb_rpl_elems; + + dst->poc = src->poc; + dst->ctb_count = src->ctb_count; + dst->flags = src->flags; + + dst->base_layer_frame = src->base_layer_frame; + + av_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); + + return 0; +} + +static av_cold int hevc_decode_free(AVCodecContext *avctx) +{ + HEVCContext *s = avctx->priv_data; + + for (int i = 0; i < FF_ARRAY_ELEMS(s->layers); i++) { + pic_arrays_free(&s->layers[i]); + av_refstruct_unref(&s->layers[i].sps); + } + + av_refstruct_unref(&s->vps); + av_refstruct_unref(&s->pps); + + ff_dovi_ctx_unref(&s->dovi_ctx); + av_buffer_unref(&s->rpu_buf); + + av_freep(&s->md5_ctx); + + av_container_fifo_free(&s->output_fifo); + + for (int layer = 0; layer < FF_ARRAY_ELEMS(s->layers); layer++) { + HEVCLayerContext *l = &s->layers[layer]; + for (int i = 0; i < FF_ARRAY_ELEMS(l->DPB); i++) { + ff_hevc_unref_frame(&l->DPB[i], ~0); + av_frame_free(&l->DPB[i].frame_grain); + } + } + + ff_hevc_ps_uninit(&s->ps); + + for (int i = 0; i < s->nb_wpp_progress; i++) + ff_thread_progress_destroy(&s->wpp_progress[i]); + av_freep(&s->wpp_progress); + + av_freep(&s->sh.entry_point_offset); + av_freep(&s->sh.offset); + av_freep(&s->sh.size); + + av_freep(&s->local_ctx); + + ff_h2645_packet_uninit(&s->pkt); + + ff_hevc_reset_sei(&s->sei); + + return 0; +} + +static av_cold int hevc_init_context(AVCodecContext *avctx) +{ + HEVCContext *s = avctx->priv_data; + + s->avctx = avctx; + + s->local_ctx = av_mallocz(sizeof(*s->local_ctx)); + if (!s->local_ctx) + return AVERROR(ENOMEM); + s->nb_local_ctx = 1; + + s->local_ctx[0].parent = s; + s->local_ctx[0].logctx = avctx; + s->local_ctx[0].common_cabac_state = &s->cabac; + + s->output_fifo = av_container_fifo_alloc_avframe(0); + if (!s->output_fifo) + return AVERROR(ENOMEM); + + for (int layer = 0; layer < FF_ARRAY_ELEMS(s->layers); layer++) { + HEVCLayerContext *l = &s->layers[layer]; + for (int i = 0; i < FF_ARRAY_ELEMS(l->DPB); i++) { + l->DPB[i].frame_grain = av_frame_alloc(); + if (!l->DPB[i].frame_grain) + return AVERROR(ENOMEM); + } + } + + s->md5_ctx = av_md5_alloc(); + if (!s->md5_ctx) + return AVERROR(ENOMEM); + + ff_bswapdsp_init(&s->bdsp); + + s->dovi_ctx.logctx = avctx; + s->eos = 0; + + ff_hevc_reset_sei(&s->sei); + + return 0; +} + +#if HAVE_THREADS +static int hevc_update_thread_context(AVCodecContext *dst, + const AVCodecContext *src) +{ + HEVCContext *s = dst->priv_data; + HEVCContext *s0 = src->priv_data; + int ret; + + for (int layer = 0; layer < FF_ARRAY_ELEMS(s->layers); layer++) { + HEVCLayerContext *l = &s->layers[layer]; + const HEVCLayerContext *l0 = &s0->layers[layer]; + for (int i = 0; i < FF_ARRAY_ELEMS(l->DPB); i++) { + ff_hevc_unref_frame(&l->DPB[i], ~0); + if (l0->DPB[i].f) { + ret = hevc_ref_frame(&l->DPB[i], &l0->DPB[i]); + if (ret < 0) + return ret; + } + } + + if (l->sps != l0->sps) { + ret = set_sps(s, l, l0->sps); + if (ret < 0) + return ret; + } + } + + for (int i = 0; i < FF_ARRAY_ELEMS(s->ps.vps_list); i++) + av_refstruct_replace(&s->ps.vps_list[i], s0->ps.vps_list[i]); + + for (int i = 0; i < FF_ARRAY_ELEMS(s->ps.sps_list); i++) + av_refstruct_replace(&s->ps.sps_list[i], s0->ps.sps_list[i]); + + for (int i = 0; i < FF_ARRAY_ELEMS(s->ps.pps_list); i++) + av_refstruct_replace(&s->ps.pps_list[i], s0->ps.pps_list[i]); + + // PPS do not persist between frames + av_refstruct_unref(&s->pps); + + s->poc_tid0 = s0->poc_tid0; + s->eos = s0->eos; + s->no_rasl_output_flag = s0->no_rasl_output_flag; + + s->is_nalff = s0->is_nalff; + s->nal_length_size = s0->nal_length_size; + s->layers_active_decode = s0->layers_active_decode; + s->layers_active_output = s0->layers_active_output; + + s->film_grain_warning_shown = s0->film_grain_warning_shown; + + if (s->nb_view_ids != s0->nb_view_ids || + memcmp(s->view_ids, s0->view_ids, sizeof(*s->view_ids) * s->nb_view_ids)) { + av_freep(&s->view_ids); + s->nb_view_ids = 0; + + if (s0->nb_view_ids) { + s->view_ids = av_memdup(s0->view_ids, s0->nb_view_ids * sizeof(*s0->view_ids)); + if (!s->view_ids) + return AVERROR(ENOMEM); + s->nb_view_ids = s0->nb_view_ids; + } + } + + ret = ff_h2645_sei_ctx_replace(&s->sei.common, &s0->sei.common); + if (ret < 0) + return ret; + + ret = av_buffer_replace(&s->sei.common.itut_t35.hdr_plus, + s0->sei.common.itut_t35.hdr_plus); + if (ret < 0) + return ret; + + ret = av_buffer_replace(&s->rpu_buf, s0->rpu_buf); + if (ret < 0) + return ret; + + ff_dovi_ctx_replace(&s->dovi_ctx, &s0->dovi_ctx); + + ret = av_buffer_replace(&s->sei.common.itut_t35.hdr_vivid, + s0->sei.common.itut_t35.hdr_vivid); + if (ret < 0) + return ret; + + s->sei.common.frame_packing = s0->sei.common.frame_packing; + s->sei.common.display_orientation = s0->sei.common.display_orientation; + s->sei.common.alternative_transfer = s0->sei.common.alternative_transfer; + s->sei.tdrdi = s0->sei.tdrdi; + s->sei.recovery_point = s0->sei.recovery_point; + s->recovery_poc = s0->recovery_poc; + + return 0; +} +#endif + +static int hevc_sei_to_context(AVCodecContext *avctx, HEVCSEI *sei) +{ + int ret; + + if (sei->tdrdi.present) { + AVBufferRef *buf; + size_t size; + AV3DReferenceDisplaysInfo *tdrdi = av_tdrdi_alloc(sei->tdrdi.num_ref_displays, &size); + + if (!tdrdi) + return AVERROR(ENOMEM); + + buf = av_buffer_create((uint8_t *)tdrdi, size, NULL, NULL, 0); + if (!buf) { + av_free(tdrdi); + return AVERROR(ENOMEM); + } + + tdrdi->prec_ref_display_width = sei->tdrdi.prec_ref_display_width; + tdrdi->ref_viewing_distance_flag = sei->tdrdi.ref_viewing_distance_flag; + tdrdi->prec_ref_viewing_dist = sei->tdrdi.prec_ref_viewing_dist; + tdrdi->num_ref_displays = sei->tdrdi.num_ref_displays; + for (int i = 0; i < sei->tdrdi.num_ref_displays; i++) { + AV3DReferenceDisplay *display = av_tdrdi_get_display(tdrdi, i); + + display->left_view_id = sei->tdrdi.left_view_id[i]; + display->right_view_id = sei->tdrdi.right_view_id[i]; + display->exponent_ref_display_width = sei->tdrdi.exponent_ref_display_width[i]; + display->mantissa_ref_display_width = sei->tdrdi.mantissa_ref_display_width[i]; + display->exponent_ref_viewing_distance = sei->tdrdi.exponent_ref_viewing_distance[i]; + display->mantissa_ref_viewing_distance = sei->tdrdi.mantissa_ref_viewing_distance[i]; + display->additional_shift_present_flag = sei->tdrdi.additional_shift_present_flag[i]; + display->num_sample_shift = sei->tdrdi.num_sample_shift[i]; + } + ret = ff_frame_new_side_data_from_buf_ext(avctx, &avctx->decoded_side_data, &avctx->nb_decoded_side_data, + AV_FRAME_DATA_3D_REFERENCE_DISPLAYS, &buf); + if (ret < 0) { + av_buffer_unref(&buf); + return ret; + } + } + + ret = ff_h2645_sei_to_context(avctx, &sei->common); + if (ret < 0) + return ret; + + return 0; +} + +static av_cold int hevc_decode_init(AVCodecContext *avctx) +{ + HEVCContext *s = avctx->priv_data; + int ret; + + ret = hevc_init_context(avctx); + if (ret < 0) + return ret; + + s->sei.picture_timing.picture_struct = 0; + s->eos = 1; + + atomic_init(&s->wpp_err, 0); + + if (!avctx->internal->is_copy) { + const AVPacketSideData *sd; + + if (avctx->extradata_size > 0 && avctx->extradata) { + ret = hevc_decode_extradata(s, avctx->extradata, avctx->extradata_size, 1); + if (ret < 0) { + return ret; + } + + ret = hevc_sei_to_context(avctx, &s->sei); + if (ret < 0) + return ret; + } + + sd = ff_get_coded_side_data(avctx, AV_PKT_DATA_DOVI_CONF); + if (sd && sd->size >= sizeof(s->dovi_ctx.cfg)) + s->dovi_ctx.cfg = *(AVDOVIDecoderConfigurationRecord *) sd->data; + } + + return 0; +} + +static av_cold void hevc_decode_flush(AVCodecContext *avctx) +{ + HEVCContext *s = avctx->priv_data; + ff_hevc_flush_dpb(s); + ff_hevc_reset_sei(&s->sei); + ff_dovi_ctx_flush(&s->dovi_ctx); + av_buffer_unref(&s->rpu_buf); + s->eos = 1; + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); +} + +#define OFFSET(x) offsetof(HEVCContext, x) +#define PAR (AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) + +static const AVOption options[] = { + { "apply_defdispwin", "Apply default display window from VUI", OFFSET(apply_defdispwin), + AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR }, + { "strict-displaywin", "strictly apply default display window size", OFFSET(apply_defdispwin), + AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR }, + { "view_ids", "Array of view IDs that should be decoded and output; a single -1 to decode all views", + .offset = OFFSET(view_ids), .type = AV_OPT_TYPE_INT | AV_OPT_TYPE_FLAG_ARRAY, + .min = -1, .max = INT_MAX, .flags = PAR }, + { "view_ids_available", "Array of available view IDs is exported here", + .offset = OFFSET(view_ids_available), .type = AV_OPT_TYPE_UINT | AV_OPT_TYPE_FLAG_ARRAY, + .flags = PAR | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY }, + { "view_pos_available", "Array of view positions for view_ids_available is exported here, as AVStereo3DView", + .offset = OFFSET(view_pos_available), .type = AV_OPT_TYPE_UINT | AV_OPT_TYPE_FLAG_ARRAY, + .flags = PAR | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY, .unit = "view_pos" }, + { "unspecified", .type = AV_OPT_TYPE_CONST, .default_val = { .i64 = AV_STEREO3D_VIEW_UNSPEC }, .unit = "view_pos" }, + { "left", .type = AV_OPT_TYPE_CONST, .default_val = { .i64 = AV_STEREO3D_VIEW_LEFT }, .unit = "view_pos" }, + { "right", .type = AV_OPT_TYPE_CONST, .default_val = { .i64 = AV_STEREO3D_VIEW_RIGHT }, .unit = "view_pos" }, + + { NULL }, +}; + +static const AVClass hevc_decoder_class = { + .class_name = "HEVC decoder", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const FFCodec ff_hevc_decoder = { + .p.name = "hevc", + CODEC_LONG_NAME("HEVC (High Efficiency Video Coding)"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .priv_data_size = sizeof(HEVCContext), + .p.priv_class = &hevc_decoder_class, + .init = hevc_decode_init, + .close = hevc_decode_free, + FF_CODEC_RECEIVE_FRAME_CB(hevc_receive_frame), + .flush = hevc_decode_flush, + UPDATE_THREAD_CONTEXT(hevc_update_thread_context), + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, + .caps_internal = FF_CODEC_CAP_EXPORTS_CROPPING | + FF_CODEC_CAP_USES_PROGRESSFRAMES | + FF_CODEC_CAP_INIT_CLEANUP, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_hevc_profiles), + .hw_configs = (const AVCodecHWConfigInternal *const []) { +#if CONFIG_HEVC_DXVA2_HWACCEL + HWACCEL_DXVA2(hevc), +#endif +#if CONFIG_HEVC_D3D11VA_HWACCEL + HWACCEL_D3D11VA(hevc), +#endif +#if CONFIG_HEVC_D3D11VA2_HWACCEL + HWACCEL_D3D11VA2(hevc), +#endif +#if CONFIG_HEVC_D3D12VA_HWACCEL + HWACCEL_D3D12VA(hevc), +#endif +#if CONFIG_HEVC_NVDEC_HWACCEL + HWACCEL_NVDEC(hevc), +#endif +#if CONFIG_HEVC_VAAPI_HWACCEL + HWACCEL_VAAPI(hevc), +#endif +#if CONFIG_HEVC_VDPAU_HWACCEL + HWACCEL_VDPAU(hevc), +#endif +#if CONFIG_HEVC_VIDEOTOOLBOX_HWACCEL + HWACCEL_VIDEOTOOLBOX(hevc), +#endif +#if CONFIG_HEVC_VULKAN_HWACCEL + HWACCEL_VULKAN(hevc), +#endif + NULL + }, +}; diff --git a/thirdparty/ffmpeg/libavcodec/hwaccels.h b/thirdparty/ffmpeg/libavcodec/hwaccels.h index 4f8da46c1..176606c94 100644 --- a/thirdparty/ffmpeg/libavcodec/hwaccels.h +++ b/thirdparty/ffmpeg/libavcodec/hwaccels.h @@ -25,6 +25,7 @@ extern const struct FFHWAccel ff_av1_d3d11va2_hwaccel; extern const struct FFHWAccel ff_av1_d3d12va_hwaccel; extern const struct FFHWAccel ff_av1_dxva2_hwaccel; extern const struct FFHWAccel ff_av1_nvdec_hwaccel; +extern const struct FFHWAccel ff_av1_v4l2request_hwaccel; extern const struct FFHWAccel ff_av1_vaapi_hwaccel; extern const struct FFHWAccel ff_av1_vdpau_hwaccel; extern const struct FFHWAccel ff_av1_videotoolbox_hwaccel; @@ -38,6 +39,7 @@ extern const struct FFHWAccel ff_h264_d3d11va2_hwaccel; extern const struct FFHWAccel ff_h264_d3d12va_hwaccel; extern const struct FFHWAccel ff_h264_dxva2_hwaccel; extern const struct FFHWAccel ff_h264_nvdec_hwaccel; +extern const struct FFHWAccel ff_h264_v4l2request_hwaccel; extern const struct FFHWAccel ff_h264_vaapi_hwaccel; extern const struct FFHWAccel ff_h264_vdpau_hwaccel; extern const struct FFHWAccel ff_h264_videotoolbox_hwaccel; @@ -47,6 +49,7 @@ extern const struct FFHWAccel ff_hevc_d3d11va2_hwaccel; extern const struct FFHWAccel ff_hevc_d3d12va_hwaccel; extern const struct FFHWAccel ff_hevc_dxva2_hwaccel; extern const struct FFHWAccel ff_hevc_nvdec_hwaccel; +extern const struct FFHWAccel ff_hevc_v4l2request_hwaccel; extern const struct FFHWAccel ff_hevc_vaapi_hwaccel; extern const struct FFHWAccel ff_hevc_vdpau_hwaccel; extern const struct FFHWAccel ff_hevc_videotoolbox_hwaccel; @@ -61,6 +64,7 @@ extern const struct FFHWAccel ff_mpeg2_d3d11va2_hwaccel; extern const struct FFHWAccel ff_mpeg2_d3d12va_hwaccel; extern const struct FFHWAccel ff_mpeg2_dxva2_hwaccel; extern const struct FFHWAccel ff_mpeg2_nvdec_hwaccel; +extern const struct FFHWAccel ff_mpeg2_v4l2request_hwaccel; extern const struct FFHWAccel ff_mpeg2_vaapi_hwaccel; extern const struct FFHWAccel ff_mpeg2_vdpau_hwaccel; extern const struct FFHWAccel ff_mpeg2_videotoolbox_hwaccel; @@ -80,12 +84,14 @@ extern const struct FFHWAccel ff_vc1_nvdec_hwaccel; extern const struct FFHWAccel ff_vc1_vaapi_hwaccel; extern const struct FFHWAccel ff_vc1_vdpau_hwaccel; extern const struct FFHWAccel ff_vp8_nvdec_hwaccel; +extern const struct FFHWAccel ff_vp8_v4l2request_hwaccel; extern const struct FFHWAccel ff_vp8_vaapi_hwaccel; extern const struct FFHWAccel ff_vp9_d3d11va_hwaccel; extern const struct FFHWAccel ff_vp9_d3d11va2_hwaccel; extern const struct FFHWAccel ff_vp9_d3d12va_hwaccel; extern const struct FFHWAccel ff_vp9_dxva2_hwaccel; extern const struct FFHWAccel ff_vp9_nvdec_hwaccel; +extern const struct FFHWAccel ff_vp9_v4l2request_hwaccel; extern const struct FFHWAccel ff_vp9_vaapi_hwaccel; extern const struct FFHWAccel ff_vp9_vdpau_hwaccel; extern const struct FFHWAccel ff_vp9_videotoolbox_hwaccel; diff --git a/thirdparty/ffmpeg/libavcodec/hwaccels.h.orig b/thirdparty/ffmpeg/libavcodec/hwaccels.h.orig new file mode 100644 index 000000000..4f8da46c1 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/hwaccels.h.orig @@ -0,0 +1,102 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_HWACCELS_H +#define AVCODEC_HWACCELS_H + +extern const struct FFHWAccel ff_apv_vulkan_hwaccel; +extern const struct FFHWAccel ff_av1_d3d11va_hwaccel; +extern const struct FFHWAccel ff_av1_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_av1_d3d12va_hwaccel; +extern const struct FFHWAccel ff_av1_dxva2_hwaccel; +extern const struct FFHWAccel ff_av1_nvdec_hwaccel; +extern const struct FFHWAccel ff_av1_vaapi_hwaccel; +extern const struct FFHWAccel ff_av1_vdpau_hwaccel; +extern const struct FFHWAccel ff_av1_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_av1_vulkan_hwaccel; +extern const struct FFHWAccel ff_dpx_vulkan_hwaccel; +extern const struct FFHWAccel ff_ffv1_vulkan_hwaccel; +extern const struct FFHWAccel ff_h263_vaapi_hwaccel; +extern const struct FFHWAccel ff_h263_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_h264_d3d11va_hwaccel; +extern const struct FFHWAccel ff_h264_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_h264_d3d12va_hwaccel; +extern const struct FFHWAccel ff_h264_dxva2_hwaccel; +extern const struct FFHWAccel ff_h264_nvdec_hwaccel; +extern const struct FFHWAccel ff_h264_vaapi_hwaccel; +extern const struct FFHWAccel ff_h264_vdpau_hwaccel; +extern const struct FFHWAccel ff_h264_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_h264_vulkan_hwaccel; +extern const struct FFHWAccel ff_hevc_d3d11va_hwaccel; +extern const struct FFHWAccel ff_hevc_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_hevc_d3d12va_hwaccel; +extern const struct FFHWAccel ff_hevc_dxva2_hwaccel; +extern const struct FFHWAccel ff_hevc_nvdec_hwaccel; +extern const struct FFHWAccel ff_hevc_vaapi_hwaccel; +extern const struct FFHWAccel ff_hevc_vdpau_hwaccel; +extern const struct FFHWAccel ff_hevc_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_hevc_vulkan_hwaccel; +extern const struct FFHWAccel ff_mjpeg_nvdec_hwaccel; +extern const struct FFHWAccel ff_mjpeg_vaapi_hwaccel; +extern const struct FFHWAccel ff_mpeg1_nvdec_hwaccel; +extern const struct FFHWAccel ff_mpeg1_vdpau_hwaccel; +extern const struct FFHWAccel ff_mpeg1_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_mpeg2_d3d11va_hwaccel; +extern const struct FFHWAccel ff_mpeg2_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_mpeg2_d3d12va_hwaccel; +extern const struct FFHWAccel ff_mpeg2_dxva2_hwaccel; +extern const struct FFHWAccel ff_mpeg2_nvdec_hwaccel; +extern const struct FFHWAccel ff_mpeg2_vaapi_hwaccel; +extern const struct FFHWAccel ff_mpeg2_vdpau_hwaccel; +extern const struct FFHWAccel ff_mpeg2_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_mpeg4_nvdec_hwaccel; +extern const struct FFHWAccel ff_mpeg4_vaapi_hwaccel; +extern const struct FFHWAccel ff_mpeg4_vdpau_hwaccel; +extern const struct FFHWAccel ff_mpeg4_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_prores_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_prores_vulkan_hwaccel; +extern const struct FFHWAccel ff_prores_raw_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_prores_raw_vulkan_hwaccel; +extern const struct FFHWAccel ff_vc1_d3d11va_hwaccel; +extern const struct FFHWAccel ff_vc1_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_vc1_d3d12va_hwaccel; +extern const struct FFHWAccel ff_vc1_dxva2_hwaccel; +extern const struct FFHWAccel ff_vc1_nvdec_hwaccel; +extern const struct FFHWAccel ff_vc1_vaapi_hwaccel; +extern const struct FFHWAccel ff_vc1_vdpau_hwaccel; +extern const struct FFHWAccel ff_vp8_nvdec_hwaccel; +extern const struct FFHWAccel ff_vp8_vaapi_hwaccel; +extern const struct FFHWAccel ff_vp9_d3d11va_hwaccel; +extern const struct FFHWAccel ff_vp9_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_vp9_d3d12va_hwaccel; +extern const struct FFHWAccel ff_vp9_dxva2_hwaccel; +extern const struct FFHWAccel ff_vp9_nvdec_hwaccel; +extern const struct FFHWAccel ff_vp9_vaapi_hwaccel; +extern const struct FFHWAccel ff_vp9_vdpau_hwaccel; +extern const struct FFHWAccel ff_vp9_videotoolbox_hwaccel; +extern const struct FFHWAccel ff_vp9_vulkan_hwaccel; +extern const struct FFHWAccel ff_vvc_vaapi_hwaccel; +extern const struct FFHWAccel ff_wmv3_d3d11va_hwaccel; +extern const struct FFHWAccel ff_wmv3_d3d11va2_hwaccel; +extern const struct FFHWAccel ff_wmv3_d3d12va_hwaccel; +extern const struct FFHWAccel ff_wmv3_dxva2_hwaccel; +extern const struct FFHWAccel ff_wmv3_nvdec_hwaccel; +extern const struct FFHWAccel ff_wmv3_vaapi_hwaccel; +extern const struct FFHWAccel ff_wmv3_vdpau_hwaccel; + +#endif /* AVCODEC_HWACCELS_H */ diff --git a/thirdparty/ffmpeg/libavcodec/hwconfig.h b/thirdparty/ffmpeg/libavcodec/hwconfig.h index ee29ca631..159064a1f 100644 --- a/thirdparty/ffmpeg/libavcodec/hwconfig.h +++ b/thirdparty/ffmpeg/libavcodec/hwconfig.h @@ -79,6 +79,8 @@ void ff_hwaccel_uninit(AVCodecContext *avctx); HW_CONFIG_HWACCEL(0, 0, 1, D3D11VA_VLD, NONE, ff_ ## codec ## _d3d11va_hwaccel) #define HWACCEL_D3D12VA(codec) \ HW_CONFIG_HWACCEL(1, 1, 0, D3D12, D3D12VA, ff_ ## codec ## _d3d12va_hwaccel) +#define HWACCEL_V4L2REQUEST(codec) \ + HW_CONFIG_HWACCEL(1, 0, 0, DRM_PRIME, V4L2REQUEST, ff_ ## codec ## _v4l2request_hwaccel) #define HW_CONFIG_ENCODER(device, frames, ad_hoc, format, device_type_) \ &(const AVCodecHWConfigInternal) { \ diff --git a/thirdparty/ffmpeg/libavcodec/mpeg12dec.c b/thirdparty/ffmpeg/libavcodec/mpeg12dec.c index 19ad08d10..ac9eb64a5 100644 --- a/thirdparty/ffmpeg/libavcodec/mpeg12dec.c +++ b/thirdparty/ffmpeg/libavcodec/mpeg12dec.c @@ -820,6 +820,9 @@ static const enum AVPixelFormat mpeg2_hwaccel_pixfmt_list_420[] = { #endif #if CONFIG_MPEG2_VIDEOTOOLBOX_HWACCEL AV_PIX_FMT_VIDEOTOOLBOX, +#endif +#if CONFIG_MPEG2_V4L2REQUEST_HWACCEL + AV_PIX_FMT_DRM_PRIME, #endif AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE @@ -2727,6 +2730,9 @@ const FFCodec ff_mpeg2video_decoder = { #endif #if CONFIG_MPEG2_VIDEOTOOLBOX_HWACCEL HWACCEL_VIDEOTOOLBOX(mpeg2), +#endif +#if CONFIG_MPEG2_V4L2REQUEST_HWACCEL + HWACCEL_V4L2REQUEST(mpeg2), #endif NULL }, diff --git a/thirdparty/ffmpeg/libavcodec/mpeg12dec.c.orig b/thirdparty/ffmpeg/libavcodec/mpeg12dec.c.orig new file mode 100644 index 000000000..19ad08d10 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/mpeg12dec.c.orig @@ -0,0 +1,2885 @@ +/* + * MPEG-1/2 decoder + * Copyright (c) 2000, 2001 Fabrice Bellard + * Copyright (c) 2002-2013 Michael Niedermayer + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * MPEG-1/2 decoder + */ + +#include "config_components.h" + +#define UNCHECKED_BITSTREAM_READER 1 +#include +#include + +#include "libavutil/attributes.h" +#include "libavutil/imgutils.h" +#include "libavutil/internal.h" +#include "libavutil/mem_internal.h" +#include "libavutil/reverse.h" +#include "libavutil/stereo3d.h" +#include "libavutil/timecode.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "error_resilience.h" +#include "get_bits.h" +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "idctdsp.h" +#include "mpeg_er.h" +#include "mpeg12.h" +#include "mpeg12data.h" +#include "mpeg12dec.h" +#include "mpegutils.h" +#include "mpegvideo.h" +#include "mpegvideodata.h" +#include "mpegvideodec.h" +#include "profiles.h" +#include "startcode.h" + +#define A53_MAX_CC_COUNT 2000 + +enum Mpeg2ClosedCaptionsFormat { + CC_FORMAT_AUTO, + CC_FORMAT_A53_PART4, + CC_FORMAT_SCTE20, + CC_FORMAT_DVD, + CC_FORMAT_DISH +}; + +typedef struct Mpeg12SliceContext { + MPVContext c; + GetBitContext gb; + + int last_dc[3]; ///< last DC values + + DECLARE_ALIGNED_32(int16_t, block)[12][64]; +} Mpeg12SliceContext; + +typedef struct Mpeg1Context { + Mpeg12SliceContext slice; + AVPanScan pan_scan; /* some temporary storage for the panscan */ + enum AVStereo3DType stereo3d_type; + int has_stereo3d; + AVBufferRef *a53_buf_ref; + enum Mpeg2ClosedCaptionsFormat cc_format; + uint8_t afd; + int has_afd; + int slice_count; + unsigned aspect_ratio_info; + int save_progressive_seq, save_chroma_format; + AVRational frame_rate_ext; /* MPEG-2 specific framerate modificator */ + unsigned frame_rate_index; + int sync; /* Did we reach a sync point like a GOP/SEQ/KEYFrame? */ + int closed_gop; + int tmpgexs; + int first_slice; + int extradata_decoded; + int vbv_delay; + int64_t bit_rate; + int64_t timecode_frame_start; /*< GOP timecode frame start number, in non drop frame format */ +} Mpeg1Context; + +/* as H.263, but only 17 codes */ +static int mpeg_decode_motion(Mpeg12SliceContext *const s, int fcode, int pred) +{ + int code, sign, val, shift; + + code = get_vlc2(&s->gb, ff_mv_vlc, MV_VLC_BITS, 2); + if (code == 0) + return pred; + if (code < 0) + return 0xffff; + + sign = get_bits1(&s->gb); + shift = fcode - 1; + val = code; + if (shift) { + val = (val - 1) << shift; + val |= get_bits(&s->gb, shift); + val++; + } + if (sign) + val = -val; + val += pred; + + /* modulo decoding */ + return sign_extend(val, 5 + shift); +} + +#define MAX_INDEX (64 - 1) +#define check_scantable_index(ctx, x) \ + do { \ + if ((x) > MAX_INDEX) { \ + av_log(ctx->c.avctx, AV_LOG_ERROR, "ac-tex damaged at %d %d\n", \ + ctx->c.mb_x, ctx->c.mb_y); \ + return AVERROR_INVALIDDATA; \ + } \ + } while (0) + +static inline int mpeg1_decode_block_inter(Mpeg12SliceContext *const s, + int16_t *block, int n) +{ + int level, i, j, run; + const uint8_t *const scantable = s->c.intra_scantable.permutated; + const uint16_t *quant_matrix = s->c.inter_matrix; + const int qscale = s->c.qscale; + + { + OPEN_READER(re, &s->gb); + i = -1; + // special case for first coefficient, no need to add second VLC table + UPDATE_CACHE(re, &s->gb); + if (((int32_t) GET_CACHE(re, &s->gb)) < 0) { + level = (3 * qscale * quant_matrix[0]) >> 5; + level = (level - 1) | 1; + if (GET_CACHE(re, &s->gb) & 0x40000000) + level = -level; + block[0] = level; + i++; + SKIP_BITS(re, &s->gb, 2); + if (((int32_t) GET_CACHE(re, &s->gb)) <= (int32_t) 0xBFFFFFFF) + goto end; + } + /* now quantify & encode AC coefficients */ + for (;;) { + GET_RL_VLC(level, run, re, &s->gb, ff_mpeg1_rl_vlc, + TEX_VLC_BITS, 2, 0); + + if (level != 0) { + i += run; + if (i > MAX_INDEX) + break; + j = scantable[i]; + level = ((level * 2 + 1) * qscale * quant_matrix[j]) >> 5; + level = (level - 1) | 1; + level = (level ^ SHOW_SBITS(re, &s->gb, 1)) - + SHOW_SBITS(re, &s->gb, 1); + SKIP_BITS(re, &s->gb, 1); + } else { + /* escape */ + run = SHOW_UBITS(re, &s->gb, 6) + 1; + LAST_SKIP_BITS(re, &s->gb, 6); + UPDATE_CACHE(re, &s->gb); + level = SHOW_SBITS(re, &s->gb, 8); + SKIP_BITS(re, &s->gb, 8); + if (level == -128) { + level = SHOW_UBITS(re, &s->gb, 8) - 256; + SKIP_BITS(re, &s->gb, 8); + } else if (level == 0) { + level = SHOW_UBITS(re, &s->gb, 8); + SKIP_BITS(re, &s->gb, 8); + } + i += run; + if (i > MAX_INDEX) + break; + j = scantable[i]; + if (level < 0) { + level = -level; + level = ((level * 2 + 1) * qscale * quant_matrix[j]) >> 5; + level = (level - 1) | 1; + level = -level; + } else { + level = ((level * 2 + 1) * qscale * quant_matrix[j]) >> 5; + level = (level - 1) | 1; + } + } + + block[j] = level; + if (((int32_t) GET_CACHE(re, &s->gb)) <= (int32_t) 0xBFFFFFFF) + break; + UPDATE_CACHE(re, &s->gb); + } +end: + LAST_SKIP_BITS(re, &s->gb, 2); + CLOSE_READER(re, &s->gb); + } + + check_scantable_index(s, i); + + s->c.block_last_index[n] = i; + return 0; +} + +static inline int mpeg2_decode_block_non_intra(Mpeg12SliceContext *const s, + int16_t *block, int n) +{ + int level, i, j, run; + const uint8_t *const scantable = s->c.intra_scantable.permutated; + const uint16_t *quant_matrix; + const int qscale = s->c.qscale; + int mismatch; + + mismatch = 1; + + { + OPEN_READER(re, &s->gb); + i = -1; + if (n < 4) + quant_matrix = s->c.inter_matrix; + else + quant_matrix = s->c.chroma_inter_matrix; + + // Special case for first coefficient, no need to add second VLC table. + UPDATE_CACHE(re, &s->gb); + if (((int32_t) GET_CACHE(re, &s->gb)) < 0) { + level = (3 * qscale * quant_matrix[0]) >> 5; + if (GET_CACHE(re, &s->gb) & 0x40000000) + level = -level; + block[0] = level; + mismatch ^= level; + i++; + SKIP_BITS(re, &s->gb, 2); + if (((int32_t) GET_CACHE(re, &s->gb)) <= (int32_t) 0xBFFFFFFF) + goto end; + } + + /* now quantify & encode AC coefficients */ + for (;;) { + GET_RL_VLC(level, run, re, &s->gb, ff_mpeg1_rl_vlc, + TEX_VLC_BITS, 2, 0); + + if (level != 0) { + i += run; + if (i > MAX_INDEX) + break; + j = scantable[i]; + level = ((level * 2 + 1) * qscale * quant_matrix[j]) >> 5; + level = (level ^ SHOW_SBITS(re, &s->gb, 1)) - + SHOW_SBITS(re, &s->gb, 1); + SKIP_BITS(re, &s->gb, 1); + } else { + /* escape */ + run = SHOW_UBITS(re, &s->gb, 6) + 1; + LAST_SKIP_BITS(re, &s->gb, 6); + UPDATE_CACHE(re, &s->gb); + level = SHOW_SBITS(re, &s->gb, 12); + SKIP_BITS(re, &s->gb, 12); + + i += run; + if (i > MAX_INDEX) + break; + j = scantable[i]; + if (level < 0) { + level = ((-level * 2 + 1) * qscale * quant_matrix[j]) >> 5; + level = -level; + } else { + level = ((level * 2 + 1) * qscale * quant_matrix[j]) >> 5; + } + } + + mismatch ^= level; + block[j] = level; + if (((int32_t) GET_CACHE(re, &s->gb)) <= (int32_t) 0xBFFFFFFF) + break; + UPDATE_CACHE(re, &s->gb); + } +end: + LAST_SKIP_BITS(re, &s->gb, 2); + CLOSE_READER(re, &s->gb); + } + block[63] ^= (mismatch & 1); + + check_scantable_index(s, i); + + s->c.block_last_index[n] = i; + return 0; +} + +static inline int mpeg2_decode_block_intra(Mpeg12SliceContext *const s, + int16_t *block, int n) +{ + int level, dc, diff, i, j, run; + int component; + const RL_VLC_ELEM *rl_vlc; + const uint8_t *const scantable = s->c.intra_scantable.permutated; + const uint16_t *quant_matrix; + const int qscale = s->c.qscale; + int mismatch; + + /* DC coefficient */ + if (n < 4) { + quant_matrix = s->c.intra_matrix; + component = 0; + } else { + quant_matrix = s->c.chroma_intra_matrix; + component = (n & 1) + 1; + } + diff = decode_dc(&s->gb, component); + dc = s->last_dc[component]; + dc += diff; + s->last_dc[component] = dc; + block[0] = dc * (1 << (3 - s->c.intra_dc_precision)); + ff_tlog(s->c.avctx, "dc=%d\n", block[0]); + mismatch = block[0] ^ 1; + i = 0; + if (s->c.intra_vlc_format) + rl_vlc = ff_mpeg2_rl_vlc; + else + rl_vlc = ff_mpeg1_rl_vlc; + + { + OPEN_READER(re, &s->gb); + /* now quantify & encode AC coefficients */ + for (;;) { + UPDATE_CACHE(re, &s->gb); + GET_RL_VLC(level, run, re, &s->gb, rl_vlc, + TEX_VLC_BITS, 2, 0); + + if (level == 127) { + break; + } else if (level != 0) { + i += run; + if (i > MAX_INDEX) + break; + j = scantable[i]; + level = (level * qscale * quant_matrix[j]) >> 4; + level = (level ^ SHOW_SBITS(re, &s->gb, 1)) - + SHOW_SBITS(re, &s->gb, 1); + LAST_SKIP_BITS(re, &s->gb, 1); + } else { + /* escape */ + run = SHOW_UBITS(re, &s->gb, 6) + 1; + SKIP_BITS(re, &s->gb, 6); + level = SHOW_SBITS(re, &s->gb, 12); + LAST_SKIP_BITS(re, &s->gb, 12); + i += run; + if (i > MAX_INDEX) + break; + j = scantable[i]; + if (level < 0) { + level = (-level * qscale * quant_matrix[j]) >> 4; + level = -level; + } else { + level = (level * qscale * quant_matrix[j]) >> 4; + } + } + + mismatch ^= level; + block[j] = level; + } + CLOSE_READER(re, &s->gb); + } + block[63] ^= mismatch & 1; + + check_scantable_index(s, i); + + return 0; +} + +static inline int get_dmv(Mpeg12SliceContext *const s) +{ + if (get_bits1(&s->gb)) + return 1 - (get_bits1(&s->gb) << 1); + else + return 0; +} + +/* motion type (for MPEG-2) */ +#define MT_FIELD 1 +#define MT_FRAME 2 +#define MT_16X8 2 +#define MT_DMV 3 + +static int mpeg_decode_mb(Mpeg12SliceContext *const s, int *mb_skip_run) +{ + int i, j, k, cbp, val, mb_type, motion_type; + const int mb_block_count = 4 + (1 << s->c.chroma_format); + int ret; + + ff_tlog(s->c.avctx, "decode_mb: x=%d y=%d\n", s->c.mb_x, s->c.mb_y); + + av_assert2(s->c.mb_skipped == 0); + + if ((*mb_skip_run)-- != 0) { + if (s->c.pict_type == AV_PICTURE_TYPE_P) { + s->c.mb_skipped = 1; + s->c.cur_pic.mb_type[s->c.mb_x + s->c.mb_y * s->c.mb_stride] = + MB_TYPE_SKIP | MB_TYPE_FORWARD_MV | MB_TYPE_16x16; + } else { + int mb_type; + + if (s->c.mb_x) + mb_type = s->c.cur_pic.mb_type[s->c.mb_x + s->c.mb_y * s->c.mb_stride - 1]; + else + // FIXME not sure if this is allowed in MPEG at all + mb_type = s->c.cur_pic.mb_type[s->c.mb_width + (s->c.mb_y - 1) * s->c.mb_stride - 1]; + if (IS_INTRA(mb_type)) { + av_log(s->c.avctx, AV_LOG_ERROR, "skip with previntra\n"); + return AVERROR_INVALIDDATA; + } + s->c.cur_pic.mb_type[s->c.mb_x + s->c.mb_y * s->c.mb_stride] = + mb_type | MB_TYPE_SKIP; + + if ((s->c.mv[0][0][0] | s->c.mv[0][0][1] | s->c.mv[1][0][0] | s->c.mv[1][0][1]) == 0) + s->c.mb_skipped = 1; + } + + return 0; + } + + switch (s->c.pict_type) { + default: + case AV_PICTURE_TYPE_I: + if (get_bits1(&s->gb) == 0) { + if (get_bits1(&s->gb) == 0) { + av_log(s->c.avctx, AV_LOG_ERROR, + "Invalid mb type in I-frame at %d %d\n", + s->c.mb_x, s->c.mb_y); + return AVERROR_INVALIDDATA; + } + mb_type = MB_TYPE_QUANT | MB_TYPE_INTRA; + } else { + mb_type = MB_TYPE_INTRA; + } + break; + case AV_PICTURE_TYPE_P: + mb_type = get_vlc2(&s->gb, ff_mb_ptype_vlc, MB_PTYPE_VLC_BITS, 1); + if (mb_type < 0) { + av_log(s->c.avctx, AV_LOG_ERROR, + "Invalid mb type in P-frame at %d %d\n", s->c.mb_x, s->c.mb_y); + return AVERROR_INVALIDDATA; + } + break; + case AV_PICTURE_TYPE_B: + mb_type = get_vlc2(&s->gb, ff_mb_btype_vlc, MB_BTYPE_VLC_BITS, 1); + if (mb_type < 0) { + av_log(s->c.avctx, AV_LOG_ERROR, + "Invalid mb type in B-frame at %d %d\n", s->c.mb_x, s->c.mb_y); + return AVERROR_INVALIDDATA; + } + break; + } + ff_tlog(s->c.avctx, "mb_type=%x\n", mb_type); +// motion_type = 0; /* avoid warning */ + if (IS_INTRA(mb_type)) { + s->c.bdsp.clear_blocks(s->block[0]); + + if (!s->c.chroma_y_shift) + s->c.bdsp.clear_blocks(s->block[6]); + + /* compute DCT type */ + // FIXME: add an interlaced_dct coded var? + if (s->c.picture_structure == PICT_FRAME && + !s->c.frame_pred_frame_dct) + s->c.interlaced_dct = get_bits1(&s->gb); + + if (IS_QUANT(mb_type)) + s->c.qscale = mpeg_get_qscale(&s->gb, s->c.q_scale_type); + + if (s->c.concealment_motion_vectors) { + /* just parse them */ + if (s->c.picture_structure != PICT_FRAME) + skip_bits1(&s->gb); /* field select */ + + s->c.mv[0][0][0] = + s->c.last_mv[0][0][0] = + s->c.last_mv[0][1][0] = mpeg_decode_motion(s, s->c.mpeg_f_code[0][0], + s->c.last_mv[0][0][0]); + s->c.mv[0][0][1] = + s->c.last_mv[0][0][1] = + s->c.last_mv[0][1][1] = mpeg_decode_motion(s, s->c.mpeg_f_code[0][1], + s->c.last_mv[0][0][1]); + + check_marker(s->c.avctx, &s->gb, "after concealment_motion_vectors"); + } else { + /* reset mv prediction */ + memset(s->c.last_mv, 0, sizeof(s->c.last_mv)); + } + s->c.mb_intra = 1; + + if (s->c.codec_id == AV_CODEC_ID_MPEG2VIDEO) { + for (i = 0; i < mb_block_count; i++) + if ((ret = mpeg2_decode_block_intra(s, s->block[i], i)) < 0) + return ret; + } else { + for (i = 0; i < 6; i++) { + ret = ff_mpeg1_decode_block_intra(&s->gb, + s->c.intra_matrix, + s->c.intra_scantable.permutated, + s->last_dc, s->block[i], + i, s->c.qscale); + if (ret < 0) { + av_log(s->c.avctx, AV_LOG_ERROR, "ac-tex damaged at %d %d\n", + s->c.mb_x, s->c.mb_y); + return ret; + } + } + } + } else { + if (mb_type & MB_TYPE_ZERO_MV) { + av_assert2(mb_type & MB_TYPE_CBP); + + s->c.mv_dir = MV_DIR_FORWARD; + if (s->c.picture_structure == PICT_FRAME) { + if (s->c.picture_structure == PICT_FRAME + && !s->c.frame_pred_frame_dct) + s->c.interlaced_dct = get_bits1(&s->gb); + s->c.mv_type = MV_TYPE_16X16; + } else { + s->c.mv_type = MV_TYPE_FIELD; + mb_type |= MB_TYPE_INTERLACED; + s->c.field_select[0][0] = s->c.picture_structure - 1; + } + + if (IS_QUANT(mb_type)) + s->c.qscale = mpeg_get_qscale(&s->gb, s->c.q_scale_type); + + s->c.last_mv[0][0][0] = 0; + s->c.last_mv[0][0][1] = 0; + s->c.last_mv[0][1][0] = 0; + s->c.last_mv[0][1][1] = 0; + s->c.mv[0][0][0] = 0; + s->c.mv[0][0][1] = 0; + } else { + av_assert2(mb_type & MB_TYPE_BIDIR_MV); + // FIXME decide if MBs in field pictures are MB_TYPE_INTERLACED + /* get additional motion vector type */ + if (s->c.picture_structure == PICT_FRAME && s->c.frame_pred_frame_dct) { + motion_type = MT_FRAME; + } else { + motion_type = get_bits(&s->gb, 2); + if (s->c.picture_structure == PICT_FRAME && HAS_CBP(mb_type)) + s->c.interlaced_dct = get_bits1(&s->gb); + } + + if (IS_QUANT(mb_type)) + s->c.qscale = mpeg_get_qscale(&s->gb, s->c.q_scale_type); + + /* motion vectors */ + s->c.mv_dir = MB_TYPE_MV_2_MV_DIR(mb_type); + ff_tlog(s->c.avctx, "motion_type=%d\n", motion_type); + switch (motion_type) { + case MT_FRAME: /* or MT_16X8 */ + if (s->c.picture_structure == PICT_FRAME) { + mb_type |= MB_TYPE_16x16; + s->c.mv_type = MV_TYPE_16X16; + for (i = 0; i < 2; i++) { + if (HAS_MV(mb_type, i)) { + /* MT_FRAME */ + s->c.mv[i][0][0] = + s->c.last_mv[i][0][0] = + s->c.last_mv[i][1][0] = + mpeg_decode_motion(s, s->c.mpeg_f_code[i][0], + s->c.last_mv[i][0][0]); + s->c.mv[i][0][1] = + s->c.last_mv[i][0][1] = + s->c.last_mv[i][1][1] = + mpeg_decode_motion(s, s->c.mpeg_f_code[i][1], + s->c.last_mv[i][0][1]); + /* full_pel: only for MPEG-1 */ + if (s->c.full_pel[i]) { + s->c.mv[i][0][0] *= 2; + s->c.mv[i][0][1] *= 2; + } + } + } + } else { + mb_type |= MB_TYPE_16x8 | MB_TYPE_INTERLACED; + s->c.mv_type = MV_TYPE_16X8; + for (i = 0; i < 2; i++) { + if (HAS_MV(mb_type, i)) { + /* MT_16X8 */ + for (j = 0; j < 2; j++) { + s->c.field_select[i][j] = get_bits1(&s->gb); + for (k = 0; k < 2; k++) { + val = mpeg_decode_motion(s, s->c.mpeg_f_code[i][k], + s->c.last_mv[i][j][k]); + s->c.last_mv[i][j][k] = val; + s->c.mv[i][j][k] = val; + } + } + } + } + } + break; + case MT_FIELD: + s->c.mv_type = MV_TYPE_FIELD; + if (s->c.picture_structure == PICT_FRAME) { + mb_type |= MB_TYPE_16x8 | MB_TYPE_INTERLACED; + for (i = 0; i < 2; i++) { + if (HAS_MV(mb_type, i)) { + for (j = 0; j < 2; j++) { + s->c.field_select[i][j] = get_bits1(&s->gb); + val = mpeg_decode_motion(s, s->c.mpeg_f_code[i][0], + s->c.last_mv[i][j][0]); + s->c.last_mv[i][j][0] = val; + s->c.mv[i][j][0] = val; + ff_tlog(s->c.avctx, "fmx=%d\n", val); + val = mpeg_decode_motion(s, s->c.mpeg_f_code[i][1], + s->c.last_mv[i][j][1] >> 1); + s->c.last_mv[i][j][1] = 2 * val; + s->c.mv[i][j][1] = val; + ff_tlog(s->c.avctx, "fmy=%d\n", val); + } + } + } + } else { + av_assert0(!s->c.progressive_sequence); + mb_type |= MB_TYPE_16x16 | MB_TYPE_INTERLACED; + for (i = 0; i < 2; i++) { + if (HAS_MV(mb_type, i)) { + s->c.field_select[i][0] = get_bits1(&s->gb); + for (k = 0; k < 2; k++) { + val = mpeg_decode_motion(s, s->c.mpeg_f_code[i][k], + s->c.last_mv[i][0][k]); + s->c.last_mv[i][0][k] = val; + s->c.last_mv[i][1][k] = val; + s->c.mv[i][0][k] = val; + } + } + } + } + break; + case MT_DMV: + if (s->c.progressive_sequence){ + av_log(s->c.avctx, AV_LOG_ERROR, "MT_DMV in progressive_sequence\n"); + return AVERROR_INVALIDDATA; + } + s->c.mv_type = MV_TYPE_DMV; + for (i = 0; i < 2; i++) { + if (HAS_MV(mb_type, i)) { + int dmx, dmy, mx, my, m; + const int my_shift = s->c.picture_structure == PICT_FRAME; + + mx = mpeg_decode_motion(s, s->c.mpeg_f_code[i][0], + s->c.last_mv[i][0][0]); + s->c.last_mv[i][0][0] = mx; + s->c.last_mv[i][1][0] = mx; + dmx = get_dmv(s); + my = mpeg_decode_motion(s, s->c.mpeg_f_code[i][1], + s->c.last_mv[i][0][1] >> my_shift); + dmy = get_dmv(s); + + + s->c.last_mv[i][0][1] = my * (1 << my_shift); + s->c.last_mv[i][1][1] = my * (1 << my_shift); + + s->c.mv[i][0][0] = mx; + s->c.mv[i][0][1] = my; + s->c.mv[i][1][0] = mx; // not used + s->c.mv[i][1][1] = my; // not used + + if (s->c.picture_structure == PICT_FRAME) { + mb_type |= MB_TYPE_16x16 | MB_TYPE_INTERLACED; + + // m = 1 + 2 * s->c.top_field_first; + m = s->c.top_field_first ? 1 : 3; + + /* top -> top pred */ + s->c.mv[i][2][0] = ((mx * m + (mx > 0)) >> 1) + dmx; + s->c.mv[i][2][1] = ((my * m + (my > 0)) >> 1) + dmy - 1; + m = 4 - m; + s->c.mv[i][3][0] = ((mx * m + (mx > 0)) >> 1) + dmx; + s->c.mv[i][3][1] = ((my * m + (my > 0)) >> 1) + dmy + 1; + } else { + mb_type |= MB_TYPE_16x16; + + s->c.mv[i][2][0] = ((mx + (mx > 0)) >> 1) + dmx; + s->c.mv[i][2][1] = ((my + (my > 0)) >> 1) + dmy; + if (s->c.picture_structure == PICT_TOP_FIELD) + s->c.mv[i][2][1]--; + else + s->c.mv[i][2][1]++; + } + } + } + break; + default: + av_log(s->c.avctx, AV_LOG_ERROR, + "00 motion_type at %d %d\n", s->c.mb_x, s->c.mb_y); + return AVERROR_INVALIDDATA; + } + } + + s->c.mb_intra = 0; + s->last_dc[0] = s->last_dc[1] = s->last_dc[2] = 128 << s->c.intra_dc_precision; + if (HAS_CBP(mb_type)) { + s->c.bdsp.clear_blocks(s->block[0]); + + cbp = get_vlc2(&s->gb, ff_mb_pat_vlc, MB_PAT_VLC_BITS, 1); + if (mb_block_count > 6) { + cbp *= 1 << mb_block_count - 6; + cbp |= get_bits(&s->gb, mb_block_count - 6); + s->c.bdsp.clear_blocks(s->block[6]); + } + if (cbp <= 0) { + av_log(s->c.avctx, AV_LOG_ERROR, + "invalid cbp %d at %d %d\n", cbp, s->c.mb_x, s->c.mb_y); + return AVERROR_INVALIDDATA; + } + + if (s->c.codec_id == AV_CODEC_ID_MPEG2VIDEO) { + cbp <<= 12 - mb_block_count; + + for (i = 0; i < mb_block_count; i++) { + if (cbp & (1 << 11)) { + if ((ret = mpeg2_decode_block_non_intra(s, s->block[i], i)) < 0) + return ret; + } else { + s->c.block_last_index[i] = -1; + } + cbp += cbp; + } + } else { + for (i = 0; i < 6; i++) { + if (cbp & 32) { + if ((ret = mpeg1_decode_block_inter(s, s->block[i], i)) < 0) + return ret; + } else { + s->c.block_last_index[i] = -1; + } + cbp += cbp; + } + } + } else { + for (i = 0; i < 12; i++) + s->c.block_last_index[i] = -1; + } + } + + s->c.cur_pic.mb_type[s->c.mb_x + s->c.mb_y * s->c.mb_stride] = mb_type; + + return 0; +} + +static av_cold int mpeg_decode_init(AVCodecContext *avctx) +{ + Mpeg1Context *s = avctx->priv_data; + MPVContext *const s2 = &s->slice.c; + int ret; + + s2->slice_ctx_size = sizeof(s->slice); + s2->out_format = FMT_MPEG1; + + if ( avctx->codec_tag != AV_RL32("VCR2") + && avctx->codec_tag != AV_RL32("BW10")) + avctx->coded_width = avctx->coded_height = 0; // do not trust dimensions from input + ret = ff_mpv_decode_init(s2, avctx); + if (ret < 0) + return ret; + + ff_mpeg12_init_vlcs(); + + s2->chroma_format = CHROMA_420; + avctx->color_range = AVCOL_RANGE_MPEG; + return 0; +} + +static const enum AVPixelFormat mpeg1_hwaccel_pixfmt_list_420[] = { +#if CONFIG_MPEG1_NVDEC_HWACCEL + AV_PIX_FMT_CUDA, +#endif +#if CONFIG_MPEG1_VDPAU_HWACCEL + AV_PIX_FMT_VDPAU, +#endif + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_NONE +}; + +static const enum AVPixelFormat mpeg2_hwaccel_pixfmt_list_420[] = { +#if CONFIG_MPEG2_NVDEC_HWACCEL + AV_PIX_FMT_CUDA, +#endif +#if CONFIG_MPEG2_VDPAU_HWACCEL + AV_PIX_FMT_VDPAU, +#endif +#if CONFIG_MPEG2_DXVA2_HWACCEL + AV_PIX_FMT_DXVA2_VLD, +#endif +#if CONFIG_MPEG2_D3D11VA_HWACCEL + AV_PIX_FMT_D3D11VA_VLD, + AV_PIX_FMT_D3D11, +#endif +#if CONFIG_MPEG2_D3D12VA_HWACCEL + AV_PIX_FMT_D3D12, +#endif +#if CONFIG_MPEG2_VAAPI_HWACCEL + AV_PIX_FMT_VAAPI, +#endif +#if CONFIG_MPEG2_VIDEOTOOLBOX_HWACCEL + AV_PIX_FMT_VIDEOTOOLBOX, +#endif + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_NONE +}; + +static const enum AVPixelFormat mpeg12_pixfmt_list_422[] = { + AV_PIX_FMT_YUV422P, + AV_PIX_FMT_NONE +}; + +static const enum AVPixelFormat mpeg12_pixfmt_list_444[] = { + AV_PIX_FMT_YUV444P, + AV_PIX_FMT_NONE +}; + +static enum AVPixelFormat mpeg_get_pixelformat(AVCodecContext *avctx) +{ + Mpeg1Context *s1 = avctx->priv_data; + MPVContext *const s = &s1->slice.c; + const enum AVPixelFormat *pix_fmts; + + if (CONFIG_GRAY && (avctx->flags & AV_CODEC_FLAG_GRAY)) + return AV_PIX_FMT_GRAY8; + + if (s->chroma_format < CHROMA_422) + pix_fmts = avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO ? + mpeg1_hwaccel_pixfmt_list_420 : + mpeg2_hwaccel_pixfmt_list_420; + else if (s->chroma_format == CHROMA_422) + pix_fmts = mpeg12_pixfmt_list_422; + else + pix_fmts = mpeg12_pixfmt_list_444; + + return ff_get_format(avctx, pix_fmts); +} + +/* Call this function when we know all parameters. + * It may be called in different places for MPEG-1 and MPEG-2. */ +static int mpeg_decode_postinit(AVCodecContext *avctx) +{ + Mpeg1Context *s1 = avctx->priv_data; + MPVContext *const s = &s1->slice.c; + int ret; + + if (avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { + // MPEG-1 aspect + AVRational aspect_inv = av_d2q(ff_mpeg1_aspect[s1->aspect_ratio_info], 255); + avctx->sample_aspect_ratio = (AVRational) { aspect_inv.den, aspect_inv.num }; + } else { // MPEG-2 + // MPEG-2 aspect + if (s1->aspect_ratio_info > 1) { + AVRational dar = + av_mul_q(av_div_q(ff_mpeg2_aspect[s1->aspect_ratio_info], + (AVRational) { s1->pan_scan.width, + s1->pan_scan.height }), + (AVRational) { s->width, s->height }); + + /* We ignore the spec here and guess a bit as reality does not + * match the spec, see for example res_change_ffmpeg_aspect.ts + * and sequence-display-aspect.mpg. + * issue1613, 621, 562 */ + if ((s1->pan_scan.width == 0) || (s1->pan_scan.height == 0) || + (av_cmp_q(dar, (AVRational) { 4, 3 }) && + av_cmp_q(dar, (AVRational) { 16, 9 }))) { + s->avctx->sample_aspect_ratio = + av_div_q(ff_mpeg2_aspect[s1->aspect_ratio_info], + (AVRational) { s->width, s->height }); + } else { + s->avctx->sample_aspect_ratio = + av_div_q(ff_mpeg2_aspect[s1->aspect_ratio_info], + (AVRational) { s1->pan_scan.width, s1->pan_scan.height }); +// issue1613 4/3 16/9 -> 16/9 +// res_change_ffmpeg_aspect.ts 4/3 225/44 ->4/3 +// widescreen-issue562.mpg 4/3 16/9 -> 16/9 +// s->avctx->sample_aspect_ratio = av_mul_q(s->avctx->sample_aspect_ratio, (AVRational) {s->width, s->height}); + ff_dlog(avctx, "aspect A %d/%d\n", + ff_mpeg2_aspect[s1->aspect_ratio_info].num, + ff_mpeg2_aspect[s1->aspect_ratio_info].den); + ff_dlog(avctx, "aspect B %d/%d\n", s->avctx->sample_aspect_ratio.num, + s->avctx->sample_aspect_ratio.den); + } + } else { + s->avctx->sample_aspect_ratio = + ff_mpeg2_aspect[s1->aspect_ratio_info]; + } + } // MPEG-2 + + if (av_image_check_sar(s->width, s->height, + avctx->sample_aspect_ratio) < 0) { + av_log(avctx, AV_LOG_WARNING, "ignoring invalid SAR: %u/%u\n", + avctx->sample_aspect_ratio.num, + avctx->sample_aspect_ratio.den); + avctx->sample_aspect_ratio = (AVRational){ 0, 1 }; + } + + if (!s->context_initialized || + avctx->coded_width != s->width || + avctx->coded_height != s->height || + s1->save_chroma_format != s->chroma_format || + (s1->save_progressive_seq != s->progressive_sequence && FFALIGN(s->height, 16) != FFALIGN(s->height, 32)) || + 0) { + if (s->context_initialized) + ff_mpv_common_end(s); + + ret = ff_set_dimensions(avctx, s->width, s->height); + if (ret < 0) + return ret; + + if (avctx->codec_id == AV_CODEC_ID_MPEG2VIDEO && s1->bit_rate && + (s1->bit_rate != 0x3FFFF*400)) { + avctx->rc_max_rate = s1->bit_rate; + } else if (avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO && s1->bit_rate && + (s1->bit_rate != 0x3FFFF*400 || s1->vbv_delay != 0xFFFF)) { + avctx->bit_rate = s1->bit_rate; + } + s1->save_progressive_seq = s->progressive_sequence; + s1->save_chroma_format = s->chroma_format; + + /* low_delay may be forced, in this case we will have B-frames + * that behave like P-frames. */ + avctx->has_b_frames = !s->low_delay; + + if (avctx->codec_id == AV_CODEC_ID_MPEG1VIDEO) { + // MPEG-1 fps + avctx->framerate = ff_mpeg12_frame_rate_tab[s1->frame_rate_index]; + avctx->chroma_sample_location = AVCHROMA_LOC_CENTER; + } else { // MPEG-2 + // MPEG-2 fps + av_reduce(&s->avctx->framerate.num, + &s->avctx->framerate.den, + ff_mpeg12_frame_rate_tab[s1->frame_rate_index].num * s1->frame_rate_ext.num, + ff_mpeg12_frame_rate_tab[s1->frame_rate_index].den * s1->frame_rate_ext.den, + 1 << 30); + + switch (s->chroma_format) { + case CHROMA_420: avctx->chroma_sample_location = AVCHROMA_LOC_LEFT; break; + case CHROMA_422: + case CHROMA_444: avctx->chroma_sample_location = AVCHROMA_LOC_TOPLEFT; break; + default: av_assert0(0); + } + } // MPEG-2 + + avctx->pix_fmt = mpeg_get_pixelformat(avctx); + + if ((ret = ff_mpv_common_init(s)) < 0) + return ret; + if (!s->avctx->lowres) + for (int i = 0; i < s->slice_context_count; i++) + ff_mpv_framesize_disable(&s->thread_context[i]->sc); + } + return 0; +} + +static int mpeg1_decode_picture(AVCodecContext *avctx, const uint8_t *buf, + int buf_size) +{ + Mpeg1Context *s1 = avctx->priv_data; + MPVContext *const s = &s1->slice.c; + GetBitContext gb0, *const gb = &gb0; + int ref, f_code, vbv_delay, ret; + + ret = init_get_bits8(gb, buf, buf_size); + if (ret < 0) + return ret; + + ref = get_bits(gb, 10); /* temporal ref */ + s->pict_type = get_bits(gb, 3); + if (s->pict_type == 0 || s->pict_type > 3) + return AVERROR_INVALIDDATA; + + vbv_delay = get_bits(gb, 16); + s1->vbv_delay = vbv_delay; + if (s->pict_type == AV_PICTURE_TYPE_P || + s->pict_type == AV_PICTURE_TYPE_B) { + s->full_pel[0] = get_bits1(gb); + f_code = get_bits(gb, 3); + if (f_code == 0 && (avctx->err_recognition & (AV_EF_BITSTREAM|AV_EF_COMPLIANT))) + return AVERROR_INVALIDDATA; + f_code += !f_code; + s->mpeg_f_code[0][0] = f_code; + s->mpeg_f_code[0][1] = f_code; + } + if (s->pict_type == AV_PICTURE_TYPE_B) { + s->full_pel[1] = get_bits1(gb); + f_code = get_bits(gb, 3); + if (f_code == 0 && (avctx->err_recognition & (AV_EF_BITSTREAM|AV_EF_COMPLIANT))) + return AVERROR_INVALIDDATA; + f_code += !f_code; + s->mpeg_f_code[1][0] = f_code; + s->mpeg_f_code[1][1] = f_code; + } + + if (avctx->debug & FF_DEBUG_PICT_INFO) + av_log(avctx, AV_LOG_DEBUG, + "vbv_delay %d, ref %d type:%d\n", vbv_delay, ref, s->pict_type); + + return 0; +} + +static void mpeg_decode_sequence_extension(Mpeg1Context *const s1, + GetBitContext *const gb) +{ + MPVContext *const s = &s1->slice.c; + int horiz_size_ext, vert_size_ext; + int bit_rate_ext; + + skip_bits(gb, 1); /* profile and level esc*/ + s->avctx->profile = get_bits(gb, 3); + s->avctx->level = get_bits(gb, 4); + s->progressive_sequence = get_bits1(gb); /* progressive_sequence */ + s->chroma_format = get_bits(gb, 2); /* chroma_format 1=420, 2=422, 3=444 */ + + if (!s->chroma_format) { + s->chroma_format = CHROMA_420; + av_log(s->avctx, AV_LOG_WARNING, "Chroma format invalid\n"); + } + + horiz_size_ext = get_bits(gb, 2); + vert_size_ext = get_bits(gb, 2); + s->width |= (horiz_size_ext << 12); + s->height |= (vert_size_ext << 12); + bit_rate_ext = get_bits(gb, 12); /* XXX: handle it */ + s1->bit_rate += (bit_rate_ext << 18) * 400LL; + check_marker(s->avctx, gb, "after bit rate extension"); + s->avctx->rc_buffer_size += get_bits(gb, 8) * 1024 * 16 << 10; + + s->low_delay = get_bits1(gb); + if (s->avctx->flags & AV_CODEC_FLAG_LOW_DELAY) + s->low_delay = 1; + + s1->frame_rate_ext.num = get_bits(gb, 2) + 1; + s1->frame_rate_ext.den = get_bits(gb, 5) + 1; + + ff_dlog(s->avctx, "sequence extension\n"); + s->codec_id = s->avctx->codec_id = AV_CODEC_ID_MPEG2VIDEO; + + if (s->avctx->debug & FF_DEBUG_PICT_INFO) + av_log(s->avctx, AV_LOG_DEBUG, + "profile: %d, level: %d ps: %d cf:%d vbv buffer: %d, bitrate:%"PRId64"\n", + s->avctx->profile, s->avctx->level, s->progressive_sequence, s->chroma_format, + s->avctx->rc_buffer_size, s1->bit_rate); +} + +static void mpeg_decode_sequence_display_extension(Mpeg1Context *const s1, + GetBitContext *const gb) +{ + MPVContext *const s = &s1->slice.c; + int color_description, w, h; + + skip_bits(gb, 3); /* video format */ + color_description = get_bits1(gb); + if (color_description) { + s->avctx->color_primaries = get_bits(gb, 8); + s->avctx->color_trc = get_bits(gb, 8); + s->avctx->colorspace = get_bits(gb, 8); + } + w = get_bits(gb, 14); + skip_bits(gb, 1); // marker + h = get_bits(gb, 14); + // remaining 3 bits are zero padding + + s1->pan_scan.width = 16 * w; + s1->pan_scan.height = 16 * h; + + if (s->avctx->debug & FF_DEBUG_PICT_INFO) + av_log(s->avctx, AV_LOG_DEBUG, "sde w:%d, h:%d\n", w, h); +} + +static void mpeg_decode_picture_display_extension(Mpeg1Context *const s1, + GetBitContext *const gb) +{ + MPVContext *const s = &s1->slice.c; + int i, nofco; + + nofco = 1; + if (s->progressive_sequence) { + if (s->repeat_first_field) { + nofco++; + if (s->top_field_first) + nofco++; + } + } else { + if (s->picture_structure == PICT_FRAME) { + nofco++; + if (s->repeat_first_field) + nofco++; + } + } + for (i = 0; i < nofco; i++) { + s1->pan_scan.position[i][0] = get_sbits(gb, 16); + skip_bits(gb, 1); // marker + s1->pan_scan.position[i][1] = get_sbits(gb, 16); + skip_bits(gb, 1); // marker + } + + if (s->avctx->debug & FF_DEBUG_PICT_INFO) + av_log(s->avctx, AV_LOG_DEBUG, + "pde (%"PRId16",%"PRId16") (%"PRId16",%"PRId16") (%"PRId16",%"PRId16")\n", + s1->pan_scan.position[0][0], s1->pan_scan.position[0][1], + s1->pan_scan.position[1][0], s1->pan_scan.position[1][1], + s1->pan_scan.position[2][0], s1->pan_scan.position[2][1]); +} + +static int load_matrix(MPVContext *const s, GetBitContext *const gb, + uint16_t matrix0[64], uint16_t matrix1[64], int intra) +{ + int i; + + for (i = 0; i < 64; i++) { + int j = s->idsp.idct_permutation[ff_zigzag_direct[i]]; + int v = get_bits(gb, 8); + if (v == 0) { + av_log(s->avctx, AV_LOG_ERROR, "matrix damaged\n"); + return AVERROR_INVALIDDATA; + } + if (intra && i == 0 && v != 8) { + av_log(s->avctx, AV_LOG_DEBUG, "intra matrix specifies invalid DC quantizer %d, ignoring\n", v); + v = 8; // needed by pink.mpg / issue1046 + } + matrix0[j] = v; + if (matrix1) + matrix1[j] = v; + } + return 0; +} + +static void mpeg_decode_quant_matrix_extension(MPVContext *const s, + GetBitContext *const gb) +{ + ff_dlog(s->avctx, "matrix extension\n"); + + if (get_bits1(gb)) + load_matrix(s, gb, s->chroma_intra_matrix, s->intra_matrix, 1); + if (get_bits1(gb)) + load_matrix(s, gb, s->chroma_inter_matrix, s->inter_matrix, 0); + if (get_bits1(gb)) + load_matrix(s, gb, s->chroma_intra_matrix, NULL, 1); + if (get_bits1(gb)) + load_matrix(s, gb, s->chroma_inter_matrix, NULL, 0); +} + +static int mpeg_decode_picture_coding_extension(Mpeg1Context *const s1, + GetBitContext *const gb) +{ + MPVContext *const s = &s1->slice.c; + + s->full_pel[0] = s->full_pel[1] = 0; + s->mpeg_f_code[0][0] = get_bits(gb, 4); + s->mpeg_f_code[0][1] = get_bits(gb, 4); + s->mpeg_f_code[1][0] = get_bits(gb, 4); + s->mpeg_f_code[1][1] = get_bits(gb, 4); + s->mpeg_f_code[0][0] += !s->mpeg_f_code[0][0]; + s->mpeg_f_code[0][1] += !s->mpeg_f_code[0][1]; + s->mpeg_f_code[1][0] += !s->mpeg_f_code[1][0]; + s->mpeg_f_code[1][1] += !s->mpeg_f_code[1][1]; + if (!s->pict_type && s->context_initialized) { + av_log(s->avctx, AV_LOG_ERROR, "Missing picture start code\n"); + if (s->avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + av_log(s->avctx, AV_LOG_WARNING, "Guessing pict_type from mpeg_f_code\n"); + if (s->mpeg_f_code[1][0] == 15 && s->mpeg_f_code[1][1] == 15) { + if (s->mpeg_f_code[0][0] == 15 && s->mpeg_f_code[0][1] == 15) + s->pict_type = AV_PICTURE_TYPE_I; + else + s->pict_type = AV_PICTURE_TYPE_P; + } else + s->pict_type = AV_PICTURE_TYPE_B; + } + + s->intra_dc_precision = get_bits(gb, 2); + s->picture_structure = get_bits(gb, 2); + s->top_field_first = get_bits1(gb); + s->frame_pred_frame_dct = get_bits1(gb); + s->concealment_motion_vectors = get_bits1(gb); + s->q_scale_type = get_bits1(gb); + s->intra_vlc_format = get_bits1(gb); + s->alternate_scan = get_bits1(gb); + s->repeat_first_field = get_bits1(gb); + s->chroma_420_type = get_bits1(gb); + s->progressive_frame = get_bits1(gb); + + // We only initialize intra_scantable.permutated, as this is all we use. + ff_permute_scantable(s->intra_scantable.permutated, + s->alternate_scan ? ff_alternate_vertical_scan : ff_zigzag_direct, + s->idsp.idct_permutation); + + /* composite display not parsed */ + ff_dlog(s->avctx, "intra_dc_precision=%d\n", s->intra_dc_precision); + ff_dlog(s->avctx, "picture_structure=%d\n", s->picture_structure); + ff_dlog(s->avctx, "top field first=%d\n", s->top_field_first); + ff_dlog(s->avctx, "repeat first field=%d\n", s->repeat_first_field); + ff_dlog(s->avctx, "conceal=%d\n", s->concealment_motion_vectors); + ff_dlog(s->avctx, "intra_vlc_format=%d\n", s->intra_vlc_format); + ff_dlog(s->avctx, "alternate_scan=%d\n", s->alternate_scan); + ff_dlog(s->avctx, "frame_pred_frame_dct=%d\n", s->frame_pred_frame_dct); + ff_dlog(s->avctx, "progressive_frame=%d\n", s->progressive_frame); + + return 0; +} + +static int mpeg_field_start(Mpeg1Context *s1, const uint8_t *buf, int buf_size) +{ + MPVContext *const s = &s1->slice.c; + AVCodecContext *avctx = s->avctx; + int second_field = 0; + int ret; + + if (!(avctx->flags2 & AV_CODEC_FLAG2_CHUNKS)) { + if (s->mb_width * s->mb_height * 11LL / (33 * 2 * 8) > buf_size) + return AVERROR_INVALIDDATA; + } + + /* start frame decoding */ + if (s->first_field || s->picture_structure == PICT_FRAME) { + AVFrameSideData *pan_scan; + + if ((ret = ff_mpv_frame_start(s, avctx)) < 0) + return ret; + + if (s->picture_structure != PICT_FRAME) { + s->cur_pic.ptr->f->flags |= AV_FRAME_FLAG_TOP_FIELD_FIRST * + (s->picture_structure == PICT_TOP_FIELD); + + for (int i = 0; i < 3; i++) { + if (s->picture_structure == PICT_BOTTOM_FIELD) { + s->cur_pic.data[i] = FF_PTR_ADD(s->cur_pic.data[i], + s->cur_pic.linesize[i]); + } + s->cur_pic.linesize[i] *= 2; + } + } + + ff_mpeg_er_frame_start(s); + + /* first check if we must repeat the frame */ + s->cur_pic.ptr->f->repeat_pict = 0; + if (s->repeat_first_field) { + if (s->progressive_sequence) { + if (s->top_field_first) + s->cur_pic.ptr->f->repeat_pict = 4; + else + s->cur_pic.ptr->f->repeat_pict = 2; + } else if (s->progressive_frame) { + s->cur_pic.ptr->f->repeat_pict = 1; + } + } + + ret = ff_frame_new_side_data(s->avctx, s->cur_pic.ptr->f, + AV_FRAME_DATA_PANSCAN, sizeof(s1->pan_scan), + &pan_scan); + if (ret < 0) + return ret; + if (pan_scan) + memcpy(pan_scan->data, &s1->pan_scan, sizeof(s1->pan_scan)); + + if (s1->a53_buf_ref) { + ret = ff_frame_new_side_data_from_buf( + s->avctx, s->cur_pic.ptr->f, AV_FRAME_DATA_A53_CC, + &s1->a53_buf_ref); + if (ret < 0) + return ret; + } + + if (s1->has_stereo3d) { + AVStereo3D *stereo = av_stereo3d_create_side_data(s->cur_pic.ptr->f); + if (!stereo) + return AVERROR(ENOMEM); + + stereo->type = s1->stereo3d_type; + s1->has_stereo3d = 0; + } + + if (s1->has_afd) { + AVFrameSideData *sd; + ret = ff_frame_new_side_data(s->avctx, s->cur_pic.ptr->f, + AV_FRAME_DATA_AFD, 1, &sd); + if (ret < 0) + return ret; + if (sd) + *sd->data = s1->afd; + s1->has_afd = 0; + } + } else { // second field + second_field = 1; + if (!s->cur_pic.ptr) { + av_log(s->avctx, AV_LOG_ERROR, "first field missing\n"); + return AVERROR_INVALIDDATA; + } + + if (s->avctx->hwaccel) { + if ((ret = FF_HW_SIMPLE_CALL(s->avctx, end_frame)) < 0) { + av_log(avctx, AV_LOG_ERROR, + "hardware accelerator failed to decode first field\n"); + return ret; + } + } + ret = ff_mpv_alloc_dummy_frames(s); + if (ret < 0) + return ret; + + for (int i = 0; i < 3; i++) { + s->cur_pic.data[i] = s->cur_pic.ptr->f->data[i]; + if (s->picture_structure == PICT_BOTTOM_FIELD) + s->cur_pic.data[i] += + s->cur_pic.ptr->f->linesize[i]; + } + } + + if (avctx->hwaccel) { + if ((ret = FF_HW_CALL(avctx, start_frame, NULL, buf, buf_size)) < 0) + return ret; + } else if (s->codec_tag == MKTAG('V', 'C', 'R', '2')) { + // Exchange UV + FFSWAP(uint8_t*, s->cur_pic.data[1], s->cur_pic.data[2]); + FFSWAP(ptrdiff_t, s->cur_pic.linesize[1], s->cur_pic.linesize[2]); + if (!second_field) { + FFSWAP(uint8_t*, s->next_pic.data[1], s->next_pic.data[2]); + FFSWAP(ptrdiff_t, s->next_pic.linesize[1], s->next_pic.linesize[2]); + FFSWAP(uint8_t*, s->last_pic.data[1], s->last_pic.data[2]); + FFSWAP(ptrdiff_t, s->last_pic.linesize[1], s->last_pic.linesize[2]); + } + } + + return 0; +} + +#define DECODE_SLICE_ERROR -1 +#define DECODE_SLICE_OK 0 + +/** + * Decode a slice. + * Mpeg12SliceContext.c.mb_y must be set to the MB row from the startcode. + * @return DECODE_SLICE_ERROR if the slice is damaged, + * DECODE_SLICE_OK if this slice is OK + */ +static int mpeg_decode_slice(Mpeg12SliceContext *const s, int mb_y, + const uint8_t **buf, int buf_size) +{ + AVCodecContext *avctx = s->c.avctx; + const int lowres = s->c.avctx->lowres; + const int field_pic = s->c.picture_structure != PICT_FRAME; + int ret; + + s->c.resync_mb_x = + s->c.resync_mb_y = -1; + + av_assert0(mb_y < s->c.mb_height); + + ret = init_get_bits8(&s->gb, *buf, buf_size); + if (ret < 0) + return ret; + + if (s->c.codec_id != AV_CODEC_ID_MPEG1VIDEO && s->c.mb_height > 2800/16) + skip_bits(&s->gb, 3); + + s->c.interlaced_dct = 0; + + s->c.qscale = mpeg_get_qscale(&s->gb, s->c.q_scale_type); + + if (s->c.qscale == 0) { + av_log(s->c.avctx, AV_LOG_ERROR, "qscale == 0\n"); + return AVERROR_INVALIDDATA; + } + + /* extra slice info */ + if (skip_1stop_8data_bits(&s->gb) < 0) + return AVERROR_INVALIDDATA; + + s->c.mb_x = 0; + + if (mb_y == 0 && s->c.codec_tag == AV_RL32("SLIF")) { + skip_bits1(&s->gb); + } else { + while (get_bits_left(&s->gb) > 0) { + int code = get_vlc2(&s->gb, ff_mbincr_vlc, + MBINCR_VLC_BITS, 2); + if (code < 0) { + av_log(s->c.avctx, AV_LOG_ERROR, "first mb_incr damaged\n"); + return AVERROR_INVALIDDATA; + } + if (code >= 33) { + if (code == 33) + s->c.mb_x += 33; + /* otherwise, stuffing, nothing to do */ + } else { + s->c.mb_x += code; + break; + } + } + } + + if (s->c.mb_x >= (unsigned) s->c.mb_width) { + av_log(s->c.avctx, AV_LOG_ERROR, "initial skip overflow\n"); + return AVERROR_INVALIDDATA; + } + + if (avctx->hwaccel) { + const uint8_t *buf_end, *buf_start = *buf - 4; /* include start_code */ + int start_code = -1; + buf_end = avpriv_find_start_code(buf_start + 2, *buf + buf_size, &start_code); + if (buf_end < *buf + buf_size) + buf_end -= 4; + s->c.mb_y = mb_y; + if (FF_HW_CALL(avctx, decode_slice, buf_start, buf_end - buf_start) < 0) + return DECODE_SLICE_ERROR; + *buf = buf_end; + return DECODE_SLICE_OK; + } + + s->c.resync_mb_x = s->c.mb_x; + s->c.resync_mb_y = s->c.mb_y = mb_y; + ff_init_block_index(&s->c); + + if (s->c.mb_y == 0 && s->c.mb_x == 0 && (s->c.first_field || s->c.picture_structure == PICT_FRAME)) { + if (s->c.avctx->debug & FF_DEBUG_PICT_INFO) { + av_log(s->c.avctx, AV_LOG_DEBUG, + "qp:%d fc:%2d%2d%2d%2d %c %s %s %s %s dc:%d pstruct:%d fdct:%d cmv:%d qtype:%d ivlc:%d rff:%d %s\n", + s->c.qscale, + s->c.mpeg_f_code[0][0], s->c.mpeg_f_code[0][1], + s->c.mpeg_f_code[1][0], s->c.mpeg_f_code[1][1], + s->c.pict_type == AV_PICTURE_TYPE_I ? 'I' : + (s->c.pict_type == AV_PICTURE_TYPE_P ? 'P' : + (s->c.pict_type == AV_PICTURE_TYPE_B ? 'B' : 'S')), + s->c.progressive_sequence ? "ps" : "", + s->c.progressive_frame ? "pf" : "", + s->c.alternate_scan ? "alt" : "", + s->c.top_field_first ? "top" : "", + s->c.intra_dc_precision, s->c.picture_structure, + s->c.frame_pred_frame_dct, s->c.concealment_motion_vectors, + s->c.q_scale_type, s->c.intra_vlc_format, + s->c.repeat_first_field, s->c.chroma_420_type ? "420" : ""); + } + } + + s->last_dc[0] = 128 << s->c.intra_dc_precision; + s->last_dc[1] = s->last_dc[0]; + s->last_dc[2] = s->last_dc[0]; + memset(s->c.last_mv, 0, sizeof(s->c.last_mv)); + + for (int mb_skip_run = 0;;) { + ret = mpeg_decode_mb(s, &mb_skip_run); + if (ret < 0) + return ret; + + // Note motion_val is normally NULL unless we want to extract the MVs. + if (s->c.cur_pic.motion_val[0]) { + const int wrap = s->c.b8_stride; + int xy = s->c.mb_x * 2 + s->c.mb_y * 2 * wrap; + int b8_xy = 4 * (s->c.mb_x + s->c.mb_y * s->c.mb_stride); + int motion_x, motion_y, dir, i; + + for (i = 0; i < 2; i++) { + for (dir = 0; dir < 2; dir++) { + if (s->c.mb_intra || + (dir == 1 && s->c.pict_type != AV_PICTURE_TYPE_B)) { + motion_x = motion_y = 0; + } else if (s->c.mv_type == MV_TYPE_16X16 || + (s->c.mv_type == MV_TYPE_FIELD && field_pic)) { + motion_x = s->c.mv[dir][0][0]; + motion_y = s->c.mv[dir][0][1]; + } else { /* if ((s->c.mv_type == MV_TYPE_FIELD) || (s->c.mv_type == MV_TYPE_16X8)) */ + motion_x = s->c.mv[dir][i][0]; + motion_y = s->c.mv[dir][i][1]; + } + + s->c.cur_pic.motion_val[dir][xy][0] = motion_x; + s->c.cur_pic.motion_val[dir][xy][1] = motion_y; + s->c.cur_pic.motion_val[dir][xy + 1][0] = motion_x; + s->c.cur_pic.motion_val[dir][xy + 1][1] = motion_y; + s->c.cur_pic.ref_index [dir][b8_xy] = + s->c.cur_pic.ref_index [dir][b8_xy + 1] = s->c.field_select[dir][i]; + av_assert2(s->c.field_select[dir][i] == 0 || + s->c.field_select[dir][i] == 1); + } + xy += wrap; + b8_xy += 2; + } + } + + s->c.dest[0] += 16 >> lowres; + s->c.dest[1] +=(16 >> lowres) >> s->c.chroma_x_shift; + s->c.dest[2] +=(16 >> lowres) >> s->c.chroma_x_shift; + + ff_mpv_reconstruct_mb(&s->c, s->block); + + if (++s->c.mb_x >= s->c.mb_width) { + const int mb_size = 16 >> s->c.avctx->lowres; + int left; + + ff_mpeg_draw_horiz_band(&s->c, mb_size * (s->c.mb_y >> field_pic), mb_size); + + s->c.mb_x = 0; + s->c.mb_y += 1 << field_pic; + + if (s->c.mb_y >= s->c.mb_height) { + int left = get_bits_left(&s->gb); + int is_d10 = s->c.chroma_format == CHROMA_422 && + s->c.pict_type == AV_PICTURE_TYPE_I && + avctx->profile == 0 && avctx->level == 5 && + s->c.intra_dc_precision == 2 && + s->c.q_scale_type == 1 && s->c.alternate_scan == 0 && + s->c.progressive_frame == 0 + /* vbv_delay == 0xBBB || 0xE10 */; + + if (left >= 32 && !is_d10) { + GetBitContext gb = s->gb; + align_get_bits(&gb); + if (show_bits(&gb, 24) == 0x060E2B) { + av_log(avctx, AV_LOG_DEBUG, "Invalid MXF data found in video stream\n"); + is_d10 = 1; + } + if (left > 32 && show_bits_long(&gb, 32) == 0x201) { + av_log(avctx, AV_LOG_DEBUG, "skipping m704 alpha (unsupported)\n"); + goto eos; + } + } + + if (left < 0 || + (left && show_bits(&s->gb, FFMIN(left, 23)) && !is_d10) || + ((avctx->err_recognition & (AV_EF_BITSTREAM | AV_EF_AGGRESSIVE)) && left > 8)) { + av_log(avctx, AV_LOG_ERROR, "end mismatch left=%d %0X at %d %d\n", + left, left>0 ? show_bits(&s->gb, FFMIN(left, 23)) : 0, s->c.mb_x, s->c.mb_y); + return AVERROR_INVALIDDATA; + } else + goto eos; + } + // There are some files out there which are missing the last slice + // in cases where the slice is completely outside the visible + // area, we detect this here instead of running into the end expecting + // more data + left = get_bits_left(&s->gb); + if (s->c.mb_y >= ((s->c.height + 15) >> 4) && + !s->c.progressive_sequence && + left <= 25 && + left >= 0 && + mb_skip_run == -1 && + (!left || show_bits(&s->gb, left) == 0)) + goto eos; + + ff_init_block_index(&s->c); + } + + /* skip mb handling */ + if (mb_skip_run == -1) { + /* read increment again */ + mb_skip_run = 0; + for (;;) { + int code = get_vlc2(&s->gb, ff_mbincr_vlc, + MBINCR_VLC_BITS, 2); + if (code < 0) { + av_log(s->c.avctx, AV_LOG_ERROR, "mb incr damaged\n"); + return AVERROR_INVALIDDATA; + } + if (code >= 33) { + if (code == 33) { + mb_skip_run += 33; + } else if (code == 35) { + if (mb_skip_run != 0 || show_bits(&s->gb, 15) != 0) { + av_log(s->c.avctx, AV_LOG_ERROR, "slice mismatch\n"); + return AVERROR_INVALIDDATA; + } + goto eos; /* end of slice */ + } + /* otherwise, stuffing, nothing to do */ + } else { + mb_skip_run += code; + break; + } + } + if (mb_skip_run) { + int i; + if (s->c.pict_type == AV_PICTURE_TYPE_I) { + av_log(s->c.avctx, AV_LOG_ERROR, + "skipped MB in I-frame at %d %d\n", s->c.mb_x, s->c.mb_y); + return AVERROR_INVALIDDATA; + } + + /* skip mb */ + s->c.mb_intra = 0; + for (i = 0; i < 12; i++) + s->c.block_last_index[i] = -1; + s->last_dc[0] = s->last_dc[1] = s->last_dc[2] = 128 << s->c.intra_dc_precision; + if (s->c.picture_structure == PICT_FRAME) + s->c.mv_type = MV_TYPE_16X16; + else + s->c.mv_type = MV_TYPE_FIELD; + if (s->c.pict_type == AV_PICTURE_TYPE_P) { + /* if P type, zero motion vector is implied */ + s->c.mv_dir = MV_DIR_FORWARD; + s->c.mv[0][0][0] = s->c.mv[0][0][1] = 0; + s->c.last_mv[0][0][0] = s->c.last_mv[0][0][1] = 0; + s->c.last_mv[0][1][0] = s->c.last_mv[0][1][1] = 0; + s->c.field_select[0][0] = (s->c.picture_structure - 1) & 1; + } else { + /* if B type, reuse previous vectors and directions */ + s->c.mv[0][0][0] = s->c.last_mv[0][0][0]; + s->c.mv[0][0][1] = s->c.last_mv[0][0][1]; + s->c.mv[1][0][0] = s->c.last_mv[1][0][0]; + s->c.mv[1][0][1] = s->c.last_mv[1][0][1]; + s->c.field_select[0][0] = (s->c.picture_structure - 1) & 1; + s->c.field_select[1][0] = (s->c.picture_structure - 1) & 1; + } + } + } + } +eos: // end of slice + if (get_bits_left(&s->gb) < 0) { + av_log(s->c.avctx, AV_LOG_ERROR, "overread %d\n", -get_bits_left(&s->gb)); + return AVERROR_INVALIDDATA; + } + *buf += (get_bits_count(&s->gb) - 1) / 8; + ff_dlog(s->c.avctx, "Slice start:%d %d end:%d %d\n", s->c.resync_mb_x, s->c.resync_mb_y, s->c.mb_x, s->c.mb_y); + return 0; +} + +static int slice_decode_thread(AVCodecContext *c, void *arg) +{ + Mpeg12SliceContext *const s = *(void **) arg; + const uint8_t *buf = s->gb.buffer; + const uint8_t *end = buf + get_bits_bytesize(&s->gb, 0); + int mb_y = s->c.start_mb_y; + const int field_pic = s->c.picture_structure != PICT_FRAME; + + s->c.er.error_count = (3 * (s->c.end_mb_y - s->c.start_mb_y) * s->c.mb_width) >> field_pic; + + for (;;) { + uint32_t start_code; + int ret; + + ret = mpeg_decode_slice(s, mb_y, &buf, end - buf); + ff_dlog(c, "ret:%d resync:%d/%d mb:%d/%d ts:%d/%d ec:%d\n", + ret, s->c.resync_mb_x, s->c.resync_mb_y, s->c.mb_x, s->c.mb_y, + s->c.start_mb_y, s->c.end_mb_y, s->c.er.error_count); + if (ret < 0) { + if (c->err_recognition & AV_EF_EXPLODE) + return ret; + if (s->c.resync_mb_x >= 0 && s->c.resync_mb_y >= 0) + ff_er_add_slice(&s->c.er, s->c.resync_mb_x, s->c.resync_mb_y, + s->c.mb_x, s->c.mb_y, + ER_AC_ERROR | ER_DC_ERROR | ER_MV_ERROR); + } else { + ff_er_add_slice(&s->c.er, s->c.resync_mb_x, s->c.resync_mb_y, + s->c.mb_x - 1, s->c.mb_y, + ER_AC_END | ER_DC_END | ER_MV_END); + } + + if (s->c.mb_y == s->c.end_mb_y) + return 0; + + start_code = -1; + buf = avpriv_find_start_code(buf, end, &start_code); + if (start_code < SLICE_MIN_START_CODE || start_code > SLICE_MAX_START_CODE) + return AVERROR_INVALIDDATA; + mb_y = start_code - SLICE_MIN_START_CODE; + if (s->c.codec_id != AV_CODEC_ID_MPEG1VIDEO && s->c.mb_height > 2800/16) + mb_y += (*buf&0xE0)<<2; + mb_y <<= field_pic; + if (s->c.picture_structure == PICT_BOTTOM_FIELD) + mb_y++; + if (mb_y >= s->c.end_mb_y) + return AVERROR_INVALIDDATA; + } +} + +/** + * Handle slice ends. + * @return 1 if it seems to be the last slice + */ +static int slice_end(AVCodecContext *avctx, AVFrame *pict, int *got_output) +{ + Mpeg1Context *s1 = avctx->priv_data; + MPVContext *const s = &s1->slice.c; + + if (!s->context_initialized || !s->cur_pic.ptr) + return 0; + + if (s->avctx->hwaccel) { + int ret = FF_HW_SIMPLE_CALL(s->avctx, end_frame); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, + "hardware accelerator failed to decode picture\n"); + return ret; + } + } + + /* end of slice reached */ + if (/* s->mb_y << field_pic == s->mb_height && */ !s->first_field && !s1->first_slice) { + /* end of image */ + + ff_er_frame_end(&s->er, NULL); + + ff_mpv_frame_end(s); + + if (s->pict_type == AV_PICTURE_TYPE_B || s->low_delay) { + int ret = av_frame_ref(pict, s->cur_pic.ptr->f); + if (ret < 0) + return ret; + ff_print_debug_info(s, s->cur_pic.ptr, pict); + ff_mpv_export_qp_table(s, pict, s->cur_pic.ptr, FF_MPV_QSCALE_TYPE_MPEG2); + *got_output = 1; + } else { + /* latency of 1 frame for I- and P-frames */ + if (s->last_pic.ptr && !s->last_pic.ptr->dummy) { + int ret = av_frame_ref(pict, s->last_pic.ptr->f); + if (ret < 0) + return ret; + ff_print_debug_info(s, s->last_pic.ptr, pict); + ff_mpv_export_qp_table(s, pict, s->last_pic.ptr, FF_MPV_QSCALE_TYPE_MPEG2); + *got_output = 1; + } + } + + return 1; + } else { + return 0; + } +} + +static int mpeg1_decode_sequence(AVCodecContext *avctx, + const uint8_t *buf, int buf_size) +{ + Mpeg1Context *s1 = avctx->priv_data; + MPVContext *const s = &s1->slice.c; + GetBitContext gb0, *const gb = &gb0; + int width, height; + int i, v, j; + + int ret = init_get_bits8(gb, buf, buf_size); + if (ret < 0) + return ret; + + width = get_bits(gb, 12); + height = get_bits(gb, 12); + if (width == 0 || height == 0) { + av_log(avctx, AV_LOG_WARNING, + "Invalid horizontal or vertical size value.\n"); + if (avctx->err_recognition & (AV_EF_BITSTREAM | AV_EF_COMPLIANT)) + return AVERROR_INVALIDDATA; + } + s1->aspect_ratio_info = get_bits(gb, 4); + if (s1->aspect_ratio_info == 0) { + av_log(avctx, AV_LOG_ERROR, "aspect ratio has forbidden 0 value\n"); + if (avctx->err_recognition & (AV_EF_BITSTREAM | AV_EF_COMPLIANT)) + return AVERROR_INVALIDDATA; + } + s1->frame_rate_index = get_bits(gb, 4); + if (s1->frame_rate_index == 0 || s1->frame_rate_index > 13) { + av_log(avctx, AV_LOG_WARNING, + "frame_rate_index %d is invalid\n", s1->frame_rate_index); + s1->frame_rate_index = 1; + } + s1->bit_rate = get_bits(gb, 18) * 400; + if (check_marker(s->avctx, gb, "in sequence header") == 0) { + return AVERROR_INVALIDDATA; + } + + s->avctx->rc_buffer_size = get_bits(gb, 10) * 1024 * 16; + skip_bits(gb, 1); + + /* get matrix */ + if (get_bits1(gb)) { + load_matrix(s, gb, s->chroma_intra_matrix, s->intra_matrix, 1); + } else { + for (i = 0; i < 64; i++) { + j = s->idsp.idct_permutation[i]; + v = ff_mpeg1_default_intra_matrix[i]; + s->intra_matrix[j] = v; + s->chroma_intra_matrix[j] = v; + } + } + if (get_bits1(gb)) { + load_matrix(s, gb, s->chroma_inter_matrix, s->inter_matrix, 0); + } else { + for (i = 0; i < 64; i++) { + int j = s->idsp.idct_permutation[i]; + v = ff_mpeg1_default_non_intra_matrix[i]; + s->inter_matrix[j] = v; + s->chroma_inter_matrix[j] = v; + } + } + + if (show_bits(gb, 23) != 0) { + av_log(s->avctx, AV_LOG_ERROR, "sequence header damaged\n"); + return AVERROR_INVALIDDATA; + } + + s->width = width; + s->height = height; + + /* We set MPEG-2 parameters so that it emulates MPEG-1. */ + s->progressive_sequence = 1; + s->progressive_frame = 1; + s->picture_structure = PICT_FRAME; + s->first_field = 0; + s->frame_pred_frame_dct = 1; + s->chroma_format = CHROMA_420; + s->codec_id = + s->avctx->codec_id = AV_CODEC_ID_MPEG1VIDEO; + if (s->avctx->flags & AV_CODEC_FLAG_LOW_DELAY) + s->low_delay = 1; + + if (s->avctx->debug & FF_DEBUG_PICT_INFO) + av_log(s->avctx, AV_LOG_DEBUG, "vbv buffer: %d, bitrate:%"PRId64", aspect_ratio_info: %d \n", + s->avctx->rc_buffer_size, s1->bit_rate, s1->aspect_ratio_info); + + return 0; +} + +static int vcr2_init_sequence(AVCodecContext *avctx) +{ + Mpeg1Context *s1 = avctx->priv_data; + MPVContext *const s = &s1->slice.c; + int i, v, ret; + + /* start new MPEG-1 context decoding */ + if (s->context_initialized) + ff_mpv_common_end(s); + + s->width = avctx->coded_width; + s->height = avctx->coded_height; + avctx->has_b_frames = 0; // true? + s->low_delay = 1; + + avctx->pix_fmt = mpeg_get_pixelformat(avctx); + + if ((ret = ff_mpv_common_init(s)) < 0) + return ret; + if (!s->avctx->lowres) + for (int i = 0; i < s->slice_context_count; i++) + ff_mpv_framesize_disable(&s->thread_context[i]->sc); + + for (i = 0; i < 64; i++) { + int j = s->idsp.idct_permutation[i]; + v = ff_mpeg1_default_intra_matrix[i]; + s->intra_matrix[j] = v; + s->chroma_intra_matrix[j] = v; + + v = ff_mpeg1_default_non_intra_matrix[i]; + s->inter_matrix[j] = v; + s->chroma_inter_matrix[j] = v; + } + + s->progressive_sequence = 1; + s->progressive_frame = 1; + s->picture_structure = PICT_FRAME; + s->first_field = 0; + s->frame_pred_frame_dct = 1; + s->chroma_format = CHROMA_420; + if (s->codec_tag == AV_RL32("BW10")) { + s->codec_id = s->avctx->codec_id = AV_CODEC_ID_MPEG1VIDEO; + } else { + s->codec_id = s->avctx->codec_id = AV_CODEC_ID_MPEG2VIDEO; + } + s1->save_progressive_seq = s->progressive_sequence; + s1->save_chroma_format = s->chroma_format; + return 0; +} + +static void mpeg_set_cc_format(AVCodecContext *avctx, enum Mpeg2ClosedCaptionsFormat format, + const char *label) +{ + Mpeg1Context *s1 = avctx->priv_data; + + av_assert2(format != CC_FORMAT_AUTO); + + if (!s1->cc_format) { + s1->cc_format = format; + + av_log(avctx, AV_LOG_DEBUG, "CC: first seen substream is %s format\n", label); + } +} + +static int mpeg_decode_a53_cc(AVCodecContext *avctx, + const uint8_t *p, int buf_size) +{ + Mpeg1Context *s1 = avctx->priv_data; + + if ((!s1->cc_format || s1->cc_format == CC_FORMAT_A53_PART4) && + buf_size >= 6 && + p[0] == 'G' && p[1] == 'A' && p[2] == '9' && p[3] == '4' && + p[4] == 3 && (p[5] & 0x40)) { + /* extract A53 Part 4 CC data */ + int cc_count = p[5] & 0x1f; + if (cc_count > 0 && buf_size >= 7 + cc_count * 3) { + int old_size = s1->a53_buf_ref ? s1->a53_buf_ref->size : 0; + const uint64_t new_size = (old_size + cc_count + * UINT64_C(3)); + int ret; + + if (new_size > 3*A53_MAX_CC_COUNT) + return AVERROR(EINVAL); + + ret = av_buffer_realloc(&s1->a53_buf_ref, new_size); + if (ret >= 0) + memcpy(s1->a53_buf_ref->data + old_size, p + 7, cc_count * UINT64_C(3)); + + mpeg_set_cc_format(avctx, CC_FORMAT_A53_PART4, "A/53 Part 4"); + } + return 1; + } else if ((!s1->cc_format || s1->cc_format == CC_FORMAT_SCTE20) && + buf_size >= 2 && + p[0] == 0x03 && (p[1]&0x7f) == 0x01) { + /* extract SCTE-20 CC data */ + GetBitContext gb; + int cc_count = 0; + int i, ret; + + ret = init_get_bits8(&gb, p + 2, buf_size - 2); + if (ret < 0) + return ret; + cc_count = get_bits(&gb, 5); + if (cc_count > 0) { + int old_size = s1->a53_buf_ref ? s1->a53_buf_ref->size : 0; + const uint64_t new_size = (old_size + cc_count + * UINT64_C(3)); + if (new_size > 3*A53_MAX_CC_COUNT) + return AVERROR(EINVAL); + + ret = av_buffer_realloc(&s1->a53_buf_ref, new_size); + if (ret >= 0) { + uint8_t field, cc1, cc2; + uint8_t *cap = s1->a53_buf_ref->data + old_size; + + memset(cap, 0, cc_count * 3); + for (i = 0; i < cc_count && get_bits_left(&gb) >= 26; i++) { + skip_bits(&gb, 2); // priority + field = get_bits(&gb, 2); + skip_bits(&gb, 5); // line_offset + cc1 = get_bits(&gb, 8); + cc2 = get_bits(&gb, 8); + skip_bits(&gb, 1); // marker + + if (!field) { // forbidden + cap[0] = cap[1] = cap[2] = 0x00; + } else { + field = (field == 2 ? 1 : 0); + if (!s1->slice.c.top_field_first) field = !field; + cap[0] = 0x04 | field; + cap[1] = ff_reverse[cc1]; + cap[2] = ff_reverse[cc2]; + } + cap += 3; + } + } + + mpeg_set_cc_format(avctx, CC_FORMAT_SCTE20, "SCTE-20"); + } + return 1; + } else if ((!s1->cc_format || s1->cc_format == CC_FORMAT_DVD) && + buf_size >= 11 && + p[0] == 'C' && p[1] == 'C' && p[2] == 0x01 && p[3] == 0xf8) { + /* extract DVD CC data + * + * uint32_t user_data_start_code 0x000001B2 (big endian) + * uint16_t user_identifier 0x4343 "CC" + * uint8_t user_data_type_code 0x01 + * uint8_t caption_block_size 0xF8 + * uint8_t + * bit 7 caption_odd_field_first 1=odd field (CC1/CC2) first 0=even field (CC3/CC4) first + * bit 6 caption_filler 0 + * bit 5:1 caption_block_count number of caption blocks (pairs of caption words = frames). Most DVDs use 15 per start of GOP. + * bit 0 caption_extra_field_added 1=one additional caption word + * + * struct caption_field_block { + * uint8_t + * bit 7:1 caption_filler 0x7F (all 1s) + * bit 0 caption_field_odd 1=odd field (this is CC1/CC2) 0=even field (this is CC3/CC4) + * uint8_t caption_first_byte + * uint8_t caption_second_byte + * } caption_block[(caption_block_count * 2) + caption_extra_field_added]; + * + * Some DVDs encode caption data for both fields with caption_field_odd=1. The only way to decode the fields + * correctly is to start on the field indicated by caption_odd_field_first and count between odd/even fields. + * Don't assume that the first caption word is the odd field. There do exist MPEG files in the wild that start + * on the even field. There also exist DVDs in the wild that encode an odd field count and the + * caption_extra_field_added/caption_odd_field_first bits change per packet to allow that. */ + int cc_count = 0; + int i, ret; + // There is a caption count field in the data, but it is often + // incorrect. So count the number of captions present. + for (i = 5; i + 6 <= buf_size && ((p[i] & 0xfe) == 0xfe); i += 6) + cc_count++; + // Transform the DVD format into A53 Part 4 format + if (cc_count > 0) { + int old_size = s1->a53_buf_ref ? s1->a53_buf_ref->size : 0; + const uint64_t new_size = (old_size + cc_count + * UINT64_C(6)); + if (new_size > 3*A53_MAX_CC_COUNT) + return AVERROR(EINVAL); + + ret = av_buffer_realloc(&s1->a53_buf_ref, new_size); + if (ret >= 0) { + uint8_t field1 = !!(p[4] & 0x80); + uint8_t *cap = s1->a53_buf_ref->data + old_size; + p += 5; + for (i = 0; i < cc_count; i++) { + cap[0] = (p[0] == 0xff && field1) ? 0xfc : 0xfd; + cap[1] = p[1]; + cap[2] = p[2]; + cap[3] = (p[3] == 0xff && !field1) ? 0xfc : 0xfd; + cap[4] = p[4]; + cap[5] = p[5]; + cap += 6; + p += 6; + } + } + + mpeg_set_cc_format(avctx, CC_FORMAT_DVD, "DVD"); + } + return 1; + } else if ((!s1->cc_format || s1->cc_format == CC_FORMAT_DISH) && + buf_size >= 12 && + p[0] == 0x05 && p[1] == 0x02) { + /* extract Dish Network CC data */ + const uint8_t cc_header = 0xf8 | 0x04 /* valid */ | 0x00 /* line 21 field 1 */; + uint8_t cc_data[4] = {0}; + int cc_count = 0; + uint8_t cc_type = p[7]; + p += 8; + buf_size -= 8; + + if (cc_type == 0x05 && buf_size >= 7) { + cc_type = p[6]; + p += 7; + buf_size -= 7; + } + + if (cc_type == 0x02 && buf_size >= 4) { /* 2-byte caption, can be repeated */ + cc_count = 1; + cc_data[0] = p[1]; + cc_data[1] = p[2]; + cc_type = p[3]; + + /* Only repeat characters when the next type flag + * is 0x04 and the characters are repeatable (i.e., less than + * 32 with the parity stripped). + */ + if (cc_type == 0x04 && (cc_data[0] & 0x7f) < 32) { + cc_count = 2; + cc_data[2] = cc_data[0]; + cc_data[3] = cc_data[1]; + } + } else if (cc_type == 0x04 && buf_size >= 5) { /* 4-byte caption, not repeated */ + cc_count = 2; + cc_data[0] = p[1]; + cc_data[1] = p[2]; + cc_data[2] = p[3]; + cc_data[3] = p[4]; + } + + if (cc_count > 0) { + int ret; + int old_size = s1->a53_buf_ref ? s1->a53_buf_ref->size : 0; + const uint64_t new_size = (old_size + cc_count * UINT64_C(3)); + if (new_size > 3 * A53_MAX_CC_COUNT) + return AVERROR(EINVAL); + + ret = av_buffer_realloc(&s1->a53_buf_ref, new_size); + if (ret >= 0) { + uint8_t *cap = s1->a53_buf_ref->data + old_size; + cap[0] = cc_header; + cap[1] = cc_data[0]; + cap[2] = cc_data[1]; + if (cc_count == 2) { + cap[3] = cc_header; + cap[4] = cc_data[2]; + cap[5] = cc_data[3]; + } + } + + mpeg_set_cc_format(avctx, CC_FORMAT_DISH, "Dish Network"); + } + return 1; + } + return 0; +} + +static void mpeg_decode_user_data(AVCodecContext *avctx, + const uint8_t *p, int buf_size) +{ + const uint8_t *buf_end = p + buf_size; + Mpeg1Context *s1 = avctx->priv_data; + +#if 0 + int i; + for(i=0; !(!p[i-2] && !p[i-1] && p[i]==1) && i 29){ + int i; + for(i=0; i<20; i++) + if (!memcmp(p+i, "\0TMPGEXS\0", 9)){ + s1->tmpgexs = 1; + } + } + /* we parse the DTG active format information */ + if (buf_end - p >= 5 && + p[0] == 'D' && p[1] == 'T' && p[2] == 'G' && p[3] == '1') { + int flags = p[4]; + p += 5; + if (flags & 0x80) { + /* skip event id */ + p += 2; + } + if (flags & 0x40) { + if (buf_end - p < 1) + return; + s1->has_afd = 1; + s1->afd = p[0] & 0x0f; + } + } else if (buf_end - p >= 6 && + p[0] == 'J' && p[1] == 'P' && p[2] == '3' && p[3] == 'D' && + p[4] == 0x03) { // S3D_video_format_length + // the 0x7F mask ignores the reserved_bit value + const uint8_t S3D_video_format_type = p[5] & 0x7F; + + if (S3D_video_format_type == 0x03 || + S3D_video_format_type == 0x04 || + S3D_video_format_type == 0x08 || + S3D_video_format_type == 0x23) { + + s1->has_stereo3d = 1; + + switch (S3D_video_format_type) { + case 0x03: + s1->stereo3d_type = AV_STEREO3D_SIDEBYSIDE; + break; + case 0x04: + s1->stereo3d_type = AV_STEREO3D_TOPBOTTOM; + break; + case 0x08: + s1->stereo3d_type = AV_STEREO3D_2D; + break; + case 0x23: + s1->stereo3d_type = AV_STEREO3D_SIDEBYSIDE_QUINCUNX; + break; + } + } + } else if (mpeg_decode_a53_cc(avctx, p, buf_size)) { + return; + } +} + +static int mpeg_decode_gop(AVCodecContext *avctx, + const uint8_t *buf, int buf_size) +{ + Mpeg1Context *s1 = avctx->priv_data; + MPVContext *const s = &s1->slice.c; + GetBitContext gb0, *const gb = &gb0; + int broken_link; + int64_t tc; + + int ret = init_get_bits8(gb, buf, buf_size); + if (ret < 0) + return ret; + + tc = s1->timecode_frame_start = get_bits(gb, 25); + + s1->closed_gop = get_bits1(gb); + /* broken_link indicates that after editing the + * reference frames of the first B-Frames after GOP I-Frame + * are missing (open gop) */ + broken_link = get_bits1(gb); + + if (s->avctx->debug & FF_DEBUG_PICT_INFO) { + char tcbuf[AV_TIMECODE_STR_SIZE]; + av_timecode_make_mpeg_tc_string(tcbuf, tc); + av_log(s->avctx, AV_LOG_DEBUG, + "GOP (%s) closed_gop=%d broken_link=%d\n", + tcbuf, s1->closed_gop, broken_link); + } + + return 0; +} + +static void mpeg12_execute_slice_threads(AVCodecContext *avctx, + Mpeg1Context *const s) +{ + if (HAVE_THREADS && (avctx->active_thread_type & FF_THREAD_SLICE) && + !avctx->hwaccel) { + MPVContext *const s2 = &s->slice.c; + int error_count = 0; + + avctx->execute(avctx, slice_decode_thread, + s2->mpeg12_contexts, NULL, + s->slice_count, sizeof(s2->mpeg12_contexts[0])); + + for (int i = 0; i < s->slice_count; i++) { + MpegEncContext *const slice = s2->thread_context[i]; + int slice_err = atomic_load_explicit(&slice->er.error_count, + memory_order_relaxed); + // error_count can get set to INT_MAX on serious errors. + // So use saturated addition. + if ((unsigned)slice_err > INT_MAX - error_count) { + error_count = INT_MAX; + break; + } + error_count += slice_err; + } + atomic_store_explicit(&s2->er.error_count, error_count, + memory_order_relaxed); + } +} + +static int decode_chunks(AVCodecContext *avctx, AVFrame *picture, + int *got_output, const uint8_t *buf, int buf_size) +{ + Mpeg1Context *s = avctx->priv_data; + MPVContext *const s2 = &s->slice.c; + const uint8_t *buf_ptr = buf; + const uint8_t *buf_end = buf + buf_size; + int ret, input_size; + int last_code = 0, skip_frame = 0; + int picture_start_code_seen = 0; + + for (;;) { + /* find next start code */ + uint32_t start_code = -1; + buf_ptr = avpriv_find_start_code(buf_ptr, buf_end, &start_code); + if (start_code > 0x1ff) { + if (!skip_frame) { + mpeg12_execute_slice_threads(avctx, s); + + ret = slice_end(avctx, picture, got_output); + if (ret < 0) + return ret; + } + s2->pict_type = 0; + + if (avctx->err_recognition & AV_EF_EXPLODE && s2->er.error_count) + return AVERROR_INVALIDDATA; + + return FFMAX(0, buf_ptr - buf); + } + + input_size = buf_end - buf_ptr; + + if (avctx->debug & FF_DEBUG_STARTCODE) + av_log(avctx, AV_LOG_DEBUG, "%3"PRIX32" at %td left %d\n", + start_code, buf_ptr - buf, input_size); + + /* prepare data for next start code */ + switch (start_code) { + case SEQ_START_CODE: + if (last_code == 0) { + mpeg1_decode_sequence(avctx, buf_ptr, input_size); + if (buf != avctx->extradata) + s->sync = 1; + } else { + av_log(avctx, AV_LOG_ERROR, + "ignoring SEQ_START_CODE after %X\n", last_code); + if (avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + break; + + case PICTURE_START_CODE: + if (picture_start_code_seen && s2->picture_structure == PICT_FRAME) { + /* If it's a frame picture, there can't be more than one picture header. + Yet, it does happen and we need to handle it. */ + av_log(avctx, AV_LOG_WARNING, "ignoring extra picture following a frame-picture\n"); + break; + } + picture_start_code_seen = 1; + + if (buf == avctx->extradata && avctx->codec_tag == AV_RL32("AVmp")) { + av_log(avctx, AV_LOG_WARNING, "ignoring picture start code in AVmp extradata\n"); + break; + } + + if (s2->width <= 0 || s2->height <= 0) { + av_log(avctx, AV_LOG_ERROR, "Invalid frame dimensions %dx%d.\n", + s2->width, s2->height); + return AVERROR_INVALIDDATA; + } + + if (s->tmpgexs){ + s2->intra_dc_precision= 3; + s2->intra_matrix[0]= 1; + } + if (s->slice_count) { + mpeg12_execute_slice_threads(avctx, s); + s->slice_count = 0; + } + if (last_code == 0 || last_code == SLICE_MIN_START_CODE) { + ret = mpeg_decode_postinit(avctx); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, + "mpeg_decode_postinit() failure\n"); + return ret; + } + + /* We have a complete image: we try to decompress it. */ + if (mpeg1_decode_picture(avctx, buf_ptr, input_size) < 0) + s2->pict_type = 0; + s->first_slice = 1; + last_code = PICTURE_START_CODE; + } else { + av_log(avctx, AV_LOG_ERROR, + "ignoring pic after %X\n", last_code); + if (avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + break; + case EXT_START_CODE: { + GetBitContext gb0, *const gb = &gb0; + + ret = init_get_bits8(gb, buf_ptr, input_size); + if (ret < 0) + return ret; + + switch (get_bits(gb, 4)) { + case 0x1: + if (last_code == 0) { + mpeg_decode_sequence_extension(s, gb); + } else { + av_log(avctx, AV_LOG_ERROR, + "ignoring seq ext after %X\n", last_code); + if (avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + break; + case 0x2: + mpeg_decode_sequence_display_extension(s, gb); + break; + case 0x3: + mpeg_decode_quant_matrix_extension(s2, gb); + break; + case 0x7: + mpeg_decode_picture_display_extension(s, gb); + break; + case 0x8: + if (last_code == PICTURE_START_CODE) { + int ret = mpeg_decode_picture_coding_extension(s, gb); + if (ret < 0) + return ret; + } else { + av_log(avctx, AV_LOG_ERROR, + "ignoring pic cod ext after %X\n", last_code); + if (avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + break; + } + break; + } + case USER_START_CODE: + mpeg_decode_user_data(avctx, buf_ptr, input_size); + break; + case GOP_START_CODE: + if (last_code == 0) { + s2->first_field = 0; + ret = mpeg_decode_gop(avctx, buf_ptr, input_size); + if (ret < 0) + return ret; + s->sync = 1; + } else { + av_log(avctx, AV_LOG_ERROR, + "ignoring GOP_START_CODE after %X\n", last_code); + if (avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + } + break; + default: + if (start_code >= SLICE_MIN_START_CODE && + start_code <= SLICE_MAX_START_CODE && last_code == PICTURE_START_CODE) { + if (s2->progressive_sequence && !s2->progressive_frame) { + s2->progressive_frame = 1; + av_log(s2->avctx, AV_LOG_ERROR, + "interlaced frame in progressive sequence, ignoring\n"); + } + + if (s2->picture_structure == 0 || + (s2->progressive_frame && s2->picture_structure != PICT_FRAME)) { + av_log(s2->avctx, AV_LOG_ERROR, + "picture_structure %d invalid, ignoring\n", + s2->picture_structure); + s2->picture_structure = PICT_FRAME; + } + + if (s2->progressive_sequence && !s2->frame_pred_frame_dct) + av_log(s2->avctx, AV_LOG_WARNING, "invalid frame_pred_frame_dct\n"); + + if (s2->picture_structure == PICT_FRAME) { + s2->first_field = 0; + s2->v_edge_pos = 16 * s2->mb_height; + } else { + s2->first_field ^= 1; + s2->v_edge_pos = 8 * s2->mb_height; + memset(s2->mbskip_table, 0, s2->mb_stride * s2->mb_height); + } + } + if (start_code >= SLICE_MIN_START_CODE && + start_code <= SLICE_MAX_START_CODE && last_code != 0) { + const int field_pic = s2->picture_structure != PICT_FRAME; + int mb_y = start_code - SLICE_MIN_START_CODE; + last_code = SLICE_MIN_START_CODE; + if (s2->codec_id != AV_CODEC_ID_MPEG1VIDEO && s2->mb_height > 2800/16) + mb_y += (*buf_ptr&0xE0)<<2; + + mb_y <<= field_pic; + if (s2->picture_structure == PICT_BOTTOM_FIELD) + mb_y++; + + if (buf_end - buf_ptr < 2) { + av_log(s2->avctx, AV_LOG_ERROR, "slice too small\n"); + return AVERROR_INVALIDDATA; + } + + if (mb_y >= s2->mb_height) { + av_log(s2->avctx, AV_LOG_ERROR, + "slice below image (%d >= %d)\n", mb_y, s2->mb_height); + return AVERROR_INVALIDDATA; + } + + if (!s2->last_pic.ptr) { + /* Skip B-frames if we do not have reference frames and + * GOP is not closed. */ + if (s2->pict_type == AV_PICTURE_TYPE_B) { + if (!s->closed_gop) { + skip_frame = 1; + av_log(s2->avctx, AV_LOG_DEBUG, + "Skipping B slice due to open GOP\n"); + break; + } + } + } + if (s2->pict_type == AV_PICTURE_TYPE_I || (s2->avctx->flags2 & AV_CODEC_FLAG2_SHOW_ALL)) + s->sync = 1; + if (!s2->next_pic.ptr) { + /* Skip P-frames if we do not have a reference frame or + * we have an invalid header. */ + if (s2->pict_type == AV_PICTURE_TYPE_P && !s->sync) { + skip_frame = 1; + av_log(s2->avctx, AV_LOG_DEBUG, + "Skipping P slice due to !sync\n"); + break; + } + } + if ((avctx->skip_frame >= AVDISCARD_NONREF && + s2->pict_type == AV_PICTURE_TYPE_B) || + (avctx->skip_frame >= AVDISCARD_NONKEY && + s2->pict_type != AV_PICTURE_TYPE_I) || + avctx->skip_frame >= AVDISCARD_ALL) { + skip_frame = 1; + break; + } + + if (!s2->context_initialized) + break; + + if (s2->codec_id == AV_CODEC_ID_MPEG2VIDEO) { + if (mb_y < avctx->skip_top || + mb_y >= s2->mb_height - avctx->skip_bottom) + break; + } + + if (!s2->pict_type) { + av_log(avctx, AV_LOG_ERROR, "Missing picture start code\n"); + if (avctx->err_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + break; + } + + if (s->first_slice) { + skip_frame = 0; + s->first_slice = 0; + if ((ret = mpeg_field_start(s, buf, buf_size)) < 0) + return ret; + } + if (!s2->cur_pic.ptr) { + av_log(avctx, AV_LOG_ERROR, + "current_picture not initialized\n"); + return AVERROR_INVALIDDATA; + } + + if (HAVE_THREADS && + (avctx->active_thread_type & FF_THREAD_SLICE) && + !avctx->hwaccel) { + int threshold = (s2->mb_height * s->slice_count + + s2->slice_context_count / 2) / + s2->slice_context_count; + if (threshold <= mb_y) { + Mpeg12SliceContext *const thread_context = s2->mpeg12_contexts[s->slice_count]; + + thread_context->c.start_mb_y = mb_y; + thread_context->c.end_mb_y = s2->mb_height; + if (s->slice_count) { + s2->thread_context[s->slice_count - 1]->end_mb_y = mb_y; + ret = ff_update_duplicate_context(&thread_context->c, s2); + if (ret < 0) + return ret; + } + ret = init_get_bits8(&thread_context->gb, buf_ptr, input_size); + if (ret < 0) + return ret; + s->slice_count++; + } + buf_ptr += 2; // FIXME add minimum number of bytes per slice + } else { + ret = mpeg_decode_slice(&s->slice, mb_y, &buf_ptr, input_size); + + if (ret < 0) { + if (avctx->err_recognition & AV_EF_EXPLODE) + return ret; + if (s2->resync_mb_x >= 0 && s2->resync_mb_y >= 0) + ff_er_add_slice(&s2->er, s2->resync_mb_x, + s2->resync_mb_y, s2->mb_x, s2->mb_y, + ER_AC_ERROR | ER_DC_ERROR | ER_MV_ERROR); + } else { + ff_er_add_slice(&s2->er, s2->resync_mb_x, + s2->resync_mb_y, s2->mb_x - 1, s2->mb_y, + ER_AC_END | ER_DC_END | ER_MV_END); + } + } + } + break; + } + } +} + +static int mpeg_decode_frame(AVCodecContext *avctx, AVFrame *picture, + int *got_output, AVPacket *avpkt) +{ + const uint8_t *buf = avpkt->data; + int ret; + int buf_size = avpkt->size; + Mpeg1Context *s = avctx->priv_data; + MPVContext *const s2 = &s->slice.c; + + if (buf_size == 0 || (buf_size == 4 && AV_RB32(buf) == SEQ_END_CODE)) { + /* special case for last picture */ + if (s2->low_delay == 0 && s2->next_pic.ptr) { + int ret = av_frame_ref(picture, s2->next_pic.ptr->f); + if (ret < 0) + return ret; + + ff_mpv_unref_picture(&s2->next_pic); + + *got_output = 1; + } + return buf_size; + } + + if (!s2->context_initialized && + (s2->codec_tag == AV_RL32("VCR2") || s2->codec_tag == AV_RL32("BW10"))) + vcr2_init_sequence(avctx); + + s->slice_count = 0; + + if (avctx->extradata && !s->extradata_decoded) { + ret = decode_chunks(avctx, picture, got_output, + avctx->extradata, avctx->extradata_size); + if (*got_output) { + av_log(avctx, AV_LOG_ERROR, "picture in extradata\n"); + av_frame_unref(picture); + *got_output = 0; + } + s->extradata_decoded = 1; + if (ret < 0 && (avctx->err_recognition & AV_EF_EXPLODE)) { + ff_mpv_unref_picture(&s2->cur_pic); + return ret; + } + } + + ret = decode_chunks(avctx, picture, got_output, buf, buf_size); + if (ret<0 || *got_output) { + ff_mpv_unref_picture(&s2->cur_pic); + + if (s->timecode_frame_start != -1 && *got_output) { + char tcbuf[AV_TIMECODE_STR_SIZE]; + AVFrameSideData *tcside = av_frame_new_side_data(picture, + AV_FRAME_DATA_GOP_TIMECODE, + sizeof(int64_t)); + if (!tcside) + return AVERROR(ENOMEM); + memcpy(tcside->data, &s->timecode_frame_start, sizeof(int64_t)); + + av_timecode_make_mpeg_tc_string(tcbuf, s->timecode_frame_start); + av_dict_set(&picture->metadata, "timecode", tcbuf, 0); + + s->timecode_frame_start = -1; + } + } + + return ret; +} + +static av_cold void flush(AVCodecContext *avctx) +{ + Mpeg1Context *s = avctx->priv_data; + + s->sync = 0; + s->closed_gop = 0; + + av_buffer_unref(&s->a53_buf_ref); + ff_mpeg_flush(avctx); +} + +static av_cold int mpeg_decode_end(AVCodecContext *avctx) +{ + Mpeg1Context *s = avctx->priv_data; + + av_buffer_unref(&s->a53_buf_ref); + return ff_mpv_decode_close(avctx); +} + +const FFCodec ff_mpeg1video_decoder = { + .p.name = "mpeg1video", + CODEC_LONG_NAME("MPEG-1 video"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG1VIDEO, + .priv_data_size = sizeof(Mpeg1Context), + .init = mpeg_decode_init, + .close = mpeg_decode_end, + FF_CODEC_DECODE_CB(mpeg_decode_frame), + .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, + .flush = flush, + .p.max_lowres = 3, + .hw_configs = (const AVCodecHWConfigInternal *const []) { +#if CONFIG_MPEG1_NVDEC_HWACCEL + HWACCEL_NVDEC(mpeg1), +#endif +#if CONFIG_MPEG1_VDPAU_HWACCEL + HWACCEL_VDPAU(mpeg1), +#endif +#if CONFIG_MPEG1_VIDEOTOOLBOX_HWACCEL + HWACCEL_VIDEOTOOLBOX(mpeg1), +#endif + NULL + }, +}; + +#define M2V_OFFSET(x) offsetof(Mpeg1Context, x) +#define M2V_PARAM AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM + +static const AVOption mpeg2video_options[] = { + { "cc_format", "extract a specific Closed Captions format", + M2V_OFFSET(cc_format), AV_OPT_TYPE_INT, { .i64 = CC_FORMAT_AUTO }, + CC_FORMAT_AUTO, CC_FORMAT_DISH, M2V_PARAM, .unit = "cc_format" }, + + { "auto", "pick first seen CC substream", 0, AV_OPT_TYPE_CONST, + { .i64 = CC_FORMAT_AUTO }, .flags = M2V_PARAM, .unit = "cc_format" }, + { "a53", "pick A/53 Part 4 CC substream", 0, AV_OPT_TYPE_CONST, + { .i64 = CC_FORMAT_A53_PART4 }, .flags = M2V_PARAM, .unit = "cc_format" }, + { "scte20", "pick SCTE-20 CC substream", 0, AV_OPT_TYPE_CONST, + { .i64 = CC_FORMAT_SCTE20 }, .flags = M2V_PARAM, .unit = "cc_format" }, + { "dvd", "pick DVD CC substream", 0, AV_OPT_TYPE_CONST, + { .i64 = CC_FORMAT_DVD }, .flags = M2V_PARAM, .unit = "cc_format" }, + { "dish", "pick Dish Network CC substream", 0, AV_OPT_TYPE_CONST, + { .i64 = CC_FORMAT_DISH }, .flags = M2V_PARAM, .unit = "cc_format" }, + { NULL } +}; + +static const AVClass mpeg2video_class = { + .class_name = "MPEG-2 video", + .item_name = av_default_item_name, + .option = mpeg2video_options, + .version = LIBAVUTIL_VERSION_INT, + .category = AV_CLASS_CATEGORY_DECODER, +}; + +const FFCodec ff_mpeg2video_decoder = { + .p.name = "mpeg2video", + CODEC_LONG_NAME("MPEG-2 video"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.priv_class = &mpeg2video_class, + .priv_data_size = sizeof(Mpeg1Context), + .init = mpeg_decode_init, + .close = mpeg_decode_end, + FF_CODEC_DECODE_CB(mpeg_decode_frame), + .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, + .flush = flush, + .p.max_lowres = 3, + .p.profiles = NULL_IF_CONFIG_SMALL(ff_mpeg2_video_profiles), + .hw_configs = (const AVCodecHWConfigInternal *const []) { +#if CONFIG_MPEG2_DXVA2_HWACCEL + HWACCEL_DXVA2(mpeg2), +#endif +#if CONFIG_MPEG2_D3D11VA_HWACCEL + HWACCEL_D3D11VA(mpeg2), +#endif +#if CONFIG_MPEG2_D3D11VA2_HWACCEL + HWACCEL_D3D11VA2(mpeg2), +#endif +#if CONFIG_MPEG2_D3D12VA_HWACCEL + HWACCEL_D3D12VA(mpeg2), +#endif +#if CONFIG_MPEG2_NVDEC_HWACCEL + HWACCEL_NVDEC(mpeg2), +#endif +#if CONFIG_MPEG2_VAAPI_HWACCEL + HWACCEL_VAAPI(mpeg2), +#endif +#if CONFIG_MPEG2_VDPAU_HWACCEL + HWACCEL_VDPAU(mpeg2), +#endif +#if CONFIG_MPEG2_VIDEOTOOLBOX_HWACCEL + HWACCEL_VIDEOTOOLBOX(mpeg2), +#endif + NULL + }, +}; + +//legacy decoder +const FFCodec ff_mpegvideo_decoder = { + .p.name = "mpegvideo", + CODEC_LONG_NAME("MPEG-1 video"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .priv_data_size = sizeof(Mpeg1Context), + .init = mpeg_decode_init, + .close = mpeg_decode_end, + FF_CODEC_DECODE_CB(mpeg_decode_frame), + .p.capabilities = AV_CODEC_CAP_DRAW_HORIZ_BAND | AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_DELAY | AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM, + .flush = flush, + .p.max_lowres = 3, +}; + +typedef struct IPUContext { + Mpeg12SliceContext m; + + int flags; +} IPUContext; + +static int ipu_decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *avpkt) +{ + IPUContext *s = avctx->priv_data; + MPVContext *const m = &s->m.c; + GetBitContext *const gb = &s->m.gb; + int16_t (*const block)[64] = s->m.block; + int ret; + + // Check for minimal intra MB size (considering mb header, luma & chroma dc VLC, ac EOB VLC) + if (avpkt->size*8LL < (avctx->width+15)/16 * ((avctx->height+15)/16) * (2LL + 3*4 + 2*2 + 2*6)) + return AVERROR_INVALIDDATA; + + ret = ff_get_buffer(avctx, frame, 0); + if (ret < 0) + return ret; + + ret = init_get_bits8(gb, avpkt->data, avpkt->size); + if (ret < 0) + return ret; + + s->flags = get_bits(gb, 8); + m->intra_dc_precision = s->flags & 3; + m->q_scale_type = !!(s->flags & 0x40); + m->intra_vlc_format = !!(s->flags & 0x20); + m->alternate_scan = !!(s->flags & 0x10); + + ff_permute_scantable(m->intra_scantable.permutated, + s->flags & 0x10 ? ff_alternate_vertical_scan : ff_zigzag_direct, + m->idsp.idct_permutation); + + s->m.last_dc[0] = s->m.last_dc[1] = s->m.last_dc[2] = 128 << (s->flags & 3); + m->qscale = 1; + + for (int y = 0; y < avctx->height; y += 16) { + int intraquant; + + for (int x = 0; x < avctx->width; x += 16) { + if (x || y) { + if (!get_bits1(gb)) + return AVERROR_INVALIDDATA; + } + if (get_bits1(gb)) { + intraquant = 0; + } else { + if (!get_bits1(gb)) + return AVERROR_INVALIDDATA; + intraquant = 1; + } + + if (s->flags & 4) + skip_bits1(gb); + + if (intraquant) + m->qscale = mpeg_get_qscale(gb, m->q_scale_type); + + memset(block, 0, 6 * sizeof(*block)); + + for (int n = 0; n < 6; n++) { + if (s->flags & 0x80) { + ret = ff_mpeg1_decode_block_intra(gb, + m->intra_matrix, + m->intra_scantable.permutated, + s->m.last_dc, block[n], + n, m->qscale); + } else { + ret = mpeg2_decode_block_intra(&s->m, block[n], n); + } + + if (ret < 0) + return ret; + } + + m->idsp.idct_put(frame->data[0] + y * frame->linesize[0] + x, + frame->linesize[0], block[0]); + m->idsp.idct_put(frame->data[0] + y * frame->linesize[0] + x + 8, + frame->linesize[0], block[1]); + m->idsp.idct_put(frame->data[0] + (y + 8) * frame->linesize[0] + x, + frame->linesize[0], block[2]); + m->idsp.idct_put(frame->data[0] + (y + 8) * frame->linesize[0] + x + 8, + frame->linesize[0], block[3]); + m->idsp.idct_put(frame->data[1] + (y >> 1) * frame->linesize[1] + (x >> 1), + frame->linesize[1], block[4]); + m->idsp.idct_put(frame->data[2] + (y >> 1) * frame->linesize[2] + (x >> 1), + frame->linesize[2], block[5]); + } + } + + align_get_bits(gb); + if (get_bits_left(gb) != 32) + return AVERROR_INVALIDDATA; + + *got_frame = 1; + + return avpkt->size; +} + +static av_cold int ipu_decode_init(AVCodecContext *avctx) +{ + IPUContext *s = avctx->priv_data; + MPVContext *const m = &s->m.c; + + avctx->pix_fmt = AV_PIX_FMT_YUV420P; + m->avctx = avctx; + + ff_idctdsp_init(&m->idsp, avctx); + ff_mpeg12_init_vlcs(); + + for (int i = 0; i < 64; i++) { + int j = m->idsp.idct_permutation[i]; + int v = ff_mpeg1_default_intra_matrix[i]; + m->intra_matrix[j] = v; + m->chroma_intra_matrix[j] = v; + } + + return 0; +} + +const FFCodec ff_ipu_decoder = { + .p.name = "ipu", + CODEC_LONG_NAME("IPU Video"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_IPU, + .priv_data_size = sizeof(IPUContext), + .init = ipu_decode_init, + FF_CODEC_DECODE_CB(ipu_decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1, +}; diff --git a/thirdparty/ffmpeg/libavcodec/v4l2_request.c b/thirdparty/ffmpeg/libavcodec/v4l2_request.c new file mode 100644 index 000000000..36eec8540 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/v4l2_request.c @@ -0,0 +1,784 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "libavutil/hwcontext_v4l2request_internal.h" +#include "libavutil/mem.h" +#include "decode.h" +#include "internal.h" +#include "v4l2_request.h" + +#define V4L2_PLANES_MAX 2 + +static const AVClass v4l2_request_context_class = { + .class_name = "V4L2RequestContext", + .item_name = av_default_item_name, + .version = LIBAVUTIL_VERSION_INT, +}; + +static inline V4L2RequestContext *v4l2_request_context(AVCodecContext *avctx) +{ + return (V4L2RequestContext *)avctx->internal->hwaccel_priv_data; +} + +static inline uint32_t v4l2_request_frameindex(AVFrame *frame) +{ + return (uint32_t)(uintptr_t)frame->data[1]; +} + +uint64_t ff_v4l2_request_get_capture_timestamp(AVFrame *frame) +{ + /* + * The CAPTURE buffer index is used as a base for V4L2 frame reference. + * This works because frames are decoded into a CAPTURE buffer that is + * closely tied to an AVFrame. + */ + struct timeval timestamp = { + .tv_sec = 0, + .tv_usec = v4l2_request_frameindex(frame) + 1, + }; + return v4l2_timeval_to_ns(×tamp); +} + +int ff_v4l2_request_query_control(AVCodecContext *avctx, + struct v4l2_query_ext_ctrl *control) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + + if (ioctl(ctx->fctxi->video_fd, VIDIOC_QUERY_EXT_CTRL, control) < 0) { + int ret = AVERROR(errno); + // Skip error logging when driver does not support control id (EINVAL) + if (errno != EINVAL) + av_log(ctx, AV_LOG_ERROR, "Failed to query control %u: %s (%d)\n", + control->id, strerror(errno), errno); + return ret; + } + + return 0; +} + +int ff_v4l2_request_query_control_default_value(AVCodecContext *avctx, + uint32_t id) +{ + struct v4l2_query_ext_ctrl control = { + .id = id, + }; + int ret; + + ret = ff_v4l2_request_query_control(avctx, &control); + if (ret < 0) + return ret; + + return control.default_value; +} + +static int v4l2_request_set_controls(V4L2RequestContext *ctx, int request_fd, + struct v4l2_ext_control *control, int count) +{ + struct v4l2_ext_controls controls = { + .controls = control, + .count = count, + .request_fd = request_fd, + .which = (request_fd >= 0) ? V4L2_CTRL_WHICH_REQUEST_VAL : 0, + }; + + if (!control || !count) + return 0; + + if (ioctl(ctx->fctxi->video_fd, VIDIOC_S_EXT_CTRLS, &controls) < 0) + return AVERROR(errno); + + return 0; +} + +int ff_v4l2_request_set_controls(AVCodecContext *avctx, + struct v4l2_ext_control *control, int count) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + int ret; + + ret = v4l2_request_set_controls(ctx, -1, control, count); + if (ret < 0) + av_log(ctx, AV_LOG_ERROR, "Failed to set %d control(s): %s (%d)\n", + count, strerror(errno), errno); + + return ret; +} + +static int v4l2_request_queue_buffer(V4L2RequestContext *ctx, + struct v4l2_buffer *buffer) +{ + struct v4l2_plane planes[V4L2_PLANES_MAX] = {}; + + if (V4L2_TYPE_IS_MULTIPLANAR(buffer->type)) { + planes[0].bytesused = buffer->bytesused; + buffer->bytesused = 0; + buffer->length = FF_ARRAY_ELEMS(planes); + buffer->m.planes = planes; + } + + // Queue the buffer + if (ioctl(ctx->fctxi->video_fd, VIDIOC_QBUF, buffer) < 0) + return AVERROR(errno); + + // Mark the buffer as queued + if (V4L2_TYPE_IS_OUTPUT(buffer->type)) + ctx->queued_output |= 1 << buffer->index; + else + ctx->queued_capture |= 1 << buffer->index; + + return 0; +} + +static int v4l2_request_queue_capture_buffer(V4L2RequestContext *ctx, + uint32_t index) +{ + struct v4l2_buffer buffer = { + .index = index, + .type = ctx->fctxi->capture.format.type, + .memory = V4L2_MEMORY_MMAP, + }; + return v4l2_request_queue_buffer(ctx, &buffer); +} + +static int v4l2_request_queue_output_buffer(V4L2RequestContext *ctx, + V4L2RequestOutputBuffer *output, + uint32_t flags) +{ + struct v4l2_buffer buffer = { + .index = output->index, + .type = ctx->fctxi->output.format.type, + .memory = V4L2_MEMORY_MMAP, + .timestamp = output->timestamp, + .bytesused = output->bytesused, + .request_fd = output->request_fd, + .flags = V4L2_BUF_FLAG_REQUEST_FD | flags, + }; + return v4l2_request_queue_buffer(ctx, &buffer); +} + +static int v4l2_request_dequeue_buffer(V4L2RequestContext *ctx, + enum v4l2_buf_type type) +{ + struct v4l2_plane planes[V4L2_PLANES_MAX] = {}; + struct v4l2_buffer buffer = { + .type = type, + .memory = V4L2_MEMORY_MMAP, + }; + + if (V4L2_TYPE_IS_MULTIPLANAR(buffer.type)) { + buffer.length = FF_ARRAY_ELEMS(planes); + buffer.m.planes = planes; + } + + // Dequeue next completed buffer + if (ioctl(ctx->fctxi->video_fd, VIDIOC_DQBUF, &buffer) < 0) + return AVERROR(errno); + + // Mark the buffer as dequeued + if (V4L2_TYPE_IS_OUTPUT(buffer.type)) + ctx->queued_output &= ~(1 << buffer.index); + else + ctx->queued_capture &= ~(1 << buffer.index); + + return 0; +} + +static inline int v4l2_request_dequeue_completed_buffers(V4L2RequestContext *ctx, + enum v4l2_buf_type type) +{ + int ret; + + do { + ret = v4l2_request_dequeue_buffer(ctx, type); + } while (!ret); + + return ret; +} + +static int v4l2_request_wait_on_capture(V4L2RequestContext *ctx, uint32_t index) +{ + enum v4l2_buf_type type = ctx->fctxi->capture.format.type; + struct pollfd pollfd = { + .fd = ctx->fctxi->video_fd, + .events = POLLIN, + }; + + ff_mutex_lock(&ctx->mutex); + + // Dequeue all completed CAPTURE buffers + if (ctx->queued_capture) + v4l2_request_dequeue_completed_buffers(ctx, type); + + // Wait on the specific CAPTURE buffer + while (ctx->queued_capture & (1 << index)) { + int ret = poll(&pollfd, 1, 2000); + if (ret <= 0) + goto fail; + + ret = v4l2_request_dequeue_buffer(ctx, type); + if (ret < 0 && ret != AVERROR(EAGAIN)) + goto fail; + } + + ff_mutex_unlock(&ctx->mutex); + return 0; + +fail: + ff_mutex_unlock(&ctx->mutex); + av_log(ctx, AV_LOG_ERROR, "Failed waiting on CAPTURE buffer %d\n", index); + return AVERROR(EINVAL); +} + +static V4L2RequestOutputBuffer *v4l2_request_next_output(V4L2RequestContext *ctx) +{ + enum v4l2_buf_type type = ctx->fctxi->output.format.type; + V4L2RequestOutputBuffer *output; + struct pollfd pollfd = { + .fd = ctx->fctxi->video_fd, + .events = POLLOUT, + }; + uint8_t index; + + ff_mutex_lock(&ctx->mutex); + + // Use next OUTPUT buffer in the circular queue + index = ctx->next_output; + output = &ctx->output[index]; + ctx->next_output = (index + 1) % FF_ARRAY_ELEMS(ctx->output); + + // Dequeue all completed OUTPUT buffers + if (ctx->queued_output) + v4l2_request_dequeue_completed_buffers(ctx, type); + + // Wait on the specific OUTPUT buffer + while (ctx->queued_output & (1 << output->index)) { + int ret = poll(&pollfd, 1, 2000); + if (ret <= 0) + goto fail; + + ret = v4l2_request_dequeue_buffer(ctx, type); + if (ret < 0 && ret != AVERROR(EAGAIN)) + goto fail; + } + + ff_mutex_unlock(&ctx->mutex); + + // Reset bytesused state + output->bytesused = 0; + + return output; + +fail: + ff_mutex_unlock(&ctx->mutex); + av_log(ctx, AV_LOG_ERROR, "Failed waiting on OUTPUT buffer %d\n", + output->index); + return NULL; +} + +static int v4l2_request_wait_on_request(V4L2RequestContext *ctx, + V4L2RequestOutputBuffer *output) +{ + struct pollfd pollfd = { + .fd = output->request_fd, + .events = POLLPRI, + }; + + // Wait on the specific request to complete + while (ctx->queued_request & (1 << output->index)) { + int ret = poll(&pollfd, 1, 2000); + if (ret <= 0) + break; + + // Mark request as dequeued + if (pollfd.revents & (POLLPRI | POLLERR)) { + ctx->queued_request &= ~(1 << output->index); + break; + } + } + + // Reinit the request object + if (ioctl(output->request_fd, MEDIA_REQUEST_IOC_REINIT) < 0) { + int ret = AVERROR(errno); + av_log(ctx, AV_LOG_ERROR, "Failed to reinit request object %d: %s (%d)\n", + output->request_fd, strerror(errno), errno); + return ret; + } + + // Ensure request is marked as dequeued + ctx->queued_request &= ~(1 << output->index); + + return 0; +} + +int ff_v4l2_request_append_output(AVCodecContext *avctx, + V4L2RequestPictureContext *pic, + const uint8_t *data, uint32_t size) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + + // Append data to OUTPUT buffer and ensure there is enough space for padding + if (pic->output->bytesused + size + AV_INPUT_BUFFER_PADDING_SIZE <= pic->output->size) { + memcpy(pic->output->addr + pic->output->bytesused, data, size); + pic->output->bytesused += size; + return 0; + } else { + av_log(ctx, AV_LOG_ERROR, + "Failed to append %u bytes data to OUTPUT buffer %d (%u of %u used)\n", + size, pic->output->index, pic->output->bytesused, pic->output->size); + return AVERROR(ENOMEM); + } +} + +static int v4l2_request_queue_decode(AVCodecContext *avctx, + V4L2RequestPictureContext *pic, + struct v4l2_ext_control *control, int count, + bool first_slice, bool last_slice) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + uint32_t flags; + int ret; + + if (first_slice) { + /* + * Wait on dequeue of the target CAPTURE buffer. Otherwise V4L2 decoder + * may use a different CAPTURE buffer than hwaccel expects. + * + * Normally decoding has already completed when a CAPTURE buffer is + * reused so this is more or less a no-op, however in some situations + * FFmpeg may reuse an AVFrame early, i.e. when no output frame was + * produced prior time, and a synchronization is necessary. + */ + ret = v4l2_request_wait_on_capture(ctx, pic->capture_index); + if (ret < 0) + return ret; + } + + ff_mutex_lock(&ctx->mutex); + + /* + * The OUTPUT buffer tied to prior use of current request object can + * independently be dequeued before the full decode request has been + * completed. This may happen when a decoder use multi stage decoding, + * e.g. rpi-hevc-dec. In such case we can start reusing the OUTPUT buffer, + * however we must wait on the prior request to fully complete before we + * can reuse the request object, and a synchronization is necessary. + */ + ret = v4l2_request_wait_on_request(ctx, pic->output); + if (ret < 0) + goto fail; + + /* + * Dequeue any completed OUTPUT buffers, this is strictly not necessary, + * however if a synchronization was necessary for the CAPTURE and/or request + * there is more than likely one or more OUTPUT buffers that can be dequeued. + */ + if (ctx->queued_output) + v4l2_request_dequeue_completed_buffers(ctx, ctx->fctxi->output.format.type); + + // Set codec controls for current request + ret = v4l2_request_set_controls(ctx, pic->output->request_fd, control, count); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to set %d control(s) for request %d: %s (%d)\n", + count, pic->output->request_fd, strerror(errno), errno); + goto fail; + } + + // Ensure there is zero padding at the end of bitstream data + memset(pic->output->addr + pic->output->bytesused, 0, AV_INPUT_BUFFER_PADDING_SIZE); + + /* + * Use CAPTURE buffer index as base for V4L2 frame reference. + * This works because a CAPTURE buffer is closely tied to a AVFrame + * and FFmpeg handle all frame reference tracking for us. + */ + pic->output->timestamp = (struct timeval) { + .tv_sec = 0, + .tv_usec = pic->capture_index + 1, + }; + + /* + * Queue the OUTPUT buffer of current request. The CAPTURE buffer may be + * hold by the V4L2 decoder unless this is the last slice of a frame. + */ + flags = last_slice ? 0 : V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF; + ret = v4l2_request_queue_output_buffer(ctx, pic->output, flags); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to queue OUTPUT buffer %d for request %d: %s (%d)\n", + pic->output->index, pic->output->request_fd, strerror(errno), errno); + goto fail; + } + + if (first_slice) { + /* + * Queue the target CAPTURE buffer, hwaccel expect and depend on that + * this specific CAPTURE buffer will be used as decode target for + * current request, otherwise frames may be output in wrong order or + * wrong CAPTURE buffer could get used as a reference frame. + */ + ret = v4l2_request_queue_capture_buffer(ctx, pic->capture_index); + if (ret < 0) { + av_log(ctx, AV_LOG_ERROR, "Failed to queue CAPTURE buffer %d for request %d: %s (%d)\n", + pic->capture_index, pic->output->request_fd, strerror(errno), errno); + goto fail; + } + } + + // Queue current request + ret = ioctl(pic->output->request_fd, MEDIA_REQUEST_IOC_QUEUE); + if (ret < 0) { + ret = AVERROR(errno); + av_log(ctx, AV_LOG_ERROR, "Failed to queue request object %d: %s (%d)\n", + pic->output->request_fd, strerror(errno), errno); + goto fail; + } + + // Mark current request as queued + ctx->queued_request |= 1 << pic->output->index; + + ret = 0; +fail: + ff_mutex_unlock(&ctx->mutex); + return ret; +} + +int ff_v4l2_request_decode_slice(AVCodecContext *avctx, + V4L2RequestPictureContext *pic, + struct v4l2_ext_control *control, int count, + bool first_slice, bool last_slice) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + + /* + * Fallback to queue each slice as a full frame when holding CAPTURE + * buffers is not supported by the driver. + */ + if ((ctx->fctxi->output.capabilities & V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF) != + V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF) + return v4l2_request_queue_decode(avctx, pic, control, count, true, true); + + return v4l2_request_queue_decode(avctx, pic, control, count, + first_slice, last_slice); +} + +int ff_v4l2_request_decode_frame(AVCodecContext *avctx, + V4L2RequestPictureContext *pic, + struct v4l2_ext_control *control, int count) +{ + return v4l2_request_queue_decode(avctx, pic, control, count, true, true); +} + +static int v4l2_request_post_process(void *logctx, AVFrame *frame) +{ + uint32_t index = v4l2_request_frameindex(frame); + FrameDecodeData *fdd = frame->private_ref; + V4L2RequestContext *ctx = fdd->hwaccel_priv; + + // Wait on CAPTURE buffer before returning the frame to application + return v4l2_request_wait_on_capture(ctx, index); +} + +int ff_v4l2_request_reset_picture(AVCodecContext *avctx, V4L2RequestPictureContext *pic) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + + // Get and wait on next OUTPUT buffer from circular queue + pic->output = v4l2_request_next_output(ctx); + if (!pic->output) + return AVERROR(EINVAL); + + return 0; +} + +int ff_v4l2_request_start_frame(AVCodecContext *avctx, + V4L2RequestPictureContext *pic, + AVFrame *frame) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + uint32_t index = v4l2_request_frameindex(frame); + FrameDecodeData *fdd = frame->private_ref; + int ret; + + // Get next OUTPUT buffer from circular queue + ret = ff_v4l2_request_reset_picture(avctx, pic); + if (ret) + return ret; + + // Ensure CAPTURE buffer is dequeued before reuse + ret = v4l2_request_wait_on_capture(ctx, index); + if (ret) + return ret; + + // Wait on CAPTURE buffer in post_process() before returning to application + fdd->hwaccel_priv = ctx; + fdd->post_process = v4l2_request_post_process; + + // CAPTURE buffer used for current frame + pic->capture_index = index; + + return 0; +} + +void ff_v4l2_request_flush(AVCodecContext *avctx) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + enum v4l2_buf_type type = ctx->fctxi->output.format.type; + struct pollfd pollfd = { + .fd = ctx->fctxi->video_fd, + .events = POLLOUT, + }; + + ff_mutex_lock(&ctx->mutex); + + // Dequeue all completed OUTPUT buffers + if (ctx->queued_output) + v4l2_request_dequeue_completed_buffers(ctx, type); + + // Wait on any remaining OUTPUT buffer + while (ctx->queued_output) { + int ret = poll(&pollfd, 1, 2000); + if (ret <= 0) + break; + + ret = v4l2_request_dequeue_buffer(ctx, type); + if (ret < 0 && ret != AVERROR(EAGAIN)) + break; + } + + // Dequeue all completed CAPTURE buffers + if (ctx->queued_capture) + v4l2_request_dequeue_completed_buffers(ctx, ctx->fctxi->capture.format.type); + + ff_mutex_unlock(&ctx->mutex); +} + +static void v4l2_request_output_buffer_uninit(V4L2RequestOutputBuffer *output) +{ + // Close the request associated with the OUTPUT buffer + if (output->request_fd >= 0) { + close(output->request_fd); + output->request_fd = -1; + } + + // Umap the OUTPUT buffer memory + if (output->addr) { + munmap(output->addr, output->size); + output->addr = NULL; + } + + // Return the OUTPUT buffer to the frames context OUTPUT pool + av_buffer_unref(&output->ref); +} + +static int v4l2_request_output_buffer_init(V4L2RequestContext *ctx, + V4L2RequestOutputBuffer *output) +{ + struct v4l2_format *format = &ctx->fctxi->output.format; + struct v4l2_buffer *buffer; + off_t offset; + void *addr; + int ret; + + // Get an OUTPUT buffer from frames context OUTPUT pool + output->ref = av_buffer_pool_get(ctx->fctxi->output.pool); + if (!output->ref) + return AVERROR(ENOMEM); + + buffer = (struct v4l2_buffer *)output->ref->data; + output->index = buffer->index; + output->size = V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.plane_fmt[0].sizeimage : + format->fmt.pix.sizeimage; + output->bytesused = 0; + + // Map the OUTPUT buffer memory, raw bitstream data is written into it + offset = V4L2_TYPE_IS_MULTIPLANAR(buffer->type) ? + buffer->m.planes[0].m.mem_offset : + buffer->m.offset; + addr = mmap(NULL, output->size, PROT_READ | PROT_WRITE, MAP_SHARED, + ctx->fctxi->video_fd, offset); + if (addr == MAP_FAILED) { + ret = AVERROR(errno); + av_log(ctx, AV_LOG_ERROR, "Failed to map OUTPUT buffer %d: %s (%d)\n", + output->index, strerror(errno), errno); + goto fail; + } + output->addr = addr; + + // Allocate and associated a request for the OUTPUT buffer + if (ioctl(ctx->fctxi->media_fd, MEDIA_IOC_REQUEST_ALLOC, &output->request_fd) < 0) { + ret = AVERROR(errno); + av_log(ctx, AV_LOG_ERROR, "Failed to allocate request for OUTPUT buffer %d: %s (%d)\n", + output->index, strerror(errno), errno); + goto fail; + } + + return 0; + +fail: + v4l2_request_output_buffer_uninit(output); + return ret; +} + +int ff_v4l2_request_frame_params(AVCodecContext *avctx, + AVBufferRef *hw_frames_ctx, + uint32_t pixelformat, + uint8_t bit_depth) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + AVHWFramesContext *hwfc = (AVHWFramesContext *)hw_frames_ctx->data; + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + + // Set parameters used during frames context initialization + fctx->pixelformat = pixelformat; + fctx->bit_depth = bit_depth; + if (ctx) { + fctx->init_controls = ctx->init_controls; + fctx->nb_init_controls = ctx->nb_init_controls; + } + + hwfc->format = AV_PIX_FMT_DRM_PRIME; + hwfc->sw_format = AV_PIX_FMT_NONE; + hwfc->width = avctx->coded_width; + hwfc->height = avctx->coded_height; + + // Pre-allocate CAPTURE buffers to ensure CAPTURE queue can be started + hwfc->initial_pool_size = 1; + + return 0; +} + +int ff_v4l2_request_uninit(AVCodecContext *avctx) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + enum v4l2_buf_type type; + + if (ctx->fctxi) { + // Flush and wait on all pending requests + ff_v4l2_request_flush(avctx); + + // Stop streaming on OUTPUT queue + type = ctx->fctxi->output.format.type; + if (ioctl(ctx->fctxi->video_fd, VIDIOC_STREAMOFF, &type) < 0) + av_log(ctx, AV_LOG_WARNING, "Failed to stop OUTPUT streaming: %s (%d)\n", + strerror(errno), errno); + + // Stop streaming on CAPTURE queue + type = ctx->fctxi->capture.format.type; + if (ioctl(ctx->fctxi->video_fd, VIDIOC_STREAMOFF, &type) < 0) + av_log(ctx, AV_LOG_WARNING, "Failed to stop CAPTURE streaming: %s (%d)\n", + strerror(errno), errno); + + // Release OUTPUT buffers and requests + for (int i = 0; i < FF_ARRAY_ELEMS(ctx->output); i++) + v4l2_request_output_buffer_uninit(&ctx->output[i]); + + ctx->fctxi = NULL; + } + + av_buffer_unref(&ctx->frames_ref); + ff_mutex_destroy(&ctx->mutex); + + return 0; +} + +int ff_v4l2_request_init(AVCodecContext *avctx, + struct v4l2_ext_control *control, int count, + int (*post_frames_ctx)(AVCodecContext *avctx)) +{ + V4L2RequestContext *ctx = v4l2_request_context(avctx); + AVHWFramesContext *hwfc; + AVV4L2RequestFramesContext *fctx; + enum v4l2_buf_type type; + int ret; + + // Set initial default values + ctx->av_class = &v4l2_request_context_class; + ctx->init_controls = control; + ctx->nb_init_controls = count; + ff_mutex_init(&ctx->mutex, NULL); + for (int i = 0; i < FF_ARRAY_ELEMS(ctx->output); i++) { + ctx->output[i].index = i; + ctx->output[i].request_fd = -1; + } + + // Create frames context and allocate initial CAPTURE buffers + ret = ff_decode_get_hw_frames_ctx(avctx, AV_HWDEVICE_TYPE_V4L2REQUEST); + if (ret < 0) + goto fail; + + ctx->frames_ref = av_buffer_ref(avctx->hw_frames_ctx); + if (!ctx->frames_ref) { + ret = AVERROR(ENOMEM); + goto fail; + } + + // Get internal hwctx from frames context + hwfc = (AVHWFramesContext *)ctx->frames_ref->data; + fctx = hwfc->hwctx; + ctx->fctxi = fctx->internal; + + // Reset init controls after successful frames context initialization + ctx->init_controls = NULL; + ctx->nb_init_controls = 0; + + // Check codec-specific controls, e.g. profile and level + if (post_frames_ctx) { + ret = post_frames_ctx(avctx); + if (ret < 0) + goto fail; + } + + // Allocate OUTPUT buffers and requests for circular queue + for (int i = 0; i < FF_ARRAY_ELEMS(ctx->output); i++) { + ret = v4l2_request_output_buffer_init(ctx, &ctx->output[i]); + if (ret < 0) + goto fail; + } + + // Start streaming on OUTPUT queue + type = ctx->fctxi->output.format.type; + if (ioctl(ctx->fctxi->video_fd, VIDIOC_STREAMON, &type) < 0) { + ret = AVERROR(errno); + av_log(ctx, AV_LOG_ERROR, "Failed to start OUTPUT streaming: %s (%d)\n", + strerror(errno), errno); + goto fail; + } + + // Start streaming on CAPTURE queue + type = ctx->fctxi->capture.format.type; + if (ioctl(ctx->fctxi->video_fd, VIDIOC_STREAMON, &type) < 0) { + ret = AVERROR(errno); + av_log(ctx, AV_LOG_ERROR, "Failed to start CAPTURE streaming: %s (%d)\n", + strerror(errno), errno); + goto fail; + } + + return 0; + +fail: + ff_v4l2_request_uninit(avctx); + return ret; +} diff --git a/thirdparty/ffmpeg/libavcodec/v4l2_request.h b/thirdparty/ffmpeg/libavcodec/v4l2_request.h new file mode 100644 index 000000000..53759f20b --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/v4l2_request.h @@ -0,0 +1,105 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_V4L2_REQUEST_H +#define AVCODEC_V4L2_REQUEST_H + +#include +#include +#include + +#include "libavutil/buffer.h" +#include "libavutil/log.h" +#include "libavutil/thread.h" +#include "avcodec.h" + +typedef struct AVV4L2RequestFramesContextInternal AVV4L2RequestFramesContextInternal; + +typedef struct V4L2RequestOutputBuffer { + AVBufferRef *ref; + uint32_t index; + int request_fd; + uint8_t *addr; + uint32_t size; + uint32_t bytesused; + struct timeval timestamp; +} V4L2RequestOutputBuffer; + +typedef struct V4L2RequestContext { + const AVClass *av_class; + AVBufferRef *frames_ref; + AVV4L2RequestFramesContextInternal *fctxi; + AVMutex mutex; + V4L2RequestOutputBuffer output[4]; + uint8_t next_output; + uint32_t queued_output; + uint32_t queued_request; + uint64_t queued_capture; + struct v4l2_ext_control *init_controls; + int nb_init_controls; +} V4L2RequestContext; + +typedef struct V4L2RequestPictureContext { + V4L2RequestOutputBuffer *output; + uint32_t capture_index; +} V4L2RequestPictureContext; + +uint64_t ff_v4l2_request_get_capture_timestamp(AVFrame *frame); + +int ff_v4l2_request_query_control(AVCodecContext *avctx, + struct v4l2_query_ext_ctrl *control); + +int ff_v4l2_request_query_control_default_value(AVCodecContext *avctx, + uint32_t id); + +int ff_v4l2_request_set_controls(AVCodecContext *avctx, + struct v4l2_ext_control *control, int count); + +int ff_v4l2_request_append_output(AVCodecContext *avctx, + V4L2RequestPictureContext *pic, + const uint8_t *data, uint32_t size); + +int ff_v4l2_request_decode_slice(AVCodecContext *avctx, + V4L2RequestPictureContext *pic, + struct v4l2_ext_control *control, int count, + bool first_slice, bool last_slice); + +int ff_v4l2_request_decode_frame(AVCodecContext *avctx, + V4L2RequestPictureContext *pic, + struct v4l2_ext_control *control, int count); + +int ff_v4l2_request_reset_picture(AVCodecContext *avctx, + V4L2RequestPictureContext *pic); + +int ff_v4l2_request_start_frame(AVCodecContext *avctx, + V4L2RequestPictureContext *pic, AVFrame *frame); + +void ff_v4l2_request_flush(AVCodecContext *avctx); + +int ff_v4l2_request_frame_params(AVCodecContext *avctx, + AVBufferRef *hw_frames_ctx, + uint32_t pixelformat, + uint8_t bit_depth); + +int ff_v4l2_request_uninit(AVCodecContext *avctx); + +int ff_v4l2_request_init(AVCodecContext *avctx, + struct v4l2_ext_control *control, int count, + int (*post_frames_ctx)(AVCodecContext *avctx)); + +#endif /* AVCODEC_V4L2_REQUEST_H */ diff --git a/thirdparty/ffmpeg/libavcodec/v4l2_request_av1.c b/thirdparty/ffmpeg/libavcodec/v4l2_request_av1.c new file mode 100644 index 000000000..4eabdbf31 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/v4l2_request_av1.c @@ -0,0 +1,636 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "libavutil/mem.h" +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "av1dec.h" +#include "internal.h" +#include "v4l2_request.h" + +#define V4L2_AV1_CONTROLS_MAX 4 + +typedef struct V4L2RequestContextAV1 { + V4L2RequestContext base; + bool has_film_grain; +} V4L2RequestContextAV1; + +typedef struct V4L2RequestControlsAV1 { + V4L2RequestPictureContext pic; + struct v4l2_ctrl_av1_sequence sequence; + struct v4l2_ctrl_av1_frame frame; + struct v4l2_ctrl_av1_film_grain film_grain; + struct v4l2_ctrl_av1_tile_group_entry tile_group_entry; + struct v4l2_ctrl_av1_tile_group_entry *tile_group_entries; + unsigned int allocated_tile_group_entries; + unsigned int num_tile_group_entries; +} V4L2RequestControlsAV1; + +static int get_bit_depth_from_seq(const AV1RawSequenceHeader *seq) +{ + if (seq->seq_profile == AV_PROFILE_AV1_PROFESSIONAL && + seq->color_config.high_bitdepth) + return seq->color_config.twelve_bit ? 12 : 10; + else + return seq->color_config.high_bitdepth ? 10 : 8; +} + +static void fill_sequence(struct v4l2_ctrl_av1_sequence *ctrl, + const AV1DecContext *s) +{ + const AV1RawSequenceHeader *seq = s->raw_seq; + + *ctrl = (struct v4l2_ctrl_av1_sequence) { + .seq_profile = seq->seq_profile, + .order_hint_bits = seq->enable_order_hint ? + seq->order_hint_bits_minus_1 + 1 : 0, + .bit_depth = get_bit_depth_from_seq(seq), + .max_frame_width_minus_1 = seq->max_frame_width_minus_1, + .max_frame_height_minus_1 = seq->max_frame_height_minus_1, + }; + + if (seq->still_picture) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_STILL_PICTURE; + + if (seq->use_128x128_superblock) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_USE_128X128_SUPERBLOCK; + + if (seq->enable_filter_intra) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_FILTER_INTRA; + + if (seq->enable_intra_edge_filter) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTRA_EDGE_FILTER; + + if (seq->enable_interintra_compound) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_INTERINTRA_COMPOUND; + + if (seq->enable_masked_compound) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_MASKED_COMPOUND; + + if (seq->enable_warped_motion) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_WARPED_MOTION; + + if (seq->enable_dual_filter) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_DUAL_FILTER; + + if (seq->enable_order_hint) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_ORDER_HINT; + + if (seq->enable_jnt_comp) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_JNT_COMP; + + if (seq->enable_ref_frame_mvs) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_REF_FRAME_MVS; + + if (seq->enable_superres) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_SUPERRES; + + if (seq->enable_cdef) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_CDEF; + + if (seq->enable_restoration) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_ENABLE_RESTORATION; + + if (seq->color_config.mono_chrome) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_MONO_CHROME; + + if (seq->color_config.color_range) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_COLOR_RANGE; + + if (seq->color_config.subsampling_x) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_X; + + if (seq->color_config.subsampling_y) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_SUBSAMPLING_Y; + + if (seq->film_grain_params_present) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_FILM_GRAIN_PARAMS_PRESENT; + + if (seq->color_config.separate_uv_delta_q) + ctrl->flags |= V4L2_AV1_SEQUENCE_FLAG_SEPARATE_UV_DELTA_Q; +} + +static void fill_frame(struct v4l2_ctrl_av1_frame *ctrl, const AV1DecContext *s) +{ + const AV1RawFrameHeader *frame_header = s->raw_frame_header; + const CodedBitstreamAV1Context *cbctx = s->cbc->priv_data; + uint8_t remap_lr_type[4] = { + V4L2_AV1_FRAME_RESTORE_NONE, + V4L2_AV1_FRAME_RESTORE_SWITCHABLE, + V4L2_AV1_FRAME_RESTORE_WIENER, + V4L2_AV1_FRAME_RESTORE_SGRPROJ, + }; + int i, j; + + *ctrl = (struct v4l2_ctrl_av1_frame) { + .tile_info = { + .context_update_tile_id = frame_header->context_update_tile_id, + .tile_cols = frame_header->tile_cols, + .tile_rows = frame_header->tile_rows, + .tile_size_bytes = frame_header->tile_cols_log2 > 0 || + frame_header->tile_rows_log2 > 0 ? + frame_header->tile_size_bytes_minus1 + 1 : 0, + }, + + .quantization = { + .base_q_idx = frame_header->base_q_idx, + .delta_q_y_dc = frame_header->delta_q_y_dc, + .delta_q_u_dc = frame_header->delta_q_u_dc, + .delta_q_u_ac = frame_header->delta_q_u_ac, + .delta_q_v_dc = frame_header->delta_q_v_dc, + .delta_q_v_ac = frame_header->delta_q_v_ac, + .qm_y = frame_header->qm_y, + .qm_u = frame_header->qm_u, + .qm_v = frame_header->qm_v, + .delta_q_res = frame_header->delta_q_res, + }, + + .loop_filter = { + .level[0] = frame_header->loop_filter_level[0], + .level[1] = frame_header->loop_filter_level[1], + .level[2] = frame_header->loop_filter_level[2], + .level[3] = frame_header->loop_filter_level[3], + .sharpness = frame_header->loop_filter_sharpness, + .mode_deltas[0] = frame_header->loop_filter_mode_deltas[0], + .mode_deltas[1] = frame_header->loop_filter_mode_deltas[1], + .delta_lf_res = frame_header->delta_lf_res, + }, + + .cdef = { + .damping_minus_3 = frame_header->cdef_damping_minus_3, + .bits = frame_header->cdef_bits, + }, + + .loop_restoration = { + .lr_unit_shift = frame_header->lr_unit_shift, + .lr_uv_shift = frame_header->lr_uv_shift, + }, + + .superres_denom = frame_header->use_superres ? + frame_header->coded_denom + AV1_SUPERRES_DENOM_MIN : + AV1_SUPERRES_NUM, + .skip_mode_frame[0] = frame_header->skip_mode_present ? + s->cur_frame.skip_mode_frame_idx[0] : 0, + .skip_mode_frame[1] = frame_header->skip_mode_present ? + s->cur_frame.skip_mode_frame_idx[1] : 0, + .primary_ref_frame = frame_header->primary_ref_frame, + .frame_type = frame_header->frame_type, + .order_hint = frame_header->order_hint, + .upscaled_width = cbctx->upscaled_width, + .interpolation_filter = frame_header->interpolation_filter, + .tx_mode = frame_header->tx_mode, + .frame_width_minus_1 = cbctx->frame_width - 1, + .frame_height_minus_1 = cbctx->frame_height - 1, + .render_width_minus_1 = cbctx->render_width - 1, + .render_height_minus_1 = cbctx->render_height - 1, + .current_frame_id = frame_header->current_frame_id, + .refresh_frame_flags = frame_header->refresh_frame_flags, + }; + + if (frame_header->segmentation_enabled) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_ENABLED; + + if (frame_header->segmentation_update_map) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_UPDATE_MAP; + + if (frame_header->segmentation_temporal_update) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_TEMPORAL_UPDATE; + + if (frame_header->segmentation_update_data) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_UPDATE_DATA; + + for (i = 0; i < AV1_MAX_SEGMENTS; i++) { + for (j = 0; j < AV1_SEG_LVL_MAX; j++) { + if (frame_header->feature_enabled[i][j]) { + ctrl->segmentation.feature_enabled[i] |= V4L2_AV1_SEGMENT_FEATURE_ENABLED(j); + ctrl->segmentation.last_active_seg_id = i; + if (j >= AV1_SEG_LVL_REF_FRAME) + ctrl->segmentation.flags |= V4L2_AV1_SEGMENTATION_FLAG_SEG_ID_PRE_SKIP; + } + ctrl->segmentation.feature_data[i][j] = frame_header->feature_value[i][j]; + } + } + + if (frame_header->uniform_tile_spacing_flag) + ctrl->tile_info.flags |= V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING; + + for (i = 0; i < frame_header->tile_cols; i++) { + ctrl->tile_info.mi_col_starts[i] = frame_header->tile_start_col_sb[i]; + ctrl->tile_info.width_in_sbs_minus_1[i] = frame_header->width_in_sbs_minus_1[i]; + } + ctrl->tile_info.mi_col_starts[i] = 2 * ((cbctx->frame_width + 7) >> 3); + + for (i = 0; i < frame_header->tile_rows; i++) { + ctrl->tile_info.mi_row_starts[i] = frame_header->tile_start_row_sb[i]; + ctrl->tile_info.height_in_sbs_minus_1[i] = frame_header->height_in_sbs_minus_1[i]; + } + ctrl->tile_info.mi_row_starts[i] = 2 * ((cbctx->frame_height + 7) >> 3); + + if (frame_header->diff_uv_delta) + ctrl->quantization.flags |= V4L2_AV1_QUANTIZATION_FLAG_DIFF_UV_DELTA; + + if (frame_header->using_qmatrix) + ctrl->quantization.flags |= V4L2_AV1_QUANTIZATION_FLAG_USING_QMATRIX; + + if (frame_header->delta_q_present) + ctrl->quantization.flags |= V4L2_AV1_QUANTIZATION_FLAG_DELTA_Q_PRESENT; + + if (frame_header->loop_filter_delta_enabled) + ctrl->loop_filter.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED; + + if (frame_header->loop_filter_delta_update) + ctrl->loop_filter.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_UPDATE; + + if (frame_header->delta_lf_present) + ctrl->loop_filter.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT; + + if (frame_header->delta_lf_multi) + ctrl->loop_filter.flags |= V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_MULTI; + + for (i = 0; i < AV1_NUM_REF_FRAMES; i++) { + ctrl->loop_filter.ref_deltas[i] = frame_header->loop_filter_ref_deltas[i]; + } + + for (i = 0; i < cbctx->num_planes; i++) { + ctrl->loop_restoration.frame_restoration_type[i] = + remap_lr_type[frame_header->lr_type[i]]; + if (frame_header->lr_type[i] != AV1_RESTORE_NONE) { + ctrl->loop_restoration.flags |= V4L2_AV1_LOOP_RESTORATION_FLAG_USES_LR; + if (i > 0) + ctrl->loop_restoration.flags |= V4L2_AV1_LOOP_RESTORATION_FLAG_USES_CHROMA_LR; + } + } + if (ctrl->loop_restoration.flags & V4L2_AV1_LOOP_RESTORATION_FLAG_USES_LR) { + ctrl->loop_restoration.loop_restoration_size[0] = + 1 << (6 + frame_header->lr_unit_shift); + ctrl->loop_restoration.loop_restoration_size[1] = + 1 << (6 + frame_header->lr_unit_shift - frame_header->lr_uv_shift); + ctrl->loop_restoration.loop_restoration_size[2] = + 1 << (6 + frame_header->lr_unit_shift - frame_header->lr_uv_shift); + } + + for (i = 0; i < AV1_TOTAL_REFS_PER_FRAME; i++) { + ctrl->global_motion.type[i] = s->cur_frame.gm_type[i]; + for (j = 0; j < 6; ++j) { + ctrl->global_motion.params[i][j] = s->cur_frame.gm_params[i][j]; + if (s->cur_frame.gm_invalid[i]) + ctrl->global_motion.invalid |= V4L2_AV1_GLOBAL_MOTION_IS_INVALID(i); + } + + if (frame_header->is_global[i]) + ctrl->global_motion.flags[i] |= V4L2_AV1_GLOBAL_MOTION_FLAG_IS_GLOBAL; + + if (frame_header->is_rot_zoom[i]) + ctrl->global_motion.flags[i] |= V4L2_AV1_GLOBAL_MOTION_FLAG_IS_ROT_ZOOM; + + if (frame_header->is_translation[i]) + ctrl->global_motion.flags[i] |= V4L2_AV1_GLOBAL_MOTION_FLAG_IS_TRANSLATION; + } + + for (i = 0; i < AV1_TOTAL_REFS_PER_FRAME; i++) { + AVFrame *ref = s->ref[i].f; + + ctrl->order_hints[i] = s->cur_frame.order_hints[i]; + if (ref) + ctrl->reference_frame_ts[i] = ff_v4l2_request_get_capture_timestamp(ref); + if (i < AV1_REFS_PER_FRAME) + ctrl->ref_frame_idx[i] = frame_header->ref_frame_idx[i]; + } + + for (i = 0; i < (1 << frame_header->cdef_bits); i++) { + ctrl->cdef.y_pri_strength[i] = frame_header->cdef_y_pri_strength[i]; + ctrl->cdef.y_sec_strength[i] = frame_header->cdef_y_sec_strength[i]; + ctrl->cdef.uv_pri_strength[i] = frame_header->cdef_uv_pri_strength[i]; + ctrl->cdef.uv_sec_strength[i] = frame_header->cdef_uv_sec_strength[i]; + } + + if (frame_header->show_frame) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_SHOW_FRAME; + + if (frame_header->showable_frame) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_SHOWABLE_FRAME; + + if (frame_header->error_resilient_mode) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ERROR_RESILIENT_MODE; + + if (frame_header->disable_cdf_update) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_DISABLE_CDF_UPDATE; + + if (frame_header->allow_screen_content_tools) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ALLOW_SCREEN_CONTENT_TOOLS; + + if (s->cur_frame.force_integer_mv) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_FORCE_INTEGER_MV; + + if (frame_header->allow_intrabc) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ALLOW_INTRABC; + + if (frame_header->use_superres) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_USE_SUPERRES; + + if (frame_header->allow_high_precision_mv) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ALLOW_HIGH_PRECISION_MV; + + if (frame_header->is_motion_mode_switchable) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_IS_MOTION_MODE_SWITCHABLE; + + if (frame_header->use_ref_frame_mvs) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_USE_REF_FRAME_MVS; + + if (frame_header->disable_frame_end_update_cdf) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_DISABLE_FRAME_END_UPDATE_CDF; + + if (frame_header->allow_warped_motion) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_ALLOW_WARPED_MOTION; + + if (frame_header->reference_select) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_REFERENCE_SELECT; + + if (frame_header->reduced_tx_set) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_REDUCED_TX_SET; + + if (frame_header->skip_mode_present) { + ctrl->flags |= V4L2_AV1_FRAME_FLAG_SKIP_MODE_ALLOWED; // FIXME + ctrl->flags |= V4L2_AV1_FRAME_FLAG_SKIP_MODE_PRESENT; + } + + if (frame_header->frame_size_override_flag) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_FRAME_SIZE_OVERRIDE; + + if (frame_header->buffer_removal_time_present_flag) { + ctrl->flags |= V4L2_AV1_FRAME_FLAG_BUFFER_REMOVAL_TIME_PRESENT; + for (i = 0; i < AV1_MAX_OPERATING_POINTS; i++) + ctrl->buffer_removal_time[i] = frame_header->buffer_removal_time[i]; + } + + if (frame_header->frame_refs_short_signaling) + ctrl->flags |= V4L2_AV1_FRAME_FLAG_FRAME_REFS_SHORT_SIGNALING; +} + +static void fill_film_grain(struct v4l2_ctrl_av1_film_grain *ctrl, + const AV1DecContext *s) +{ + const AV1RawFilmGrainParams *film_grain = &s->cur_frame.film_grain; + int i; + + *ctrl = (struct v4l2_ctrl_av1_film_grain) { + .cr_mult = film_grain->cr_mult, + .grain_seed = film_grain->grain_seed, + .film_grain_params_ref_idx = film_grain->film_grain_params_ref_idx, + .num_y_points = film_grain->num_y_points, + .num_cb_points = film_grain->num_cb_points, + .num_cr_points = film_grain->num_cr_points, + .grain_scaling_minus_8 = film_grain->grain_scaling_minus_8, + .ar_coeff_lag = film_grain->ar_coeff_lag, + .ar_coeff_shift_minus_6 = film_grain->ar_coeff_shift_minus_6, + .grain_scale_shift = film_grain->grain_scale_shift, + .cb_mult = film_grain->cb_mult, + .cb_luma_mult = film_grain->cb_luma_mult, + .cr_luma_mult = film_grain->cr_luma_mult, + .cb_offset = film_grain->cb_offset, + .cr_offset = film_grain->cr_offset, + }; + + if (film_grain->apply_grain) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_APPLY_GRAIN; + + if (film_grain->update_grain) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_UPDATE_GRAIN; + + if (film_grain->chroma_scaling_from_luma) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_CHROMA_SCALING_FROM_LUMA; + + if (film_grain->overlap_flag) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_OVERLAP; + + if (film_grain->clip_to_restricted_range) + ctrl->flags |= V4L2_AV1_FILM_GRAIN_FLAG_CLIP_TO_RESTRICTED_RANGE; + + if (!film_grain->apply_grain) + return; + + for (i = 0; i < film_grain->num_y_points; i++) { + ctrl->point_y_value[i] = film_grain->point_y_value[i]; + ctrl->point_y_scaling[i] = film_grain->point_y_scaling[i]; + } + + for (i = 0; i < film_grain->num_cb_points; i++) { + ctrl->point_cb_value[i] = film_grain->point_cb_value[i]; + ctrl->point_cb_scaling[i] = film_grain->point_cb_scaling[i]; + } + + for (i = 0; i < film_grain->num_cr_points; i++) { + ctrl->point_cr_value[i] = film_grain->point_cr_value[i]; + ctrl->point_cr_scaling[i] = film_grain->point_cr_scaling[i]; + } + + for (i = 0; i < 24; i++) { + ctrl->ar_coeffs_y_plus_128[i] = film_grain->ar_coeffs_y_plus_128[i]; + } + + for (i = 0; i < 25; i++) { + ctrl->ar_coeffs_cb_plus_128[i] = film_grain->ar_coeffs_cb_plus_128[i]; + ctrl->ar_coeffs_cr_plus_128[i] = film_grain->ar_coeffs_cr_plus_128[i]; + } +} + +static int v4l2_request_av1_start_frame(AVCodecContext *avctx, + av_unused const AVBufferRef *buf_ref, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + const AV1DecContext *s = avctx->priv_data; + V4L2RequestContextAV1 *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsAV1 *controls = s->cur_frame.hwaccel_picture_private; + int ret; + + ret = ff_v4l2_request_start_frame(avctx, &controls->pic, s->cur_frame.f); + if (ret) + return ret; + + fill_sequence(&controls->sequence, s); + fill_frame(&controls->frame, s); + + if (ctx->has_film_grain) + fill_film_grain(&controls->film_grain, s); + + controls->tile_group_entries = &controls->tile_group_entry; + controls->allocated_tile_group_entries = 0; + controls->num_tile_group_entries = 0; + + return 0; +} + +static int v4l2_request_av1_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, uint32_t size) +{ + const AV1DecContext *s = avctx->priv_data; + const AV1RawFrameHeader *fh = s->raw_frame_header; + V4L2RequestControlsAV1 *controls = s->cur_frame.hwaccel_picture_private; + + controls->num_tile_group_entries = fh->tile_cols * fh->tile_rows; + if (controls->num_tile_group_entries > V4L2_AV1_MAX_TILE_COUNT) + return AVERROR(EINVAL); + + if (controls->num_tile_group_entries > 1 && + controls->num_tile_group_entries > controls->allocated_tile_group_entries) { + struct v4l2_ctrl_av1_tile_group_entry *tile_group_entries; + + tile_group_entries = av_realloc_array(controls->allocated_tile_group_entries ? + controls->tile_group_entries : NULL, + controls->num_tile_group_entries, + sizeof(*controls->tile_group_entries)); + if (!tile_group_entries) + return AVERROR(ENOMEM); + + if (!controls->allocated_tile_group_entries) + memcpy(tile_group_entries, controls->tile_group_entries, + sizeof(*controls->tile_group_entries)); + + controls->tile_group_entries = tile_group_entries; + controls->allocated_tile_group_entries = controls->num_tile_group_entries; + } + + for (int i = 0; i < controls->num_tile_group_entries; i++) { + controls->tile_group_entries[i] = (struct v4l2_ctrl_av1_tile_group_entry) { + .tile_offset = controls->pic.output->bytesused + + s->tile_group_info[i].tile_offset, + .tile_size = s->tile_group_info[i].tile_size, + .tile_row = s->tile_group_info[i].tile_row, + .tile_col = s->tile_group_info[i].tile_column, + }; + } + + return ff_v4l2_request_append_output(avctx, &controls->pic, buffer, size); +} + +static int v4l2_request_av1_end_frame(AVCodecContext *avctx) +{ + const AV1DecContext *s = avctx->priv_data; + V4L2RequestContextAV1 *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsAV1 *controls = s->cur_frame.hwaccel_picture_private; + int count = 0; + + struct v4l2_ext_control control[V4L2_AV1_CONTROLS_MAX] = {}; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_AV1_SEQUENCE, + .ptr = &controls->sequence, + .size = sizeof(controls->sequence), + }; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_AV1_FRAME, + .ptr = &controls->frame, + .size = sizeof(controls->frame), + }; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY, + .ptr = controls->tile_group_entries, + .size = sizeof(*controls->tile_group_entries) * + FFMAX(controls->num_tile_group_entries, 1), + }; + + if (ctx->has_film_grain) { + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_AV1_FILM_GRAIN, + .ptr = &controls->film_grain, + .size = sizeof(controls->film_grain), + }; + } + + return ff_v4l2_request_decode_frame(avctx, &controls->pic, control, count); +} + +static void v4l2_request_av1_free_frame_priv(AVRefStructOpaque hwctx, void *data) +{ + V4L2RequestControlsAV1 *controls = data; + + if (controls->allocated_tile_group_entries) + av_freep(&controls->tile_group_entries); +} + +static int v4l2_request_av1_post_frames_ctx(AVCodecContext *avctx) +{ + V4L2RequestContextAV1 *ctx = avctx->internal->hwaccel_priv_data; + + struct v4l2_query_ext_ctrl film_grain = { + .id = V4L2_CID_STATELESS_AV1_FILM_GRAIN, + }; + + // TODO: check V4L2_CID_MPEG_VIDEO_AV1_PROFILE + // TODO: check V4L2_CID_MPEG_VIDEO_AV1_LEVEL + + if (!ff_v4l2_request_query_control(avctx, &film_grain)) + ctx->has_film_grain = true; + else + ctx->has_film_grain = false; + + return 0; +} + +static int v4l2_request_av1_init(AVCodecContext *avctx) +{ + const AV1DecContext *s = avctx->priv_data; + struct v4l2_ctrl_av1_sequence sequence; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_AV1_SEQUENCE, + .ptr = &sequence, + .size = sizeof(sequence), + }, + }; + + fill_sequence(&sequence, s); + + return ff_v4l2_request_init(avctx, control, FF_ARRAY_ELEMS(control), + v4l2_request_av1_post_frames_ctx); +} + +static int v4l2_request_av1_frame_params(AVCodecContext *avctx, + AVBufferRef *hw_frames_ctx) +{ + const AV1DecContext *s = avctx->priv_data; + const AV1RawSequenceHeader *seq = s ? s->raw_seq : NULL; + uint8_t bit_depth = seq ? get_bit_depth_from_seq(seq) : 0; + + return ff_v4l2_request_frame_params(avctx, hw_frames_ctx, + V4L2_PIX_FMT_AV1_FRAME, bit_depth); +} + +const FFHWAccel ff_av1_v4l2request_hwaccel = { + .p.name = "av1_v4l2request", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_AV1, + .p.pix_fmt = AV_PIX_FMT_DRM_PRIME, + .start_frame = v4l2_request_av1_start_frame, + .decode_slice = v4l2_request_av1_decode_slice, + .end_frame = v4l2_request_av1_end_frame, + .flush = ff_v4l2_request_flush, + .free_frame_priv = v4l2_request_av1_free_frame_priv, + .frame_priv_data_size = sizeof(V4L2RequestControlsAV1), + .init = v4l2_request_av1_init, + .uninit = ff_v4l2_request_uninit, + .priv_data_size = sizeof(V4L2RequestContextAV1), + .frame_params = v4l2_request_av1_frame_params, + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/thirdparty/ffmpeg/libavcodec/v4l2_request_h264.c b/thirdparty/ffmpeg/libavcodec/v4l2_request_h264.c new file mode 100644 index 000000000..5abf63109 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/v4l2_request_h264.c @@ -0,0 +1,532 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "h264dec.h" +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "internal.h" +#include "v4l2_request.h" + +typedef struct V4L2RequestContextH264 { + V4L2RequestContext base; + enum v4l2_stateless_h264_decode_mode decode_mode; + enum v4l2_stateless_h264_start_code start_code; +} V4L2RequestContextH264; + +typedef struct V4L2RequestControlsH264 { + V4L2RequestPictureContext pic; + struct v4l2_ctrl_h264_sps sps; + struct v4l2_ctrl_h264_pps pps; + struct v4l2_ctrl_h264_scaling_matrix scaling_matrix; + struct v4l2_ctrl_h264_decode_params decode_params; + struct v4l2_ctrl_h264_slice_params slice_params; + struct v4l2_ctrl_h264_pred_weights pred_weights; + bool pred_weights_required; + bool first_slice; + int num_slices; +} V4L2RequestControlsH264; + +static uint8_t nalu_slice_start_code[] = { 0x00, 0x00, 0x01 }; + +static void fill_weight_factors(struct v4l2_h264_weight_factors *weight_factors, + int list, const H264SliceContext *sl) +{ + for (int i = 0; i < sl->ref_count[list]; i++) { + if (sl->pwt.luma_weight_flag[list]) { + weight_factors->luma_weight[i] = sl->pwt.luma_weight[i][list][0]; + weight_factors->luma_offset[i] = sl->pwt.luma_weight[i][list][1]; + } else { + weight_factors->luma_weight[i] = 1 << sl->pwt.luma_log2_weight_denom; + weight_factors->luma_offset[i] = 0; + } + for (int j = 0; j < 2; j++) { + if (sl->pwt.chroma_weight_flag[list]) { + weight_factors->chroma_weight[i][j] = sl->pwt.chroma_weight[i][list][j][0]; + weight_factors->chroma_offset[i][j] = sl->pwt.chroma_weight[i][list][j][1]; + } else { + weight_factors->chroma_weight[i][j] = 1 << sl->pwt.chroma_log2_weight_denom; + weight_factors->chroma_offset[i][j] = 0; + } + } + } +} + +static void fill_dpb_entry(struct v4l2_h264_dpb_entry *entry, + const H264Picture *pic, int long_idx) +{ + entry->reference_ts = ff_v4l2_request_get_capture_timestamp(pic->f); + entry->pic_num = pic->pic_id; + entry->frame_num = pic->long_ref ? long_idx : pic->frame_num; + entry->fields = pic->reference & V4L2_H264_FRAME_REF; + entry->flags = V4L2_H264_DPB_ENTRY_FLAG_VALID; + if (entry->fields) + entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_ACTIVE; + if (pic->long_ref) + entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM; + if (pic->field_picture) + entry->flags |= V4L2_H264_DPB_ENTRY_FLAG_FIELD; + if (pic->field_poc[0] != INT_MAX) + entry->top_field_order_cnt = pic->field_poc[0]; + if (pic->field_poc[1] != INT_MAX) + entry->bottom_field_order_cnt = pic->field_poc[1]; +} + +static void fill_dpb(struct v4l2_ctrl_h264_decode_params *decode_params, + const H264Context *h) +{ + int entries = 0; + + for (int i = 0; i < h->short_ref_count; i++) { + const H264Picture *pic = h->short_ref[i]; + if (pic && (pic->field_poc[0] != INT_MAX || pic->field_poc[1] != INT_MAX)) + fill_dpb_entry(&decode_params->dpb[entries++], pic, pic->pic_id); + } + + if (!h->long_ref_count) + return; + + for (int i = 0; i < FF_ARRAY_ELEMS(h->long_ref); i++) { + const H264Picture *pic = h->long_ref[i]; + if (pic && (pic->field_poc[0] != INT_MAX || pic->field_poc[1] != INT_MAX)) + fill_dpb_entry(&decode_params->dpb[entries++], pic, i); + } +} + +static void fill_ref_list(struct v4l2_h264_reference *reference, + struct v4l2_ctrl_h264_decode_params *decode_params, + const H264Ref *ref) +{ + uint64_t timestamp; + + if (!ref->parent) + return; + + timestamp = ff_v4l2_request_get_capture_timestamp(ref->parent->f); + + for (uint8_t i = 0; i < FF_ARRAY_ELEMS(decode_params->dpb); i++) { + struct v4l2_h264_dpb_entry *entry = &decode_params->dpb[i]; + if ((entry->flags & V4L2_H264_DPB_ENTRY_FLAG_VALID) && + entry->reference_ts == timestamp) { + reference->fields = ref->reference & V4L2_H264_FRAME_REF; + reference->index = i; + return; + } + } +} + +static void fill_sps(struct v4l2_ctrl_h264_sps *ctrl, const H264Context *h) +{ + const SPS *sps = h->ps.sps; + + *ctrl = (struct v4l2_ctrl_h264_sps) { + .profile_idc = sps->profile_idc, + .constraint_set_flags = sps->constraint_set_flags, + .level_idc = sps->level_idc, + .seq_parameter_set_id = sps->sps_id, + .chroma_format_idc = sps->chroma_format_idc, + .bit_depth_luma_minus8 = sps->bit_depth_luma - 8, + .bit_depth_chroma_minus8 = sps->bit_depth_chroma - 8, + .log2_max_frame_num_minus4 = sps->log2_max_frame_num - 4, + .pic_order_cnt_type = sps->poc_type, + .log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_poc_lsb - 4, + .max_num_ref_frames = sps->ref_frame_count, + .num_ref_frames_in_pic_order_cnt_cycle = sps->poc_cycle_length, + .offset_for_non_ref_pic = sps->offset_for_non_ref_pic, + .offset_for_top_to_bottom_field = sps->offset_for_top_to_bottom_field, + .pic_width_in_mbs_minus1 = h->mb_width - 1, + .pic_height_in_map_units_minus1 = sps->frame_mbs_only_flag ? + h->mb_height - 1 : h->mb_height / 2 - 1, + }; + + if (sps->poc_cycle_length > 0 && sps->poc_cycle_length <= 255) + memcpy(ctrl->offset_for_ref_frame, sps->offset_for_ref_frame, + sps->poc_cycle_length * sizeof(ctrl->offset_for_ref_frame[0])); + + if (sps->residual_color_transform_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE; + + if (sps->transform_bypass) + ctrl->flags |= V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS; + + if (sps->delta_pic_order_always_zero_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO; + + if (sps->gaps_in_frame_num_allowed_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_GAPS_IN_FRAME_NUM_VALUE_ALLOWED; + + if (sps->frame_mbs_only_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY; + + if (sps->mb_aff) + ctrl->flags |= V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD; + + if (sps->direct_8x8_inference_flag) + ctrl->flags |= V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE; +} + +static void fill_pps(struct v4l2_ctrl_h264_pps *ctrl, const H264Context *h) +{ + const SPS *sps = h->ps.sps; + const PPS *pps = h->ps.pps; + const H264SliceContext *sl = &h->slice_ctx[0]; + int qp_bd_offset = 6 * (sps->bit_depth_luma - 8); + + *ctrl = (struct v4l2_ctrl_h264_pps) { + .pic_parameter_set_id = sl->pps_id, + .seq_parameter_set_id = pps->sps_id, + .num_slice_groups_minus1 = pps->slice_group_count - 1, + .num_ref_idx_l0_default_active_minus1 = pps->ref_count[0] - 1, + .num_ref_idx_l1_default_active_minus1 = pps->ref_count[1] - 1, + .weighted_bipred_idc = pps->weighted_bipred_idc, + .pic_init_qp_minus26 = pps->init_qp - 26 - qp_bd_offset, + .pic_init_qs_minus26 = pps->init_qs - 26 - qp_bd_offset, + .chroma_qp_index_offset = pps->chroma_qp_index_offset[0], + .second_chroma_qp_index_offset = pps->chroma_qp_index_offset[1], + }; + + if (pps->cabac) + ctrl->flags |= V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE; + + if (pps->pic_order_present) + ctrl->flags |= V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT; + + if (pps->weighted_pred) + ctrl->flags |= V4L2_H264_PPS_FLAG_WEIGHTED_PRED; + + if (pps->deblocking_filter_parameters_present) + ctrl->flags |= V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT; + + if (pps->constrained_intra_pred) + ctrl->flags |= V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED; + + if (pps->redundant_pic_cnt_present) + ctrl->flags |= V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT; + + if (pps->transform_8x8_mode) + ctrl->flags |= V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE; + + /* FFmpeg always provide a scaling matrix */ + ctrl->flags |= V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT; +} + +static int v4l2_request_h264_start_frame(AVCodecContext *avctx, + av_unused const AVBufferRef *buf_ref, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + const H264Context *h = avctx->priv_data; + const PPS *pps = h->ps.pps; + const SPS *sps = h->ps.sps; + const H264SliceContext *sl = &h->slice_ctx[0]; + V4L2RequestControlsH264 *controls = h->cur_pic_ptr->hwaccel_picture_private; + int ret; + + ret = ff_v4l2_request_start_frame(avctx, &controls->pic, h->cur_pic_ptr->f); + if (ret) + return ret; + + fill_sps(&controls->sps, h); + fill_pps(&controls->pps, h); + + memcpy(controls->scaling_matrix.scaling_list_4x4, pps->scaling_matrix4, + sizeof(controls->scaling_matrix.scaling_list_4x4)); + memcpy(controls->scaling_matrix.scaling_list_8x8[0], pps->scaling_matrix8[0], + sizeof(controls->scaling_matrix.scaling_list_8x8[0])); + memcpy(controls->scaling_matrix.scaling_list_8x8[1], pps->scaling_matrix8[3], + sizeof(controls->scaling_matrix.scaling_list_8x8[1])); + + if (sps->chroma_format_idc == 3) { + memcpy(controls->scaling_matrix.scaling_list_8x8[2], pps->scaling_matrix8[1], + sizeof(controls->scaling_matrix.scaling_list_8x8[2])); + memcpy(controls->scaling_matrix.scaling_list_8x8[3], pps->scaling_matrix8[4], + sizeof(controls->scaling_matrix.scaling_list_8x8[3])); + memcpy(controls->scaling_matrix.scaling_list_8x8[4], pps->scaling_matrix8[2], + sizeof(controls->scaling_matrix.scaling_list_8x8[4])); + memcpy(controls->scaling_matrix.scaling_list_8x8[5], pps->scaling_matrix8[5], + sizeof(controls->scaling_matrix.scaling_list_8x8[5])); + } + + controls->decode_params = (struct v4l2_ctrl_h264_decode_params) { + .nal_ref_idc = h->nal_ref_idc, + .frame_num = h->poc.frame_num, + .top_field_order_cnt = h->cur_pic_ptr->field_poc[0] != INT_MAX ? + h->cur_pic_ptr->field_poc[0] : 0, + .bottom_field_order_cnt = h->cur_pic_ptr->field_poc[1] != INT_MAX ? + h->cur_pic_ptr->field_poc[1] : 0, + .idr_pic_id = sl->idr_pic_id, + .pic_order_cnt_lsb = sl->poc_lsb, + .delta_pic_order_cnt_bottom = sl->delta_poc_bottom, + .delta_pic_order_cnt0 = sl->delta_poc[0], + .delta_pic_order_cnt1 = sl->delta_poc[1], + /* Size in bits of dec_ref_pic_marking() syntax element. */ + .dec_ref_pic_marking_bit_size = sl->ref_pic_marking_bit_size, + /* Size in bits of pic order count syntax. */ + .pic_order_cnt_bit_size = sl->pic_order_cnt_bit_size, + .slice_group_change_cycle = 0, /* slice group not supported by FFmpeg */ + }; + + if (h->picture_idr) + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC; + + if (FIELD_PICTURE(h)) + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC; + + if (h->picture_structure == PICT_BOTTOM_FIELD) + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD; + +#if defined(V4L2_H264_DECODE_PARAM_FLAG_PFRAME) + if (sl->slice_type_nos == AV_PICTURE_TYPE_P) + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_PFRAME; +#endif + +#if defined(V4L2_H264_DECODE_PARAM_FLAG_BFRAME) + if (sl->slice_type_nos == AV_PICTURE_TYPE_B) + controls->decode_params.flags |= V4L2_H264_DECODE_PARAM_FLAG_BFRAME; +#endif + + fill_dpb(&controls->decode_params, h); + + controls->first_slice = true; + controls->num_slices = 0; + + return 0; +} + +static int v4l2_request_h264_queue_decode(AVCodecContext *avctx, bool last_slice) +{ + const H264Context *h = avctx->priv_data; + V4L2RequestContextH264 *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsH264 *controls = h->cur_pic_ptr->hwaccel_picture_private; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_H264_SPS, + .ptr = &controls->sps, + .size = sizeof(controls->sps), + }, + { + .id = V4L2_CID_STATELESS_H264_PPS, + .ptr = &controls->pps, + .size = sizeof(controls->pps), + }, + { + .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, + .ptr = &controls->scaling_matrix, + .size = sizeof(controls->scaling_matrix), + }, + { + .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, + .ptr = &controls->decode_params, + .size = sizeof(controls->decode_params), + }, + { + .id = V4L2_CID_STATELESS_H264_SLICE_PARAMS, + .ptr = &controls->slice_params, + .size = sizeof(controls->slice_params), + }, + { + .id = V4L2_CID_STATELESS_H264_PRED_WEIGHTS, + .ptr = &controls->pred_weights, + .size = sizeof(controls->pred_weights), + }, + }; + + if (ctx->decode_mode == V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED) { + int count = FF_ARRAY_ELEMS(control) - (controls->pred_weights_required ? 0 : 1); + return ff_v4l2_request_decode_slice(avctx, &controls->pic, control, count, + controls->first_slice, last_slice); + } + + return ff_v4l2_request_decode_frame(avctx, &controls->pic, + control, FF_ARRAY_ELEMS(control) - 2); +} + +static int v4l2_request_h264_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, uint32_t size) +{ + const H264Context *h = avctx->priv_data; + const PPS *pps = h->ps.pps; + const H264SliceContext *sl = &h->slice_ctx[0]; + V4L2RequestContextH264 *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsH264 *controls = h->cur_pic_ptr->hwaccel_picture_private; + int i, ret, count; + + if (ctx->decode_mode == V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED && + controls->num_slices) { + ret = v4l2_request_h264_queue_decode(avctx, false); + if (ret) + return ret; + + ff_v4l2_request_reset_picture(avctx, &controls->pic); + controls->first_slice = 0; + } + + if (ctx->start_code == V4L2_STATELESS_H264_START_CODE_ANNEX_B) { + ret = ff_v4l2_request_append_output(avctx, &controls->pic, + nalu_slice_start_code, 3); + if (ret) + return ret; + } + + ret = ff_v4l2_request_append_output(avctx, &controls->pic, buffer, size); + if (ret) + return ret; + + if (ctx->decode_mode != V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED) + return 0; + + controls->slice_params = (struct v4l2_ctrl_h264_slice_params) { + /* Offset in bits to slice_data() from the beginning of this slice. */ + .header_bit_size = get_bits_count(&sl->gb), + + .first_mb_in_slice = sl->first_mb_addr, + + .slice_type = ff_h264_get_slice_type(sl), + .colour_plane_id = 0, /* separate colour plane not supported by FFmpeg */ + .redundant_pic_cnt = sl->redundant_pic_count, + .cabac_init_idc = sl->cabac_init_idc, + .slice_qp_delta = sl->qscale - pps->init_qp, + .slice_qs_delta = 0, /* not implemented by FFmpeg */ + .disable_deblocking_filter_idc = sl->deblocking_filter < 2 ? + !sl->deblocking_filter : + sl->deblocking_filter, + .slice_alpha_c0_offset_div2 = sl->slice_alpha_c0_offset / 2, + .slice_beta_offset_div2 = sl->slice_beta_offset / 2, + .num_ref_idx_l0_active_minus1 = sl->list_count > 0 ? sl->ref_count[0] - 1 : 0, + .num_ref_idx_l1_active_minus1 = sl->list_count > 1 ? sl->ref_count[1] - 1 : 0, + }; + + if (sl->slice_type == AV_PICTURE_TYPE_B && sl->direct_spatial_mv_pred) + controls->slice_params.flags |= V4L2_H264_SLICE_FLAG_DIRECT_SPATIAL_MV_PRED; + + /* V4L2_H264_SLICE_FLAG_SP_FOR_SWITCH: not implemented by FFmpeg */ + + controls->pred_weights_required = + V4L2_H264_CTRL_PRED_WEIGHTS_REQUIRED(&controls->pps, &controls->slice_params); + if (controls->pred_weights_required) { + controls->pred_weights.chroma_log2_weight_denom = sl->pwt.chroma_log2_weight_denom; + controls->pred_weights.luma_log2_weight_denom = sl->pwt.luma_log2_weight_denom; + } + + count = sl->list_count > 0 ? sl->ref_count[0] : 0; + for (i = 0; i < count; i++) + fill_ref_list(&controls->slice_params.ref_pic_list0[i], + &controls->decode_params, &sl->ref_list[0][i]); + if (count && controls->pred_weights_required) + fill_weight_factors(&controls->pred_weights.weight_factors[0], 0, sl); + + count = sl->list_count > 1 ? sl->ref_count[1] : 0; + for (i = 0; i < count; i++) + fill_ref_list(&controls->slice_params.ref_pic_list1[i], + &controls->decode_params, &sl->ref_list[1][i]); + if (count && controls->pred_weights_required) + fill_weight_factors(&controls->pred_weights.weight_factors[1], 1, sl); + + controls->num_slices++; + return 0; +} + +static int v4l2_request_h264_end_frame(AVCodecContext *avctx) +{ + return v4l2_request_h264_queue_decode(avctx, true); +} + +static int v4l2_request_h264_post_frames_ctx(AVCodecContext *avctx) +{ + V4L2RequestContextH264 *ctx = avctx->internal->hwaccel_priv_data; + + struct v4l2_ext_control control[] = { + { .id = V4L2_CID_STATELESS_H264_DECODE_MODE, }, + { .id = V4L2_CID_STATELESS_H264_START_CODE, }, + }; + + ctx->decode_mode = ff_v4l2_request_query_control_default_value(avctx, + V4L2_CID_STATELESS_H264_DECODE_MODE); + if (ctx->decode_mode != V4L2_STATELESS_H264_DECODE_MODE_SLICE_BASED && + ctx->decode_mode != V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED) { + av_log(ctx, AV_LOG_VERBOSE, "Unsupported decode mode: %d\n", + ctx->decode_mode); + return AVERROR(EINVAL); + } + + ctx->start_code = ff_v4l2_request_query_control_default_value(avctx, + V4L2_CID_STATELESS_H264_START_CODE); + if (ctx->start_code != V4L2_STATELESS_H264_START_CODE_NONE && + ctx->start_code != V4L2_STATELESS_H264_START_CODE_ANNEX_B) { + av_log(ctx, AV_LOG_VERBOSE, "Unsupported start code: %d\n", + ctx->start_code); + return AVERROR(EINVAL); + } + + // TODO: check V4L2_CID_MPEG_VIDEO_H264_PROFILE control + // TODO: check V4L2_CID_MPEG_VIDEO_H264_LEVEL control + + control[0].value = ctx->decode_mode; + control[1].value = ctx->start_code; + + return ff_v4l2_request_set_controls(avctx, control, FF_ARRAY_ELEMS(control)); +} + +static int v4l2_request_h264_init(AVCodecContext *avctx) +{ + const H264Context *h = avctx->priv_data; + struct v4l2_ctrl_h264_sps sps; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_H264_SPS, + .ptr = &sps, + .size = sizeof(sps), + }, + }; + + fill_sps(&sps, h); + + return ff_v4l2_request_init(avctx, control, FF_ARRAY_ELEMS(control), + v4l2_request_h264_post_frames_ctx); +} + +static int v4l2_request_h264_frame_params(AVCodecContext *avctx, + AVBufferRef *hw_frames_ctx) +{ + const H264Context *h = avctx->priv_data; + const SPS *sps = h ? h->ps.sps : NULL; + uint8_t bit_depth = sps ? sps->bit_depth_luma : 0; + + return ff_v4l2_request_frame_params(avctx, hw_frames_ctx, + V4L2_PIX_FMT_H264_SLICE, bit_depth); +} + +const FFHWAccel ff_h264_v4l2request_hwaccel = { + .p.name = "h264_v4l2request", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_H264, + .p.pix_fmt = AV_PIX_FMT_DRM_PRIME, + .start_frame = v4l2_request_h264_start_frame, + .decode_slice = v4l2_request_h264_decode_slice, + .end_frame = v4l2_request_h264_end_frame, + .flush = ff_v4l2_request_flush, + .frame_priv_data_size = sizeof(V4L2RequestControlsH264), + .init = v4l2_request_h264_init, + .uninit = ff_v4l2_request_uninit, + .priv_data_size = sizeof(V4L2RequestContextH264), + .frame_params = v4l2_request_h264_frame_params, + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/thirdparty/ffmpeg/libavcodec/v4l2_request_hevc.c b/thirdparty/ffmpeg/libavcodec/v4l2_request_hevc.c new file mode 100644 index 000000000..26cba2c75 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/v4l2_request_hevc.c @@ -0,0 +1,752 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "libavutil/mem.h" +#include "hevc/hevcdec.h" +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "internal.h" +#include "v4l2_request.h" + +#define V4L2_HEVC_CONTROLS_MAX 6 + +typedef struct V4L2RequestContextHEVC { + V4L2RequestContext base; + enum v4l2_stateless_hevc_decode_mode decode_mode; + enum v4l2_stateless_hevc_start_code start_code; + unsigned int max_slice_params; + unsigned int max_entry_point_offsets; + bool has_scaling_matrix; +} V4L2RequestContextHEVC; + +typedef struct V4L2RequestControlsHEVC { + V4L2RequestPictureContext pic; + struct v4l2_ctrl_hevc_sps sps; + struct v4l2_ctrl_hevc_pps pps; + struct v4l2_ctrl_hevc_decode_params decode_params; + struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix; + struct v4l2_ctrl_hevc_slice_params slice_params; + struct v4l2_ctrl_hevc_slice_params *frame_slice_params; + unsigned int allocated_slice_params; + unsigned int num_slice_params; + uint32_t *entry_point_offsets; + unsigned int allocated_entry_point_offsets; + unsigned int num_entry_point_offsets; + bool first_slice; +} V4L2RequestControlsHEVC; + +static uint8_t nalu_slice_start_code[] = { 0x00, 0x00, 0x01 }; + +static void fill_pred_weight_table(struct v4l2_hevc_pred_weight_table *table, + const HEVCContext *h) +{ + int32_t luma_weight_denom, chroma_weight_denom; + const SliceHeader *sh = &h->sh; + + if (sh->slice_type == HEVC_SLICE_I || + (sh->slice_type == HEVC_SLICE_P && !h->pps->weighted_pred_flag) || + (sh->slice_type == HEVC_SLICE_B && !h->pps->weighted_bipred_flag)) + return; + + table->luma_log2_weight_denom = sh->luma_log2_weight_denom; + + if (h->pps->sps->chroma_format_idc) + table->delta_chroma_log2_weight_denom = sh->chroma_log2_weight_denom - + sh->luma_log2_weight_denom; + + luma_weight_denom = (1 << sh->luma_log2_weight_denom); + chroma_weight_denom = (1 << sh->chroma_log2_weight_denom); + + for (int i = 0; i < 15 && i < sh->nb_refs[L0]; i++) { + table->delta_luma_weight_l0[i] = sh->luma_weight_l0[i] - luma_weight_denom; + table->luma_offset_l0[i] = sh->luma_offset_l0[i]; + table->delta_chroma_weight_l0[i][0] = sh->chroma_weight_l0[i][0] - chroma_weight_denom; + table->delta_chroma_weight_l0[i][1] = sh->chroma_weight_l0[i][1] - chroma_weight_denom; + table->chroma_offset_l0[i][0] = sh->chroma_offset_l0[i][0]; + table->chroma_offset_l0[i][1] = sh->chroma_offset_l0[i][1]; + } + + if (sh->slice_type != HEVC_SLICE_B) + return; + + for (int i = 0; i < 15 && i < sh->nb_refs[L1]; i++) { + table->delta_luma_weight_l1[i] = sh->luma_weight_l1[i] - luma_weight_denom; + table->luma_offset_l1[i] = sh->luma_offset_l1[i]; + table->delta_chroma_weight_l1[i][0] = sh->chroma_weight_l1[i][0] - chroma_weight_denom; + table->delta_chroma_weight_l1[i][1] = sh->chroma_weight_l1[i][1] - chroma_weight_denom; + table->chroma_offset_l1[i][0] = sh->chroma_offset_l1[i][0]; + table->chroma_offset_l1[i][1] = sh->chroma_offset_l1[i][1]; + } +} + +static uint8_t get_ref_pic_index(const HEVCContext *h, const HEVCFrame *frame, + struct v4l2_ctrl_hevc_decode_params *decode_params) +{ + uint64_t timestamp; + + if (!frame || !frame->f) + return 0; + + timestamp = ff_v4l2_request_get_capture_timestamp(frame->f); + + for (uint8_t i = 0; i < decode_params->num_active_dpb_entries; i++) { + struct v4l2_hevc_dpb_entry *entry = &decode_params->dpb[i]; + if (entry->timestamp == timestamp) + return i; + } + + return 0; +} + +static void fill_decode_params(struct v4l2_ctrl_hevc_decode_params *decode_params, + const HEVCContext *h) +{ + const HEVCFrame *pic = h->cur_frame; + const HEVCLayerContext *l = &h->layers[h->cur_layer]; + const SliceHeader *sh = &h->sh; + int i, entries = 0; + + *decode_params = (struct v4l2_ctrl_hevc_decode_params) { + .pic_order_cnt_val = h->poc, + .short_term_ref_pic_set_size = sh->short_term_ref_pic_set_size, + .long_term_ref_pic_set_size = sh->long_term_ref_pic_set_size, + .num_poc_st_curr_before = h->rps[ST_CURR_BEF].nb_refs, + .num_poc_st_curr_after = h->rps[ST_CURR_AFT].nb_refs, + .num_poc_lt_curr = h->rps[LT_CURR].nb_refs, + }; + +#if HAVE_STRUCT_V4L2_CTRL_HEVC_DECODE_PARAMS_NUM_DELTA_POCS_OF_REF_RPS_IDX + if (h->sh.short_term_ref_pic_set_sps_flag == 0 && h->sh.short_term_rps) + decode_params->num_delta_pocs_of_ref_rps_idx = + h->sh.short_term_rps->rps_idx_num_delta_pocs; +#endif + + for (i = 0; i < FF_ARRAY_ELEMS(l->DPB); i++) { + const HEVCFrame *frame = &l->DPB[i]; + if (frame != pic && + (frame->flags & (HEVC_FRAME_FLAG_LONG_REF | HEVC_FRAME_FLAG_SHORT_REF))) { + struct v4l2_hevc_dpb_entry *entry = &decode_params->dpb[entries++]; + + entry->timestamp = ff_v4l2_request_get_capture_timestamp(frame->f); + entry->field_pic = !!(frame->f->flags & AV_FRAME_FLAG_INTERLACED); + entry->flags = 0; + if (frame->flags & HEVC_FRAME_FLAG_LONG_REF) + entry->flags |= V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE; + + entry->pic_order_cnt_val = frame->poc; + } + } + + decode_params->num_active_dpb_entries = entries; + + if (IS_IRAP(h)) + decode_params->flags |= V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC; + + if (IS_IDR(h)) + decode_params->flags |= V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC; + + if (sh->no_output_of_prior_pics_flag) + decode_params->flags |= V4L2_HEVC_DECODE_PARAM_FLAG_NO_OUTPUT_OF_PRIOR; + + for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) { + decode_params->poc_st_curr_before[i] = + get_ref_pic_index(h, h->rps[ST_CURR_BEF].ref[i], decode_params); + decode_params->poc_st_curr_after[i] = + get_ref_pic_index(h, h->rps[ST_CURR_AFT].ref[i], decode_params); + decode_params->poc_lt_curr[i] = + get_ref_pic_index(h, h->rps[LT_CURR].ref[i], decode_params); + } +} + +static int fill_slice_params(V4L2RequestControlsHEVC *controls, int slice, + uint32_t size, bool max_entry_point_offsets, + const HEVCContext *h) +{ + struct v4l2_ctrl_hevc_slice_params *slice_params = &controls->frame_slice_params[slice]; + struct v4l2_ctrl_hevc_decode_params *decode_params = &controls->decode_params; + const SliceHeader *sh = &h->sh; + RefPicList *rpl; + int i, offsets; + + *slice_params = (struct v4l2_ctrl_hevc_slice_params) { + .bit_size = (size - sh->data_offset) * 8, + .data_byte_offset = controls->pic.output->bytesused + sh->data_offset, + .num_entry_point_offsets = sh->num_entry_point_offsets, + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: NAL unit header */ + .nal_unit_type = h->nal_unit_type, + .nuh_temporal_id_plus1 = h->temporal_id + 1, + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */ + .slice_type = sh->slice_type, + .colour_plane_id = sh->colour_plane_id, + .slice_pic_order_cnt = sh->poc, + .num_ref_idx_l0_active_minus1 = sh->nb_refs[L0] ? sh->nb_refs[L0] - 1 : 0, + .num_ref_idx_l1_active_minus1 = sh->nb_refs[L1] ? sh->nb_refs[L1] - 1 : 0, + .collocated_ref_idx = sh->slice_temporal_mvp_enabled_flag ? + sh->collocated_ref_idx : 0, + .five_minus_max_num_merge_cand = sh->slice_type == HEVC_SLICE_I ? + 0 : 5 - sh->max_num_merge_cand, + .slice_qp_delta = sh->slice_qp_delta, + .slice_cb_qp_offset = sh->slice_cb_qp_offset, + .slice_cr_qp_offset = sh->slice_cr_qp_offset, + .slice_act_y_qp_offset = 0, + .slice_act_cb_qp_offset = 0, + .slice_act_cr_qp_offset = 0, + .slice_beta_offset_div2 = sh->beta_offset / 2, + .slice_tc_offset_div2 = sh->tc_offset / 2, + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture timing SEI message */ + .pic_struct = h->sei.picture_timing.picture_struct, + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: General slice segment header */ + .slice_segment_addr = sh->slice_segment_addr, + .short_term_ref_pic_set_size = sh->short_term_ref_pic_set_size, + .long_term_ref_pic_set_size = sh->long_term_ref_pic_set_size, + }; + + if (h->pps->pps_slice_act_qp_offsets_present_flag) { + slice_params->slice_act_y_qp_offset = sh->slice_act_y_qp_offset; + slice_params->slice_act_cb_qp_offset = sh->slice_act_cb_qp_offset; + slice_params->slice_act_cr_qp_offset = sh->slice_act_cr_qp_offset; + } + + if (sh->slice_sample_adaptive_offset_flag[0]) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA; + + if (sh->slice_sample_adaptive_offset_flag[1]) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA; + + if (sh->slice_temporal_mvp_enabled_flag) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED; + + if (sh->mvd_l1_zero_flag) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO; + + if (sh->cabac_init_flag) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT; + + if (sh->collocated_list == L0) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0; + + if (sh->use_integer_mv_flag) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_USE_INTEGER_MV; + + if (sh->disable_deblocking_filter_flag) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED; + + if (sh->slice_loop_filter_across_slices_enabled_flag) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED; + + if (sh->dependent_slice_segment_flag) + slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT; + + if (sh->slice_type != HEVC_SLICE_I) { + rpl = &h->cur_frame->refPicList[0]; + for (i = 0; i < rpl->nb_refs; i++) + slice_params->ref_idx_l0[i] = get_ref_pic_index(h, rpl->ref[i], decode_params); + } + + if (sh->slice_type == HEVC_SLICE_B) { + rpl = &h->cur_frame->refPicList[1]; + for (i = 0; i < rpl->nb_refs; i++) + slice_params->ref_idx_l1[i] = get_ref_pic_index(h, rpl->ref[i], decode_params); + } + + fill_pred_weight_table(&slice_params->pred_weight_table, h); + + if (!max_entry_point_offsets) + return 0; + + if (controls->allocated_entry_point_offsets < controls->num_entry_point_offsets + sh->num_entry_point_offsets) { + void *entry_point_offsets = controls->entry_point_offsets; + offsets = controls->allocated_entry_point_offsets == 0 ? 128 : controls->allocated_entry_point_offsets * 2; + while (controls->num_entry_point_offsets + sh->num_entry_point_offsets > offsets) + offsets *= 2; + entry_point_offsets = av_realloc_array(entry_point_offsets, offsets, sizeof(*controls->entry_point_offsets)); + if (!entry_point_offsets) + return AVERROR(ENOMEM); + controls->entry_point_offsets = entry_point_offsets; + controls->allocated_entry_point_offsets = offsets; + } + + for (i = 0, offsets = controls->num_entry_point_offsets; i < sh->num_entry_point_offsets; i++) + controls->entry_point_offsets[offsets + i] = sh->entry_point_offset[i]; + controls->num_entry_point_offsets += sh->num_entry_point_offsets; + + return 0; +} + +static void fill_sps(struct v4l2_ctrl_hevc_sps *ctrl, const HEVCContext *h) +{ + const HEVCPPS *pps = h->pps; + const HEVCSPS *sps = pps->sps; + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: Sequence parameter set */ + *ctrl = (struct v4l2_ctrl_hevc_sps) { + .video_parameter_set_id = sps->vps_id, + .seq_parameter_set_id = pps->sps_id, + .pic_width_in_luma_samples = sps->width, + .pic_height_in_luma_samples = sps->height, + .bit_depth_luma_minus8 = sps->bit_depth - 8, + .bit_depth_chroma_minus8 = sps->bit_depth_chroma - 8, + .log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_poc_lsb - 4, + .sps_max_dec_pic_buffering_minus1 = + sps->temporal_layer[sps->max_sub_layers - 1].max_dec_pic_buffering - 1, + .sps_max_num_reorder_pics = + sps->temporal_layer[sps->max_sub_layers - 1].num_reorder_pics, + .sps_max_latency_increase_plus1 = + sps->temporal_layer[sps->max_sub_layers - 1].max_latency_increase + 1, + .log2_min_luma_coding_block_size_minus3 = sps->log2_min_cb_size - 3, + .log2_diff_max_min_luma_coding_block_size = + sps->log2_diff_max_min_coding_block_size, + .log2_min_luma_transform_block_size_minus2 = sps->log2_min_tb_size - 2, + .log2_diff_max_min_luma_transform_block_size = + sps->log2_max_trafo_size - sps->log2_min_tb_size, + .max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter, + .max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra, + .pcm_sample_bit_depth_luma_minus1 = sps->pcm.bit_depth - 1, + .pcm_sample_bit_depth_chroma_minus1 = sps->pcm.bit_depth_chroma - 1, + .log2_min_pcm_luma_coding_block_size_minus3 = sps->pcm.log2_min_pcm_cb_size - 3, + .log2_diff_max_min_pcm_luma_coding_block_size = + sps->pcm.log2_max_pcm_cb_size - sps->pcm.log2_min_pcm_cb_size, + .num_short_term_ref_pic_sets = sps->nb_st_rps, + .num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps, + .chroma_format_idc = sps->chroma_format_idc, + .sps_max_sub_layers_minus1 = sps->max_sub_layers - 1, + }; + + if (sps->separate_colour_plane) + ctrl->flags |= V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE; + + if (sps->scaling_list_enabled) + ctrl->flags |= V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED; + + if (sps->amp_enabled) + ctrl->flags |= V4L2_HEVC_SPS_FLAG_AMP_ENABLED; + + if (sps->sao_enabled) + ctrl->flags |= V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET; + + if (sps->pcm_enabled) + ctrl->flags |= V4L2_HEVC_SPS_FLAG_PCM_ENABLED; + + if (sps->pcm_loop_filter_disabled) + ctrl->flags |= V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED; + + if (sps->long_term_ref_pics_present) + ctrl->flags |= V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT; + + if (sps->temporal_mvp_enabled) + ctrl->flags |= V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED; + + if (sps->strong_intra_smoothing_enabled) + ctrl->flags |= V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED; +} + +static int v4l2_request_hevc_start_frame(AVCodecContext *avctx, + av_unused const AVBufferRef *buf_ref, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + const HEVCContext *h = avctx->priv_data; + const HEVCPPS *pps = h->pps; + const HEVCSPS *sps = pps->sps; + V4L2RequestContextHEVC *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsHEVC *controls = h->cur_frame->hwaccel_picture_private; + const SliceHeader *sh = &h->sh; + int ret; + + ret = ff_v4l2_request_start_frame(avctx, &controls->pic, h->cur_frame->f); + if (ret) + return ret; + + fill_sps(&controls->sps, h); + fill_decode_params(&controls->decode_params, h); + + if (ctx->has_scaling_matrix) { + const ScalingList *sl = pps->scaling_list_data_present_flag ? + &pps->scaling_list : + sps->scaling_list_enabled ? + &sps->scaling_list : NULL; + if (sl) { + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 16; j++) + controls->scaling_matrix.scaling_list_4x4[i][j] = sl->sl[0][i][j]; + for (int j = 0; j < 64; j++) { + controls->scaling_matrix.scaling_list_8x8[i][j] = sl->sl[1][i][j]; + controls->scaling_matrix.scaling_list_16x16[i][j] = sl->sl[2][i][j]; + if (i < 2) + controls->scaling_matrix.scaling_list_32x32[i][j] = sl->sl[3][i * 3][j]; + } + controls->scaling_matrix.scaling_list_dc_coef_16x16[i] = sl->sl_dc[0][i]; + if (i < 2) + controls->scaling_matrix.scaling_list_dc_coef_32x32[i] = sl->sl_dc[1][i * 3]; + } + } + } + + /* ISO/IEC 23008-2, ITU-T Rec. H.265: Picture parameter set */ + controls->pps = (struct v4l2_ctrl_hevc_pps) { + .pic_parameter_set_id = sh->pps_id, + .num_extra_slice_header_bits = pps->num_extra_slice_header_bits, + .num_ref_idx_l0_default_active_minus1 = pps->num_ref_idx_l0_default_active - 1, + .num_ref_idx_l1_default_active_minus1 = pps->num_ref_idx_l1_default_active - 1, + .init_qp_minus26 = pps->pic_init_qp_minus26, + .diff_cu_qp_delta_depth = pps->diff_cu_qp_delta_depth, + .pps_cb_qp_offset = pps->cb_qp_offset, + .pps_cr_qp_offset = pps->cr_qp_offset, + .pps_beta_offset_div2 = pps->beta_offset / 2, + .pps_tc_offset_div2 = pps->tc_offset / 2, + .log2_parallel_merge_level_minus2 = pps->log2_parallel_merge_level - 2, + }; + + if (pps->dependent_slice_segments_enabled_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED; + + if (pps->output_flag_present_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT; + + if (pps->sign_data_hiding_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED; + + if (pps->cabac_init_present_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT; + + if (pps->constrained_intra_pred_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED; + + if (pps->transform_skip_enabled_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED; + + if (pps->cu_qp_delta_enabled_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED; + + if (pps->pic_slice_level_chroma_qp_offsets_present_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT; + + if (pps->weighted_pred_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED; + + if (pps->weighted_bipred_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED; + + if (pps->transquant_bypass_enable_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED; + + if (pps->tiles_enabled_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_TILES_ENABLED; + + if (pps->entropy_coding_sync_enabled_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED; + + if (pps->loop_filter_across_tiles_enabled_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED; + + if (pps->seq_loop_filter_across_slices_enabled_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED; + + if (pps->deblocking_filter_override_enabled_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED; + + if (pps->disable_dbf) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER; + + if (pps->lists_modification_present_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT; + + if (pps->slice_header_extension_present_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT; + + if (pps->deblocking_filter_control_present_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT; + + if (pps->uniform_spacing_flag) + controls->pps.flags |= V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING; + + if (pps->tiles_enabled_flag) { + controls->pps.num_tile_columns_minus1 = pps->num_tile_columns - 1; + controls->pps.num_tile_rows_minus1 = pps->num_tile_rows - 1; + + for (int i = 0; i < pps->num_tile_columns; i++) + controls->pps.column_width_minus1[i] = pps->column_width[i] - 1; + + for (int i = 0; i < pps->num_tile_rows; i++) + controls->pps.row_height_minus1[i] = pps->row_height[i] - 1; + } + + controls->first_slice = true; + controls->frame_slice_params = &controls->slice_params; + controls->allocated_slice_params = 0; + controls->num_slice_params = 0; + controls->allocated_entry_point_offsets = 0; + controls->num_entry_point_offsets = 0; + + return 0; +} + +static int v4l2_request_hevc_queue_decode(AVCodecContext *avctx, bool last_slice) +{ + const HEVCContext *h = avctx->priv_data; + V4L2RequestContextHEVC *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsHEVC *controls = h->cur_frame->hwaccel_picture_private; + int count = 0; + + struct v4l2_ext_control control[V4L2_HEVC_CONTROLS_MAX] = {}; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_HEVC_SPS, + .ptr = &controls->sps, + .size = sizeof(controls->sps), + }; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_HEVC_PPS, + .ptr = &controls->pps, + .size = sizeof(controls->pps), + }; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, + .ptr = &controls->decode_params, + .size = sizeof(controls->decode_params), + }; + + if (ctx->has_scaling_matrix) { + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, + .ptr = &controls->scaling_matrix, + .size = sizeof(controls->scaling_matrix), + }; + } + + if (ctx->max_slice_params && controls->num_slice_params) { + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, + .ptr = controls->frame_slice_params, + .size = sizeof(*controls->frame_slice_params) * + FFMIN(controls->num_slice_params, ctx->max_slice_params), + }; + } + + if (ctx->max_entry_point_offsets && controls->num_entry_point_offsets) { + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS, + .ptr = controls->entry_point_offsets, + .size = sizeof(*controls->entry_point_offsets) * + FFMIN(controls->num_entry_point_offsets, + ctx->max_entry_point_offsets), + }; + } + + if (ctx->decode_mode == V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED) + return ff_v4l2_request_decode_slice(avctx, &controls->pic, control, count, + controls->first_slice, last_slice); + + return ff_v4l2_request_decode_frame(avctx, &controls->pic, control, count); +} + +static int v4l2_request_hevc_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, uint32_t size) +{ + const HEVCContext *h = avctx->priv_data; + V4L2RequestContextHEVC *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsHEVC *controls = h->cur_frame->hwaccel_picture_private; + const SliceHeader *sh = &h->sh; + int ret, slice = controls->num_slice_params; + + if (ctx->decode_mode == V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED && + (slice >= ctx->max_slice_params || (ctx->max_entry_point_offsets && + (controls->num_entry_point_offsets + sh->num_entry_point_offsets > ctx->max_entry_point_offsets)))) { + ret = v4l2_request_hevc_queue_decode(avctx, false); + if (ret) + return ret; + + ff_v4l2_request_reset_picture(avctx, &controls->pic); + slice = controls->num_slice_params = 0; + controls->num_entry_point_offsets = 0; + controls->first_slice = false; + } + + if (ctx->start_code == V4L2_STATELESS_HEVC_START_CODE_ANNEX_B) { + ret = ff_v4l2_request_append_output(avctx, &controls->pic, + nalu_slice_start_code, 3); + if (ret) + return ret; + } + + if (ctx->max_slice_params) { + if (slice && controls->allocated_slice_params < slice + 1) { + void *slice_params = controls->allocated_slice_params == 0 ? NULL : controls->frame_slice_params; + int slices = controls->allocated_slice_params == 0 ? 8 : controls->allocated_slice_params * 2; + slice_params = av_realloc_array(slice_params, slices, sizeof(*controls->frame_slice_params)); + if (!slice_params) + return AVERROR(ENOMEM); + if (controls->allocated_slice_params == 0) + memcpy(slice_params, controls->frame_slice_params, sizeof(*controls->frame_slice_params)); + controls->frame_slice_params = slice_params; + controls->allocated_slice_params = slices; + } + + ret = fill_slice_params(controls, slice, size, + !!ctx->max_entry_point_offsets, h); + if (ret) + return ret; + } + + ret = ff_v4l2_request_append_output(avctx, &controls->pic, buffer, size); + if (ret) + return ret; + + controls->num_slice_params++; + return 0; +} + +static int v4l2_request_hevc_end_frame(AVCodecContext *avctx) +{ + return v4l2_request_hevc_queue_decode(avctx, true); +} + +static void v4l2_request_hevc_free_frame_priv(AVRefStructOpaque hwctx, void *data) +{ + V4L2RequestControlsHEVC *controls = data; + + if (controls->allocated_slice_params) + av_freep(&controls->frame_slice_params); + + av_freep(&controls->entry_point_offsets); +} + +static int v4l2_request_hevc_post_frames_ctx(AVCodecContext *avctx) +{ + V4L2RequestContextHEVC *ctx = avctx->internal->hwaccel_priv_data; + int ret; + + struct v4l2_ext_control control[] = { + { .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, }, + { .id = V4L2_CID_STATELESS_HEVC_START_CODE, }, + }; + struct v4l2_query_ext_ctrl scaling_matrix = { + .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, + }; + struct v4l2_query_ext_ctrl entry_point_offsets = { + .id = V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS, + }; + struct v4l2_query_ext_ctrl slice_params = { + .id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS, + }; + + ctx->decode_mode = ff_v4l2_request_query_control_default_value(avctx, + V4L2_CID_STATELESS_HEVC_DECODE_MODE); + if (ctx->decode_mode != V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED && + ctx->decode_mode != V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED) { + av_log(ctx, AV_LOG_VERBOSE, "Unsupported decode mode: %d\n", + ctx->decode_mode); + return AVERROR(EINVAL); + } + + ctx->start_code = ff_v4l2_request_query_control_default_value(avctx, + V4L2_CID_STATELESS_HEVC_START_CODE); + if (ctx->start_code != V4L2_STATELESS_HEVC_START_CODE_NONE && + ctx->start_code != V4L2_STATELESS_HEVC_START_CODE_ANNEX_B) { + av_log(ctx, AV_LOG_VERBOSE, "Unsupported start code: %d\n", + ctx->start_code); + return AVERROR(EINVAL); + } + + // TODO: check V4L2_CID_MPEG_VIDEO_HEVC_PROFILE control + // TODO: check V4L2_CID_MPEG_VIDEO_HEVC_LEVEL control + + ret = ff_v4l2_request_query_control(avctx, &scaling_matrix); + if (!ret) + ctx->has_scaling_matrix = true; + else + ctx->has_scaling_matrix = false; + + ret = ff_v4l2_request_query_control(avctx, &entry_point_offsets); + if (!ret) + ctx->max_entry_point_offsets = FFMAX(entry_point_offsets.dims[0], 1); + else + ctx->max_entry_point_offsets = 0; + + ret = ff_v4l2_request_query_control(avctx, &slice_params); + if (!ret) + ctx->max_slice_params = FFMAX(slice_params.dims[0], 1); + else + ctx->max_slice_params = 0; + + av_log(ctx, AV_LOG_VERBOSE, "%s-based decoder with SLICE_PARAMS=%u, " + "ENTRY_POINT_OFFSETS=%u and SCALING_MATRIX=%d controls\n", + ctx->decode_mode == V4L2_STATELESS_HEVC_DECODE_MODE_SLICE_BASED ? "slice" : "frame", + ctx->max_slice_params, ctx->max_entry_point_offsets, ctx->has_scaling_matrix); + + control[0].value = ctx->decode_mode; + control[1].value = ctx->start_code; + + return ff_v4l2_request_set_controls(avctx, control, FF_ARRAY_ELEMS(control)); +} + +static int v4l2_request_hevc_init(AVCodecContext *avctx) +{ + const HEVCContext *h = avctx->priv_data; + struct v4l2_ctrl_hevc_sps sps; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_HEVC_SPS, + .ptr = &sps, + .size = sizeof(sps), + }, + }; + + fill_sps(&sps, h); + + return ff_v4l2_request_init(avctx, control, FF_ARRAY_ELEMS(control), + v4l2_request_hevc_post_frames_ctx); +} + +static int v4l2_request_hevc_frame_params(AVCodecContext *avctx, + AVBufferRef *hw_frames_ctx) +{ + const HEVCContext *h = avctx->priv_data; + const HEVCSPS *sps = h && h->pps ? h->pps->sps : NULL; + uint8_t bit_depth = sps ? sps->bit_depth : 0; + + return ff_v4l2_request_frame_params(avctx, hw_frames_ctx, + V4L2_PIX_FMT_HEVC_SLICE, bit_depth); +} + +const FFHWAccel ff_hevc_v4l2request_hwaccel = { + .p.name = "hevc_v4l2request", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_HEVC, + .p.pix_fmt = AV_PIX_FMT_DRM_PRIME, + .start_frame = v4l2_request_hevc_start_frame, + .decode_slice = v4l2_request_hevc_decode_slice, + .end_frame = v4l2_request_hevc_end_frame, + .flush = ff_v4l2_request_flush, + .free_frame_priv = v4l2_request_hevc_free_frame_priv, + .frame_priv_data_size = sizeof(V4L2RequestControlsHEVC), + .init = v4l2_request_hevc_init, + .uninit = ff_v4l2_request_uninit, + .priv_data_size = sizeof(V4L2RequestContextHEVC), + .frame_params = v4l2_request_hevc_frame_params, + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/thirdparty/ffmpeg/libavcodec/v4l2_request_mpeg2.c b/thirdparty/ffmpeg/libavcodec/v4l2_request_mpeg2.c new file mode 100644 index 000000000..5d89b7168 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/v4l2_request_mpeg2.c @@ -0,0 +1,184 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "mathops.h" +#include "mpegvideo.h" +#include "v4l2_request.h" + +typedef struct V4L2RequestControlsMPEG2 { + V4L2RequestPictureContext pic; + struct v4l2_ctrl_mpeg2_sequence sequence; + struct v4l2_ctrl_mpeg2_picture picture; + struct v4l2_ctrl_mpeg2_quantisation quantisation; +} V4L2RequestControlsMPEG2; + +static int v4l2_request_mpeg2_start_frame(AVCodecContext *avctx, + av_unused const AVBufferRef *buf_ref, + av_unused const uint8_t *buffer, + av_unused uint32_t size) +{ + const MpegEncContext *s = avctx->priv_data; + V4L2RequestControlsMPEG2 *controls = s->cur_pic.ptr->hwaccel_picture_private; + int ret; + + ret = ff_v4l2_request_start_frame(avctx, &controls->pic, s->cur_pic.ptr->f); + if (ret) + return ret; + + controls->sequence = (struct v4l2_ctrl_mpeg2_sequence) { + /* ISO/IEC 13818-2, ITU-T Rec. H.262: Sequence header */ + .horizontal_size = s->width, + .vertical_size = s->height, + .vbv_buffer_size = controls->pic.output->size, + + /* ISO/IEC 13818-2, ITU-T Rec. H.262: Sequence extension */ + .profile_and_level_indication = 0, + .chroma_format = s->chroma_format, + }; + + if (s->progressive_sequence) + controls->sequence.flags |= V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE; + + controls->picture = (struct v4l2_ctrl_mpeg2_picture) { + /* ISO/IEC 13818-2, ITU-T Rec. H.262: Picture header */ + .picture_coding_type = s->pict_type, + + /* ISO/IEC 13818-2, ITU-T Rec. H.262: Picture coding extension */ + .f_code[0][0] = s->mpeg_f_code[0][0], + .f_code[0][1] = s->mpeg_f_code[0][1], + .f_code[1][0] = s->mpeg_f_code[1][0], + .f_code[1][1] = s->mpeg_f_code[1][1], + .picture_structure = s->picture_structure, + .intra_dc_precision = s->intra_dc_precision, + }; + + if (s->top_field_first) + controls->picture.flags |= V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST; + + if (s->frame_pred_frame_dct) + controls->picture.flags |= V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT; + + if (s->concealment_motion_vectors) + controls->picture.flags |= V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV; + + if (s->intra_vlc_format) + controls->picture.flags |= V4L2_MPEG2_PIC_FLAG_INTRA_VLC; + + if (s->q_scale_type) + controls->picture.flags |= V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE; + + if (s->alternate_scan) + controls->picture.flags |= V4L2_MPEG2_PIC_FLAG_ALT_SCAN; + + if (s->repeat_first_field) + controls->picture.flags |= V4L2_MPEG2_PIC_FLAG_REPEAT_FIRST; + + if (s->progressive_frame) + controls->picture.flags |= V4L2_MPEG2_PIC_FLAG_PROGRESSIVE; + + switch (s->pict_type) { + case AV_PICTURE_TYPE_B: + if (s->next_pic.ptr) + controls->picture.backward_ref_ts = + ff_v4l2_request_get_capture_timestamp(s->next_pic.ptr->f); + // fall-through + case AV_PICTURE_TYPE_P: + if (s->last_pic.ptr) + controls->picture.forward_ref_ts = + ff_v4l2_request_get_capture_timestamp(s->last_pic.ptr->f); + } + + for (int i = 0; i < 64; i++) { + int n = s->idsp.idct_permutation[ff_zigzag_direct[i]]; + controls->quantisation.intra_quantiser_matrix[i] = s->intra_matrix[n]; + controls->quantisation.non_intra_quantiser_matrix[i] = s->inter_matrix[n]; + controls->quantisation.chroma_intra_quantiser_matrix[i] = s->chroma_intra_matrix[n]; + controls->quantisation.chroma_non_intra_quantiser_matrix[i] = s->chroma_inter_matrix[n]; + } + + return 0; +} + +static int v4l2_request_mpeg2_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, uint32_t size) +{ + const MpegEncContext *s = avctx->priv_data; + V4L2RequestControlsMPEG2 *controls = s->cur_pic.ptr->hwaccel_picture_private; + + return ff_v4l2_request_append_output(avctx, &controls->pic, buffer, size); +} + +static int v4l2_request_mpeg2_end_frame(AVCodecContext *avctx) +{ + const MpegEncContext *s = avctx->priv_data; + V4L2RequestControlsMPEG2 *controls = s->cur_pic.ptr->hwaccel_picture_private; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_MPEG2_SEQUENCE, + .ptr = &controls->sequence, + .size = sizeof(controls->sequence), + }, + { + .id = V4L2_CID_STATELESS_MPEG2_PICTURE, + .ptr = &controls->picture, + .size = sizeof(controls->picture), + }, + { + .id = V4L2_CID_STATELESS_MPEG2_QUANTISATION, + .ptr = &controls->quantisation, + .size = sizeof(controls->quantisation), + }, + }; + + return ff_v4l2_request_decode_frame(avctx, &controls->pic, + control, FF_ARRAY_ELEMS(control)); +} + +static int v4l2_request_mpeg2_init(AVCodecContext *avctx) +{ + return ff_v4l2_request_init(avctx, NULL, 0, NULL); +} + +static int v4l2_request_mpeg2_frame_params(AVCodecContext *avctx, + AVBufferRef *hw_frames_ctx) +{ + return ff_v4l2_request_frame_params(avctx, hw_frames_ctx, + V4L2_PIX_FMT_MPEG2_SLICE, 8); +} + +const FFHWAccel ff_mpeg2_v4l2request_hwaccel = { + .p.name = "mpeg2_v4l2request", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_MPEG2VIDEO, + .p.pix_fmt = AV_PIX_FMT_DRM_PRIME, + .start_frame = v4l2_request_mpeg2_start_frame, + .decode_slice = v4l2_request_mpeg2_decode_slice, + .end_frame = v4l2_request_mpeg2_end_frame, + .flush = ff_v4l2_request_flush, + .frame_priv_data_size = sizeof(V4L2RequestControlsMPEG2), + .init = v4l2_request_mpeg2_init, + .uninit = ff_v4l2_request_uninit, + .priv_data_size = sizeof(V4L2RequestContext), + .frame_params = v4l2_request_mpeg2_frame_params, + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/thirdparty/ffmpeg/libavcodec/v4l2_request_vp8.c b/thirdparty/ffmpeg/libavcodec/v4l2_request_vp8.c new file mode 100644 index 000000000..3fb23951d --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/v4l2_request_vp8.c @@ -0,0 +1,242 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "v4l2_request.h" +#include "vp8.h" + +typedef struct V4L2RequestControlsVP8 { + V4L2RequestPictureContext pic; + struct v4l2_ctrl_vp8_frame frame; +} V4L2RequestControlsVP8; + +static int v4l2_request_vp8_start_frame(AVCodecContext *avctx, + av_unused const AVBufferRef *buf_ref, + const uint8_t *buffer, + av_unused uint32_t size) +{ + const VP8Context *s = avctx->priv_data; + V4L2RequestControlsVP8 *controls = s->framep[VP8_FRAME_CURRENT]->hwaccel_picture_private; + struct v4l2_ctrl_vp8_frame *ctrl = &controls->frame; + unsigned int header_size = 3 + 7 * s->keyframe; + const uint8_t *data = buffer + header_size; + int ret, i, j, k; + + ret = ff_v4l2_request_start_frame(avctx, &controls->pic, + s->framep[VP8_FRAME_CURRENT]->tf.f); + if (ret) + return ret; + + *ctrl = (struct v4l2_ctrl_vp8_frame) { + .lf = { + .sharpness_level = s->filter.sharpness, + .level = s->filter.level, + }, + + .quant = { + .y_ac_qi = s->quant.yac_qi, + .y_dc_delta = s->quant.ydc_delta, + .y2_dc_delta = s->quant.y2dc_delta, + .y2_ac_delta = s->quant.y2ac_delta, + .uv_dc_delta = s->quant.uvdc_delta, + .uv_ac_delta = s->quant.uvac_delta, + }, + + .coder_state = { + .range = s->coder_state_at_header_end.range, + .value = s->coder_state_at_header_end.value, + .bit_count = s->coder_state_at_header_end.bit_count, + }, + + .width = avctx->width, + .height = avctx->height, + + .horizontal_scale = 0, /* scale not supported by FFmpeg */ + .vertical_scale = 0, /* scale not supported by FFmpeg */ + + .version = s->profile & 0x3, + .prob_skip_false = s->prob->mbskip, + .prob_intra = s->prob->intra, + .prob_last = s->prob->last, + .prob_gf = s->prob->golden, + .num_dct_parts = s->num_coeff_partitions, + + .first_part_size = s->header_partition_size, + .first_part_header_bits = (8 * (s->coder_state_at_header_end.input - data) - + s->coder_state_at_header_end.bit_count - 8), + }; + + for (i = 0; i < 4; i++) { + ctrl->segment.quant_update[i] = s->segmentation.base_quant[i]; + ctrl->segment.lf_update[i] = s->segmentation.filter_level[i]; + } + + for (i = 0; i < 3; i++) + ctrl->segment.segment_probs[i] = s->prob->segmentid[i]; + + if (s->segmentation.enabled) + ctrl->segment.flags |= V4L2_VP8_SEGMENT_FLAG_ENABLED; + + if (s->segmentation.update_map) + ctrl->segment.flags |= V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP; + + if (s->segmentation.update_feature_data) + ctrl->segment.flags |= V4L2_VP8_SEGMENT_FLAG_UPDATE_FEATURE_DATA; + + if (!s->segmentation.absolute_vals) + ctrl->segment.flags |= V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE; + + for (i = 0; i < 4; i++) { + ctrl->lf.ref_frm_delta[i] = s->lf_delta.ref[i]; + ctrl->lf.mb_mode_delta[i] = s->lf_delta.mode[i + MODE_I4x4]; + } + + if (s->lf_delta.enabled) + ctrl->lf.flags |= V4L2_VP8_LF_ADJ_ENABLE; + + if (s->lf_delta.update) + ctrl->lf.flags |= V4L2_VP8_LF_DELTA_UPDATE; + + if (s->filter.simple) + ctrl->lf.flags |= V4L2_VP8_LF_FILTER_TYPE_SIMPLE; + + if (s->keyframe) { + static const uint8_t keyframe_y_mode_probs[4] = { + 145, 156, 163, 128 + }; + static const uint8_t keyframe_uv_mode_probs[3] = { + 142, 114, 183 + }; + + memcpy(ctrl->entropy.y_mode_probs, keyframe_y_mode_probs, 4); + memcpy(ctrl->entropy.uv_mode_probs, keyframe_uv_mode_probs, 3); + } else { + for (i = 0; i < 4; i++) + ctrl->entropy.y_mode_probs[i] = s->prob->pred16x16[i]; + for (i = 0; i < 3; i++) + ctrl->entropy.uv_mode_probs[i] = s->prob->pred8x8c[i]; + } + for (i = 0; i < 2; i++) + for (j = 0; j < 19; j++) + ctrl->entropy.mv_probs[i][j] = s->prob->mvc[i][j]; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 8; j++) { + static const int coeff_bands_inverse[8] = { + 0, 1, 2, 3, 5, 6, 4, 15 + }; + int coeff_pos = coeff_bands_inverse[j]; + + for (k = 0; k < 3; k++) { + memcpy(ctrl->entropy.coeff_probs[i][j][k], + s->prob->token[i][coeff_pos][k], 11); + } + } + } + + for (i = 0; i < 8; i++) + ctrl->dct_part_sizes[i] = s->coeff_partition_size[i]; + + if (s->framep[VP8_FRAME_PREVIOUS]) + ctrl->last_frame_ts = + ff_v4l2_request_get_capture_timestamp(s->framep[VP8_FRAME_PREVIOUS]->tf.f); + if (s->framep[VP8_FRAME_GOLDEN]) + ctrl->golden_frame_ts = + ff_v4l2_request_get_capture_timestamp(s->framep[VP8_FRAME_GOLDEN]->tf.f); + if (s->framep[VP8_FRAME_ALTREF]) + ctrl->alt_frame_ts = + ff_v4l2_request_get_capture_timestamp(s->framep[VP8_FRAME_ALTREF]->tf.f); + + if (s->keyframe) + ctrl->flags |= V4L2_VP8_FRAME_FLAG_KEY_FRAME; + + if (s->profile & 0x4) + ctrl->flags |= V4L2_VP8_FRAME_FLAG_EXPERIMENTAL; + + if (!s->invisible) + ctrl->flags |= V4L2_VP8_FRAME_FLAG_SHOW_FRAME; + + if (s->mbskip_enabled) + ctrl->flags |= V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF; + + if (s->sign_bias[VP8_FRAME_GOLDEN]) + ctrl->flags |= V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN; + + if (s->sign_bias[VP8_FRAME_ALTREF]) + ctrl->flags |= V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT; + + return 0; +} + +static int v4l2_request_vp8_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, uint32_t size) +{ + const VP8Context *s = avctx->priv_data; + V4L2RequestControlsVP8 *controls = s->framep[VP8_FRAME_CURRENT]->hwaccel_picture_private; + + return ff_v4l2_request_append_output(avctx, &controls->pic, buffer, size); +} + +static int v4l2_request_vp8_end_frame(AVCodecContext *avctx) +{ + const VP8Context *s = avctx->priv_data; + V4L2RequestControlsVP8 *controls = s->framep[VP8_FRAME_CURRENT]->hwaccel_picture_private; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_VP8_FRAME, + .ptr = &controls->frame, + .size = sizeof(controls->frame), + }, + }; + + return ff_v4l2_request_decode_frame(avctx, &controls->pic, + control, FF_ARRAY_ELEMS(control)); +} + +static int v4l2_request_vp8_init(AVCodecContext *avctx) +{ + return ff_v4l2_request_init(avctx, NULL, 0, NULL); +} + +static int v4l2_request_vp8_frame_params(AVCodecContext *avctx, + AVBufferRef *hw_frames_ctx) +{ + return ff_v4l2_request_frame_params(avctx, hw_frames_ctx, + V4L2_PIX_FMT_VP8_FRAME, 8); +} + +const FFHWAccel ff_vp8_v4l2request_hwaccel = { + .p.name = "vp8_v4l2request", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP8, + .p.pix_fmt = AV_PIX_FMT_DRM_PRIME, + .start_frame = v4l2_request_vp8_start_frame, + .decode_slice = v4l2_request_vp8_decode_slice, + .end_frame = v4l2_request_vp8_end_frame, + .flush = ff_v4l2_request_flush, + .frame_priv_data_size = sizeof(V4L2RequestControlsVP8), + .init = v4l2_request_vp8_init, + .uninit = ff_v4l2_request_uninit, + .priv_data_size = sizeof(V4L2RequestContext), + .frame_params = v4l2_request_vp8_frame_params, + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/thirdparty/ffmpeg/libavcodec/v4l2_request_vp9.c b/thirdparty/ffmpeg/libavcodec/v4l2_request_vp9.c new file mode 100644 index 000000000..e93937daf --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/v4l2_request_vp9.c @@ -0,0 +1,530 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "internal.h" +#include "v4l2_request.h" +#include "vp89_rac.h" +#include "vp9dec.h" + +#define V4L2_VP9_CONTROLS_MAX 2 + +typedef struct V4L2RequestContextVP9 { + V4L2RequestContext base; + bool has_compressed_hdr; +} V4L2RequestContextVP9; + +typedef struct V4L2RequestControlsVP9 { + V4L2RequestPictureContext pic; + struct v4l2_ctrl_vp9_frame frame; + struct v4l2_ctrl_vp9_compressed_hdr compressed_hdr; +} V4L2RequestControlsVP9; + +// differential forward probability updates +static int read_prob_delta(VPXRangeCoder *c) +{ + static const uint8_t inv_map_table[255] = { + 7, 20, 33, 46, 59, 72, 85, 98, 111, 124, 137, 150, 163, 176, + 189, 202, 215, 228, 241, 254, 1, 2, 3, 4, 5, 6, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 253, + }; + int d; + + /* This code is trying to do a differential probability update. For a + * current probability A in the range [1, 255], the difference to a new + * probability of any value can be expressed differentially as 1-A, 255-A + * where some part of this (absolute range) exists both in positive as + * well as the negative part, whereas another part only exists in one + * half. We're trying to code this shared part differentially, i.e. + * times two where the value of the lowest bit specifies the sign, and + * the single part is then coded on top of this. This absolute difference + * then again has a value of [0, 254], but a bigger value in this range + * indicates that we're further away from the original value A, so we + * can code this as a VLC code, since higher values are increasingly + * unlikely. The first 20 values in inv_map_table[] allow 'cheap, rough' + * updates vs. the 'fine, exact' updates further down the range, which + * adds one extra dimension to this differential update model. */ + + if (!vp89_rac_get(c)) { + d = vp89_rac_get_uint(c, 4) + 0; + } else if (!vp89_rac_get(c)) { + d = vp89_rac_get_uint(c, 4) + 16; + } else if (!vp89_rac_get(c)) { + d = vp89_rac_get_uint(c, 5) + 32; + } else { + d = vp89_rac_get_uint(c, 7); + if (d >= 65) + d = (d << 1) - 65 + vp89_rac_get(c); + d += 64; + av_assert2(d < FF_ARRAY_ELEMS(inv_map_table)); + } + + return inv_map_table[d]; +} + +static void fill_compressed_hdr(struct v4l2_ctrl_vp9_compressed_hdr *ctrl, + const uint8_t *buffer, uint32_t size, + AVCodecContext *avctx) +{ + const VP9Context *s = avctx->priv_data; + enum CompPredMode comppredmode; + int ret, i, j, k, l, m, n; + VPXRangeCoder c; + + ret = ff_vpx_init_range_decoder(&c, buffer + s->s.h.uncompressed_header_size, + s->s.h.compressed_header_size); + if (ret < 0) + return; + + if (vpx_rac_get_prob_branchy(&c, 128)) // marker bit + return; + + // txfm updates + if (s->s.h.lossless) { + ctrl->tx_mode = V4L2_VP9_TX_MODE_ONLY_4X4; + } else { + ctrl->tx_mode = vp89_rac_get_uint(&c, 2); + if (ctrl->tx_mode == V4L2_VP9_TX_MODE_ALLOW_32X32) + ctrl->tx_mode += vp89_rac_get(&c); + + if (ctrl->tx_mode == V4L2_VP9_TX_MODE_SELECT) { + for (i = 0; i < 2; i++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->tx8[i][0] = read_prob_delta(&c); + for (i = 0; i < 2; i++) + for (j = 0; j < 2; j++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->tx16[i][j] = read_prob_delta(&c); + for (i = 0; i < 2; i++) + for (j = 0; j < 3; j++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->tx32[i][j] = read_prob_delta(&c); + } + } + + // coef updates + for (i = 0; i < 4; i++) { + if (vp89_rac_get(&c)) { + for (j = 0; j < 2; j++) + for (k = 0; k < 2; k++) + for (l = 0; l < 6; l++) + for (m = 0; m < 6; m++) { + if (m >= 3 && l == 0) // dc only has 3 pt + break; + for (n = 0; n < 3; n++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->coef[i][j][k][l][m][n] = + read_prob_delta(&c); + } + } + if (ctrl->tx_mode == i) + break; + } + + // mode updates + for (i = 0; i < 3; i++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->skip[i] = read_prob_delta(&c); + if (!s->s.h.keyframe && !s->s.h.intraonly) { + for (i = 0; i < 7; i++) + for (j = 0; j < 3; j++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->inter_mode[i][j] = read_prob_delta(&c); + + if (s->s.h.filtermode == FILTER_SWITCHABLE) + for (i = 0; i < 4; i++) + for (j = 0; j < 2; j++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->interp_filter[i][j] = read_prob_delta(&c); + + for (i = 0; i < 4; i++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->is_inter[i] = read_prob_delta(&c); + + if (s->s.h.allowcompinter) { + comppredmode = vp89_rac_get(&c); + if (comppredmode) + comppredmode += vp89_rac_get(&c); + if (comppredmode == PRED_SWITCHABLE) + for (i = 0; i < 5; i++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->comp_mode[i] = read_prob_delta(&c); + } else { + comppredmode = PRED_SINGLEREF; + } + + if (comppredmode != PRED_COMPREF) { + for (i = 0; i < 5; i++) { + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->single_ref[i][0] = read_prob_delta(&c); + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->single_ref[i][1] = read_prob_delta(&c); + } + } + + if (comppredmode != PRED_SINGLEREF) { + for (i = 0; i < 5; i++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->comp_ref[i] = read_prob_delta(&c); + } + + for (i = 0; i < 4; i++) + for (j = 0; j < 9; j++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->y_mode[i][j] = read_prob_delta(&c); + + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + for (k = 0; k < 3; k++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->partition[(i * 4) + j][k] = read_prob_delta(&c); + + // mv fields + for (i = 0; i < 3; i++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->mv.joint[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1; + + for (i = 0; i < 2; i++) { + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->mv.sign[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1; + + for (j = 0; j < 10; j++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->mv.classes[i][j] = (vp89_rac_get_uint(&c, 7) << 1) | 1; + + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->mv.class0_bit[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1; + + for (j = 0; j < 10; j++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->mv.bits[i][j] = (vp89_rac_get_uint(&c, 7) << 1) | 1; + } + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) + for (k = 0; k < 3; k++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->mv.class0_fr[i][j][k] = + (vp89_rac_get_uint(&c, 7) << 1) | 1; + + for (j = 0; j < 3; j++) + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->mv.fr[i][j] = (vp89_rac_get_uint(&c, 7) << 1) | 1; + } + + if (s->s.h.highprecisionmvs) { + for (i = 0; i < 2; i++) { + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->mv.class0_hp[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1; + + if (vpx_rac_get_prob_branchy(&c, 252)) + ctrl->mv.hp[i] = (vp89_rac_get_uint(&c, 7) << 1) | 1; + } + } + } + + memcpy(ctrl->uv_mode, s->prob.p.uv_mode, sizeof(ctrl->uv_mode)); +} + +static void fill_frame(struct v4l2_ctrl_vp9_frame *ctrl, AVCodecContext *avctx) +{ + const VP9Context *s = avctx->priv_data; + AVFrame *ref; + int i; + + *ctrl = (struct v4l2_ctrl_vp9_frame) { + .lf = { + .level = s->s.h.filter.level, + .sharpness = s->s.h.filter.sharpness, + }, + + .quant = { + .base_q_idx = s->s.h.yac_qi, + .delta_q_y_dc = s->s.h.ydc_qdelta, + .delta_q_uv_dc = s->s.h.uvdc_qdelta, + .delta_q_uv_ac = s->s.h.uvac_qdelta, + }, + + .compressed_header_size = s->s.h.compressed_header_size, + .uncompressed_header_size = s->s.h.uncompressed_header_size, + .frame_width_minus_1 = avctx->width - 1, + .frame_height_minus_1 = avctx->height - 1, + .render_width_minus_1 = s->w - 1, + .render_height_minus_1 = s->h - 1, + .reset_frame_context = s->s.h.resetctx > 0 ? s->s.h.resetctx - 1 : 0, + .frame_context_idx = s->s.h.framectxid, + .profile = s->s.h.profile, + .bit_depth = s->s.h.bpp, + .interpolation_filter = s->s.h.filtermode ^ (s->s.h.filtermode <= 1), + .tile_cols_log2 = s->s.h.tiling.log2_tile_cols, + .tile_rows_log2 = s->s.h.tiling.log2_tile_rows, + .reference_mode = s->s.h.comppredmode, + }; + + for (i = 0; i < 4; i++) + ctrl->lf.ref_deltas[i] = s->s.h.lf_delta.ref[i]; + + for (i = 0; i < 2; i++) + ctrl->lf.mode_deltas[i] = s->s.h.lf_delta.mode[i]; + + if (s->s.h.lf_delta.enabled) + ctrl->lf.flags |= V4L2_VP9_LOOP_FILTER_FLAG_DELTA_ENABLED; + + if (s->s.h.lf_delta.updated) + ctrl->lf.flags |= V4L2_VP9_LOOP_FILTER_FLAG_DELTA_UPDATE; + + for (i = 0; i < 8; i++) { + if (s->s.h.segmentation.feat[i].q_enabled) { + ctrl->seg.feature_data[i][V4L2_VP9_SEG_LVL_ALT_Q] = + s->s.h.segmentation.feat[i].q_val; + ctrl->seg.feature_enabled[i] |= 1 << V4L2_VP9_SEG_LVL_ALT_Q; + } + + if (s->s.h.segmentation.feat[i].lf_enabled) { + ctrl->seg.feature_data[i][V4L2_VP9_SEG_LVL_ALT_L] = + s->s.h.segmentation.feat[i].lf_val; + ctrl->seg.feature_enabled[i] |= 1 << V4L2_VP9_SEG_LVL_ALT_L; + } + + if (s->s.h.segmentation.feat[i].ref_enabled) { + ctrl->seg.feature_data[i][V4L2_VP9_SEG_LVL_REF_FRAME] = + s->s.h.segmentation.feat[i].ref_val; + ctrl->seg.feature_enabled[i] |= 1 << V4L2_VP9_SEG_LVL_REF_FRAME; + } + + if (s->s.h.segmentation.feat[i].skip_enabled) + ctrl->seg.feature_enabled[i] |= 1 << V4L2_VP9_SEG_LVL_SKIP; + } + + for (i = 0; i < 7; i++) + ctrl->seg.tree_probs[i] = s->s.h.segmentation.prob[i]; + + if (s->s.h.segmentation.temporal) { + for (i = 0; i < 3; i++) + ctrl->seg.pred_probs[i] = s->s.h.segmentation.pred_prob[i]; + } else { + memset(ctrl->seg.pred_probs, 255, sizeof(ctrl->seg.pred_probs)); + } + + if (s->s.h.segmentation.enabled) + ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_ENABLED; + + if (s->s.h.segmentation.update_map) + ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_UPDATE_MAP; + + if (s->s.h.segmentation.temporal) + ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_TEMPORAL_UPDATE; + + if (s->frame_header->segmentation_update_data) + ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_UPDATE_DATA; + + if (s->s.h.segmentation.absolute_vals) + ctrl->seg.flags |= V4L2_VP9_SEGMENTATION_FLAG_ABS_OR_DELTA_UPDATE; + + if (s->s.h.keyframe) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_KEY_FRAME; + + if (!s->s.h.invisible) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_SHOW_FRAME; + + if (s->s.h.errorres) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_ERROR_RESILIENT; + + if (s->s.h.intraonly) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_INTRA_ONLY; + + if (!s->s.h.keyframe && s->s.h.highprecisionmvs) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_ALLOW_HIGH_PREC_MV; + + if (s->s.h.refreshctx) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_REFRESH_FRAME_CTX; + + if (s->s.h.parallelmode) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_PARALLEL_DEC_MODE; + + if (s->ss_h) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_X_SUBSAMPLING; + + if (s->ss_v) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_Y_SUBSAMPLING; + + if (avctx->color_range == AVCOL_RANGE_JPEG) + ctrl->flags |= V4L2_VP9_FRAME_FLAG_COLOR_RANGE_FULL_SWING; + + ref = s->s.refs[s->s.h.refidx[0]].f; + if (ref && ref->private_ref) + ctrl->last_frame_ts = ff_v4l2_request_get_capture_timestamp(ref); + + ref = s->s.refs[s->s.h.refidx[1]].f; + if (ref && ref->private_ref) + ctrl->golden_frame_ts = ff_v4l2_request_get_capture_timestamp(ref); + + ref = s->s.refs[s->s.h.refidx[2]].f; + if (ref && ref->private_ref) + ctrl->alt_frame_ts = ff_v4l2_request_get_capture_timestamp(ref); + + if (s->s.h.signbias[0]) + ctrl->ref_frame_sign_bias |= V4L2_VP9_SIGN_BIAS_LAST; + + if (s->s.h.signbias[1]) + ctrl->ref_frame_sign_bias |= V4L2_VP9_SIGN_BIAS_GOLDEN; + + if (s->s.h.signbias[2]) + ctrl->ref_frame_sign_bias |= V4L2_VP9_SIGN_BIAS_ALT; +} + +static int v4l2_request_vp9_start_frame(AVCodecContext *avctx, + av_unused const AVBufferRef *buf_ref, + const uint8_t *buffer, + uint32_t size) +{ + const VP9SharedContext *h = avctx->priv_data; + const VP9Frame *f = &h->frames[CUR_FRAME]; + V4L2RequestContextVP9 *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsVP9 *controls = f->hwaccel_picture_private; + int ret; + + ret = ff_v4l2_request_start_frame(avctx, &controls->pic, f->tf.f); + if (ret) + return ret; + + fill_frame(&controls->frame, avctx); + + if (ctx->has_compressed_hdr) + fill_compressed_hdr(&controls->compressed_hdr, buffer, size, avctx); + + return 0; +} + +static int v4l2_request_vp9_decode_slice(AVCodecContext *avctx, + const uint8_t *buffer, uint32_t size) +{ + const VP9SharedContext *h = avctx->priv_data; + V4L2RequestControlsVP9 *controls = h->frames[CUR_FRAME].hwaccel_picture_private; + + return ff_v4l2_request_append_output(avctx, &controls->pic, buffer, size); +} + +static int v4l2_request_vp9_end_frame(AVCodecContext *avctx) +{ + const VP9SharedContext *h = avctx->priv_data; + V4L2RequestContextVP9 *ctx = avctx->internal->hwaccel_priv_data; + V4L2RequestControlsVP9 *controls = h->frames[CUR_FRAME].hwaccel_picture_private; + int count = 0; + + struct v4l2_ext_control control[V4L2_VP9_CONTROLS_MAX] = {}; + + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_VP9_FRAME, + .ptr = &controls->frame, + .size = sizeof(controls->frame), + }; + + if (ctx->has_compressed_hdr) { + control[count++] = (struct v4l2_ext_control) { + .id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, + .ptr = &controls->compressed_hdr, + .size = sizeof(controls->compressed_hdr), + }; + } + + return ff_v4l2_request_decode_frame(avctx, &controls->pic, control, count); +} + +static int v4l2_request_vp9_post_frames_ctx(AVCodecContext *avctx) +{ + V4L2RequestContextVP9 *ctx = avctx->internal->hwaccel_priv_data; + + struct v4l2_query_ext_ctrl compressed_hdr = { + .id = V4L2_CID_STATELESS_VP9_COMPRESSED_HDR, + }; + + // TODO: check V4L2_CID_MPEG_VIDEO_VP9_PROFILE + // TODO: check V4L2_CID_MPEG_VIDEO_VP9_LEVEL + + if (!ff_v4l2_request_query_control(avctx, &compressed_hdr)) + ctx->has_compressed_hdr = true; + else + ctx->has_compressed_hdr = false; + + return 0; +} + +static int v4l2_request_vp9_init(AVCodecContext *avctx) +{ + struct v4l2_ctrl_vp9_frame frame; + + struct v4l2_ext_control control[] = { + { + .id = V4L2_CID_STATELESS_VP9_FRAME, + .ptr = &frame, + .size = sizeof(frame), + }, + }; + + fill_frame(&frame, avctx); + + return ff_v4l2_request_init(avctx, control, FF_ARRAY_ELEMS(control), + v4l2_request_vp9_post_frames_ctx); +} + +static int v4l2_request_vp9_frame_params(AVCodecContext *avctx, + AVBufferRef *hw_frames_ctx) +{ + const VP9Context *s = avctx->priv_data; + uint8_t bit_depth = s ? s->s.h.bpp : 0; + + return ff_v4l2_request_frame_params(avctx, hw_frames_ctx, + V4L2_PIX_FMT_VP9_FRAME, bit_depth); +} + +const FFHWAccel ff_vp9_v4l2request_hwaccel = { + .p.name = "vp9_v4l2request", + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .p.pix_fmt = AV_PIX_FMT_DRM_PRIME, + .start_frame = v4l2_request_vp9_start_frame, + .decode_slice = v4l2_request_vp9_decode_slice, + .end_frame = v4l2_request_vp9_end_frame, + .flush = ff_v4l2_request_flush, + .frame_priv_data_size = sizeof(V4L2RequestControlsVP9), + .init = v4l2_request_vp9_init, + .uninit = ff_v4l2_request_uninit, + .priv_data_size = sizeof(V4L2RequestContextVP9), + .frame_params = v4l2_request_vp9_frame_params, + .caps_internal = HWACCEL_CAP_ASYNC_SAFE, +}; diff --git a/thirdparty/ffmpeg/libavcodec/vp8.c b/thirdparty/ffmpeg/libavcodec/vp8.c index c54e79173..758689466 100644 --- a/thirdparty/ffmpeg/libavcodec/vp8.c +++ b/thirdparty/ffmpeg/libavcodec/vp8.c @@ -185,6 +185,9 @@ static enum AVPixelFormat get_pixel_format(VP8Context *s) #endif #if CONFIG_VP8_NVDEC_HWACCEL AV_PIX_FMT_CUDA, +#endif +#if CONFIG_VP8_V4L2REQUEST_HWACCEL + AV_PIX_FMT_DRM_PRIME, #endif AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE, @@ -2976,6 +2979,9 @@ const FFCodec ff_vp8_decoder = { #endif #if CONFIG_VP8_NVDEC_HWACCEL HWACCEL_NVDEC(vp8), +#endif +#if CONFIG_VP8_V4L2REQUEST_HWACCEL + HWACCEL_V4L2REQUEST(vp8), #endif NULL }, diff --git a/thirdparty/ffmpeg/libavcodec/vp8.c.orig b/thirdparty/ffmpeg/libavcodec/vp8.c.orig new file mode 100644 index 000000000..c54e79173 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/vp8.c.orig @@ -0,0 +1,2983 @@ +/* + * VP7/VP8 compatible video decoder + * + * Copyright (C) 2010 David Conrad + * Copyright (C) 2010 Ronald S. Bultje + * Copyright (C) 2010 Fiona Glaser + * Copyright (C) 2012 Daniel Kang + * Copyright (C) 2014 Peter Ross + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_components.h" + +#include "libavutil/attributes.h" +#include "libavutil/mem.h" +#include "libavutil/mem_internal.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "mathops.h" +#include "progressframe.h" +#include "libavutil/refstruct.h" +#include "thread.h" +#include "vp8.h" +#include "vp89_rac.h" +#include "vp8data.h" +#include "vpx_rac.h" + +#if ARCH_ARM +# include "arm/vp8.h" +#endif + +// fixme: add 1 bit to all the calls to this? +static int vp8_rac_get_sint(VPXRangeCoder *c, int bits) +{ + int v; + + if (!vp89_rac_get(c)) + return 0; + + v = vp89_rac_get_uint(c, bits); + + if (vp89_rac_get(c)) + v = -v; + + return v; +} + +static int vp8_rac_get_nn(VPXRangeCoder *c) +{ + int v = vp89_rac_get_uint(c, 7) << 1; + return v + !v; +} + +// DCTextra +static int vp8_rac_get_coeff(VPXRangeCoder *c, const uint8_t *prob) +{ + int v = 0; + + do { + v = (v<<1) + vpx_rac_get_prob(c, *prob++); + } while (*prob); + + return v; +} + +static void free_buffers(VP8Context *s) +{ + int i; + if (s->thread_data) + for (i = 0; i < MAX_THREADS; i++) { +#if HAVE_THREADS + pthread_cond_destroy(&s->thread_data[i].cond); + pthread_mutex_destroy(&s->thread_data[i].lock); +#endif + av_freep(&s->thread_data[i].filter_strength); + } + av_freep(&s->thread_data); + av_freep(&s->macroblocks_base); + av_freep(&s->intra4x4_pred_mode_top); + av_freep(&s->top_nnz); + av_freep(&s->top_border); + + s->macroblocks = NULL; +} + +static int vp8_alloc_frame(VP8Context *s, VP8Frame *f, int ref) +{ + int ret = ff_progress_frame_get_buffer(s->avctx, &f->tf, + ref ? AV_GET_BUFFER_FLAG_REF : 0); + if (ret < 0) + return ret; + f->seg_map = av_refstruct_allocz(s->mb_width * s->mb_height); + if (!f->seg_map) { + ret = AVERROR(ENOMEM); + goto fail; + } + ret = ff_hwaccel_frame_priv_alloc(s->avctx, &f->hwaccel_picture_private); + if (ret < 0) + goto fail; + + return 0; + +fail: + av_refstruct_unref(&f->seg_map); + ff_progress_frame_unref(&f->tf); + return ret; +} + +static void vp8_release_frame(VP8Frame *f) +{ + av_refstruct_unref(&f->seg_map); + av_refstruct_unref(&f->hwaccel_picture_private); + ff_progress_frame_unref(&f->tf); +} + +static av_cold void vp8_decode_flush_impl(AVCodecContext *avctx, int free_mem) +{ + VP8Context *s = avctx->priv_data; + int i; + + for (i = 0; i < FF_ARRAY_ELEMS(s->frames); i++) + vp8_release_frame(&s->frames[i]); + memset(s->framep, 0, sizeof(s->framep)); + + if (free_mem) + free_buffers(s); + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); +} + +static av_cold void vp8_decode_flush(AVCodecContext *avctx) +{ + vp8_decode_flush_impl(avctx, 0); +} + +static VP8Frame *vp8_find_free_buffer(VP8Context *s) +{ + VP8Frame *frame = NULL; + int i; + + // find a free buffer + for (i = 0; i < 5; i++) + if (&s->frames[i] != s->framep[VP8_FRAME_CURRENT] && + &s->frames[i] != s->framep[VP8_FRAME_PREVIOUS] && + &s->frames[i] != s->framep[VP8_FRAME_GOLDEN] && + &s->frames[i] != s->framep[VP8_FRAME_ALTREF]) { + frame = &s->frames[i]; + break; + } + if (i == 5) { + av_log(s->avctx, AV_LOG_FATAL, "Ran out of free frames!\n"); + abort(); + } + if (frame->tf.f) + vp8_release_frame(frame); + + return frame; +} + +static enum AVPixelFormat get_pixel_format(VP8Context *s) +{ + enum AVPixelFormat pix_fmts[] = { +#if CONFIG_VP8_VAAPI_HWACCEL + AV_PIX_FMT_VAAPI, +#endif +#if CONFIG_VP8_NVDEC_HWACCEL + AV_PIX_FMT_CUDA, +#endif + AV_PIX_FMT_YUV420P, + AV_PIX_FMT_NONE, + }; + + return ff_get_format(s->avctx, pix_fmts); +} + +static av_always_inline +int update_dimensions(VP8Context *s, int width, int height, int is_vp7) +{ + AVCodecContext *avctx = s->avctx; + int i, ret, dim_reset = 0; + + if (width != s->avctx->width || ((width+15)/16 != s->mb_width || (height+15)/16 != s->mb_height) && s->macroblocks_base || + height != s->avctx->height) { + vp8_decode_flush_impl(s->avctx, 1); + + ret = ff_set_dimensions(s->avctx, width, height); + if (ret < 0) + return ret; + + dim_reset = (s->macroblocks_base != NULL); + } + + if ((s->pix_fmt == AV_PIX_FMT_NONE || dim_reset) && + !s->actually_webp && !is_vp7) { + s->pix_fmt = get_pixel_format(s); + if (s->pix_fmt < 0) + return AVERROR(EINVAL); + avctx->pix_fmt = s->pix_fmt; + } + + s->mb_width = (s->avctx->coded_width + 15) / 16; + s->mb_height = (s->avctx->coded_height + 15) / 16; + + s->mb_layout = is_vp7 || avctx->active_thread_type == FF_THREAD_SLICE && + avctx->thread_count > 1; + if (!s->mb_layout) { // Frame threading and one thread + s->macroblocks_base = av_mallocz((s->mb_width + s->mb_height * 2 + 1) * + sizeof(*s->macroblocks)); + s->intra4x4_pred_mode_top = av_mallocz(s->mb_width * 4); + } else // Sliced threading + s->macroblocks_base = av_mallocz((s->mb_width + 2) * (s->mb_height + 2) * + sizeof(*s->macroblocks)); + s->top_nnz = av_mallocz(s->mb_width * sizeof(*s->top_nnz)); + s->top_border = av_mallocz((s->mb_width + 1) * sizeof(*s->top_border)); + s->thread_data = av_mallocz(MAX_THREADS * sizeof(VP8ThreadData)); + + if (!s->macroblocks_base || !s->top_nnz || !s->top_border || + !s->thread_data || (!s->intra4x4_pred_mode_top && !s->mb_layout)) { + free_buffers(s); + return AVERROR(ENOMEM); + } + + for (i = 0; i < MAX_THREADS; i++) { + s->thread_data[i].filter_strength = + av_mallocz(s->mb_width * sizeof(*s->thread_data[0].filter_strength)); + if (!s->thread_data[i].filter_strength) { + free_buffers(s); + return AVERROR(ENOMEM); + } +#if HAVE_THREADS + ret = pthread_mutex_init(&s->thread_data[i].lock, NULL); + if (ret) { + free_buffers(s); + return AVERROR(ret); + } + ret = pthread_cond_init(&s->thread_data[i].cond, NULL); + if (ret) { + free_buffers(s); + return AVERROR(ret); + } +#endif + } + + s->macroblocks = s->macroblocks_base + 1; + + return 0; +} + +static int vp7_update_dimensions(VP8Context *s, int width, int height) +{ + return update_dimensions(s, width, height, IS_VP7); +} + +static int vp8_update_dimensions(VP8Context *s, int width, int height) +{ + return update_dimensions(s, width, height, IS_VP8); +} + + +static void parse_segment_info(VP8Context *s) +{ + VPXRangeCoder *c = &s->c; + int i; + + s->segmentation.update_map = vp89_rac_get(c); + s->segmentation.update_feature_data = vp89_rac_get(c); + + if (s->segmentation.update_feature_data) { + s->segmentation.absolute_vals = vp89_rac_get(c); + + for (i = 0; i < 4; i++) + s->segmentation.base_quant[i] = vp8_rac_get_sint(c, 7); + + for (i = 0; i < 4; i++) + s->segmentation.filter_level[i] = vp8_rac_get_sint(c, 6); + } + if (s->segmentation.update_map) + for (i = 0; i < 3; i++) + s->prob->segmentid[i] = vp89_rac_get(c) ? vp89_rac_get_uint(c, 8) : 255; +} + +static void update_lf_deltas(VP8Context *s) +{ + VPXRangeCoder *c = &s->c; + int i; + + for (i = 0; i < 4; i++) { + if (vp89_rac_get(c)) { + s->lf_delta.ref[i] = vp89_rac_get_uint(c, 6); + + if (vp89_rac_get(c)) + s->lf_delta.ref[i] = -s->lf_delta.ref[i]; + } + } + + for (i = MODE_I4x4; i <= VP8_MVMODE_SPLIT; i++) { + if (vp89_rac_get(c)) { + s->lf_delta.mode[i] = vp89_rac_get_uint(c, 6); + + if (vp89_rac_get(c)) + s->lf_delta.mode[i] = -s->lf_delta.mode[i]; + } + } +} + +static int setup_partitions(VP8Context *s, const uint8_t *buf, int buf_size) +{ + const uint8_t *sizes = buf; + int i; + int ret; + + s->num_coeff_partitions = 1 << vp89_rac_get_uint(&s->c, 2); + + buf += 3 * (s->num_coeff_partitions - 1); + buf_size -= 3 * (s->num_coeff_partitions - 1); + if (buf_size < 0) + return -1; + + for (i = 0; i < s->num_coeff_partitions - 1; i++) { + int size = AV_RL24(sizes + 3 * i); + if (buf_size - size < 0) + return -1; + s->coeff_partition_size[i] = size; + + ret = ff_vpx_init_range_decoder(&s->coeff_partition[i], buf, size); + if (ret < 0) + return ret; + buf += size; + buf_size -= size; + } + + s->coeff_partition_size[i] = buf_size; + + return ff_vpx_init_range_decoder(&s->coeff_partition[i], buf, buf_size); +} + +static void vp7_get_quants(VP8Context *s) +{ + VPXRangeCoder *c = &s->c; + + int yac_qi = vp89_rac_get_uint(c, 7); + int ydc_qi = vp89_rac_get(c) ? vp89_rac_get_uint(c, 7) : yac_qi; + int y2dc_qi = vp89_rac_get(c) ? vp89_rac_get_uint(c, 7) : yac_qi; + int y2ac_qi = vp89_rac_get(c) ? vp89_rac_get_uint(c, 7) : yac_qi; + int uvdc_qi = vp89_rac_get(c) ? vp89_rac_get_uint(c, 7) : yac_qi; + int uvac_qi = vp89_rac_get(c) ? vp89_rac_get_uint(c, 7) : yac_qi; + + s->qmat[0].luma_qmul[0] = vp7_ydc_qlookup[ydc_qi]; + s->qmat[0].luma_qmul[1] = vp7_yac_qlookup[yac_qi]; + s->qmat[0].luma_dc_qmul[0] = vp7_y2dc_qlookup[y2dc_qi]; + s->qmat[0].luma_dc_qmul[1] = vp7_y2ac_qlookup[y2ac_qi]; + s->qmat[0].chroma_qmul[0] = FFMIN(vp7_ydc_qlookup[uvdc_qi], 132); + s->qmat[0].chroma_qmul[1] = vp7_yac_qlookup[uvac_qi]; +} + +static void vp8_get_quants(VP8Context *s) +{ + VPXRangeCoder *c = &s->c; + int i, base_qi; + + s->quant.yac_qi = vp89_rac_get_uint(c, 7); + s->quant.ydc_delta = vp8_rac_get_sint(c, 4); + s->quant.y2dc_delta = vp8_rac_get_sint(c, 4); + s->quant.y2ac_delta = vp8_rac_get_sint(c, 4); + s->quant.uvdc_delta = vp8_rac_get_sint(c, 4); + s->quant.uvac_delta = vp8_rac_get_sint(c, 4); + + for (i = 0; i < 4; i++) { + if (s->segmentation.enabled) { + base_qi = s->segmentation.base_quant[i]; + if (!s->segmentation.absolute_vals) + base_qi += s->quant.yac_qi; + } else + base_qi = s->quant.yac_qi; + + s->qmat[i].luma_qmul[0] = vp8_dc_qlookup[av_clip_uintp2(base_qi + s->quant.ydc_delta, 7)]; + s->qmat[i].luma_qmul[1] = vp8_ac_qlookup[av_clip_uintp2(base_qi, 7)]; + s->qmat[i].luma_dc_qmul[0] = vp8_dc_qlookup[av_clip_uintp2(base_qi + s->quant.y2dc_delta, 7)] * 2; + /* 101581>>16 is equivalent to 155/100 */ + s->qmat[i].luma_dc_qmul[1] = vp8_ac_qlookup[av_clip_uintp2(base_qi + s->quant.y2ac_delta, 7)] * 101581 >> 16; + s->qmat[i].chroma_qmul[0] = vp8_dc_qlookup[av_clip_uintp2(base_qi + s->quant.uvdc_delta, 7)]; + s->qmat[i].chroma_qmul[1] = vp8_ac_qlookup[av_clip_uintp2(base_qi + s->quant.uvac_delta, 7)]; + + s->qmat[i].luma_dc_qmul[1] = FFMAX(s->qmat[i].luma_dc_qmul[1], 8); + s->qmat[i].chroma_qmul[0] = FFMIN(s->qmat[i].chroma_qmul[0], 132); + } +} + +/** + * Determine which buffers golden and altref should be updated with after this frame. + * The spec isn't clear here, so I'm going by my understanding of what libvpx does + * + * Intra frames update all 3 references + * Inter frames update VP8_FRAME_PREVIOUS if the update_last flag is set + * If the update (golden|altref) flag is set, it's updated with the current frame + * if update_last is set, and VP8_FRAME_PREVIOUS otherwise. + * If the flag is not set, the number read means: + * 0: no update + * 1: VP8_FRAME_PREVIOUS + * 2: update golden with altref, or update altref with golden + */ +static VP8FrameType ref_to_update(VP8Context *s, int update, VP8FrameType ref) +{ + VPXRangeCoder *c = &s->c; + + if (update) + return VP8_FRAME_CURRENT; + + switch (vp89_rac_get_uint(c, 2)) { + case 1: + return VP8_FRAME_PREVIOUS; + case 2: + return (ref == VP8_FRAME_GOLDEN) ? VP8_FRAME_ALTREF : VP8_FRAME_GOLDEN; + } + return VP8_FRAME_NONE; +} + +static void vp78_reset_probability_tables(VP8Context *s) +{ + int i, j; + for (i = 0; i < 4; i++) + for (j = 0; j < 16; j++) + memcpy(s->prob->token[i][j], vp8_token_default_probs[i][vp8_coeff_band[j]], + sizeof(s->prob->token[i][j])); +} + +static void vp78_update_probability_tables(VP8Context *s) +{ + VPXRangeCoder *c = &s->c; + int i, j, k, l, m; + + for (i = 0; i < 4; i++) + for (j = 0; j < 8; j++) + for (k = 0; k < 3; k++) + for (l = 0; l < NUM_DCT_TOKENS-1; l++) + if (vpx_rac_get_prob_branchy(c, ff_vp8_token_update_probs[i][j][k][l])) { + int prob = vp89_rac_get_uint(c, 8); + for (m = 0; vp8_coeff_band_indexes[j][m] >= 0; m++) + s->prob->token[i][vp8_coeff_band_indexes[j][m]][k][l] = prob; + } +} + +#define VP7_MVC_SIZE 17 +#define VP8_MVC_SIZE 19 + +static void vp78_update_pred16x16_pred8x8_mvc_probabilities(VP8Context *s, + int mvc_size) +{ + VPXRangeCoder *c = &s->c; + int i, j; + + if (vp89_rac_get(c)) + for (i = 0; i < 4; i++) + s->prob->pred16x16[i] = vp89_rac_get_uint(c, 8); + if (vp89_rac_get(c)) + for (i = 0; i < 3; i++) + s->prob->pred8x8c[i] = vp89_rac_get_uint(c, 8); + + // 17.2 MV probability update + for (i = 0; i < 2; i++) + for (j = 0; j < mvc_size; j++) + if (vpx_rac_get_prob_branchy(c, vp8_mv_update_prob[i][j])) + s->prob->mvc[i][j] = vp8_rac_get_nn(c); +} + +static void update_refs(VP8Context *s) +{ + VPXRangeCoder *c = &s->c; + + int update_golden = vp89_rac_get(c); + int update_altref = vp89_rac_get(c); + + s->update_golden = ref_to_update(s, update_golden, VP8_FRAME_GOLDEN); + s->update_altref = ref_to_update(s, update_altref, VP8_FRAME_ALTREF); +} + +static void copy_chroma(AVFrame *dst, const AVFrame *src, int width, int height) +{ + int i, j; + + for (j = 1; j < 3; j++) { + for (i = 0; i < height / 2; i++) + memcpy(dst->data[j] + i * dst->linesize[j], + src->data[j] + i * src->linesize[j], width / 2); + } +} + +static void fade(uint8_t *dst, ptrdiff_t dst_linesize, + const uint8_t *src, ptrdiff_t src_linesize, + int width, int height, + int alpha, int beta) +{ + int i, j; + for (j = 0; j < height; j++) { + const uint8_t *src2 = src + j * src_linesize; + uint8_t *dst2 = dst + j * dst_linesize; + for (i = 0; i < width; i++) { + uint8_t y = src2[i]; + dst2[i] = av_clip_uint8(y + ((y * beta) >> 8) + alpha); + } + } +} + +static int vp7_fade_frame(VP8Context *s, int alpha, int beta) +{ + int ret; + + if (!s->keyframe && (alpha || beta)) { + int width = s->mb_width * 16; + int height = s->mb_height * 16; + const AVFrame *src; + AVFrame *dst; + + if (!s->framep[VP8_FRAME_PREVIOUS] || + !s->framep[VP8_FRAME_GOLDEN]) { + av_log(s->avctx, AV_LOG_WARNING, "Discarding interframe without a prior keyframe!\n"); + return AVERROR_INVALIDDATA; + } + + src = + dst = s->framep[VP8_FRAME_PREVIOUS]->tf.f; + + /* preserve the golden frame, write a new previous frame */ + if (s->framep[VP8_FRAME_GOLDEN] == s->framep[VP8_FRAME_PREVIOUS]) { + VP8Frame *prev_frame = vp8_find_free_buffer(s); + + ret = vp8_alloc_frame(s, prev_frame, 1); + if (ret < 0) + return ret; + s->framep[VP8_FRAME_PREVIOUS] = prev_frame; + + dst = s->framep[VP8_FRAME_PREVIOUS]->tf.f; + + copy_chroma(dst, src, width, height); + } + + fade(dst->data[0], dst->linesize[0], + src->data[0], src->linesize[0], + width, height, alpha, beta); + } + + return 0; +} + +static int vp7_decode_frame_header(VP8Context *s, const uint8_t *buf, int buf_size) +{ + VPXRangeCoder *c = &s->c; + int part1_size, hscale, vscale, i, j, ret; + int width = s->avctx->width; + int height = s->avctx->height; + int alpha = 0; + int beta = 0; + int fade_present = 1; + + if (buf_size < 4) { + return AVERROR_INVALIDDATA; + } + + s->profile = (buf[0] >> 1) & 7; + if (s->profile > 1) { + avpriv_request_sample(s->avctx, "Unknown profile %d", s->profile); + return AVERROR_INVALIDDATA; + } + + s->keyframe = !(buf[0] & 1); + s->invisible = 0; + part1_size = AV_RL24(buf) >> 4; + + if (buf_size < 4 - s->profile + part1_size) { + av_log(s->avctx, AV_LOG_ERROR, "Buffer size %d is too small, needed : %d\n", buf_size, 4 - s->profile + part1_size); + return AVERROR_INVALIDDATA; + } + + buf += 4 - s->profile; + buf_size -= 4 - s->profile; + + memcpy(s->put_pixels_tab, s->vp8dsp.put_vp8_epel_pixels_tab, sizeof(s->put_pixels_tab)); + + ret = ff_vpx_init_range_decoder(c, buf, part1_size); + if (ret < 0) + return ret; + buf += part1_size; + buf_size -= part1_size; + + /* A. Dimension information (keyframes only) */ + if (s->keyframe) { + width = vp89_rac_get_uint(c, 12); + height = vp89_rac_get_uint(c, 12); + hscale = vp89_rac_get_uint(c, 2); + vscale = vp89_rac_get_uint(c, 2); + if (hscale || vscale) + avpriv_request_sample(s->avctx, "Upscaling"); + + s->update_golden = s->update_altref = VP8_FRAME_CURRENT; + vp78_reset_probability_tables(s); + memcpy(s->prob->pred16x16, vp8_pred16x16_prob_inter, + sizeof(s->prob->pred16x16)); + memcpy(s->prob->pred8x8c, vp8_pred8x8c_prob_inter, + sizeof(s->prob->pred8x8c)); + for (i = 0; i < 2; i++) + memcpy(s->prob->mvc[i], vp7_mv_default_prob[i], + sizeof(vp7_mv_default_prob[i])); + memset(&s->segmentation, 0, sizeof(s->segmentation)); + memset(&s->lf_delta, 0, sizeof(s->lf_delta)); + memcpy(s->prob[0].scan, ff_zigzag_scan, sizeof(s->prob[0].scan)); + } + + if (s->keyframe || s->profile > 0) + memset(s->inter_dc_pred, 0 , sizeof(s->inter_dc_pred)); + + /* B. Decoding information for all four macroblock-level features */ + for (i = 0; i < 4; i++) { + s->feature_enabled[i] = vp89_rac_get(c); + if (s->feature_enabled[i]) { + s->feature_present_prob[i] = vp89_rac_get_uint(c, 8); + + for (j = 0; j < 3; j++) + s->feature_index_prob[i][j] = + vp89_rac_get(c) ? vp89_rac_get_uint(c, 8) : 255; + + if (vp7_feature_value_size[s->profile][i]) + for (j = 0; j < 4; j++) + s->feature_value[i][j] = + vp89_rac_get(c) ? vp89_rac_get_uint(c, vp7_feature_value_size[s->profile][i]) : 0; + } + } + + s->segmentation.enabled = 0; + s->segmentation.update_map = 0; + s->lf_delta.enabled = 0; + + s->num_coeff_partitions = 1; + ret = ff_vpx_init_range_decoder(&s->coeff_partition[0], buf, buf_size); + if (ret < 0) + return ret; + + if (!s->macroblocks_base || /* first frame */ + width != s->avctx->width || height != s->avctx->height || + (width + 15) / 16 != s->mb_width || (height + 15) / 16 != s->mb_height) { + if ((ret = vp7_update_dimensions(s, width, height)) < 0) + return ret; + } + + /* C. Dequantization indices */ + vp7_get_quants(s); + + /* D. Golden frame update flag (a Flag) for interframes only */ + if (!s->keyframe) { + s->update_golden = vp89_rac_get(c) ? VP8_FRAME_CURRENT : VP8_FRAME_NONE; + s->sign_bias[VP8_FRAME_GOLDEN] = 0; + } + + s->update_last = 1; + s->update_probabilities = 1; + + if (s->profile > 0) { + s->update_probabilities = vp89_rac_get(c); + if (!s->update_probabilities) + s->prob[1] = s->prob[0]; + + if (!s->keyframe) + fade_present = vp89_rac_get(c); + } + + if (vpx_rac_is_end(c)) + return AVERROR_INVALIDDATA; + /* E. Fading information for previous frame */ + if (fade_present && vp89_rac_get(c)) { + alpha = (int8_t) vp89_rac_get_uint(c, 8); + beta = (int8_t) vp89_rac_get_uint(c, 8); + } + + /* F. Loop filter type */ + if (!s->profile) + s->filter.simple = vp89_rac_get(c); + + /* G. DCT coefficient ordering specification */ + if (vp89_rac_get(c)) + for (i = 1; i < 16; i++) + s->prob[0].scan[i] = ff_zigzag_scan[vp89_rac_get_uint(c, 4)]; + + /* H. Loop filter levels */ + if (s->profile > 0) + s->filter.simple = vp89_rac_get(c); + s->filter.level = vp89_rac_get_uint(c, 6); + s->filter.sharpness = vp89_rac_get_uint(c, 3); + + /* I. DCT coefficient probability update; 13.3 Token Probability Updates */ + vp78_update_probability_tables(s); + + s->mbskip_enabled = 0; + + /* J. The remaining frame header data occurs ONLY FOR INTERFRAMES */ + if (!s->keyframe) { + s->prob->intra = vp89_rac_get_uint(c, 8); + s->prob->last = vp89_rac_get_uint(c, 8); + vp78_update_pred16x16_pred8x8_mvc_probabilities(s, VP7_MVC_SIZE); + } + + if (vpx_rac_is_end(c)) + return AVERROR_INVALIDDATA; + + if ((ret = vp7_fade_frame(s, alpha, beta)) < 0) + return ret; + + return 0; +} + +static int vp8_decode_frame_header(VP8Context *s, const uint8_t *buf, int buf_size) +{ + VPXRangeCoder *c = &s->c; + int header_size, hscale, vscale, ret; + int width = s->avctx->width; + int height = s->avctx->height; + + if (buf_size < 3) { + av_log(s->avctx, AV_LOG_ERROR, "Insufficient data (%d) for header\n", buf_size); + return AVERROR_INVALIDDATA; + } + + s->keyframe = !(buf[0] & 1); + s->profile = (buf[0]>>1) & 7; + s->invisible = !(buf[0] & 0x10); + header_size = AV_RL24(buf) >> 5; + buf += 3; + buf_size -= 3; + + s->header_partition_size = header_size; + + if (s->profile > 3) + av_log(s->avctx, AV_LOG_WARNING, "Unknown profile %d\n", s->profile); + + if (!s->profile) + memcpy(s->put_pixels_tab, s->vp8dsp.put_vp8_epel_pixels_tab, + sizeof(s->put_pixels_tab)); + else // profile 1-3 use bilinear, 4+ aren't defined so whatever + memcpy(s->put_pixels_tab, s->vp8dsp.put_vp8_bilinear_pixels_tab, + sizeof(s->put_pixels_tab)); + + if (header_size > buf_size - 7 * s->keyframe) { + av_log(s->avctx, AV_LOG_ERROR, "Header size larger than data provided\n"); + return AVERROR_INVALIDDATA; + } + + if (s->keyframe) { + if (AV_RL24(buf) != 0x2a019d) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid start code 0x%x\n", AV_RL24(buf)); + return AVERROR_INVALIDDATA; + } + width = AV_RL16(buf + 3) & 0x3fff; + height = AV_RL16(buf + 5) & 0x3fff; + hscale = buf[4] >> 6; + vscale = buf[6] >> 6; + buf += 7; + buf_size -= 7; + + if (hscale || vscale) + avpriv_request_sample(s->avctx, "Upscaling"); + + s->update_golden = s->update_altref = VP8_FRAME_CURRENT; + vp78_reset_probability_tables(s); + memcpy(s->prob->pred16x16, vp8_pred16x16_prob_inter, + sizeof(s->prob->pred16x16)); + memcpy(s->prob->pred8x8c, vp8_pred8x8c_prob_inter, + sizeof(s->prob->pred8x8c)); + memcpy(s->prob->mvc, vp8_mv_default_prob, + sizeof(s->prob->mvc)); + memset(&s->segmentation, 0, sizeof(s->segmentation)); + memset(&s->lf_delta, 0, sizeof(s->lf_delta)); + } + + ret = ff_vpx_init_range_decoder(c, buf, header_size); + if (ret < 0) + return ret; + buf += header_size; + buf_size -= header_size; + + if (s->keyframe) { + s->colorspace = vp89_rac_get(c); + if (s->colorspace) + av_log(s->avctx, AV_LOG_WARNING, "Unspecified colorspace\n"); + s->fullrange = vp89_rac_get(c); + } + + if ((s->segmentation.enabled = vp89_rac_get(c))) + parse_segment_info(s); + else + s->segmentation.update_map = 0; // FIXME: move this to some init function? + + s->filter.simple = vp89_rac_get(c); + s->filter.level = vp89_rac_get_uint(c, 6); + s->filter.sharpness = vp89_rac_get_uint(c, 3); + + if ((s->lf_delta.enabled = vp89_rac_get(c))) { + s->lf_delta.update = vp89_rac_get(c); + if (s->lf_delta.update) + update_lf_deltas(s); + } + + if (setup_partitions(s, buf, buf_size)) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid partitions\n"); + return AVERROR_INVALIDDATA; + } + + if (!s->macroblocks_base || /* first frame */ + width != s->avctx->width || height != s->avctx->height || + (width+15)/16 != s->mb_width || (height+15)/16 != s->mb_height) + if ((ret = vp8_update_dimensions(s, width, height)) < 0) + return ret; + + vp8_get_quants(s); + + if (!s->keyframe) { + update_refs(s); + s->sign_bias[VP8_FRAME_GOLDEN] = vp89_rac_get(c); + s->sign_bias[VP8_FRAME_ALTREF] = vp89_rac_get(c); + } + + // if we aren't saving this frame's probabilities for future frames, + // make a copy of the current probabilities + if (!(s->update_probabilities = vp89_rac_get(c))) + s->prob[1] = s->prob[0]; + + s->update_last = s->keyframe || vp89_rac_get(c); + + vp78_update_probability_tables(s); + + if ((s->mbskip_enabled = vp89_rac_get(c))) + s->prob->mbskip = vp89_rac_get_uint(c, 8); + + if (!s->keyframe) { + s->prob->intra = vp89_rac_get_uint(c, 8); + s->prob->last = vp89_rac_get_uint(c, 8); + s->prob->golden = vp89_rac_get_uint(c, 8); + vp78_update_pred16x16_pred8x8_mvc_probabilities(s, VP8_MVC_SIZE); + } + + // Record the entropy coder state here so that hwaccels can use it. + s->c.code_word = vpx_rac_renorm(&s->c); + s->coder_state_at_header_end.input = s->c.buffer - (-s->c.bits / 8); + s->coder_state_at_header_end.range = s->c.high; + s->coder_state_at_header_end.value = s->c.code_word >> 16; + s->coder_state_at_header_end.bit_count = -s->c.bits % 8; + + return 0; +} + +static av_always_inline +void clamp_mv(const VP8mvbounds *s, VP8mv *dst, const VP8mv *src) +{ + dst->x = av_clip(src->x, av_clip(s->mv_min.x, INT16_MIN, INT16_MAX), + av_clip(s->mv_max.x, INT16_MIN, INT16_MAX)); + dst->y = av_clip(src->y, av_clip(s->mv_min.y, INT16_MIN, INT16_MAX), + av_clip(s->mv_max.y, INT16_MIN, INT16_MAX)); +} + +/** + * Motion vector coding, 17.1. + */ +static av_always_inline int read_mv_component(VPXRangeCoder *c, const uint8_t *p, int vp7) +{ + int bit, x = 0; + + if (vpx_rac_get_prob_branchy(c, p[0])) { + int i; + + for (i = 0; i < 3; i++) + x += vpx_rac_get_prob(c, p[9 + i]) << i; + for (i = (vp7 ? 7 : 9); i > 3; i--) + x += vpx_rac_get_prob(c, p[9 + i]) << i; + if (!(x & (vp7 ? 0xF0 : 0xFFF0)) || vpx_rac_get_prob(c, p[12])) + x += 8; + } else { + // small_mvtree + const uint8_t *ps = p + 2; + bit = vpx_rac_get_prob(c, *ps); + ps += 1 + 3 * bit; + x += 4 * bit; + bit = vpx_rac_get_prob(c, *ps); + ps += 1 + bit; + x += 2 * bit; + x += vpx_rac_get_prob(c, *ps); + } + + return (x && vpx_rac_get_prob(c, p[1])) ? -x : x; +} + +static int vp7_read_mv_component(VPXRangeCoder *c, const uint8_t *p) +{ + return read_mv_component(c, p, 1); +} + +static int vp8_read_mv_component(VPXRangeCoder *c, const uint8_t *p) +{ + return read_mv_component(c, p, 0); +} + +static av_always_inline +const uint8_t *get_submv_prob(uint32_t left, uint32_t top, int is_vp7) +{ + if (is_vp7) + return vp7_submv_prob; + + if (left == top) + return vp8_submv_prob[4 - !!left]; + if (!top) + return vp8_submv_prob[2]; + return vp8_submv_prob[1 - !!left]; +} + +/** + * Split motion vector prediction, 16.4. + * @returns the number of motion vectors parsed (2, 4 or 16) + */ +static av_always_inline +int decode_splitmvs(const VP8Context *s, VPXRangeCoder *c, VP8Macroblock *mb, + int layout, int is_vp7) +{ + int part_idx; + int n, num; + const VP8Macroblock *top_mb; + const VP8Macroblock *left_mb = &mb[-1]; + const uint8_t *mbsplits_left = vp8_mbsplits[left_mb->partitioning]; + const uint8_t *mbsplits_top, *mbsplits_cur, *firstidx; + const VP8mv *top_mv; + const VP8mv *left_mv = left_mb->bmv; + const VP8mv *cur_mv = mb->bmv; + + if (!layout) // layout is inlined, s->mb_layout is not + top_mb = &mb[2]; + else + top_mb = &mb[-s->mb_width - 1]; + mbsplits_top = vp8_mbsplits[top_mb->partitioning]; + top_mv = top_mb->bmv; + + if (vpx_rac_get_prob_branchy(c, vp8_mbsplit_prob[0])) { + if (vpx_rac_get_prob_branchy(c, vp8_mbsplit_prob[1])) + part_idx = VP8_SPLITMVMODE_16x8 + vpx_rac_get_prob(c, vp8_mbsplit_prob[2]); + else + part_idx = VP8_SPLITMVMODE_8x8; + } else { + part_idx = VP8_SPLITMVMODE_4x4; + } + + num = vp8_mbsplit_count[part_idx]; + mbsplits_cur = vp8_mbsplits[part_idx], + firstidx = vp8_mbfirstidx[part_idx]; + mb->partitioning = part_idx; + + for (n = 0; n < num; n++) { + int k = firstidx[n]; + uint32_t left, above; + const uint8_t *submv_prob; + + if (!(k & 3)) + left = AV_RN32A(&left_mv[mbsplits_left[k + 3]]); + else + left = AV_RN32A(&cur_mv[mbsplits_cur[k - 1]]); + if (k <= 3) + above = AV_RN32A(&top_mv[mbsplits_top[k + 12]]); + else + above = AV_RN32A(&cur_mv[mbsplits_cur[k - 4]]); + + submv_prob = get_submv_prob(left, above, is_vp7); + + if (vpx_rac_get_prob_branchy(c, submv_prob[0])) { + if (vpx_rac_get_prob_branchy(c, submv_prob[1])) { + if (vpx_rac_get_prob_branchy(c, submv_prob[2])) { + mb->bmv[n].y = mb->mv.y + + read_mv_component(c, s->prob->mvc[0], is_vp7); + mb->bmv[n].x = mb->mv.x + + read_mv_component(c, s->prob->mvc[1], is_vp7); + } else { + AV_ZERO32(&mb->bmv[n]); + } + } else { + AV_WN32A(&mb->bmv[n], above); + } + } else { + AV_WN32A(&mb->bmv[n], left); + } + } + + return num; +} + +/** + * The vp7 reference decoder uses a padding macroblock column (added to right + * edge of the frame) to guard against illegal macroblock offsets. The + * algorithm has bugs that permit offsets to straddle the padding column. + * This function replicates those bugs. + * + * @param[out] edge_x macroblock x address + * @param[out] edge_y macroblock y address + * + * @return macroblock offset legal (boolean) + */ +static int vp7_calculate_mb_offset(int mb_x, int mb_y, int mb_width, + int xoffset, int yoffset, int boundary, + int *edge_x, int *edge_y) +{ + int vwidth = mb_width + 1; + int new = (mb_y + yoffset) * vwidth + mb_x + xoffset; + if (new < boundary || new % vwidth == vwidth - 1) + return 0; + *edge_y = new / vwidth; + *edge_x = new % vwidth; + return 1; +} + +static const VP8mv *get_bmv_ptr(const VP8Macroblock *mb, int subblock) +{ + return &mb->bmv[mb->mode == VP8_MVMODE_SPLIT ? vp8_mbsplits[mb->partitioning][subblock] : 0]; +} + +static av_always_inline +void vp7_decode_mvs(VP8Context *s, VP8Macroblock *mb, + int mb_x, int mb_y, int layout) +{ + enum { CNT_ZERO, CNT_NEAREST, CNT_NEAR }; + enum { VP8_EDGE_TOP, VP8_EDGE_LEFT, VP8_EDGE_TOPLEFT }; + int idx = CNT_ZERO; + VP8mv near_mv[3]; + uint8_t cnt[3] = { 0 }; + VPXRangeCoder *c = &s->c; + int i; + + AV_ZERO32(&near_mv[0]); + AV_ZERO32(&near_mv[1]); + AV_ZERO32(&near_mv[2]); + + for (i = 0; i < VP7_MV_PRED_COUNT; i++) { + const VP7MVPred * pred = &vp7_mv_pred[i]; + int edge_x, edge_y; + + if (vp7_calculate_mb_offset(mb_x, mb_y, s->mb_width, pred->xoffset, + pred->yoffset, !s->profile, &edge_x, &edge_y)) { + const VP8Macroblock *edge = (s->mb_layout == 1) + ? s->macroblocks_base + 1 + edge_x + + (s->mb_width + 1) * (edge_y + 1) + : s->macroblocks + edge_x + + (s->mb_height - edge_y - 1) * 2; + uint32_t mv = AV_RN32A(get_bmv_ptr(edge, vp7_mv_pred[i].subblock)); + if (mv) { + if (AV_RN32A(&near_mv[CNT_NEAREST])) { + if (mv == AV_RN32A(&near_mv[CNT_NEAREST])) { + idx = CNT_NEAREST; + } else if (AV_RN32A(&near_mv[CNT_NEAR])) { + if (mv != AV_RN32A(&near_mv[CNT_NEAR])) + continue; + idx = CNT_NEAR; + } else { + AV_WN32A(&near_mv[CNT_NEAR], mv); + idx = CNT_NEAR; + } + } else { + AV_WN32A(&near_mv[CNT_NEAREST], mv); + idx = CNT_NEAREST; + } + } else { + idx = CNT_ZERO; + } + } else { + idx = CNT_ZERO; + } + cnt[idx] += vp7_mv_pred[i].score; + } + + mb->partitioning = VP8_SPLITMVMODE_NONE; + + if (vpx_rac_get_prob_branchy(c, vp7_mode_contexts[cnt[CNT_ZERO]][0])) { + mb->mode = VP8_MVMODE_MV; + + if (vpx_rac_get_prob_branchy(c, vp7_mode_contexts[cnt[CNT_NEAREST]][1])) { + + if (vpx_rac_get_prob_branchy(c, vp7_mode_contexts[cnt[CNT_NEAR]][2])) { + + if (cnt[CNT_NEAREST] > cnt[CNT_NEAR]) + AV_WN32A(&mb->mv, cnt[CNT_ZERO] > cnt[CNT_NEAREST] ? 0 : AV_RN32A(&near_mv[CNT_NEAREST])); + else + AV_WN32A(&mb->mv, cnt[CNT_ZERO] > cnt[CNT_NEAR] ? 0 : AV_RN32A(&near_mv[CNT_NEAR])); + + if (vpx_rac_get_prob_branchy(c, vp7_mode_contexts[cnt[CNT_NEAR]][3])) { + mb->mode = VP8_MVMODE_SPLIT; + mb->mv = mb->bmv[decode_splitmvs(s, c, mb, layout, IS_VP7) - 1]; + } else { + mb->mv.y += vp7_read_mv_component(c, s->prob->mvc[0]); + mb->mv.x += vp7_read_mv_component(c, s->prob->mvc[1]); + mb->bmv[0] = mb->mv; + } + } else { + mb->mv = near_mv[CNT_NEAR]; + mb->bmv[0] = mb->mv; + } + } else { + mb->mv = near_mv[CNT_NEAREST]; + mb->bmv[0] = mb->mv; + } + } else { + mb->mode = VP8_MVMODE_ZERO; + AV_ZERO32(&mb->mv); + mb->bmv[0] = mb->mv; + } +} + +static av_always_inline +void vp8_decode_mvs(VP8Context *s, const VP8mvbounds *mv_bounds, VP8Macroblock *mb, + int mb_x, int mb_y, int layout) +{ + VP8Macroblock *mb_edge[3] = { 0 /* top */, + mb - 1 /* left */, + 0 /* top-left */ }; + enum { CNT_ZERO, CNT_NEAREST, CNT_NEAR, CNT_SPLITMV }; + enum { VP8_EDGE_TOP, VP8_EDGE_LEFT, VP8_EDGE_TOPLEFT }; + int idx = CNT_ZERO; + int cur_sign_bias = s->sign_bias[mb->ref_frame]; + const int8_t *sign_bias = s->sign_bias; + VP8mv near_mv[4]; + uint8_t cnt[4] = { 0 }; + VPXRangeCoder *c = &s->c; + + if (!layout) { // layout is inlined (s->mb_layout is not) + mb_edge[0] = mb + 2; + mb_edge[2] = mb + 1; + } else { + mb_edge[0] = mb - s->mb_width - 1; + mb_edge[2] = mb - s->mb_width - 2; + } + + AV_ZERO32(&near_mv[0]); + AV_ZERO32(&near_mv[1]); + AV_ZERO32(&near_mv[2]); + + /* Process MB on top, left and top-left */ +#define MV_EDGE_CHECK(n) \ + { \ + const VP8Macroblock *edge = mb_edge[n]; \ + int edge_ref = edge->ref_frame; \ + if (edge_ref != VP8_FRAME_CURRENT) { \ + uint32_t mv = AV_RN32A(&edge->mv); \ + if (mv) { \ + if (cur_sign_bias != sign_bias[edge_ref]) { \ + /* SWAR negate of the values in mv. */ \ + mv = ~mv; \ + mv = ((mv & 0x7fff7fff) + \ + 0x00010001) ^ (mv & 0x80008000); \ + } \ + if (!n || mv != AV_RN32A(&near_mv[idx])) \ + AV_WN32A(&near_mv[++idx], mv); \ + cnt[idx] += 1 + (n != 2); \ + } else \ + cnt[CNT_ZERO] += 1 + (n != 2); \ + } \ + } + + MV_EDGE_CHECK(0) + MV_EDGE_CHECK(1) + MV_EDGE_CHECK(2) + + mb->partitioning = VP8_SPLITMVMODE_NONE; + if (vpx_rac_get_prob_branchy(c, vp8_mode_contexts[cnt[CNT_ZERO]][0])) { + mb->mode = VP8_MVMODE_MV; + + /* If we have three distinct MVs, merge first and last if they're the same */ + if (cnt[CNT_SPLITMV] && + AV_RN32A(&near_mv[1 + VP8_EDGE_TOP]) == AV_RN32A(&near_mv[1 + VP8_EDGE_TOPLEFT])) + cnt[CNT_NEAREST] += 1; + + /* Swap near and nearest if necessary */ + if (cnt[CNT_NEAR] > cnt[CNT_NEAREST]) { + FFSWAP(uint8_t, cnt[CNT_NEAREST], cnt[CNT_NEAR]); + FFSWAP(VP8mv, near_mv[CNT_NEAREST], near_mv[CNT_NEAR]); + } + + if (vpx_rac_get_prob_branchy(c, vp8_mode_contexts[cnt[CNT_NEAREST]][1])) { + if (vpx_rac_get_prob_branchy(c, vp8_mode_contexts[cnt[CNT_NEAR]][2])) { + /* Choose the best mv out of 0,0 and the nearest mv */ + clamp_mv(mv_bounds, &mb->mv, &near_mv[CNT_ZERO + (cnt[CNT_NEAREST] >= cnt[CNT_ZERO])]); + cnt[CNT_SPLITMV] = ((mb_edge[VP8_EDGE_LEFT]->mode == VP8_MVMODE_SPLIT) + + (mb_edge[VP8_EDGE_TOP]->mode == VP8_MVMODE_SPLIT)) * 2 + + (mb_edge[VP8_EDGE_TOPLEFT]->mode == VP8_MVMODE_SPLIT); + + if (vpx_rac_get_prob_branchy(c, vp8_mode_contexts[cnt[CNT_SPLITMV]][3])) { + mb->mode = VP8_MVMODE_SPLIT; + mb->mv = mb->bmv[decode_splitmvs(s, c, mb, layout, IS_VP8) - 1]; + } else { + mb->mv.y += vp8_read_mv_component(c, s->prob->mvc[0]); + mb->mv.x += vp8_read_mv_component(c, s->prob->mvc[1]); + mb->bmv[0] = mb->mv; + } + } else { + clamp_mv(mv_bounds, &mb->mv, &near_mv[CNT_NEAR]); + mb->bmv[0] = mb->mv; + } + } else { + clamp_mv(mv_bounds, &mb->mv, &near_mv[CNT_NEAREST]); + mb->bmv[0] = mb->mv; + } + } else { + mb->mode = VP8_MVMODE_ZERO; + AV_ZERO32(&mb->mv); + mb->bmv[0] = mb->mv; + } +} + +static av_always_inline +void decode_intra4x4_modes(VP8Context *s, VPXRangeCoder *c, VP8Macroblock *mb, + int mb_x, int keyframe, int layout) +{ + uint8_t *intra4x4 = mb->intra4x4_pred_mode_mb; + + if (layout) { + VP8Macroblock *mb_top = mb - s->mb_width - 1; + memcpy(mb->intra4x4_pred_mode_top, mb_top->intra4x4_pred_mode_top, 4); + } + if (keyframe) { + int x, y; + uint8_t *top; + uint8_t *const left = s->intra4x4_pred_mode_left; + if (layout) + top = mb->intra4x4_pred_mode_top; + else + top = s->intra4x4_pred_mode_top + 4 * mb_x; + for (y = 0; y < 4; y++) { + for (x = 0; x < 4; x++) { + const uint8_t *ctx; + ctx = vp8_pred4x4_prob_intra[top[x]][left[y]]; + *intra4x4 = vp89_rac_get_tree(c, vp8_pred4x4_tree, ctx); + left[y] = top[x] = *intra4x4; + intra4x4++; + } + } + } else { + int i; + for (i = 0; i < 16; i++) + intra4x4[i] = vp89_rac_get_tree(c, vp8_pred4x4_tree, + vp8_pred4x4_prob_inter); + } +} + +static av_always_inline +void decode_mb_mode(VP8Context *s, const VP8mvbounds *mv_bounds, + VP8Macroblock *mb, int mb_x, int mb_y, + uint8_t *segment, const uint8_t *ref, int layout, int is_vp7) +{ + VPXRangeCoder *c = &s->c; + static const char * const vp7_feature_name[] = { "q-index", + "lf-delta", + "partial-golden-update", + "blit-pitch" }; + if (is_vp7) { + int i; + *segment = 0; + for (i = 0; i < 4; i++) { + if (s->feature_enabled[i]) { + if (vpx_rac_get_prob_branchy(c, s->feature_present_prob[i])) { + int index = vp89_rac_get_tree(c, vp7_feature_index_tree, + s->feature_index_prob[i]); + av_log(s->avctx, AV_LOG_WARNING, + "Feature %s present in macroblock (value 0x%x)\n", + vp7_feature_name[i], s->feature_value[i][index]); + } + } + } + } else if (s->segmentation.update_map) { + int bit = vpx_rac_get_prob(c, s->prob->segmentid[0]); + *segment = vpx_rac_get_prob(c, s->prob->segmentid[1+bit]) + 2*bit; + } else if (s->segmentation.enabled) + *segment = ref ? *ref : *segment; + mb->segment = *segment; + + mb->skip = s->mbskip_enabled ? vpx_rac_get_prob(c, s->prob->mbskip) : 0; + + if (s->keyframe) { + mb->mode = vp89_rac_get_tree(c, vp8_pred16x16_tree_intra, + vp8_pred16x16_prob_intra); + + if (mb->mode == MODE_I4x4) { + decode_intra4x4_modes(s, c, mb, mb_x, 1, layout); + } else { + const uint32_t modes = (is_vp7 ? vp7_pred4x4_mode + : vp8_pred4x4_mode)[mb->mode] * 0x01010101u; + if (s->mb_layout) + AV_WN32A(mb->intra4x4_pred_mode_top, modes); + else + AV_WN32A(s->intra4x4_pred_mode_top + 4 * mb_x, modes); + AV_WN32A(s->intra4x4_pred_mode_left, modes); + } + + mb->chroma_pred_mode = vp89_rac_get_tree(c, vp8_pred8x8c_tree, + vp8_pred8x8c_prob_intra); + mb->ref_frame = VP8_FRAME_CURRENT; + } else if (vpx_rac_get_prob_branchy(c, s->prob->intra)) { + // inter MB, 16.2 + if (vpx_rac_get_prob_branchy(c, s->prob->last)) + mb->ref_frame = + (!is_vp7 && vpx_rac_get_prob(c, s->prob->golden)) ? VP8_FRAME_ALTREF + : VP8_FRAME_GOLDEN; + else + mb->ref_frame = VP8_FRAME_PREVIOUS; + s->ref_count[mb->ref_frame - 1]++; + + // motion vectors, 16.3 + if (is_vp7) + vp7_decode_mvs(s, mb, mb_x, mb_y, layout); + else + vp8_decode_mvs(s, mv_bounds, mb, mb_x, mb_y, layout); + } else { + // intra MB, 16.1 + mb->mode = vp89_rac_get_tree(c, vp8_pred16x16_tree_inter, + s->prob->pred16x16); + + if (mb->mode == MODE_I4x4) + decode_intra4x4_modes(s, c, mb, mb_x, 0, layout); + + mb->chroma_pred_mode = vp89_rac_get_tree(c, vp8_pred8x8c_tree, + s->prob->pred8x8c); + mb->ref_frame = VP8_FRAME_CURRENT; + mb->partitioning = VP8_SPLITMVMODE_NONE; + AV_ZERO32(&mb->bmv[0]); + } +} + +/** + * @param r arithmetic bitstream reader context + * @param block destination for block coefficients + * @param probs probabilities to use when reading trees from the bitstream + * @param i initial coeff index, 0 unless a separate DC block is coded + * @param qmul array holding the dc/ac dequant factor at position 0/1 + * + * @return 0 if no coeffs were decoded + * otherwise, the index of the last coeff decoded plus one + */ +static av_always_inline +int decode_block_coeffs_internal(VPXRangeCoder *r, int16_t block[16], + uint8_t probs[16][3][NUM_DCT_TOKENS - 1], + int i, const uint8_t *token_prob, const int16_t qmul[2], + const uint8_t scan[16], int vp7) +{ + VPXRangeCoder c = *r; + goto skip_eob; + do { + int coeff; +restart: + if (!vpx_rac_get_prob_branchy(&c, token_prob[0])) // DCT_EOB + break; + +skip_eob: + if (!vpx_rac_get_prob_branchy(&c, token_prob[1])) { // DCT_0 + if (++i == 16) + break; // invalid input; blocks should end with EOB + token_prob = probs[i][0]; + if (vp7) + goto restart; + goto skip_eob; + } + + if (!vpx_rac_get_prob_branchy(&c, token_prob[2])) { // DCT_1 + coeff = 1; + token_prob = probs[i + 1][1]; + } else { + if (!vpx_rac_get_prob_branchy(&c, token_prob[3])) { // DCT 2,3,4 + coeff = vpx_rac_get_prob_branchy(&c, token_prob[4]); + if (coeff) + coeff += vpx_rac_get_prob(&c, token_prob[5]); + coeff += 2; + } else { + // DCT_CAT* + if (!vpx_rac_get_prob_branchy(&c, token_prob[6])) { + if (!vpx_rac_get_prob_branchy(&c, token_prob[7])) { // DCT_CAT1 + coeff = 5 + vpx_rac_get_prob(&c, vp8_dct_cat1_prob[0]); + } else { // DCT_CAT2 + coeff = 7; + coeff += vpx_rac_get_prob(&c, vp8_dct_cat2_prob[0]) << 1; + coeff += vpx_rac_get_prob(&c, vp8_dct_cat2_prob[1]); + } + } else { // DCT_CAT3 and up + int a = vpx_rac_get_prob(&c, token_prob[8]); + int b = vpx_rac_get_prob(&c, token_prob[9 + a]); + int cat = (a << 1) + b; + coeff = 3 + (8 << cat); + coeff += vp8_rac_get_coeff(&c, ff_vp8_dct_cat_prob[cat]); + } + } + token_prob = probs[i + 1][2]; + } + block[scan[i]] = (vp89_rac_get(&c) ? -coeff : coeff) * qmul[!!i]; + } while (++i < 16); + + *r = c; + return i; +} + +static av_always_inline +int inter_predict_dc(int16_t block[16], int16_t pred[2]) +{ + int16_t dc = block[0]; + int ret = 0; + + if (pred[1] > 3) { + dc += pred[0]; + ret = 1; + } + + if (!pred[0] | !dc | ((int32_t)pred[0] ^ (int32_t)dc) >> 31) { + block[0] = pred[0] = dc; + pred[1] = 0; + } else { + if (pred[0] == dc) + pred[1]++; + block[0] = pred[0] = dc; + } + + return ret; +} + +static int vp7_decode_block_coeffs_internal(VPXRangeCoder *r, + int16_t block[16], + uint8_t probs[16][3][NUM_DCT_TOKENS - 1], + int i, const uint8_t *token_prob, + const int16_t qmul[2], + const uint8_t scan[16]) +{ + return decode_block_coeffs_internal(r, block, probs, i, + token_prob, qmul, scan, IS_VP7); +} + +#ifndef vp8_decode_block_coeffs_internal +static int vp8_decode_block_coeffs_internal(VPXRangeCoder *r, + int16_t block[16], + uint8_t probs[16][3][NUM_DCT_TOKENS - 1], + int i, const uint8_t *token_prob, + const int16_t qmul[2]) +{ + return decode_block_coeffs_internal(r, block, probs, i, + token_prob, qmul, ff_zigzag_scan, IS_VP8); +} +#endif + +/** + * @param c arithmetic bitstream reader context + * @param block destination for block coefficients + * @param probs probabilities to use when reading trees from the bitstream + * @param i initial coeff index, 0 unless a separate DC block is coded + * @param zero_nhood the initial prediction context for number of surrounding + * all-zero blocks (only left/top, so 0-2) + * @param qmul array holding the dc/ac dequant factor at position 0/1 + * @param scan scan pattern (VP7 only) + * + * @return 0 if no coeffs were decoded + * otherwise, the index of the last coeff decoded plus one + */ +static av_always_inline +int decode_block_coeffs(VPXRangeCoder *c, int16_t block[16], + uint8_t probs[16][3][NUM_DCT_TOKENS - 1], + int i, int zero_nhood, const int16_t qmul[2], + const uint8_t scan[16], int vp7) +{ + const uint8_t *token_prob = probs[i][zero_nhood]; + if (!vpx_rac_get_prob_branchy(c, token_prob[0])) // DCT_EOB + return 0; + return vp7 ? vp7_decode_block_coeffs_internal(c, block, probs, i, + token_prob, qmul, scan) + : vp8_decode_block_coeffs_internal(c, block, probs, i, + token_prob, qmul); +} + +static av_always_inline +void decode_mb_coeffs(VP8Context *s, VP8ThreadData *td, VPXRangeCoder *c, + VP8Macroblock *mb, uint8_t t_nnz[9], uint8_t l_nnz[9], + int is_vp7) +{ + int i, x, y, luma_start = 0, luma_ctx = 3; + int nnz_pred, nnz, nnz_total = 0; + int segment = mb->segment; + int block_dc = 0; + + if (mb->mode != MODE_I4x4 && (is_vp7 || mb->mode != VP8_MVMODE_SPLIT)) { + nnz_pred = t_nnz[8] + l_nnz[8]; + + // decode DC values and do hadamard + nnz = decode_block_coeffs(c, td->block_dc, s->prob->token[1], 0, + nnz_pred, s->qmat[segment].luma_dc_qmul, + ff_zigzag_scan, is_vp7); + l_nnz[8] = t_nnz[8] = !!nnz; + + if (is_vp7 && mb->mode > MODE_I4x4) { + nnz |= inter_predict_dc(td->block_dc, + s->inter_dc_pred[mb->ref_frame - 1]); + } + + if (nnz) { + nnz_total += nnz; + block_dc = 1; + if (nnz == 1) + s->vp8dsp.vp8_luma_dc_wht_dc(td->block, td->block_dc); + else + s->vp8dsp.vp8_luma_dc_wht(td->block, td->block_dc); + } + luma_start = 1; + luma_ctx = 0; + } + + // luma blocks + for (y = 0; y < 4; y++) + for (x = 0; x < 4; x++) { + nnz_pred = l_nnz[y] + t_nnz[x]; + nnz = decode_block_coeffs(c, td->block[y][x], + s->prob->token[luma_ctx], + luma_start, nnz_pred, + s->qmat[segment].luma_qmul, + s->prob[0].scan, is_vp7); + /* nnz+block_dc may be one more than the actual last index, + * but we don't care */ + td->non_zero_count_cache[y][x] = nnz + block_dc; + t_nnz[x] = l_nnz[y] = !!nnz; + nnz_total += nnz; + } + + // chroma blocks + // TODO: what to do about dimensions? 2nd dim for luma is x, + // but for chroma it's (y<<1)|x + for (i = 4; i < 6; i++) + for (y = 0; y < 2; y++) + for (x = 0; x < 2; x++) { + nnz_pred = l_nnz[i + 2 * y] + t_nnz[i + 2 * x]; + nnz = decode_block_coeffs(c, td->block[i][(y << 1) + x], + s->prob->token[2], 0, nnz_pred, + s->qmat[segment].chroma_qmul, + s->prob[0].scan, is_vp7); + td->non_zero_count_cache[i][(y << 1) + x] = nnz; + t_nnz[i + 2 * x] = l_nnz[i + 2 * y] = !!nnz; + nnz_total += nnz; + } + + // if there were no coded coeffs despite the macroblock not being marked skip, + // we MUST not do the inner loop filter and should not do IDCT + // Since skip isn't used for bitstream prediction, just manually set it. + if (!nnz_total) + mb->skip = 1; +} + +static av_always_inline +void backup_mb_border(uint8_t *top_border, const uint8_t *src_y, + const uint8_t *src_cb, const uint8_t *src_cr, + ptrdiff_t linesize, ptrdiff_t uvlinesize, int simple) +{ + AV_COPY128(top_border, src_y + 15 * linesize); + if (!simple) { + AV_COPY64(top_border + 16, src_cb + 7 * uvlinesize); + AV_COPY64(top_border + 24, src_cr + 7 * uvlinesize); + } +} + +static av_always_inline +void xchg_mb_border(uint8_t *top_border, uint8_t *src_y, uint8_t *src_cb, + uint8_t *src_cr, ptrdiff_t linesize, ptrdiff_t uvlinesize, int mb_x, + int mb_y, int mb_width, int simple, int xchg) +{ + uint8_t *top_border_m1 = top_border - 32; // for TL prediction + src_y -= linesize; + src_cb -= uvlinesize; + src_cr -= uvlinesize; + +#define XCHG(a, b, xchg) \ + do { \ + if (xchg) \ + AV_SWAP64(b, a); \ + else \ + AV_COPY64(b, a); \ + } while (0) + + XCHG(top_border_m1 + 8, src_y - 8, xchg); + XCHG(top_border, src_y, xchg); + XCHG(top_border + 8, src_y + 8, 1); + if (mb_x < mb_width - 1) + XCHG(top_border + 32, src_y + 16, 1); + + // only copy chroma for normal loop filter + // or to initialize the top row to 127 + if (!simple || !mb_y) { + XCHG(top_border_m1 + 16, src_cb - 8, xchg); + XCHG(top_border_m1 + 24, src_cr - 8, xchg); + XCHG(top_border + 16, src_cb, 1); + XCHG(top_border + 24, src_cr, 1); + } +} + +static av_always_inline +int check_dc_pred8x8_mode(int mode, int mb_x, int mb_y) +{ + if (!mb_x) + return mb_y ? TOP_DC_PRED8x8 : DC_128_PRED8x8; + else + return mb_y ? mode : LEFT_DC_PRED8x8; +} + +static av_always_inline +int check_tm_pred8x8_mode(int mode, int mb_x, int mb_y, int vp7) +{ + if (!mb_x) + return mb_y ? VERT_PRED8x8 : (vp7 ? DC_128_PRED8x8 : DC_129_PRED8x8); + else + return mb_y ? mode : HOR_PRED8x8; +} + +static av_always_inline +int check_intra_pred8x8_mode_emuedge(int mode, int mb_x, int mb_y, int vp7) +{ + switch (mode) { + case DC_PRED8x8: + return check_dc_pred8x8_mode(mode, mb_x, mb_y); + case VERT_PRED8x8: + return !mb_y ? (vp7 ? DC_128_PRED8x8 : DC_127_PRED8x8) : mode; + case HOR_PRED8x8: + return !mb_x ? (vp7 ? DC_128_PRED8x8 : DC_129_PRED8x8) : mode; + case PLANE_PRED8x8: /* TM */ + return check_tm_pred8x8_mode(mode, mb_x, mb_y, vp7); + } + return mode; +} + +static av_always_inline +int check_tm_pred4x4_mode(int mode, int mb_x, int mb_y, int vp7) +{ + if (!mb_x) { + return mb_y ? VERT_VP8_PRED : (vp7 ? DC_128_PRED : DC_129_PRED); + } else { + return mb_y ? mode : HOR_VP8_PRED; + } +} + +static av_always_inline +int check_intra_pred4x4_mode_emuedge(int mode, int mb_x, int mb_y, + int *copy_buf, int vp7) +{ + switch (mode) { + case VERT_PRED: + if (!mb_x && mb_y) { + *copy_buf = 1; + return mode; + } + av_fallthrough; + case DIAG_DOWN_LEFT_PRED: + case VERT_LEFT_PRED: + return !mb_y ? (vp7 ? DC_128_PRED : DC_127_PRED) : mode; + case HOR_PRED: + if (!mb_y) { + *copy_buf = 1; + return mode; + } + av_fallthrough; + case HOR_UP_PRED: + return !mb_x ? (vp7 ? DC_128_PRED : DC_129_PRED) : mode; + case TM_VP8_PRED: + return check_tm_pred4x4_mode(mode, mb_x, mb_y, vp7); + case DC_PRED: /* 4x4 DC doesn't use the same "H.264-style" exceptions + * as 16x16/8x8 DC */ + case DIAG_DOWN_RIGHT_PRED: + case VERT_RIGHT_PRED: + case HOR_DOWN_PRED: + if (!mb_y || !mb_x) + *copy_buf = 1; + return mode; + } + return mode; +} + +static av_always_inline +void intra_predict(VP8Context *s, VP8ThreadData *td, uint8_t *const dst[3], + VP8Macroblock *mb, int mb_x, int mb_y, int is_vp7) +{ + int x, y, mode, nnz; + uint32_t tr; + + /* for the first row, we need to run xchg_mb_border to init the top edge + * to 127 otherwise, skip it if we aren't going to deblock */ + if (mb_y && (s->deblock_filter || !mb_y) && td->thread_nr == 0) + xchg_mb_border(s->top_border[mb_x + 1], dst[0], dst[1], dst[2], + s->linesize, s->uvlinesize, mb_x, mb_y, s->mb_width, + s->filter.simple, 1); + + if (mb->mode < MODE_I4x4) { + mode = check_intra_pred8x8_mode_emuedge(mb->mode, mb_x, mb_y, is_vp7); + s->hpc.pred16x16[mode](dst[0], s->linesize); + } else { + uint8_t *ptr = dst[0]; + const uint8_t *intra4x4 = mb->intra4x4_pred_mode_mb; + const uint8_t lo = is_vp7 ? 128 : 127; + const uint8_t hi = is_vp7 ? 128 : 129; + const uint8_t tr_top[4] = { lo, lo, lo, lo }; + + // all blocks on the right edge of the macroblock use bottom edge + // the top macroblock for their topright edge + const uint8_t *tr_right = ptr - s->linesize + 16; + + // if we're on the right edge of the frame, said edge is extended + // from the top macroblock + if (mb_y && mb_x == s->mb_width - 1) { + tr = tr_right[-1] * 0x01010101u; + tr_right = (uint8_t *) &tr; + } + + if (mb->skip) + AV_ZERO128(td->non_zero_count_cache); + + for (y = 0; y < 4; y++) { + const uint8_t *topright = ptr + 4 - s->linesize; + for (x = 0; x < 4; x++) { + int copy = 0; + ptrdiff_t linesize = s->linesize; + uint8_t *dst = ptr + 4 * x; + LOCAL_ALIGNED(4, uint8_t, copy_dst, [5 * 8]); + + if ((y == 0 || x == 3) && mb_y == 0) { + topright = tr_top; + } else if (x == 3) + topright = tr_right; + + mode = check_intra_pred4x4_mode_emuedge(intra4x4[x], mb_x + x, + mb_y + y, ©, is_vp7); + if (copy) { + dst = copy_dst + 12; + linesize = 8; + if (!(mb_y + y)) { + copy_dst[3] = lo; + AV_WN32A(copy_dst + 4, lo * 0x01010101U); + } else { + AV_COPY32(copy_dst + 4, ptr + 4 * x - s->linesize); + if (!(mb_x + x)) { + copy_dst[3] = hi; + } else { + copy_dst[3] = ptr[4 * x - s->linesize - 1]; + } + } + if (!(mb_x + x)) { + copy_dst[11] = + copy_dst[19] = + copy_dst[27] = + copy_dst[35] = hi; + } else { + copy_dst[11] = ptr[4 * x - 1]; + copy_dst[19] = ptr[4 * x + s->linesize - 1]; + copy_dst[27] = ptr[4 * x + s->linesize * 2 - 1]; + copy_dst[35] = ptr[4 * x + s->linesize * 3 - 1]; + } + } + s->hpc.pred4x4[mode](dst, topright, linesize); + if (copy) { + AV_COPY32(ptr + 4 * x, copy_dst + 12); + AV_COPY32(ptr + 4 * x + s->linesize, copy_dst + 20); + AV_COPY32(ptr + 4 * x + s->linesize * 2, copy_dst + 28); + AV_COPY32(ptr + 4 * x + s->linesize * 3, copy_dst + 36); + } + + nnz = td->non_zero_count_cache[y][x]; + if (nnz) { + if (nnz == 1) + s->vp8dsp.vp8_idct_dc_add(ptr + 4 * x, + td->block[y][x], s->linesize); + else + s->vp8dsp.vp8_idct_add(ptr + 4 * x, + td->block[y][x], s->linesize); + } + topright += 4; + } + + ptr += 4 * s->linesize; + intra4x4 += 4; + } + } + + mode = check_intra_pred8x8_mode_emuedge(mb->chroma_pred_mode, + mb_x, mb_y, is_vp7); + s->hpc.pred8x8[mode](dst[1], s->uvlinesize); + s->hpc.pred8x8[mode](dst[2], s->uvlinesize); + + if (mb_y && (s->deblock_filter || !mb_y) && td->thread_nr == 0) + xchg_mb_border(s->top_border[mb_x + 1], dst[0], dst[1], dst[2], + s->linesize, s->uvlinesize, mb_x, mb_y, s->mb_width, + s->filter.simple, 0); +} + +static const uint8_t subpel_idx[3][8] = { + { 0, 1, 2, 1, 2, 1, 2, 1 }, // nr. of left extra pixels, + // also function pointer index + { 0, 3, 5, 3, 5, 3, 5, 3 }, // nr. of extra pixels required + { 0, 2, 3, 2, 3, 2, 3, 2 }, // nr. of right extra pixels +}; + +/** + * luma MC function + * + * @param s VP8 decoding context + * @param dst target buffer for block data at block position + * @param ref reference picture buffer at origin (0, 0) + * @param mv motion vector (relative to block position) to get pixel data from + * @param x_off horizontal position of block from origin (0, 0) + * @param y_off vertical position of block from origin (0, 0) + * @param block_w width of block (16, 8 or 4) + * @param block_h height of block (always same as block_w) + * @param width width of src/dst plane data + * @param height height of src/dst plane data + * @param linesize size of a single line of plane data, including padding + * @param mc_func motion compensation function pointers (bilinear or sixtap MC) + */ +static av_always_inline +void vp8_mc_luma(VP8Context *s, VP8ThreadData *td, uint8_t *dst, + const ProgressFrame *ref, const VP8mv *mv, + int x_off, int y_off, int block_w, int block_h, + int width, int height, ptrdiff_t linesize, + vp8_mc_func mc_func[3][3]) +{ + const uint8_t *src = ref->f->data[0]; + + if (AV_RN32A(mv)) { + ptrdiff_t src_linesize = linesize; + + int mx = (mv->x * 2) & 7, mx_idx = subpel_idx[0][mx]; + int my = (mv->y * 2) & 7, my_idx = subpel_idx[0][my]; + + x_off += mv->x >> 2; + y_off += mv->y >> 2; + + // edge emulation + ff_progress_frame_await(ref, (3 + y_off + block_h + subpel_idx[2][my]) >> 4); + src += y_off * linesize + x_off; + if (x_off < mx_idx || x_off >= width - block_w - subpel_idx[2][mx] || + y_off < my_idx || y_off >= height - block_h - subpel_idx[2][my]) { + s->vdsp.emulated_edge_mc(td->edge_emu_buffer, + src - my_idx * linesize - mx_idx, + EDGE_EMU_LINESIZE, linesize, + block_w + subpel_idx[1][mx], + block_h + subpel_idx[1][my], + x_off - mx_idx, y_off - my_idx, + width, height); + src = td->edge_emu_buffer + mx_idx + EDGE_EMU_LINESIZE * my_idx; + src_linesize = EDGE_EMU_LINESIZE; + } + mc_func[my_idx][mx_idx](dst, linesize, src, src_linesize, block_h, mx, my); + } else { + ff_progress_frame_await(ref, (3 + y_off + block_h) >> 4); + mc_func[0][0](dst, linesize, src + y_off * linesize + x_off, + linesize, block_h, 0, 0); + } +} + +/** + * chroma MC function + * + * @param s VP8 decoding context + * @param dst1 target buffer for block data at block position (U plane) + * @param dst2 target buffer for block data at block position (V plane) + * @param ref reference picture buffer at origin (0, 0) + * @param mv motion vector (relative to block position) to get pixel data from + * @param x_off horizontal position of block from origin (0, 0) + * @param y_off vertical position of block from origin (0, 0) + * @param block_w width of block (16, 8 or 4) + * @param block_h height of block (always same as block_w) + * @param width width of src/dst plane data + * @param height height of src/dst plane data + * @param linesize size of a single line of plane data, including padding + * @param mc_func motion compensation function pointers (bilinear or sixtap MC) + */ +static av_always_inline +void vp8_mc_chroma(VP8Context *s, VP8ThreadData *td, uint8_t *dst1, + uint8_t *dst2, const ProgressFrame *ref, const VP8mv *mv, + int x_off, int y_off, int block_w, int block_h, + int width, int height, ptrdiff_t linesize, + vp8_mc_func mc_func[3][3]) +{ + const uint8_t *src1 = ref->f->data[1], *src2 = ref->f->data[2]; + + if (AV_RN32A(mv)) { + int mx = mv->x & 7, mx_idx = subpel_idx[0][mx]; + int my = mv->y & 7, my_idx = subpel_idx[0][my]; + + x_off += mv->x >> 3; + y_off += mv->y >> 3; + + // edge emulation + src1 += y_off * linesize + x_off; + src2 += y_off * linesize + x_off; + ff_progress_frame_await(ref, (3 + y_off + block_h + subpel_idx[2][my]) >> 3); + if (x_off < mx_idx || x_off >= width - block_w - subpel_idx[2][mx] || + y_off < my_idx || y_off >= height - block_h - subpel_idx[2][my]) { + s->vdsp.emulated_edge_mc(td->edge_emu_buffer, + src1 - my_idx * linesize - mx_idx, + EDGE_EMU_LINESIZE, linesize, + block_w + subpel_idx[1][mx], + block_h + subpel_idx[1][my], + x_off - mx_idx, y_off - my_idx, width, height); + src1 = td->edge_emu_buffer + mx_idx + EDGE_EMU_LINESIZE * my_idx; + mc_func[my_idx][mx_idx](dst1, linesize, src1, EDGE_EMU_LINESIZE, block_h, mx, my); + + s->vdsp.emulated_edge_mc(td->edge_emu_buffer, + src2 - my_idx * linesize - mx_idx, + EDGE_EMU_LINESIZE, linesize, + block_w + subpel_idx[1][mx], + block_h + subpel_idx[1][my], + x_off - mx_idx, y_off - my_idx, width, height); + src2 = td->edge_emu_buffer + mx_idx + EDGE_EMU_LINESIZE * my_idx; + mc_func[my_idx][mx_idx](dst2, linesize, src2, EDGE_EMU_LINESIZE, block_h, mx, my); + } else { + mc_func[my_idx][mx_idx](dst1, linesize, src1, linesize, block_h, mx, my); + mc_func[my_idx][mx_idx](dst2, linesize, src2, linesize, block_h, mx, my); + } + } else { + ff_progress_frame_await(ref, (3 + y_off + block_h) >> 3); + mc_func[0][0](dst1, linesize, src1 + y_off * linesize + x_off, linesize, block_h, 0, 0); + mc_func[0][0](dst2, linesize, src2 + y_off * linesize + x_off, linesize, block_h, 0, 0); + } +} + +static av_always_inline +void vp8_mc_part(VP8Context *s, VP8ThreadData *td, uint8_t *const dst[3], + const ProgressFrame *ref_frame, int x_off, int y_off, + int bx_off, int by_off, int block_w, int block_h, + int width, int height, const VP8mv *mv) +{ + VP8mv uvmv = *mv; + + /* Y */ + vp8_mc_luma(s, td, dst[0] + by_off * s->linesize + bx_off, + ref_frame, mv, x_off + bx_off, y_off + by_off, + block_w, block_h, width, height, s->linesize, + s->put_pixels_tab[block_w == 8]); + + /* U/V */ + if (s->profile == 3) { + /* this block only applies VP8; it is safe to check + * only the profile, as VP7 profile <= 1 */ + uvmv.x &= ~7; + uvmv.y &= ~7; + } + x_off >>= 1; + y_off >>= 1; + bx_off >>= 1; + by_off >>= 1; + width >>= 1; + height >>= 1; + block_w >>= 1; + block_h >>= 1; + vp8_mc_chroma(s, td, dst[1] + by_off * s->uvlinesize + bx_off, + dst[2] + by_off * s->uvlinesize + bx_off, ref_frame, + &uvmv, x_off + bx_off, y_off + by_off, + block_w, block_h, width, height, s->uvlinesize, + s->put_pixels_tab[1 + (block_w == 4)]); +} + +/* Fetch pixels for estimated mv 4 macroblocks ahead. + * Optimized for 64-byte cache lines. Inspired by ffh264 prefetch_motion. */ +static av_always_inline +void prefetch_motion(const VP8Context *s, const VP8Macroblock *mb, + int mb_x, int mb_y, int mb_xy, int ref) +{ + /* Don't prefetch refs that haven't been used very often this frame. */ + if (s->ref_count[ref - 1] > (mb_xy >> 5)) { + int x_off = mb_x << 4, y_off = mb_y << 4; + int mx = (mb->mv.x >> 2) + x_off + 8; + int my = (mb->mv.y >> 2) + y_off; + uint8_t **src = s->framep[ref]->tf.f->data; + int off = mx + (my + (mb_x & 3) * 4) * s->linesize + 64; + /* For threading, a ff_thread_await_progress here might be useful, but + * it actually slows down the decoder. Since a bad prefetch doesn't + * generate bad decoder output, we don't run it here. */ + s->vdsp.prefetch(src[0] + off, s->linesize, 4); + off = (mx >> 1) + ((my >> 1) + (mb_x & 7)) * s->uvlinesize + 64; + s->vdsp.prefetch(src[1] + off, src[2] - src[1], 2); + } +} + +/** + * Apply motion vectors to prediction buffer, chapter 18. + */ +static av_always_inline +void inter_predict(VP8Context *s, VP8ThreadData *td, uint8_t *const dst[3], + VP8Macroblock *mb, int mb_x, int mb_y) +{ + int x_off = mb_x << 4, y_off = mb_y << 4; + int width = 16 * s->mb_width, height = 16 * s->mb_height; + const ProgressFrame *ref = &s->framep[mb->ref_frame]->tf; + const VP8mv *bmv = mb->bmv; + + switch (mb->partitioning) { + case VP8_SPLITMVMODE_NONE: + vp8_mc_part(s, td, dst, ref, x_off, y_off, + 0, 0, 16, 16, width, height, &mb->mv); + break; + case VP8_SPLITMVMODE_4x4: { + int x, y; + VP8mv uvmv; + + /* Y */ + for (y = 0; y < 4; y++) { + for (x = 0; x < 4; x++) { + vp8_mc_luma(s, td, dst[0] + 4 * y * s->linesize + x * 4, + ref, &bmv[4 * y + x], + 4 * x + x_off, 4 * y + y_off, 4, 4, + width, height, s->linesize, + s->put_pixels_tab[2]); + } + } + + /* U/V */ + x_off >>= 1; + y_off >>= 1; + width >>= 1; + height >>= 1; + for (y = 0; y < 2; y++) { + for (x = 0; x < 2; x++) { + uvmv.x = mb->bmv[2 * y * 4 + 2 * x ].x + + mb->bmv[2 * y * 4 + 2 * x + 1].x + + mb->bmv[(2 * y + 1) * 4 + 2 * x ].x + + mb->bmv[(2 * y + 1) * 4 + 2 * x + 1].x; + uvmv.y = mb->bmv[2 * y * 4 + 2 * x ].y + + mb->bmv[2 * y * 4 + 2 * x + 1].y + + mb->bmv[(2 * y + 1) * 4 + 2 * x ].y + + mb->bmv[(2 * y + 1) * 4 + 2 * x + 1].y; + uvmv.x = (uvmv.x + 2 + FF_SIGNBIT(uvmv.x)) >> 2; + uvmv.y = (uvmv.y + 2 + FF_SIGNBIT(uvmv.y)) >> 2; + if (s->profile == 3) { + uvmv.x &= ~7; + uvmv.y &= ~7; + } + vp8_mc_chroma(s, td, dst[1] + 4 * y * s->uvlinesize + x * 4, + dst[2] + 4 * y * s->uvlinesize + x * 4, ref, + &uvmv, 4 * x + x_off, 4 * y + y_off, 4, 4, + width, height, s->uvlinesize, + s->put_pixels_tab[2]); + } + } + break; + } + case VP8_SPLITMVMODE_16x8: + vp8_mc_part(s, td, dst, ref, x_off, y_off, + 0, 0, 16, 8, width, height, &bmv[0]); + vp8_mc_part(s, td, dst, ref, x_off, y_off, + 0, 8, 16, 8, width, height, &bmv[1]); + break; + case VP8_SPLITMVMODE_8x16: + vp8_mc_part(s, td, dst, ref, x_off, y_off, + 0, 0, 8, 16, width, height, &bmv[0]); + vp8_mc_part(s, td, dst, ref, x_off, y_off, + 8, 0, 8, 16, width, height, &bmv[1]); + break; + case VP8_SPLITMVMODE_8x8: + vp8_mc_part(s, td, dst, ref, x_off, y_off, + 0, 0, 8, 8, width, height, &bmv[0]); + vp8_mc_part(s, td, dst, ref, x_off, y_off, + 8, 0, 8, 8, width, height, &bmv[1]); + vp8_mc_part(s, td, dst, ref, x_off, y_off, + 0, 8, 8, 8, width, height, &bmv[2]); + vp8_mc_part(s, td, dst, ref, x_off, y_off, + 8, 8, 8, 8, width, height, &bmv[3]); + break; + } +} + +static av_always_inline +void idct_mb(VP8Context *s, VP8ThreadData *td, uint8_t *const dst[3], + const VP8Macroblock *mb) +{ + int x, y, ch; + + if (mb->mode != MODE_I4x4) { + uint8_t *y_dst = dst[0]; + for (y = 0; y < 4; y++) { + uint32_t nnz4 = AV_RL32(td->non_zero_count_cache[y]); + if (nnz4) { + if (nnz4 & ~0x01010101) { + for (x = 0; x < 4; x++) { + if ((uint8_t) nnz4 == 1) + s->vp8dsp.vp8_idct_dc_add(y_dst + 4 * x, + td->block[y][x], + s->linesize); + else if ((uint8_t) nnz4 > 1) + s->vp8dsp.vp8_idct_add(y_dst + 4 * x, + td->block[y][x], + s->linesize); + nnz4 >>= 8; + if (!nnz4) + break; + } + } else { + s->vp8dsp.vp8_idct_dc_add4y(y_dst, td->block[y], s->linesize); + } + } + y_dst += 4 * s->linesize; + } + } + + for (ch = 0; ch < 2; ch++) { + uint32_t nnz4 = AV_RL32(td->non_zero_count_cache[4 + ch]); + if (nnz4) { + uint8_t *ch_dst = dst[1 + ch]; + if (nnz4 & ~0x01010101) { + for (y = 0; y < 2; y++) { + for (x = 0; x < 2; x++) { + if ((uint8_t) nnz4 == 1) + s->vp8dsp.vp8_idct_dc_add(ch_dst + 4 * x, + td->block[4 + ch][(y << 1) + x], + s->uvlinesize); + else if ((uint8_t) nnz4 > 1) + s->vp8dsp.vp8_idct_add(ch_dst + 4 * x, + td->block[4 + ch][(y << 1) + x], + s->uvlinesize); + nnz4 >>= 8; + if (!nnz4) + goto chroma_idct_end; + } + ch_dst += 4 * s->uvlinesize; + } + } else { + s->vp8dsp.vp8_idct_dc_add4uv(ch_dst, td->block[4 + ch], s->uvlinesize); + } + } +chroma_idct_end: + ; + } +} + +static av_always_inline +void filter_level_for_mb(const VP8Context *s, const VP8Macroblock *mb, + VP8FilterStrength *f, int is_vp7) +{ + int interior_limit, filter_level; + + if (s->segmentation.enabled) { + filter_level = s->segmentation.filter_level[mb->segment]; + if (!s->segmentation.absolute_vals) + filter_level += s->filter.level; + } else + filter_level = s->filter.level; + + if (s->lf_delta.enabled) { + filter_level += s->lf_delta.ref[mb->ref_frame]; + filter_level += s->lf_delta.mode[mb->mode]; + } + + filter_level = av_clip_uintp2(filter_level, 6); + + interior_limit = filter_level; + if (s->filter.sharpness) { + interior_limit >>= (s->filter.sharpness + 3) >> 2; + interior_limit = FFMIN(interior_limit, 9 - s->filter.sharpness); + } + interior_limit = FFMAX(interior_limit, 1); + + f->filter_level = filter_level; + f->inner_limit = interior_limit; + f->inner_filter = is_vp7 || !mb->skip || mb->mode == MODE_I4x4 || + mb->mode == VP8_MVMODE_SPLIT; +} + +static av_always_inline +void filter_mb(const VP8Context *s, uint8_t *const dst[3], const VP8FilterStrength *f, + int mb_x, int mb_y, int is_vp7) +{ + int mbedge_lim, bedge_lim_y, bedge_lim_uv, hev_thresh; + int filter_level = f->filter_level; + int inner_limit = f->inner_limit; + int inner_filter = f->inner_filter; + ptrdiff_t linesize = s->linesize; + ptrdiff_t uvlinesize = s->uvlinesize; + static const uint8_t hev_thresh_lut[2][64] = { + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2 } + }; + + if (!filter_level) + return; + + if (is_vp7) { + bedge_lim_y = filter_level; + bedge_lim_uv = filter_level * 2; + mbedge_lim = filter_level + 2; + } else { + bedge_lim_y = + bedge_lim_uv = filter_level * 2 + inner_limit; + mbedge_lim = bedge_lim_y + 4; + } + + hev_thresh = hev_thresh_lut[s->keyframe][filter_level]; + + if (mb_x) { + s->vp8dsp.vp8_h_loop_filter16y(dst[0], linesize, + mbedge_lim, inner_limit, hev_thresh); + s->vp8dsp.vp8_h_loop_filter8uv(dst[1], dst[2], uvlinesize, + mbedge_lim, inner_limit, hev_thresh); + } + +#define H_LOOP_FILTER_16Y_INNER(cond) \ + if (cond && inner_filter) { \ + s->vp8dsp.vp8_h_loop_filter16y_inner(dst[0] + 4, linesize, \ + bedge_lim_y, inner_limit, \ + hev_thresh); \ + s->vp8dsp.vp8_h_loop_filter16y_inner(dst[0] + 8, linesize, \ + bedge_lim_y, inner_limit, \ + hev_thresh); \ + s->vp8dsp.vp8_h_loop_filter16y_inner(dst[0] + 12, linesize, \ + bedge_lim_y, inner_limit, \ + hev_thresh); \ + s->vp8dsp.vp8_h_loop_filter8uv_inner(dst[1] + 4, dst[2] + 4, \ + uvlinesize, bedge_lim_uv, \ + inner_limit, hev_thresh); \ + } + + H_LOOP_FILTER_16Y_INNER(!is_vp7) + + if (mb_y) { + s->vp8dsp.vp8_v_loop_filter16y(dst[0], linesize, + mbedge_lim, inner_limit, hev_thresh); + s->vp8dsp.vp8_v_loop_filter8uv(dst[1], dst[2], uvlinesize, + mbedge_lim, inner_limit, hev_thresh); + } + + if (inner_filter) { + s->vp8dsp.vp8_v_loop_filter16y_inner(dst[0] + 4 * linesize, + linesize, bedge_lim_y, + inner_limit, hev_thresh); + s->vp8dsp.vp8_v_loop_filter16y_inner(dst[0] + 8 * linesize, + linesize, bedge_lim_y, + inner_limit, hev_thresh); + s->vp8dsp.vp8_v_loop_filter16y_inner(dst[0] + 12 * linesize, + linesize, bedge_lim_y, + inner_limit, hev_thresh); + s->vp8dsp.vp8_v_loop_filter8uv_inner(dst[1] + 4 * uvlinesize, + dst[2] + 4 * uvlinesize, + uvlinesize, bedge_lim_uv, + inner_limit, hev_thresh); + } + + H_LOOP_FILTER_16Y_INNER(is_vp7) +} + +static av_always_inline +void filter_mb_simple(const VP8Context *s, uint8_t *dst, const VP8FilterStrength *f, + int mb_x, int mb_y) +{ + int mbedge_lim, bedge_lim; + int filter_level = f->filter_level; + int inner_limit = f->inner_limit; + int inner_filter = f->inner_filter; + ptrdiff_t linesize = s->linesize; + + if (!filter_level) + return; + + bedge_lim = 2 * filter_level + inner_limit; + mbedge_lim = bedge_lim + 4; + + if (mb_x) + s->vp8dsp.vp8_h_loop_filter_simple(dst, linesize, mbedge_lim); + if (inner_filter) { + s->vp8dsp.vp8_h_loop_filter_simple(dst + 4, linesize, bedge_lim); + s->vp8dsp.vp8_h_loop_filter_simple(dst + 8, linesize, bedge_lim); + s->vp8dsp.vp8_h_loop_filter_simple(dst + 12, linesize, bedge_lim); + } + + if (mb_y) + s->vp8dsp.vp8_v_loop_filter_simple(dst, linesize, mbedge_lim); + if (inner_filter) { + s->vp8dsp.vp8_v_loop_filter_simple(dst + 4 * linesize, linesize, bedge_lim); + s->vp8dsp.vp8_v_loop_filter_simple(dst + 8 * linesize, linesize, bedge_lim); + s->vp8dsp.vp8_v_loop_filter_simple(dst + 12 * linesize, linesize, bedge_lim); + } +} + +#define MARGIN (16 << 2) +static av_always_inline +int vp78_decode_mv_mb_modes(AVCodecContext *avctx, VP8Frame *curframe, + const VP8Frame *prev_frame, int is_vp7) +{ + VP8Context *s = avctx->priv_data; + int mb_x, mb_y; + + s->mv_bounds.mv_min.y = -MARGIN; + s->mv_bounds.mv_max.y = ((s->mb_height - 1) << 6) + MARGIN; + for (mb_y = 0; mb_y < s->mb_height; mb_y++) { + VP8Macroblock *mb = s->macroblocks_base + + ((s->mb_width + 1) * (mb_y + 1) + 1); + int mb_xy = mb_y * s->mb_width; + + AV_WN32A(s->intra4x4_pred_mode_left, DC_PRED * 0x01010101); + + s->mv_bounds.mv_min.x = -MARGIN; + s->mv_bounds.mv_max.x = ((s->mb_width - 1) << 6) + MARGIN; + + for (mb_x = 0; mb_x < s->mb_width; mb_x++, mb_xy++, mb++) { + if (vpx_rac_is_end(&s->c)) { + return AVERROR_INVALIDDATA; + } + if (mb_y == 0) + AV_WN32A((mb - s->mb_width - 1)->intra4x4_pred_mode_top, + DC_PRED * 0x01010101); + decode_mb_mode(s, &s->mv_bounds, mb, mb_x, mb_y, curframe->seg_map + mb_xy, + prev_frame && prev_frame->seg_map ? + prev_frame->seg_map + mb_xy : NULL, 1, is_vp7); + s->mv_bounds.mv_min.x -= 64; + s->mv_bounds.mv_max.x -= 64; + } + s->mv_bounds.mv_min.y -= 64; + s->mv_bounds.mv_max.y -= 64; + } + return 0; +} + +static int vp7_decode_mv_mb_modes(AVCodecContext *avctx, VP8Frame *cur_frame, + const VP8Frame *prev_frame) +{ + return vp78_decode_mv_mb_modes(avctx, cur_frame, prev_frame, IS_VP7); +} + +static int vp8_decode_mv_mb_modes(AVCodecContext *avctx, VP8Frame *cur_frame, + const VP8Frame *prev_frame) +{ + return vp78_decode_mv_mb_modes(avctx, cur_frame, prev_frame, IS_VP8); +} + +#if HAVE_THREADS +#define check_thread_pos(td, otd, mb_x_check, mb_y_check) \ + do { \ + int tmp = (mb_y_check << 16) | (mb_x_check & 0xFFFF); \ + if (atomic_load(&otd->thread_mb_pos) < tmp) { \ + pthread_mutex_lock(&otd->lock); \ + atomic_store(&td->wait_mb_pos, tmp); \ + do { \ + if (atomic_load(&otd->thread_mb_pos) >= tmp) \ + break; \ + pthread_cond_wait(&otd->cond, &otd->lock); \ + } while (1); \ + atomic_store(&td->wait_mb_pos, INT_MAX); \ + pthread_mutex_unlock(&otd->lock); \ + } \ + } while (0) + +#define update_pos(td, mb_y, mb_x) \ + do { \ + int pos = (mb_y << 16) | (mb_x & 0xFFFF); \ + int sliced_threading = (avctx->active_thread_type == FF_THREAD_SLICE) && \ + (num_jobs > 1); \ + int is_null = !next_td || !prev_td; \ + int pos_check = (is_null) ? 1 : \ + (next_td != td && pos >= atomic_load(&next_td->wait_mb_pos)) || \ + (prev_td != td && pos >= atomic_load(&prev_td->wait_mb_pos)); \ + atomic_store(&td->thread_mb_pos, pos); \ + if (sliced_threading && pos_check) { \ + pthread_mutex_lock(&td->lock); \ + pthread_cond_broadcast(&td->cond); \ + pthread_mutex_unlock(&td->lock); \ + } \ + } while (0) +#else +#define check_thread_pos(td, otd, mb_x_check, mb_y_check) while(0) +#define update_pos(td, mb_y, mb_x) while(0) +#endif + +static av_always_inline int decode_mb_row_no_filter(AVCodecContext *avctx, void *tdata, + int jobnr, int threadnr, int is_vp7) +{ + VP8Context *s = avctx->priv_data; + VP8ThreadData *prev_td, *next_td, *td = &s->thread_data[threadnr]; + int mb_y = atomic_load(&td->thread_mb_pos) >> 16; + int mb_x, mb_xy = mb_y * s->mb_width; + int num_jobs = s->num_jobs; + const VP8Frame *prev_frame = s->prev_frame; + VP8Frame *curframe = s->curframe; + VPXRangeCoder *coeff_c = &s->coeff_partition[mb_y & (s->num_coeff_partitions - 1)]; + + VP8Macroblock *mb; + uint8_t *dst[3] = { + curframe->tf.f->data[0] + 16 * mb_y * s->linesize, + curframe->tf.f->data[1] + 8 * mb_y * s->uvlinesize, + curframe->tf.f->data[2] + 8 * mb_y * s->uvlinesize + }; + + if (vpx_rac_is_end(&s->c)) + return AVERROR_INVALIDDATA; + + if (mb_y == 0) + prev_td = td; + else + prev_td = &s->thread_data[(jobnr + num_jobs - 1) % num_jobs]; + if (mb_y == s->mb_height - 1) + next_td = td; + else + next_td = &s->thread_data[(jobnr + 1) % num_jobs]; + if (s->mb_layout == 1) + mb = s->macroblocks_base + ((s->mb_width + 1) * (mb_y + 1) + 1); + else { + // Make sure the previous frame has read its segmentation map, + // if we reuse the same map. + if (prev_frame && s->segmentation.enabled && + !s->segmentation.update_map) + ff_progress_frame_await(&prev_frame->tf, mb_y); + mb = s->macroblocks + (s->mb_height - mb_y - 1) * 2; + memset(mb - 1, 0, sizeof(*mb)); // zero left macroblock + AV_WN32A(s->intra4x4_pred_mode_left, DC_PRED * 0x01010101); + } + + if (!is_vp7 || mb_y == 0) + memset(td->left_nnz, 0, sizeof(td->left_nnz)); + + td->mv_bounds.mv_min.x = -MARGIN; + td->mv_bounds.mv_max.x = ((s->mb_width - 1) << 6) + MARGIN; + + for (mb_x = 0; mb_x < s->mb_width; mb_x++, mb_xy++, mb++) { + if (vpx_rac_is_end(&s->c)) + return AVERROR_INVALIDDATA; + // Wait for previous thread to read mb_x+2, and reach mb_y-1. + if (prev_td != td) { + if (threadnr != 0) { + check_thread_pos(td, prev_td, + mb_x + (is_vp7 ? 2 : 1), + mb_y - (is_vp7 ? 2 : 1)); + } else { + check_thread_pos(td, prev_td, + mb_x + (is_vp7 ? 2 : 1) + s->mb_width + 3, + mb_y - (is_vp7 ? 2 : 1)); + } + } + + s->vdsp.prefetch(dst[0] + (mb_x & 3) * 4 * s->linesize + 64, + s->linesize, 4); + s->vdsp.prefetch(dst[1] + (mb_x & 7) * s->uvlinesize + 64, + dst[2] - dst[1], 2); + + if (!s->mb_layout) + decode_mb_mode(s, &td->mv_bounds, mb, mb_x, mb_y, curframe->seg_map + mb_xy, + prev_frame && prev_frame->seg_map ? + prev_frame->seg_map + mb_xy : NULL, 0, is_vp7); + + prefetch_motion(s, mb, mb_x, mb_y, mb_xy, VP8_FRAME_PREVIOUS); + + if (!mb->skip) { + if (vpx_rac_is_end(coeff_c)) + return AVERROR_INVALIDDATA; + decode_mb_coeffs(s, td, coeff_c, mb, s->top_nnz[mb_x], td->left_nnz, is_vp7); + } + + if (mb->mode <= MODE_I4x4) + intra_predict(s, td, dst, mb, mb_x, mb_y, is_vp7); + else + inter_predict(s, td, dst, mb, mb_x, mb_y); + + prefetch_motion(s, mb, mb_x, mb_y, mb_xy, VP8_FRAME_GOLDEN); + + if (!mb->skip) { + idct_mb(s, td, dst, mb); + } else { + AV_ZERO64(td->left_nnz); + AV_WN64(s->top_nnz[mb_x], 0); // array of 9, so unaligned + + /* Reset DC block predictors if they would exist + * if the mb had coefficients */ + if (mb->mode != MODE_I4x4 && mb->mode != VP8_MVMODE_SPLIT) { + td->left_nnz[8] = 0; + s->top_nnz[mb_x][8] = 0; + } + } + + if (s->deblock_filter) + filter_level_for_mb(s, mb, &td->filter_strength[mb_x], is_vp7); + + if (s->deblock_filter && num_jobs != 1 && threadnr == num_jobs - 1) { + if (s->filter.simple) + backup_mb_border(s->top_border[mb_x + 1], dst[0], + NULL, NULL, s->linesize, 0, 1); + else + backup_mb_border(s->top_border[mb_x + 1], dst[0], + dst[1], dst[2], s->linesize, s->uvlinesize, 0); + } + + prefetch_motion(s, mb, mb_x, mb_y, mb_xy, VP8_FRAME_ALTREF); + + dst[0] += 16; + dst[1] += 8; + dst[2] += 8; + td->mv_bounds.mv_min.x -= 64; + td->mv_bounds.mv_max.x -= 64; + + if (mb_x == s->mb_width + 1) { + update_pos(td, mb_y, s->mb_width + 3); + } else { + update_pos(td, mb_y, mb_x); + } + } + return 0; +} + +static av_always_inline void filter_mb_row(AVCodecContext *avctx, void *tdata, + int jobnr, int threadnr, int is_vp7) +{ + VP8Context *s = avctx->priv_data; + VP8ThreadData *td = &s->thread_data[threadnr]; + int mb_x, mb_y = atomic_load(&td->thread_mb_pos) >> 16, num_jobs = s->num_jobs; + AVFrame *curframe = s->curframe->tf.f; + VP8ThreadData *prev_td, *next_td; + uint8_t *dst[3] = { + curframe->data[0] + 16 * mb_y * s->linesize, + curframe->data[1] + 8 * mb_y * s->uvlinesize, + curframe->data[2] + 8 * mb_y * s->uvlinesize + }; + + if (mb_y == 0) + prev_td = td; + else + prev_td = &s->thread_data[(jobnr + num_jobs - 1) % num_jobs]; + if (mb_y == s->mb_height - 1) + next_td = td; + else + next_td = &s->thread_data[(jobnr + 1) % num_jobs]; + + for (mb_x = 0; mb_x < s->mb_width; mb_x++) { + const VP8FilterStrength *f = &td->filter_strength[mb_x]; + if (prev_td != td) + check_thread_pos(td, prev_td, + (mb_x + 1) + (s->mb_width + 3), mb_y - 1); + if (next_td != td) + if (next_td != &s->thread_data[0]) + check_thread_pos(td, next_td, mb_x + 1, mb_y + 1); + + if (num_jobs == 1) { + if (s->filter.simple) + backup_mb_border(s->top_border[mb_x + 1], dst[0], + NULL, NULL, s->linesize, 0, 1); + else + backup_mb_border(s->top_border[mb_x + 1], dst[0], + dst[1], dst[2], s->linesize, s->uvlinesize, 0); + } + + if (s->filter.simple) + filter_mb_simple(s, dst[0], f, mb_x, mb_y); + else + filter_mb(s, dst, f, mb_x, mb_y, is_vp7); + dst[0] += 16; + dst[1] += 8; + dst[2] += 8; + + update_pos(td, mb_y, (s->mb_width + 3) + mb_x); + } +} + +static av_always_inline +int vp78_decode_mb_row_sliced(AVCodecContext *avctx, void *tdata, int jobnr, + int threadnr, int is_vp7) +{ + const VP8Context *s = avctx->priv_data; + VP8ThreadData *td = &s->thread_data[jobnr]; + VP8ThreadData *next_td = NULL, *prev_td = NULL; + VP8Frame *curframe = s->curframe; + int mb_y, num_jobs = s->num_jobs; + int ret; + + td->thread_nr = threadnr; + td->mv_bounds.mv_min.y = -MARGIN - 64 * threadnr; + td->mv_bounds.mv_max.y = ((s->mb_height - 1) << 6) + MARGIN - 64 * threadnr; + for (mb_y = jobnr; mb_y < s->mb_height; mb_y += num_jobs) { + atomic_store(&td->thread_mb_pos, mb_y << 16); + ret = s->decode_mb_row_no_filter(avctx, tdata, jobnr, threadnr); + if (ret < 0) { + update_pos(td, s->mb_height, INT_MAX & 0xFFFF); + return ret; + } + if (s->deblock_filter) + s->filter_mb_row(avctx, tdata, jobnr, threadnr); + update_pos(td, mb_y, INT_MAX & 0xFFFF); + + td->mv_bounds.mv_min.y -= 64 * num_jobs; + td->mv_bounds.mv_max.y -= 64 * num_jobs; + + if (avctx->active_thread_type == FF_THREAD_FRAME) + ff_progress_frame_report(&curframe->tf, mb_y); + } + + return 0; +} + +static int vp7_decode_mb_row_sliced(AVCodecContext *avctx, void *tdata, + int jobnr, int threadnr) +{ + return vp78_decode_mb_row_sliced(avctx, tdata, jobnr, threadnr, IS_VP7); +} + +static int vp8_decode_mb_row_sliced(AVCodecContext *avctx, void *tdata, + int jobnr, int threadnr) +{ + return vp78_decode_mb_row_sliced(avctx, tdata, jobnr, threadnr, IS_VP8); +} + +static av_always_inline +int vp78_decode_frame(AVCodecContext *avctx, AVFrame *rframe, int *got_frame, + const AVPacket *avpkt, int is_vp7) +{ + VP8Context *s = avctx->priv_data; + int ret, i, referenced, num_jobs; + enum AVDiscard skip_thresh; + VP8Frame *av_uninit(curframe), *prev_frame; + + if (is_vp7) + ret = vp7_decode_frame_header(s, avpkt->data, avpkt->size); + else + ret = vp8_decode_frame_header(s, avpkt->data, avpkt->size); + + if (ret < 0) + goto err; + + if (!is_vp7 && s->actually_webp) { + // VP8 in WebP is supposed to be intra-only. Enforce this here + // to ensure that output is reproducible with frame-threading. + if (!s->keyframe) + return AVERROR_INVALIDDATA; + // avctx->pix_fmt already set in caller. + } else if (!is_vp7 && s->pix_fmt == AV_PIX_FMT_NONE) { + s->pix_fmt = get_pixel_format(s); + if (s->pix_fmt < 0) { + ret = AVERROR(EINVAL); + goto err; + } + avctx->pix_fmt = s->pix_fmt; + } + + prev_frame = s->framep[VP8_FRAME_CURRENT]; + + referenced = s->update_last || s->update_golden == VP8_FRAME_CURRENT || + s->update_altref == VP8_FRAME_CURRENT; + + skip_thresh = !referenced ? AVDISCARD_NONREF + : !s->keyframe ? AVDISCARD_NONKEY + : AVDISCARD_ALL; + + if (avctx->skip_frame >= skip_thresh) { + s->invisible = 1; + memcpy(&s->next_framep[0], &s->framep[0], sizeof(s->framep[0]) * 4); + goto skip_decode; + } + s->deblock_filter = s->filter.level && avctx->skip_loop_filter < skip_thresh; + + // release no longer referenced frames + for (i = 0; i < 5; i++) + if (s->frames[i].tf.f && + &s->frames[i] != prev_frame && + &s->frames[i] != s->framep[VP8_FRAME_PREVIOUS] && + &s->frames[i] != s->framep[VP8_FRAME_GOLDEN] && + &s->frames[i] != s->framep[VP8_FRAME_ALTREF]) + vp8_release_frame(&s->frames[i]); + + if (!s->colorspace) + avctx->colorspace = AVCOL_SPC_BT470BG; + if (s->fullrange) + avctx->color_range = AVCOL_RANGE_JPEG; + else + avctx->color_range = AVCOL_RANGE_MPEG; + + /* Given that arithmetic probabilities are updated every frame, it's quite + * likely that the values we have on a random interframe are complete + * junk if we didn't start decode on a keyframe. So just don't display + * anything rather than junk. */ + if (!s->keyframe && (!s->framep[VP8_FRAME_PREVIOUS] || + !s->framep[VP8_FRAME_GOLDEN] || + !s->framep[VP8_FRAME_ALTREF])) { + av_log(avctx, AV_LOG_WARNING, + "Discarding interframe without a prior keyframe!\n"); + ret = AVERROR_INVALIDDATA; + goto err; + } + + curframe = vp8_find_free_buffer(s); + if ((ret = vp8_alloc_frame(s, curframe, referenced)) < 0) + goto err; + s->framep[VP8_FRAME_CURRENT] = curframe; + if (s->keyframe) + curframe->tf.f->flags |= AV_FRAME_FLAG_KEY; + else + curframe->tf.f->flags &= ~AV_FRAME_FLAG_KEY; + curframe->tf.f->pict_type = s->keyframe ? AV_PICTURE_TYPE_I + : AV_PICTURE_TYPE_P; + + // check if golden and altref are swapped + if (s->update_altref != VP8_FRAME_NONE) + s->next_framep[VP8_FRAME_ALTREF] = s->framep[s->update_altref]; + else + s->next_framep[VP8_FRAME_ALTREF] = s->framep[VP8_FRAME_ALTREF]; + + if (s->update_golden != VP8_FRAME_NONE) + s->next_framep[VP8_FRAME_GOLDEN] = s->framep[s->update_golden]; + else + s->next_framep[VP8_FRAME_GOLDEN] = s->framep[VP8_FRAME_GOLDEN]; + + if (s->update_last) + s->next_framep[VP8_FRAME_PREVIOUS] = curframe; + else + s->next_framep[VP8_FRAME_PREVIOUS] = s->framep[VP8_FRAME_PREVIOUS]; + + s->next_framep[VP8_FRAME_CURRENT] = curframe; + + if (!is_vp7 && !s->actually_webp) + ff_thread_finish_setup(avctx); + + if (!is_vp7 && avctx->hwaccel) { + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + ret = hwaccel->start_frame(avctx, avpkt->buf, avpkt->data, avpkt->size); + if (ret < 0) + goto err; + + ret = hwaccel->decode_slice(avctx, avpkt->data, avpkt->size); + if (ret < 0) + goto err; + + ret = hwaccel->end_frame(avctx); + if (ret < 0) + goto err; + + } else { + s->linesize = curframe->tf.f->linesize[0]; + s->uvlinesize = curframe->tf.f->linesize[1]; + + memset(s->top_nnz, 0, s->mb_width * sizeof(*s->top_nnz)); + /* Zero macroblock structures for top/top-left prediction + * from outside the frame. */ + if (!s->mb_layout) + memset(s->macroblocks + s->mb_height * 2 - 1, 0, + (s->mb_width + 1) * sizeof(*s->macroblocks)); + if (!s->mb_layout && s->keyframe) + memset(s->intra4x4_pred_mode_top, DC_PRED, s->mb_width * 4); + + memset(s->ref_count, 0, sizeof(s->ref_count)); + + if (s->mb_layout == 1) { + // Make sure the previous frame has read its segmentation map, + // if we reuse the same map. + if (prev_frame && s->segmentation.enabled && + !s->segmentation.update_map) + ff_progress_frame_await(&prev_frame->tf, 1); + if (is_vp7) + ret = vp7_decode_mv_mb_modes(avctx, curframe, prev_frame); + else + ret = vp8_decode_mv_mb_modes(avctx, curframe, prev_frame); + if (ret < 0) + goto err; + } + + if (avctx->active_thread_type == FF_THREAD_FRAME) + num_jobs = 1; + else + num_jobs = FFMIN(s->num_coeff_partitions, avctx->thread_count); + s->num_jobs = num_jobs; + s->curframe = curframe; + s->prev_frame = prev_frame; + s->mv_bounds.mv_min.y = -MARGIN; + s->mv_bounds.mv_max.y = ((s->mb_height - 1) << 6) + MARGIN; + for (i = 0; i < MAX_THREADS; i++) { + VP8ThreadData *td = &s->thread_data[i]; + atomic_init(&td->thread_mb_pos, 0); + atomic_init(&td->wait_mb_pos, INT_MAX); + } + if (is_vp7) + avctx->execute2(avctx, vp7_decode_mb_row_sliced, s->thread_data, NULL, + num_jobs); + else + avctx->execute2(avctx, vp8_decode_mb_row_sliced, s->thread_data, NULL, + num_jobs); + } + + ff_progress_frame_report(&curframe->tf, INT_MAX); + memcpy(&s->framep[0], &s->next_framep[0], sizeof(s->framep[0]) * 4); + +skip_decode: + // if future frames don't use the updated probabilities, + // reset them to the values we saved + if (!s->update_probabilities) + s->prob[0] = s->prob[1]; + + if (!s->invisible) { + if ((ret = av_frame_ref(rframe, curframe->tf.f)) < 0) + return ret; + *got_frame = 1; + } + + return avpkt->size; +err: + memcpy(&s->next_framep[0], &s->framep[0], sizeof(s->framep[0]) * 4); + return ret; +} + +av_cold int ff_vp8_decode_free(AVCodecContext *avctx) +{ + vp8_decode_flush_impl(avctx, 1); + + return 0; +} + +static av_cold void vp78_decode_init(AVCodecContext *avctx) +{ + VP8Context *s = avctx->priv_data; + + s->avctx = avctx; + s->pix_fmt = AV_PIX_FMT_NONE; + avctx->pix_fmt = AV_PIX_FMT_YUV420P; + + ff_videodsp_init(&s->vdsp, 8); + + ff_vp78dsp_init(&s->vp8dsp); + + /* does not change for VP8 */ + memcpy(s->prob[0].scan, ff_zigzag_scan, sizeof(s->prob[0].scan)); +} + +#if CONFIG_VP8_DECODER +static int vp8_decode_mb_row_no_filter(AVCodecContext *avctx, void *tdata, + int jobnr, int threadnr) +{ + return decode_mb_row_no_filter(avctx, tdata, jobnr, threadnr, 0); +} + +static void vp8_filter_mb_row(AVCodecContext *avctx, void *tdata, + int jobnr, int threadnr) +{ + filter_mb_row(avctx, tdata, jobnr, threadnr, 0); +} + +int ff_vp8_decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *avpkt) +{ + return vp78_decode_frame(avctx, frame, got_frame, avpkt, IS_VP8); +} + +av_cold int ff_vp8_decode_init(AVCodecContext *avctx) +{ + VP8Context *s = avctx->priv_data; + + vp78_decode_init(avctx); + ff_h264_pred_init(&s->hpc, AV_CODEC_ID_VP8, 8, 1); + ff_vp8dsp_init(&s->vp8dsp); + s->decode_mb_row_no_filter = vp8_decode_mb_row_no_filter; + s->filter_mb_row = vp8_filter_mb_row; + + return 0; +} + +#if HAVE_THREADS +static void vp8_replace_frame(VP8Frame *dst, const VP8Frame *src) +{ + ff_progress_frame_replace(&dst->tf, &src->tf); + av_refstruct_replace(&dst->seg_map, src->seg_map); + av_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); +} + +#define REBASE(pic) ((pic) ? (pic) - &s_src->frames[0] + &s->frames[0] : NULL) + +static int vp8_decode_update_thread_context(AVCodecContext *dst, + const AVCodecContext *src) +{ + VP8Context *s = dst->priv_data, *s_src = src->priv_data; + + if (s->macroblocks_base && + (s_src->mb_width != s->mb_width || s_src->mb_height != s->mb_height)) { + free_buffers(s); + s->mb_width = s_src->mb_width; + s->mb_height = s_src->mb_height; + } + + s->pix_fmt = s_src->pix_fmt; + s->prob[0] = s_src->prob[!s_src->update_probabilities]; + s->segmentation = s_src->segmentation; + s->lf_delta = s_src->lf_delta; + memcpy(s->sign_bias, s_src->sign_bias, sizeof(s->sign_bias)); + + for (int i = 0; i < FF_ARRAY_ELEMS(s_src->frames); i++) + vp8_replace_frame(&s->frames[i], &s_src->frames[i]); + + s->framep[0] = REBASE(s_src->next_framep[0]); + s->framep[1] = REBASE(s_src->next_framep[1]); + s->framep[2] = REBASE(s_src->next_framep[2]); + s->framep[3] = REBASE(s_src->next_framep[3]); + + return 0; +} +#endif /* HAVE_THREADS */ +#endif /* CONFIG_VP8_DECODER */ + +#if CONFIG_VP7_DECODER +static int vp7_decode_mb_row_no_filter(AVCodecContext *avctx, void *tdata, + int jobnr, int threadnr) +{ + return decode_mb_row_no_filter(avctx, tdata, jobnr, threadnr, 1); +} + +static void vp7_filter_mb_row(AVCodecContext *avctx, void *tdata, + int jobnr, int threadnr) +{ + filter_mb_row(avctx, tdata, jobnr, threadnr, 1); +} + +static int vp7_decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *avpkt) +{ + return vp78_decode_frame(avctx, frame, got_frame, avpkt, IS_VP7); +} + +av_cold static int vp7_decode_init(AVCodecContext *avctx) +{ + VP8Context *s = avctx->priv_data; + + vp78_decode_init(avctx); + ff_h264_pred_init(&s->hpc, AV_CODEC_ID_VP7, 8, 1); + ff_vp7dsp_init(&s->vp8dsp); + s->decode_mb_row_no_filter = vp7_decode_mb_row_no_filter; + s->filter_mb_row = vp7_filter_mb_row; + + return 0; +} + +const FFCodec ff_vp7_decoder = { + .p.name = "vp7", + CODEC_LONG_NAME("On2 VP7"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP7, + .priv_data_size = sizeof(VP8Context), + .init = vp7_decode_init, + .close = ff_vp8_decode_free, + FF_CODEC_DECODE_CB(vp7_decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1, + .flush = vp8_decode_flush, + .caps_internal = FF_CODEC_CAP_USES_PROGRESSFRAMES, +}; +#endif /* CONFIG_VP7_DECODER */ + +#if CONFIG_VP8_DECODER +const FFCodec ff_vp8_decoder = { + .p.name = "vp8", + CODEC_LONG_NAME("On2 VP8"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP8, + .priv_data_size = sizeof(VP8Context), + .init = ff_vp8_decode_init, + .close = ff_vp8_decode_free, + FF_CODEC_DECODE_CB(ff_vp8_decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | + AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_USES_PROGRESSFRAMES, + .flush = vp8_decode_flush, + UPDATE_THREAD_CONTEXT(vp8_decode_update_thread_context), + .hw_configs = (const AVCodecHWConfigInternal *const []) { +#if CONFIG_VP8_VAAPI_HWACCEL + HWACCEL_VAAPI(vp8), +#endif +#if CONFIG_VP8_NVDEC_HWACCEL + HWACCEL_NVDEC(vp8), +#endif + NULL + }, +}; +#endif /* CONFIG_VP8_DECODER */ diff --git a/thirdparty/ffmpeg/libavcodec/vp9.c b/thirdparty/ffmpeg/libavcodec/vp9.c index 8ea313532..6b5d65aa7 100644 --- a/thirdparty/ffmpeg/libavcodec/vp9.c +++ b/thirdparty/ffmpeg/libavcodec/vp9.c @@ -168,6 +168,7 @@ static int update_size(AVCodecContext *avctx, int w, int h) CONFIG_VP9_D3D11VA_HWACCEL * 2 + \ CONFIG_VP9_D3D12VA_HWACCEL + \ CONFIG_VP9_NVDEC_HWACCEL + \ + CONFIG_VP9_V4L2REQUEST_HWACCEL + \ CONFIG_VP9_VAAPI_HWACCEL + \ CONFIG_VP9_VDPAU_HWACCEL + \ CONFIG_VP9_VIDEOTOOLBOX_HWACCEL + \ @@ -213,6 +214,9 @@ static int update_size(AVCodecContext *avctx, int w, int h) #endif #if CONFIG_VP9_VULKAN_HWACCEL *fmtp++ = AV_PIX_FMT_VULKAN; +#endif +#if CONFIG_VP9_V4L2REQUEST_HWACCEL + *fmtp++ = AV_PIX_FMT_DRM_PRIME; #endif break; case AV_PIX_FMT_YUV420P12: @@ -1958,6 +1962,9 @@ const FFCodec ff_vp9_decoder = { #endif #if CONFIG_VP9_VULKAN_HWACCEL HWACCEL_VULKAN(vp9), +#endif +#if CONFIG_VP9_V4L2REQUEST_HWACCEL + HWACCEL_V4L2REQUEST(vp9), #endif NULL }, diff --git a/thirdparty/ffmpeg/libavcodec/vp9.c.orig b/thirdparty/ffmpeg/libavcodec/vp9.c.orig new file mode 100644 index 000000000..8ea313532 --- /dev/null +++ b/thirdparty/ffmpeg/libavcodec/vp9.c.orig @@ -0,0 +1,1964 @@ +/* + * VP9 compatible video decoder + * + * Copyright (C) 2013 Ronald S. Bultje + * Copyright (C) 2013 Clément BÅ“sch + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config_components.h" + +#include "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "get_bits.h" +#include "hwaccel_internal.h" +#include "hwconfig.h" +#include "profiles.h" +#include "progressframe.h" +#include "libavutil/refstruct.h" +#include "thread.h" +#include "pthread_internal.h" + +#include "videodsp.h" +#include "vp89_rac.h" +#include "vp9.h" +#include "vp9data.h" +#include "vp9dec.h" +#include "vpx_rac.h" +#include "libavutil/attributes.h" +#include "libavutil/avassert.h" +#include "libavutil/mem.h" +#include "libavutil/pixdesc.h" +#include "libavutil/video_enc_params.h" + +#define VP9_SYNCCODE 0x498342 + +#if HAVE_THREADS +DEFINE_OFFSET_ARRAY(VP9Context, vp9_context, pthread_init_cnt, + (offsetof(VP9Context, progress_mutex)), + (offsetof(VP9Context, progress_cond))); + +static int vp9_alloc_entries(AVCodecContext *avctx, int n) { + VP9Context *s = avctx->priv_data; + + if (avctx->active_thread_type & FF_THREAD_SLICE) { + if (s->entries) + av_freep(&s->entries); + + s->entries = av_malloc_array(n, sizeof(atomic_int)); + if (!s->entries) + return AVERROR(ENOMEM); + } + return 0; +} + +static void vp9_report_tile_progress(VP9Context *s, int field, int n) { + pthread_mutex_lock(&s->progress_mutex); + atomic_fetch_add_explicit(&s->entries[field], n, memory_order_release); + pthread_cond_signal(&s->progress_cond); + pthread_mutex_unlock(&s->progress_mutex); +} + +static void vp9_await_tile_progress(VP9Context *s, int field, int n) { + if (atomic_load_explicit(&s->entries[field], memory_order_acquire) >= n) + return; + + pthread_mutex_lock(&s->progress_mutex); + while (atomic_load_explicit(&s->entries[field], memory_order_relaxed) != n) + pthread_cond_wait(&s->progress_cond, &s->progress_mutex); + pthread_mutex_unlock(&s->progress_mutex); +} +#else +static int vp9_alloc_entries(AVCodecContext *avctx, int n) { return 0; } +#endif + +static void vp9_tile_data_free(VP9TileData *td) +{ + av_freep(&td->b_base); + av_freep(&td->block_base); + av_freep(&td->block_structure); +} + +static void vp9_frame_unref(VP9Frame *f) +{ + ff_progress_frame_unref(&f->tf); + av_refstruct_unref(&f->header_ref); + av_refstruct_unref(&f->extradata); + av_refstruct_unref(&f->hwaccel_picture_private); + f->segmentation_map = NULL; +} + +static int vp9_frame_alloc(AVCodecContext *avctx, VP9Frame *f) +{ + VP9Context *s = avctx->priv_data; + int ret, sz; + + ret = ff_progress_frame_get_buffer(avctx, &f->tf, AV_GET_BUFFER_FLAG_REF); + if (ret < 0) + return ret; + + sz = 64 * s->sb_cols * s->sb_rows; + if (sz != s->frame_extradata_pool_size) { + av_refstruct_pool_uninit(&s->frame_extradata_pool); + s->frame_extradata_pool = av_refstruct_pool_alloc(sz * (1 + sizeof(VP9mvrefPair)), + AV_REFSTRUCT_POOL_FLAG_ZERO_EVERY_TIME); + if (!s->frame_extradata_pool) { + s->frame_extradata_pool_size = 0; + ret = AVERROR(ENOMEM); + goto fail; + } + s->frame_extradata_pool_size = sz; + } + f->extradata = av_refstruct_pool_get(s->frame_extradata_pool); + if (!f->extradata) { + ret = AVERROR(ENOMEM); + goto fail; + } + + f->segmentation_map = f->extradata; + f->mv = (VP9mvrefPair *) ((char*)f->extradata + sz); + + ret = ff_hwaccel_frame_priv_alloc(avctx, &f->hwaccel_picture_private); + if (ret < 0) + goto fail; + + return 0; + +fail: + vp9_frame_unref(f); + return ret; +} + +static void vp9_frame_replace(VP9Frame *dst, const VP9Frame *src) +{ + av_refstruct_replace(&dst->header_ref, src->header_ref); + dst->frame_header = src->frame_header; + + ff_progress_frame_replace(&dst->tf, &src->tf); + + av_refstruct_replace(&dst->extradata, src->extradata); + + dst->segmentation_map = src->segmentation_map; + dst->mv = src->mv; + dst->uses_2pass = src->uses_2pass; + + av_refstruct_replace(&dst->hwaccel_picture_private, + src->hwaccel_picture_private); +} + +static int update_size(AVCodecContext *avctx, int w, int h) +{ +#define HWACCEL_MAX (CONFIG_VP9_DXVA2_HWACCEL + \ + CONFIG_VP9_D3D11VA_HWACCEL * 2 + \ + CONFIG_VP9_D3D12VA_HWACCEL + \ + CONFIG_VP9_NVDEC_HWACCEL + \ + CONFIG_VP9_VAAPI_HWACCEL + \ + CONFIG_VP9_VDPAU_HWACCEL + \ + CONFIG_VP9_VIDEOTOOLBOX_HWACCEL + \ + CONFIG_VP9_VULKAN_HWACCEL) + enum AVPixelFormat pix_fmts[HWACCEL_MAX + 2], *fmtp = pix_fmts; + VP9Context *s = avctx->priv_data; + uint8_t *p; + int bytesperpixel = s->bytesperpixel, ret, cols, rows; + int lflvl_len, i; + int changed = 0; + + av_assert0(w > 0 && h > 0); + + if (!(s->pix_fmt == s->gf_fmt && w == s->w && h == s->h)) { + changed = 1; + if ((ret = ff_set_dimensions(avctx, w, h)) < 0) + return ret; + + switch (s->pix_fmt) { + case AV_PIX_FMT_YUV420P: + case AV_PIX_FMT_YUV420P10: +#if CONFIG_VP9_DXVA2_HWACCEL + *fmtp++ = AV_PIX_FMT_DXVA2_VLD; +#endif +#if CONFIG_VP9_D3D11VA_HWACCEL + *fmtp++ = AV_PIX_FMT_D3D11VA_VLD; + *fmtp++ = AV_PIX_FMT_D3D11; +#endif +#if CONFIG_VP9_D3D12VA_HWACCEL + *fmtp++ = AV_PIX_FMT_D3D12; +#endif +#if CONFIG_VP9_NVDEC_HWACCEL + *fmtp++ = AV_PIX_FMT_CUDA; +#endif +#if CONFIG_VP9_VAAPI_HWACCEL + *fmtp++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_VP9_VDPAU_HWACCEL + *fmtp++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_VP9_VIDEOTOOLBOX_HWACCEL + *fmtp++ = AV_PIX_FMT_VIDEOTOOLBOX; +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV420P12: +#if CONFIG_VP9_NVDEC_HWACCEL + *fmtp++ = AV_PIX_FMT_CUDA; +#endif +#if CONFIG_VP9_VAAPI_HWACCEL + *fmtp++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_VP9_VDPAU_HWACCEL + *fmtp++ = AV_PIX_FMT_VDPAU; +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_YUV444P: + case AV_PIX_FMT_YUV444P10: + case AV_PIX_FMT_YUV444P12: +#if CONFIG_VP9_VAAPI_HWACCEL + *fmtp++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + case AV_PIX_FMT_GBRP: + case AV_PIX_FMT_GBRP10: + case AV_PIX_FMT_GBRP12: +#if CONFIG_VP9_VAAPI_HWACCEL + *fmtp++ = AV_PIX_FMT_VAAPI; +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + *fmtp++ = AV_PIX_FMT_VULKAN; +#endif + break; + } + + *fmtp++ = s->pix_fmt; + *fmtp = AV_PIX_FMT_NONE; + + ret = ff_get_format(avctx, pix_fmts); + if (ret < 0) { + ff_set_dimensions(avctx, s->w, s->h); + return ret; + } + + avctx->pix_fmt = ret; + s->gf_fmt = s->pix_fmt; + s->w = w; + s->h = h; + } + + cols = (w + 7) >> 3; + rows = (h + 7) >> 3; + + if (s->intra_pred_data[0] && cols == s->cols && rows == s->rows && s->pix_fmt == s->last_fmt) + return changed; + + s->last_fmt = s->pix_fmt; + s->sb_cols = (w + 63) >> 6; + s->sb_rows = (h + 63) >> 6; + s->cols = (w + 7) >> 3; + s->rows = (h + 7) >> 3; + lflvl_len = avctx->active_thread_type == FF_THREAD_SLICE ? s->sb_rows : 1; + +#define assign(var, type, n) var = (type) p; p += s->sb_cols * (n) * sizeof(*var) + av_freep(&s->intra_pred_data[0]); + // FIXME we slightly over-allocate here for subsampled chroma, but a little + // bit of padding shouldn't affect performance... + p = av_malloc(s->sb_cols * (128 + 192 * bytesperpixel + + lflvl_len * sizeof(*s->lflvl) + 16 * sizeof(*s->above_mv_ctx))); + if (!p) + return AVERROR(ENOMEM); + assign(s->intra_pred_data[0], uint8_t *, 64 * bytesperpixel); + assign(s->intra_pred_data[1], uint8_t *, 64 * bytesperpixel); + assign(s->intra_pred_data[2], uint8_t *, 64 * bytesperpixel); + assign(s->above_y_nnz_ctx, uint8_t *, 16); + assign(s->above_mode_ctx, uint8_t *, 16); + assign(s->above_mv_ctx, VP9mv(*)[2], 16); + assign(s->above_uv_nnz_ctx[0], uint8_t *, 16); + assign(s->above_uv_nnz_ctx[1], uint8_t *, 16); + assign(s->above_partition_ctx, uint8_t *, 8); + assign(s->above_skip_ctx, uint8_t *, 8); + assign(s->above_txfm_ctx, uint8_t *, 8); + assign(s->above_segpred_ctx, uint8_t *, 8); + assign(s->above_intra_ctx, uint8_t *, 8); + assign(s->above_comp_ctx, uint8_t *, 8); + assign(s->above_ref_ctx, uint8_t *, 8); + assign(s->above_filter_ctx, uint8_t *, 8); + assign(s->lflvl, VP9Filter *, lflvl_len); +#undef assign + + if (s->td) { + for (i = 0; i < s->active_tile_cols; i++) + vp9_tile_data_free(&s->td[i]); + } + + if (s->s.h.bpp != s->last_bpp) { + ff_vp9dsp_init(&s->dsp, s->s.h.bpp, avctx->flags & AV_CODEC_FLAG_BITEXACT); + ff_videodsp_init(&s->vdsp, s->s.h.bpp); + s->last_bpp = s->s.h.bpp; + changed = 1; + } + + return changed; +} + +static int update_block_buffers(AVCodecContext *avctx) +{ + int i; + VP9Context *s = avctx->priv_data; + int chroma_blocks, chroma_eobs, bytesperpixel = s->bytesperpixel; + VP9TileData *td = &s->td[0]; + + if (td->b_base && td->block_base && s->block_alloc_using_2pass == s->s.frames[CUR_FRAME].uses_2pass) + return 0; + + vp9_tile_data_free(td); + chroma_blocks = 64 * 64 >> (s->ss_h + s->ss_v); + chroma_eobs = 16 * 16 >> (s->ss_h + s->ss_v); + if (s->s.frames[CUR_FRAME].uses_2pass) { + int sbs = s->sb_cols * s->sb_rows; + + td->b_base = av_malloc_array(s->cols * s->rows, sizeof(VP9Block)); + td->block_base = av_mallocz(((64 * 64 + 2 * chroma_blocks) * bytesperpixel * sizeof(int16_t) + + 16 * 16 + 2 * chroma_eobs) * sbs); + if (!td->b_base || !td->block_base) + return AVERROR(ENOMEM); + td->uvblock_base[0] = td->block_base + sbs * 64 * 64 * bytesperpixel; + td->uvblock_base[1] = td->uvblock_base[0] + sbs * chroma_blocks * bytesperpixel; + td->eob_base = (uint8_t *) (td->uvblock_base[1] + sbs * chroma_blocks * bytesperpixel); + td->uveob_base[0] = td->eob_base + 16 * 16 * sbs; + td->uveob_base[1] = td->uveob_base[0] + chroma_eobs * sbs; + + if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) { + td->block_structure = av_malloc_array(s->cols * s->rows, sizeof(*td->block_structure)); + if (!td->block_structure) + return AVERROR(ENOMEM); + } + } else { + for (i = 1; i < s->active_tile_cols; i++) + vp9_tile_data_free(&s->td[i]); + + for (i = 0; i < s->active_tile_cols; i++) { + s->td[i].b_base = av_malloc(sizeof(VP9Block)); + s->td[i].block_base = av_mallocz((64 * 64 + 2 * chroma_blocks) * bytesperpixel * sizeof(int16_t) + + 16 * 16 + 2 * chroma_eobs); + if (!s->td[i].b_base || !s->td[i].block_base) + return AVERROR(ENOMEM); + s->td[i].uvblock_base[0] = s->td[i].block_base + 64 * 64 * bytesperpixel; + s->td[i].uvblock_base[1] = s->td[i].uvblock_base[0] + chroma_blocks * bytesperpixel; + s->td[i].eob_base = (uint8_t *) (s->td[i].uvblock_base[1] + chroma_blocks * bytesperpixel); + s->td[i].uveob_base[0] = s->td[i].eob_base + 16 * 16; + s->td[i].uveob_base[1] = s->td[i].uveob_base[0] + chroma_eobs; + + if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) { + s->td[i].block_structure = av_malloc_array(s->cols * s->rows, sizeof(*td->block_structure)); + if (!s->td[i].block_structure) + return AVERROR(ENOMEM); + } + } + } + s->block_alloc_using_2pass = s->s.frames[CUR_FRAME].uses_2pass; + + return 0; +} + +// The sign bit is at the end, not the start, of a bit sequence +static av_always_inline int get_sbits_inv(GetBitContext *gb, int n) +{ + int v = get_bits(gb, n); + return get_bits1(gb) ? -v : v; +} + +static av_always_inline int inv_recenter_nonneg(int v, int m) +{ + if (v > 2 * m) + return v; + if (v & 1) + return m - ((v + 1) >> 1); + return m + (v >> 1); +} + +// differential forward probability updates +static int update_prob(VPXRangeCoder *c, int p) +{ + static const uint8_t inv_map_table[255] = { + 7, 20, 33, 46, 59, 72, 85, 98, 111, 124, 137, 150, 163, 176, + 189, 202, 215, 228, 241, 254, 1, 2, 3, 4, 5, 6, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 99, 100, + 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 125, 126, 127, 128, 129, 130, + 131, 132, 133, 134, 135, 136, 138, 139, 140, 141, 142, 143, 144, 145, + 146, 147, 148, 149, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, + 161, 162, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 216, 217, 218, 219, 220, 221, + 222, 223, 224, 225, 226, 227, 229, 230, 231, 232, 233, 234, 235, 236, + 237, 238, 239, 240, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, + 252, 253, 253, + }; + int d; + + /* This code is trying to do a differential probability update. For a + * current probability A in the range [1, 255], the difference to a new + * probability of any value can be expressed differentially as 1-A, 255-A + * where some part of this (absolute range) exists both in positive as + * well as the negative part, whereas another part only exists in one + * half. We're trying to code this shared part differentially, i.e. + * times two where the value of the lowest bit specifies the sign, and + * the single part is then coded on top of this. This absolute difference + * then again has a value of [0, 254], but a bigger value in this range + * indicates that we're further away from the original value A, so we + * can code this as a VLC code, since higher values are increasingly + * unlikely. The first 20 values in inv_map_table[] allow 'cheap, rough' + * updates vs. the 'fine, exact' updates further down the range, which + * adds one extra dimension to this differential update model. */ + + if (!vp89_rac_get(c)) { + d = vp89_rac_get_uint(c, 4) + 0; + } else if (!vp89_rac_get(c)) { + d = vp89_rac_get_uint(c, 4) + 16; + } else if (!vp89_rac_get(c)) { + d = vp89_rac_get_uint(c, 5) + 32; + } else { + d = vp89_rac_get_uint(c, 7); + if (d >= 65) + d = (d << 1) - 65 + vp89_rac_get(c); + d += 64; + av_assert2(d < FF_ARRAY_ELEMS(inv_map_table)); + } + + return p <= 128 ? 1 + inv_recenter_nonneg(inv_map_table[d], p - 1) : + 255 - inv_recenter_nonneg(inv_map_table[d], 255 - p); +} + +static int read_colorspace_details(AVCodecContext *avctx) +{ + static const enum AVColorSpace colorspaces[8] = { + AVCOL_SPC_UNSPECIFIED, AVCOL_SPC_BT470BG, AVCOL_SPC_BT709, AVCOL_SPC_SMPTE170M, + AVCOL_SPC_SMPTE240M, AVCOL_SPC_BT2020_NCL, AVCOL_SPC_RESERVED, AVCOL_SPC_RGB, + }; + VP9Context *s = avctx->priv_data; + int bits = avctx->profile <= 1 ? 0 : 1 + get_bits1(&s->gb); // 0:8, 1:10, 2:12 + + s->bpp_index = bits; + s->s.h.bpp = 8 + bits * 2; + s->bytesperpixel = (7 + s->s.h.bpp) >> 3; + avctx->colorspace = colorspaces[get_bits(&s->gb, 3)]; + if (avctx->colorspace == AVCOL_SPC_RGB) { // RGB = profile 1 + static const enum AVPixelFormat pix_fmt_rgb[3] = { + AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRP12 + }; + s->ss_h = s->ss_v = 0; + avctx->color_range = AVCOL_RANGE_JPEG; + s->pix_fmt = pix_fmt_rgb[bits]; + if (avctx->profile & 1) { + if (get_bits1(&s->gb)) { + av_log(avctx, AV_LOG_ERROR, "Reserved bit set in RGB\n"); + return AVERROR_INVALIDDATA; + } + } else { + av_log(avctx, AV_LOG_ERROR, "RGB not supported in profile %d\n", + avctx->profile); + return AVERROR_INVALIDDATA; + } + } else { + static const enum AVPixelFormat pix_fmt_for_ss[3][2 /* v */][2 /* h */] = { + { { AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUV422P }, + { AV_PIX_FMT_YUV440P, AV_PIX_FMT_YUV420P } }, + { { AV_PIX_FMT_YUV444P10, AV_PIX_FMT_YUV422P10 }, + { AV_PIX_FMT_YUV440P10, AV_PIX_FMT_YUV420P10 } }, + { { AV_PIX_FMT_YUV444P12, AV_PIX_FMT_YUV422P12 }, + { AV_PIX_FMT_YUV440P12, AV_PIX_FMT_YUV420P12 } } + }; + avctx->color_range = get_bits1(&s->gb) ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + if (avctx->profile & 1) { + s->ss_h = get_bits1(&s->gb); + s->ss_v = get_bits1(&s->gb); + s->pix_fmt = pix_fmt_for_ss[bits][s->ss_v][s->ss_h]; + if (s->pix_fmt == AV_PIX_FMT_YUV420P) { + av_log(avctx, AV_LOG_ERROR, "YUV 4:2:0 not supported in profile %d\n", + avctx->profile); + return AVERROR_INVALIDDATA; + } else if (get_bits1(&s->gb)) { + av_log(avctx, AV_LOG_ERROR, "Profile %d color details reserved bit set\n", + avctx->profile); + return AVERROR_INVALIDDATA; + } + } else { + s->ss_h = s->ss_v = 1; + s->pix_fmt = pix_fmt_for_ss[bits][1][1]; + } + } + + return 0; +} + +static int decode_frame_header(AVCodecContext *avctx, + const uint8_t *data, int size, int *ref) +{ + VP9Context *s = avctx->priv_data; + int c, i, j, k, l, m, n, w, h, max, size2, ret, sharp; + int last_invisible; + const uint8_t *data2; + int changed; + + /* general header */ + if ((ret = init_get_bits8(&s->gb, data, size)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize bitstream reader\n"); + return ret; + } + if (get_bits(&s->gb, 2) != 0x2) { // frame marker + av_log(avctx, AV_LOG_ERROR, "Invalid frame marker\n"); + return AVERROR_INVALIDDATA; + } + avctx->profile = get_bits1(&s->gb); + avctx->profile |= get_bits1(&s->gb) << 1; + if (avctx->profile == 3) avctx->profile += get_bits1(&s->gb); + if (avctx->profile > 3) { + av_log(avctx, AV_LOG_ERROR, "Profile %d is not yet supported\n", avctx->profile); + return AVERROR_INVALIDDATA; + } + s->s.h.profile = avctx->profile; + if (get_bits1(&s->gb)) { + *ref = get_bits(&s->gb, 3); + return 0; + } + + s->last_keyframe = s->s.h.keyframe; + s->s.h.keyframe = !get_bits1(&s->gb); + + last_invisible = s->s.h.invisible; + s->s.h.invisible = !get_bits1(&s->gb); + s->s.h.errorres = get_bits1(&s->gb); + s->s.h.use_last_frame_mvs = !s->s.h.errorres && !last_invisible; + + if (s->s.h.keyframe) { + if (get_bits(&s->gb, 24) != VP9_SYNCCODE) { // synccode + av_log(avctx, AV_LOG_ERROR, "Invalid sync code\n"); + return AVERROR_INVALIDDATA; + } + if ((ret = read_colorspace_details(avctx)) < 0) + return ret; + // for profile 1, here follows the subsampling bits + s->s.h.refreshrefmask = 0xff; + w = get_bits(&s->gb, 16) + 1; + h = get_bits(&s->gb, 16) + 1; + if (get_bits1(&s->gb)) // display size + skip_bits(&s->gb, 32); + } else { + s->s.h.intraonly = s->s.h.invisible ? get_bits1(&s->gb) : 0; + s->s.h.resetctx = s->s.h.errorres ? 0 : get_bits(&s->gb, 2); + if (s->s.h.intraonly) { + if (get_bits(&s->gb, 24) != VP9_SYNCCODE) { // synccode + av_log(avctx, AV_LOG_ERROR, "Invalid sync code\n"); + return AVERROR_INVALIDDATA; + } + if (avctx->profile >= 1) { + if ((ret = read_colorspace_details(avctx)) < 0) + return ret; + } else { + s->ss_h = s->ss_v = 1; + s->s.h.bpp = 8; + s->bpp_index = 0; + s->bytesperpixel = 1; + s->pix_fmt = AV_PIX_FMT_YUV420P; + avctx->colorspace = AVCOL_SPC_BT470BG; + avctx->color_range = AVCOL_RANGE_MPEG; + } + s->s.h.refreshrefmask = get_bits(&s->gb, 8); + w = get_bits(&s->gb, 16) + 1; + h = get_bits(&s->gb, 16) + 1; + if (get_bits1(&s->gb)) // display size + skip_bits(&s->gb, 32); + } else { + s->s.h.refreshrefmask = get_bits(&s->gb, 8); + s->s.h.refidx[0] = get_bits(&s->gb, 3); + s->s.h.signbias[0] = get_bits1(&s->gb) && !s->s.h.errorres; + s->s.h.refidx[1] = get_bits(&s->gb, 3); + s->s.h.signbias[1] = get_bits1(&s->gb) && !s->s.h.errorres; + s->s.h.refidx[2] = get_bits(&s->gb, 3); + s->s.h.signbias[2] = get_bits1(&s->gb) && !s->s.h.errorres; + if (!s->s.refs[s->s.h.refidx[0]].f || + !s->s.refs[s->s.h.refidx[1]].f || + !s->s.refs[s->s.h.refidx[2]].f) { + av_log(avctx, AV_LOG_ERROR, "Not all references are available\n"); + return AVERROR_INVALIDDATA; + } + if (get_bits1(&s->gb)) { + w = s->s.refs[s->s.h.refidx[0]].f->width; + h = s->s.refs[s->s.h.refidx[0]].f->height; + } else if (get_bits1(&s->gb)) { + w = s->s.refs[s->s.h.refidx[1]].f->width; + h = s->s.refs[s->s.h.refidx[1]].f->height; + } else if (get_bits1(&s->gb)) { + w = s->s.refs[s->s.h.refidx[2]].f->width; + h = s->s.refs[s->s.h.refidx[2]].f->height; + } else { + w = get_bits(&s->gb, 16) + 1; + h = get_bits(&s->gb, 16) + 1; + } + // Note that in this code, "CUR_FRAME" is actually before we + // have formally allocated a frame, and thus actually represents + // the _last_ frame + s->s.h.use_last_frame_mvs &= s->s.frames[CUR_FRAME].tf.f && + s->s.frames[CUR_FRAME].tf.f->width == w && + s->s.frames[CUR_FRAME].tf.f->height == h; + if (get_bits1(&s->gb)) // display size + skip_bits(&s->gb, 32); + s->s.h.highprecisionmvs = get_bits1(&s->gb); + s->s.h.filtermode = get_bits1(&s->gb) ? FILTER_SWITCHABLE : + get_bits(&s->gb, 2); + s->s.h.allowcompinter = s->s.h.signbias[0] != s->s.h.signbias[1] || + s->s.h.signbias[0] != s->s.h.signbias[2]; + if (s->s.h.allowcompinter) { + if (s->s.h.signbias[0] == s->s.h.signbias[1]) { + s->s.h.fixcompref = 2; + s->s.h.varcompref[0] = 0; + s->s.h.varcompref[1] = 1; + } else if (s->s.h.signbias[0] == s->s.h.signbias[2]) { + s->s.h.fixcompref = 1; + s->s.h.varcompref[0] = 0; + s->s.h.varcompref[1] = 2; + } else { + s->s.h.fixcompref = 0; + s->s.h.varcompref[0] = 1; + s->s.h.varcompref[1] = 2; + } + } + } + } + s->s.h.refreshctx = s->s.h.errorres ? 0 : get_bits1(&s->gb); + s->s.h.parallelmode = s->s.h.errorres ? 1 : get_bits1(&s->gb); + s->s.h.framectxid = c = get_bits(&s->gb, 2); + if (s->s.h.keyframe || s->s.h.intraonly) + s->s.h.framectxid = 0; // BUG: libvpx ignores this field in keyframes + + /* loopfilter header data */ + if (s->s.h.keyframe || s->s.h.errorres || s->s.h.intraonly) { + // reset loopfilter defaults + s->s.h.lf_delta.ref[0] = 1; + s->s.h.lf_delta.ref[1] = 0; + s->s.h.lf_delta.ref[2] = -1; + s->s.h.lf_delta.ref[3] = -1; + s->s.h.lf_delta.mode[0] = 0; + s->s.h.lf_delta.mode[1] = 0; + memset(s->s.h.segmentation.feat, 0, sizeof(s->s.h.segmentation.feat)); + } + s->s.h.filter.level = get_bits(&s->gb, 6); + sharp = get_bits(&s->gb, 3); + // if sharpness changed, reinit lim/mblim LUTs. if it didn't change, keep + // the old cache values since they are still valid + if (s->s.h.filter.sharpness != sharp) { + for (i = 1; i <= 63; i++) { + int limit = i; + + if (sharp > 0) { + limit >>= (sharp + 3) >> 2; + limit = FFMIN(limit, 9 - sharp); + } + limit = FFMAX(limit, 1); + + s->filter_lut.lim_lut[i] = limit; + s->filter_lut.mblim_lut[i] = 2 * (i + 2) + limit; + } + } + s->s.h.filter.sharpness = sharp; + if ((s->s.h.lf_delta.enabled = get_bits1(&s->gb))) { + if ((s->s.h.lf_delta.updated = get_bits1(&s->gb))) { + for (i = 0; i < 4; i++) + if (get_bits1(&s->gb)) + s->s.h.lf_delta.ref[i] = get_sbits_inv(&s->gb, 6); + for (i = 0; i < 2; i++) + if (get_bits1(&s->gb)) + s->s.h.lf_delta.mode[i] = get_sbits_inv(&s->gb, 6); + } + } + + /* quantization header data */ + s->s.h.yac_qi = get_bits(&s->gb, 8); + s->s.h.ydc_qdelta = get_bits1(&s->gb) ? get_sbits_inv(&s->gb, 4) : 0; + s->s.h.uvdc_qdelta = get_bits1(&s->gb) ? get_sbits_inv(&s->gb, 4) : 0; + s->s.h.uvac_qdelta = get_bits1(&s->gb) ? get_sbits_inv(&s->gb, 4) : 0; + s->s.h.lossless = s->s.h.yac_qi == 0 && s->s.h.ydc_qdelta == 0 && + s->s.h.uvdc_qdelta == 0 && s->s.h.uvac_qdelta == 0; + + /* segmentation header info */ + if ((s->s.h.segmentation.enabled = get_bits1(&s->gb))) { + if ((s->s.h.segmentation.update_map = get_bits1(&s->gb))) { + for (i = 0; i < 7; i++) + s->s.h.segmentation.prob[i] = get_bits1(&s->gb) ? + get_bits(&s->gb, 8) : 255; + if ((s->s.h.segmentation.temporal = get_bits1(&s->gb))) + for (i = 0; i < 3; i++) + s->s.h.segmentation.pred_prob[i] = get_bits1(&s->gb) ? + get_bits(&s->gb, 8) : 255; + } + + if (get_bits1(&s->gb)) { + s->s.h.segmentation.absolute_vals = get_bits1(&s->gb); + for (i = 0; i < 8; i++) { + if ((s->s.h.segmentation.feat[i].q_enabled = get_bits1(&s->gb))) + s->s.h.segmentation.feat[i].q_val = get_sbits_inv(&s->gb, 8); + if ((s->s.h.segmentation.feat[i].lf_enabled = get_bits1(&s->gb))) + s->s.h.segmentation.feat[i].lf_val = get_sbits_inv(&s->gb, 6); + if ((s->s.h.segmentation.feat[i].ref_enabled = get_bits1(&s->gb))) + s->s.h.segmentation.feat[i].ref_val = get_bits(&s->gb, 2); + s->s.h.segmentation.feat[i].skip_enabled = get_bits1(&s->gb); + } + } + } else { + // Reset fields under segmentation switch if segmentation is disabled. + // This is necessary because some hwaccels don't ignore these fields + // if segmentation is disabled. + s->s.h.segmentation.temporal = 0; + s->s.h.segmentation.update_map = 0; + } + + // set qmul[] based on Y/UV, AC/DC and segmentation Q idx deltas + for (i = 0; i < (s->s.h.segmentation.enabled ? 8 : 1); i++) { + int qyac, qydc, quvac, quvdc, lflvl, sh; + + if (s->s.h.segmentation.enabled && s->s.h.segmentation.feat[i].q_enabled) { + if (s->s.h.segmentation.absolute_vals) + qyac = av_clip_uintp2(s->s.h.segmentation.feat[i].q_val, 8); + else + qyac = av_clip_uintp2(s->s.h.yac_qi + s->s.h.segmentation.feat[i].q_val, 8); + } else { + qyac = s->s.h.yac_qi; + } + qydc = av_clip_uintp2(qyac + s->s.h.ydc_qdelta, 8); + quvdc = av_clip_uintp2(qyac + s->s.h.uvdc_qdelta, 8); + quvac = av_clip_uintp2(qyac + s->s.h.uvac_qdelta, 8); + qyac = av_clip_uintp2(qyac, 8); + + s->s.h.segmentation.feat[i].qmul[0][0] = ff_vp9_dc_qlookup[s->bpp_index][qydc]; + s->s.h.segmentation.feat[i].qmul[0][1] = ff_vp9_ac_qlookup[s->bpp_index][qyac]; + s->s.h.segmentation.feat[i].qmul[1][0] = ff_vp9_dc_qlookup[s->bpp_index][quvdc]; + s->s.h.segmentation.feat[i].qmul[1][1] = ff_vp9_ac_qlookup[s->bpp_index][quvac]; + + sh = s->s.h.filter.level >= 32; + if (s->s.h.segmentation.enabled && s->s.h.segmentation.feat[i].lf_enabled) { + if (s->s.h.segmentation.absolute_vals) + lflvl = av_clip_uintp2(s->s.h.segmentation.feat[i].lf_val, 6); + else + lflvl = av_clip_uintp2(s->s.h.filter.level + s->s.h.segmentation.feat[i].lf_val, 6); + } else { + lflvl = s->s.h.filter.level; + } + if (s->s.h.lf_delta.enabled) { + s->s.h.segmentation.feat[i].lflvl[0][0] = + s->s.h.segmentation.feat[i].lflvl[0][1] = + av_clip_uintp2(lflvl + (s->s.h.lf_delta.ref[0] * (1 << sh)), 6); + for (j = 1; j < 4; j++) { + s->s.h.segmentation.feat[i].lflvl[j][0] = + av_clip_uintp2(lflvl + ((s->s.h.lf_delta.ref[j] + + s->s.h.lf_delta.mode[0]) * (1 << sh)), 6); + s->s.h.segmentation.feat[i].lflvl[j][1] = + av_clip_uintp2(lflvl + ((s->s.h.lf_delta.ref[j] + + s->s.h.lf_delta.mode[1]) * (1 << sh)), 6); + } + } else { + memset(s->s.h.segmentation.feat[i].lflvl, lflvl, + sizeof(s->s.h.segmentation.feat[i].lflvl)); + } + } + + /* tiling info */ + if ((changed = update_size(avctx, w, h)) < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize decoder for %dx%d @ %d\n", + w, h, s->pix_fmt); + return changed; + } + for (s->s.h.tiling.log2_tile_cols = 0; + s->sb_cols > (64 << s->s.h.tiling.log2_tile_cols); + s->s.h.tiling.log2_tile_cols++) ; + for (max = 0; (s->sb_cols >> max) >= 4; max++) ; + max = FFMAX(0, max - 1); + while (max > s->s.h.tiling.log2_tile_cols) { + if (get_bits1(&s->gb)) + s->s.h.tiling.log2_tile_cols++; + else + break; + } + s->s.h.tiling.log2_tile_rows = decode012(&s->gb); + s->s.h.tiling.tile_rows = 1 << s->s.h.tiling.log2_tile_rows; + if (s->s.h.tiling.tile_cols != (1 << s->s.h.tiling.log2_tile_cols) || changed) { + int n_range_coders; + VPXRangeCoder *rc; + + if (s->td) { + for (i = 0; i < s->active_tile_cols; i++) + vp9_tile_data_free(&s->td[i]); + av_freep(&s->td); + } + + s->s.h.tiling.tile_cols = 1 << s->s.h.tiling.log2_tile_cols; + s->active_tile_cols = avctx->active_thread_type == FF_THREAD_SLICE ? + s->s.h.tiling.tile_cols : 1; + vp9_alloc_entries(avctx, s->sb_rows); + if (avctx->active_thread_type == FF_THREAD_SLICE) { + n_range_coders = 4; // max_tile_rows + } else { + n_range_coders = s->s.h.tiling.tile_cols; + } + s->td = av_calloc(s->active_tile_cols, sizeof(VP9TileData) + + n_range_coders * sizeof(VPXRangeCoder)); + if (!s->td) + return AVERROR(ENOMEM); + rc = (VPXRangeCoder *) &s->td[s->active_tile_cols]; + for (i = 0; i < s->active_tile_cols; i++) { + s->td[i].s = s; + s->td[i].c_b = rc; + rc += n_range_coders; + } + } + + /* check reference frames */ + if (!s->s.h.keyframe && !s->s.h.intraonly) { + int valid_ref_frame = 0; + for (i = 0; i < 3; i++) { + AVFrame *ref = s->s.refs[s->s.h.refidx[i]].f; + int refw = ref->width, refh = ref->height; + + if (ref->format != avctx->pix_fmt) { + av_log(avctx, AV_LOG_ERROR, + "Ref pixfmt (%s) did not match current frame (%s)", + av_get_pix_fmt_name(ref->format), + av_get_pix_fmt_name(avctx->pix_fmt)); + return AVERROR_INVALIDDATA; + } else if (refw == w && refh == h) { + s->mvscale[i][0] = s->mvscale[i][1] = 0; + } else { + /* Check to make sure at least one of frames that */ + /* this frame references has valid dimensions */ + if (w * 2 < refw || h * 2 < refh || w > 16 * refw || h > 16 * refh) { + av_log(avctx, AV_LOG_WARNING, + "Invalid ref frame dimensions %dx%d for frame size %dx%d\n", + refw, refh, w, h); + s->mvscale[i][0] = s->mvscale[i][1] = REF_INVALID_SCALE; + continue; + } + s->mvscale[i][0] = (refw << 14) / w; + s->mvscale[i][1] = (refh << 14) / h; + s->mvstep[i][0] = 16 * s->mvscale[i][0] >> 14; + s->mvstep[i][1] = 16 * s->mvscale[i][1] >> 14; + } + valid_ref_frame++; + } + if (!valid_ref_frame) { + av_log(avctx, AV_LOG_ERROR, "No valid reference frame is found, bitstream not supported\n"); + return AVERROR_INVALIDDATA; + } + } + + if (s->s.h.keyframe || s->s.h.errorres || (s->s.h.intraonly && s->s.h.resetctx == 3)) { + s->prob_ctx[0].p = s->prob_ctx[1].p = s->prob_ctx[2].p = + s->prob_ctx[3].p = ff_vp9_default_probs; + memcpy(s->prob_ctx[0].coef, ff_vp9_default_coef_probs, + sizeof(ff_vp9_default_coef_probs)); + memcpy(s->prob_ctx[1].coef, ff_vp9_default_coef_probs, + sizeof(ff_vp9_default_coef_probs)); + memcpy(s->prob_ctx[2].coef, ff_vp9_default_coef_probs, + sizeof(ff_vp9_default_coef_probs)); + memcpy(s->prob_ctx[3].coef, ff_vp9_default_coef_probs, + sizeof(ff_vp9_default_coef_probs)); + } else if (s->s.h.intraonly && s->s.h.resetctx == 2) { + s->prob_ctx[c].p = ff_vp9_default_probs; + memcpy(s->prob_ctx[c].coef, ff_vp9_default_coef_probs, + sizeof(ff_vp9_default_coef_probs)); + } + + // next 16 bits is size of the rest of the header (arith-coded) + s->s.h.compressed_header_size = size2 = get_bits(&s->gb, 16); + s->s.h.uncompressed_header_size = (get_bits_count(&s->gb) + 7) / 8; + + data2 = align_get_bits(&s->gb); + if (size2 > size - (data2 - data)) { + av_log(avctx, AV_LOG_ERROR, "Invalid compressed header size\n"); + return AVERROR_INVALIDDATA; + } + ret = ff_vpx_init_range_decoder(&s->c, data2, size2); + if (ret < 0) + return ret; + + if (vpx_rac_get_prob_branchy(&s->c, 128)) { // marker bit + av_log(avctx, AV_LOG_ERROR, "Marker bit was set\n"); + return AVERROR_INVALIDDATA; + } + + for (i = 0; i < s->active_tile_cols; i++) { + if (s->s.h.keyframe || s->s.h.intraonly) { + memset(s->td[i].counts.coef, 0, sizeof(s->td[0].counts.coef)); + memset(s->td[i].counts.eob, 0, sizeof(s->td[0].counts.eob)); + } else { + memset(&s->td[i].counts, 0, sizeof(s->td[0].counts)); + } + s->td[i].nb_block_structure = 0; + } + + /* FIXME is it faster to not copy here, but do it down in the fw updates + * as explicit copies if the fw update is missing (and skip the copy upon + * fw update)? */ + s->prob.p = s->prob_ctx[c].p; + + // txfm updates + if (s->s.h.lossless) { + s->s.h.txfmmode = TX_4X4; + } else { + s->s.h.txfmmode = vp89_rac_get_uint(&s->c, 2); + if (s->s.h.txfmmode == 3) + s->s.h.txfmmode += vp89_rac_get(&s->c); + + if (s->s.h.txfmmode == TX_SWITCHABLE) { + for (i = 0; i < 2; i++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.tx8p[i] = update_prob(&s->c, s->prob.p.tx8p[i]); + for (i = 0; i < 2; i++) + for (j = 0; j < 2; j++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.tx16p[i][j] = + update_prob(&s->c, s->prob.p.tx16p[i][j]); + for (i = 0; i < 2; i++) + for (j = 0; j < 3; j++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.tx32p[i][j] = + update_prob(&s->c, s->prob.p.tx32p[i][j]); + } + } + + // coef updates + for (i = 0; i < 4; i++) { + uint8_t (*ref)[2][6][6][3] = s->prob_ctx[c].coef[i]; + if (vp89_rac_get(&s->c)) { + for (j = 0; j < 2; j++) + for (k = 0; k < 2; k++) + for (l = 0; l < 6; l++) + for (m = 0; m < 6; m++) { + uint8_t *p = s->prob.coef[i][j][k][l][m]; + uint8_t *r = ref[j][k][l][m]; + if (m >= 3 && l == 0) // dc only has 3 pt + break; + for (n = 0; n < 3; n++) { + if (vpx_rac_get_prob_branchy(&s->c, 252)) + p[n] = update_prob(&s->c, r[n]); + else + p[n] = r[n]; + } + memcpy(&p[3], ff_vp9_model_pareto8[p[2]], 8); + } + } else { + for (j = 0; j < 2; j++) + for (k = 0; k < 2; k++) + for (l = 0; l < 6; l++) + for (m = 0; m < 6; m++) { + uint8_t *p = s->prob.coef[i][j][k][l][m]; + uint8_t *r = ref[j][k][l][m]; + if (m > 3 && l == 0) // dc only has 3 pt + break; + memcpy(p, r, 3); + memcpy(&p[3], ff_vp9_model_pareto8[p[2]], 8); + } + } + if (s->s.h.txfmmode == i) + break; + } + + // mode updates + for (i = 0; i < 3; i++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.skip[i] = update_prob(&s->c, s->prob.p.skip[i]); + if (!s->s.h.keyframe && !s->s.h.intraonly) { + for (i = 0; i < 7; i++) + for (j = 0; j < 3; j++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_mode[i][j] = + update_prob(&s->c, s->prob.p.mv_mode[i][j]); + + if (s->s.h.filtermode == FILTER_SWITCHABLE) + for (i = 0; i < 4; i++) + for (j = 0; j < 2; j++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.filter[i][j] = + update_prob(&s->c, s->prob.p.filter[i][j]); + + for (i = 0; i < 4; i++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.intra[i] = update_prob(&s->c, s->prob.p.intra[i]); + + if (s->s.h.allowcompinter) { + s->s.h.comppredmode = vp89_rac_get(&s->c); + if (s->s.h.comppredmode) + s->s.h.comppredmode += vp89_rac_get(&s->c); + if (s->s.h.comppredmode == PRED_SWITCHABLE) + for (i = 0; i < 5; i++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.comp[i] = + update_prob(&s->c, s->prob.p.comp[i]); + } else { + s->s.h.comppredmode = PRED_SINGLEREF; + } + + if (s->s.h.comppredmode != PRED_COMPREF) { + for (i = 0; i < 5; i++) { + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.single_ref[i][0] = + update_prob(&s->c, s->prob.p.single_ref[i][0]); + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.single_ref[i][1] = + update_prob(&s->c, s->prob.p.single_ref[i][1]); + } + } + + if (s->s.h.comppredmode != PRED_SINGLEREF) { + for (i = 0; i < 5; i++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.comp_ref[i] = + update_prob(&s->c, s->prob.p.comp_ref[i]); + } + + for (i = 0; i < 4; i++) + for (j = 0; j < 9; j++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.y_mode[i][j] = + update_prob(&s->c, s->prob.p.y_mode[i][j]); + + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + for (k = 0; k < 3; k++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.partition[3 - i][j][k] = + update_prob(&s->c, + s->prob.p.partition[3 - i][j][k]); + + // mv fields don't use the update_prob subexp model for some reason + for (i = 0; i < 3; i++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_joint[i] = (vp89_rac_get_uint(&s->c, 7) << 1) | 1; + + for (i = 0; i < 2; i++) { + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_comp[i].sign = + (vp89_rac_get_uint(&s->c, 7) << 1) | 1; + + for (j = 0; j < 10; j++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_comp[i].classes[j] = + (vp89_rac_get_uint(&s->c, 7) << 1) | 1; + + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_comp[i].class0 = + (vp89_rac_get_uint(&s->c, 7) << 1) | 1; + + for (j = 0; j < 10; j++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_comp[i].bits[j] = + (vp89_rac_get_uint(&s->c, 7) << 1) | 1; + } + + for (i = 0; i < 2; i++) { + for (j = 0; j < 2; j++) + for (k = 0; k < 3; k++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_comp[i].class0_fp[j][k] = + (vp89_rac_get_uint(&s->c, 7) << 1) | 1; + + for (j = 0; j < 3; j++) + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_comp[i].fp[j] = + (vp89_rac_get_uint(&s->c, 7) << 1) | 1; + } + + if (s->s.h.highprecisionmvs) { + for (i = 0; i < 2; i++) { + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_comp[i].class0_hp = + (vp89_rac_get_uint(&s->c, 7) << 1) | 1; + + if (vpx_rac_get_prob_branchy(&s->c, 252)) + s->prob.p.mv_comp[i].hp = + (vp89_rac_get_uint(&s->c, 7) << 1) | 1; + } + } + } + + return (data2 - data) + size2; +} + +static void decode_sb(VP9TileData *td, int row, int col, VP9Filter *lflvl, + ptrdiff_t yoff, ptrdiff_t uvoff, enum BlockLevel bl) +{ + const VP9Context *s = td->s; + int c = ((s->above_partition_ctx[col] >> (3 - bl)) & 1) | + (((td->left_partition_ctx[row & 0x7] >> (3 - bl)) & 1) << 1); + const uint8_t *p = s->s.h.keyframe || s->s.h.intraonly ? ff_vp9_default_kf_partition_probs[bl][c] : + s->prob.p.partition[bl][c]; + enum BlockPartition bp; + ptrdiff_t hbs = 4 >> bl; + AVFrame *f = s->s.frames[CUR_FRAME].tf.f; + ptrdiff_t y_stride = f->linesize[0], uv_stride = f->linesize[1]; + int bytesperpixel = s->bytesperpixel; + + if (bl == BL_8X8) { + bp = vp89_rac_get_tree(td->c, ff_vp9_partition_tree, p); + ff_vp9_decode_block(td, row, col, lflvl, yoff, uvoff, bl, bp); + } else if (col + hbs < s->cols) { // FIXME why not <=? + if (row + hbs < s->rows) { // FIXME why not <=? + bp = vp89_rac_get_tree(td->c, ff_vp9_partition_tree, p); + switch (bp) { + case PARTITION_NONE: + ff_vp9_decode_block(td, row, col, lflvl, yoff, uvoff, bl, bp); + break; + case PARTITION_H: + ff_vp9_decode_block(td, row, col, lflvl, yoff, uvoff, bl, bp); + yoff += hbs * 8 * y_stride; + uvoff += hbs * 8 * uv_stride >> s->ss_v; + ff_vp9_decode_block(td, row + hbs, col, lflvl, yoff, uvoff, bl, bp); + break; + case PARTITION_V: + ff_vp9_decode_block(td, row, col, lflvl, yoff, uvoff, bl, bp); + yoff += hbs * 8 * bytesperpixel; + uvoff += hbs * 8 * bytesperpixel >> s->ss_h; + ff_vp9_decode_block(td, row, col + hbs, lflvl, yoff, uvoff, bl, bp); + break; + case PARTITION_SPLIT: + decode_sb(td, row, col, lflvl, yoff, uvoff, bl + 1); + decode_sb(td, row, col + hbs, lflvl, + yoff + 8 * hbs * bytesperpixel, + uvoff + (8 * hbs * bytesperpixel >> s->ss_h), bl + 1); + yoff += hbs * 8 * y_stride; + uvoff += hbs * 8 * uv_stride >> s->ss_v; + decode_sb(td, row + hbs, col, lflvl, yoff, uvoff, bl + 1); + decode_sb(td, row + hbs, col + hbs, lflvl, + yoff + 8 * hbs * bytesperpixel, + uvoff + (8 * hbs * bytesperpixel >> s->ss_h), bl + 1); + break; + default: + av_unreachable("ff_vp9_partition_tree only has " + "the four PARTITION_* terminal codes"); + } + } else if (vpx_rac_get_prob_branchy(td->c, p[1])) { + bp = PARTITION_SPLIT; + decode_sb(td, row, col, lflvl, yoff, uvoff, bl + 1); + decode_sb(td, row, col + hbs, lflvl, + yoff + 8 * hbs * bytesperpixel, + uvoff + (8 * hbs * bytesperpixel >> s->ss_h), bl + 1); + } else { + bp = PARTITION_H; + ff_vp9_decode_block(td, row, col, lflvl, yoff, uvoff, bl, bp); + } + } else if (row + hbs < s->rows) { // FIXME why not <=? + if (vpx_rac_get_prob_branchy(td->c, p[2])) { + bp = PARTITION_SPLIT; + decode_sb(td, row, col, lflvl, yoff, uvoff, bl + 1); + yoff += hbs * 8 * y_stride; + uvoff += hbs * 8 * uv_stride >> s->ss_v; + decode_sb(td, row + hbs, col, lflvl, yoff, uvoff, bl + 1); + } else { + bp = PARTITION_V; + ff_vp9_decode_block(td, row, col, lflvl, yoff, uvoff, bl, bp); + } + } else { + bp = PARTITION_SPLIT; + decode_sb(td, row, col, lflvl, yoff, uvoff, bl + 1); + } + td->counts.partition[bl][c][bp]++; +} + +static void decode_sb_mem(VP9TileData *td, int row, int col, VP9Filter *lflvl, + ptrdiff_t yoff, ptrdiff_t uvoff, enum BlockLevel bl) +{ + const VP9Context *s = td->s; + VP9Block *b = td->b; + ptrdiff_t hbs = 4 >> bl; + AVFrame *f = s->s.frames[CUR_FRAME].tf.f; + ptrdiff_t y_stride = f->linesize[0], uv_stride = f->linesize[1]; + int bytesperpixel = s->bytesperpixel; + + if (bl == BL_8X8) { + av_assert2(b->bl == BL_8X8); + ff_vp9_decode_block(td, row, col, lflvl, yoff, uvoff, b->bl, b->bp); + } else if (td->b->bl == bl) { + ff_vp9_decode_block(td, row, col, lflvl, yoff, uvoff, b->bl, b->bp); + if (b->bp == PARTITION_H && row + hbs < s->rows) { + yoff += hbs * 8 * y_stride; + uvoff += hbs * 8 * uv_stride >> s->ss_v; + ff_vp9_decode_block(td, row + hbs, col, lflvl, yoff, uvoff, b->bl, b->bp); + } else if (b->bp == PARTITION_V && col + hbs < s->cols) { + yoff += hbs * 8 * bytesperpixel; + uvoff += hbs * 8 * bytesperpixel >> s->ss_h; + ff_vp9_decode_block(td, row, col + hbs, lflvl, yoff, uvoff, b->bl, b->bp); + } + } else { + decode_sb_mem(td, row, col, lflvl, yoff, uvoff, bl + 1); + if (col + hbs < s->cols) { // FIXME why not <=? + if (row + hbs < s->rows) { + decode_sb_mem(td, row, col + hbs, lflvl, yoff + 8 * hbs * bytesperpixel, + uvoff + (8 * hbs * bytesperpixel >> s->ss_h), bl + 1); + yoff += hbs * 8 * y_stride; + uvoff += hbs * 8 * uv_stride >> s->ss_v; + decode_sb_mem(td, row + hbs, col, lflvl, yoff, uvoff, bl + 1); + decode_sb_mem(td, row + hbs, col + hbs, lflvl, + yoff + 8 * hbs * bytesperpixel, + uvoff + (8 * hbs * bytesperpixel >> s->ss_h), bl + 1); + } else { + yoff += hbs * 8 * bytesperpixel; + uvoff += hbs * 8 * bytesperpixel >> s->ss_h; + decode_sb_mem(td, row, col + hbs, lflvl, yoff, uvoff, bl + 1); + } + } else if (row + hbs < s->rows) { + yoff += hbs * 8 * y_stride; + uvoff += hbs * 8 * uv_stride >> s->ss_v; + decode_sb_mem(td, row + hbs, col, lflvl, yoff, uvoff, bl + 1); + } + } +} + +static void set_tile_offset(int *start, int *end, int idx, int log2_n, int n) +{ + int sb_start = ( idx * n) >> log2_n; + int sb_end = ((idx + 1) * n) >> log2_n; + *start = FFMIN(sb_start, n) << 3; + *end = FFMIN(sb_end, n) << 3; +} + +static void free_buffers(VP9Context *s) +{ + int i; + + av_freep(&s->intra_pred_data[0]); + for (i = 0; i < s->active_tile_cols; i++) + vp9_tile_data_free(&s->td[i]); +} + +static av_cold int vp9_decode_free(AVCodecContext *avctx) +{ + VP9Context *s = avctx->priv_data; + int i; + + for (int i = 0; i < 3; i++) + vp9_frame_unref(&s->s.frames[i]); + av_refstruct_pool_uninit(&s->frame_extradata_pool); + for (i = 0; i < 8; i++) { + ff_progress_frame_unref(&s->s.refs[i]); + ff_progress_frame_unref(&s->next_refs[i]); + vp9_frame_unref(&s->s.ref_frames[i]); + } + + free_buffers(s); +#if HAVE_THREADS + av_freep(&s->entries); + ff_pthread_free(s, vp9_context_offsets); +#endif + + av_refstruct_unref(&s->header_ref); + ff_cbs_fragment_free(&s->current_frag); + ff_cbs_close(&s->cbc); + + av_freep(&s->td); + return 0; +} + +static int decode_tiles(AVCodecContext *avctx, + const uint8_t *data, int size) +{ + VP9Context *s = avctx->priv_data; + VP9TileData *td = &s->td[0]; + int row, col, tile_row, tile_col, ret; + int bytesperpixel; + int tile_row_start, tile_row_end, tile_col_start, tile_col_end; + AVFrame *f; + ptrdiff_t yoff, uvoff, ls_y, ls_uv; + + f = s->s.frames[CUR_FRAME].tf.f; + ls_y = f->linesize[0]; + ls_uv =f->linesize[1]; + bytesperpixel = s->bytesperpixel; + + yoff = uvoff = 0; + for (tile_row = 0; tile_row < s->s.h.tiling.tile_rows; tile_row++) { + set_tile_offset(&tile_row_start, &tile_row_end, + tile_row, s->s.h.tiling.log2_tile_rows, s->sb_rows); + + for (tile_col = 0; tile_col < s->s.h.tiling.tile_cols; tile_col++) { + int64_t tile_size; + + if (tile_col == s->s.h.tiling.tile_cols - 1 && + tile_row == s->s.h.tiling.tile_rows - 1) { + tile_size = size; + } else { + tile_size = AV_RB32(data); + data += 4; + size -= 4; + } + if (tile_size > size) + return AVERROR_INVALIDDATA; + ret = ff_vpx_init_range_decoder(&td->c_b[tile_col], data, tile_size); + if (ret < 0) + return ret; + if (vpx_rac_get_prob_branchy(&td->c_b[tile_col], 128)) // marker bit + return AVERROR_INVALIDDATA; + data += tile_size; + size -= tile_size; + } + + for (row = tile_row_start; row < tile_row_end; + row += 8, yoff += ls_y * 64, uvoff += ls_uv * 64 >> s->ss_v) { + VP9Filter *lflvl_ptr = s->lflvl; + ptrdiff_t yoff2 = yoff, uvoff2 = uvoff; + + for (tile_col = 0; tile_col < s->s.h.tiling.tile_cols; tile_col++) { + set_tile_offset(&tile_col_start, &tile_col_end, + tile_col, s->s.h.tiling.log2_tile_cols, s->sb_cols); + td->tile_col_start = tile_col_start; + if (s->pass != 2) { + memset(td->left_partition_ctx, 0, 8); + memset(td->left_skip_ctx, 0, 8); + if (s->s.h.keyframe || s->s.h.intraonly) { + memset(td->left_mode_ctx, DC_PRED, 16); + } else { + memset(td->left_mode_ctx, NEARESTMV, 8); + } + memset(td->left_y_nnz_ctx, 0, 16); + memset(td->left_uv_nnz_ctx, 0, 32); + memset(td->left_segpred_ctx, 0, 8); + + td->c = &td->c_b[tile_col]; + } + + for (col = tile_col_start; + col < tile_col_end; + col += 8, yoff2 += 64 * bytesperpixel, + uvoff2 += 64 * bytesperpixel >> s->ss_h, lflvl_ptr++) { + // FIXME integrate with lf code (i.e. zero after each + // use, similar to invtxfm coefficients, or similar) + if (s->pass != 1) { + memset(lflvl_ptr->mask, 0, sizeof(lflvl_ptr->mask)); + } + + if (s->pass == 2) { + decode_sb_mem(td, row, col, lflvl_ptr, + yoff2, uvoff2, BL_64X64); + } else { + if (vpx_rac_is_end(td->c)) { + return AVERROR_INVALIDDATA; + } + decode_sb(td, row, col, lflvl_ptr, + yoff2, uvoff2, BL_64X64); + } + } + } + + if (s->pass == 1) + continue; + + // backup pre-loopfilter reconstruction data for intra + // prediction of next row of sb64s + if (row + 8 < s->rows) { + memcpy(s->intra_pred_data[0], + f->data[0] + yoff + 63 * ls_y, + 8 * s->cols * bytesperpixel); + memcpy(s->intra_pred_data[1], + f->data[1] + uvoff + ((64 >> s->ss_v) - 1) * ls_uv, + 8 * s->cols * bytesperpixel >> s->ss_h); + memcpy(s->intra_pred_data[2], + f->data[2] + uvoff + ((64 >> s->ss_v) - 1) * ls_uv, + 8 * s->cols * bytesperpixel >> s->ss_h); + } + + // loopfilter one row + if (s->s.h.filter.level) { + yoff2 = yoff; + uvoff2 = uvoff; + lflvl_ptr = s->lflvl; + for (col = 0; col < s->cols; + col += 8, yoff2 += 64 * bytesperpixel, + uvoff2 += 64 * bytesperpixel >> s->ss_h, lflvl_ptr++) { + ff_vp9_loopfilter_sb(avctx, lflvl_ptr, row, col, + yoff2, uvoff2); + } + } + + // FIXME maybe we can make this more finegrained by running the + // loopfilter per-block instead of after each sbrow + // In fact that would also make intra pred left preparation easier? + ff_progress_frame_report(&s->s.frames[CUR_FRAME].tf, row >> 3); + } + } + return 0; +} + +#if HAVE_THREADS +static av_always_inline +int decode_tiles_mt(AVCodecContext *avctx, void *tdata, int jobnr, + int threadnr) +{ + VP9Context *s = avctx->priv_data; + VP9TileData *td = &s->td[jobnr]; + ptrdiff_t uvoff, yoff, ls_y, ls_uv; + int bytesperpixel = s->bytesperpixel, row, col, tile_row; + unsigned tile_cols_len; + int tile_row_start, tile_row_end, tile_col_start, tile_col_end; + VP9Filter *lflvl_ptr_base; + AVFrame *f; + + f = s->s.frames[CUR_FRAME].tf.f; + ls_y = f->linesize[0]; + ls_uv =f->linesize[1]; + + set_tile_offset(&tile_col_start, &tile_col_end, + jobnr, s->s.h.tiling.log2_tile_cols, s->sb_cols); + td->tile_col_start = tile_col_start; + uvoff = (64 * bytesperpixel >> s->ss_h)*(tile_col_start >> 3); + yoff = (64 * bytesperpixel)*(tile_col_start >> 3); + lflvl_ptr_base = s->lflvl+(tile_col_start >> 3); + + for (tile_row = 0; tile_row < s->s.h.tiling.tile_rows; tile_row++) { + set_tile_offset(&tile_row_start, &tile_row_end, + tile_row, s->s.h.tiling.log2_tile_rows, s->sb_rows); + + td->c = &td->c_b[tile_row]; + for (row = tile_row_start; row < tile_row_end; + row += 8, yoff += ls_y * 64, uvoff += ls_uv * 64 >> s->ss_v) { + ptrdiff_t yoff2 = yoff, uvoff2 = uvoff; + VP9Filter *lflvl_ptr = lflvl_ptr_base+s->sb_cols*(row >> 3); + + memset(td->left_partition_ctx, 0, 8); + memset(td->left_skip_ctx, 0, 8); + if (s->s.h.keyframe || s->s.h.intraonly) { + memset(td->left_mode_ctx, DC_PRED, 16); + } else { + memset(td->left_mode_ctx, NEARESTMV, 8); + } + memset(td->left_y_nnz_ctx, 0, 16); + memset(td->left_uv_nnz_ctx, 0, 32); + memset(td->left_segpred_ctx, 0, 8); + + for (col = tile_col_start; + col < tile_col_end; + col += 8, yoff2 += 64 * bytesperpixel, + uvoff2 += 64 * bytesperpixel >> s->ss_h, lflvl_ptr++) { + // FIXME integrate with lf code (i.e. zero after each + // use, similar to invtxfm coefficients, or similar) + memset(lflvl_ptr->mask, 0, sizeof(lflvl_ptr->mask)); + decode_sb(td, row, col, lflvl_ptr, + yoff2, uvoff2, BL_64X64); + } + + // backup pre-loopfilter reconstruction data for intra + // prediction of next row of sb64s + tile_cols_len = tile_col_end - tile_col_start; + if (row + 8 < s->rows) { + memcpy(s->intra_pred_data[0] + (tile_col_start * 8 * bytesperpixel), + f->data[0] + yoff + 63 * ls_y, + 8 * tile_cols_len * bytesperpixel); + memcpy(s->intra_pred_data[1] + (tile_col_start * 8 * bytesperpixel >> s->ss_h), + f->data[1] + uvoff + ((64 >> s->ss_v) - 1) * ls_uv, + 8 * tile_cols_len * bytesperpixel >> s->ss_h); + memcpy(s->intra_pred_data[2] + (tile_col_start * 8 * bytesperpixel >> s->ss_h), + f->data[2] + uvoff + ((64 >> s->ss_v) - 1) * ls_uv, + 8 * tile_cols_len * bytesperpixel >> s->ss_h); + } + + vp9_report_tile_progress(s, row >> 3, 1); + } + } + return 0; +} + +static av_always_inline +int loopfilter_proc(AVCodecContext *avctx) +{ + VP9Context *s = avctx->priv_data; + ptrdiff_t uvoff, yoff, ls_y, ls_uv; + VP9Filter *lflvl_ptr; + int bytesperpixel = s->bytesperpixel, col, i; + AVFrame *f; + + f = s->s.frames[CUR_FRAME].tf.f; + ls_y = f->linesize[0]; + ls_uv =f->linesize[1]; + + for (i = 0; i < s->sb_rows; i++) { + vp9_await_tile_progress(s, i, s->s.h.tiling.tile_cols); + + if (s->s.h.filter.level) { + yoff = (ls_y * 64)*i; + uvoff = (ls_uv * 64 >> s->ss_v)*i; + lflvl_ptr = s->lflvl+s->sb_cols*i; + for (col = 0; col < s->cols; + col += 8, yoff += 64 * bytesperpixel, + uvoff += 64 * bytesperpixel >> s->ss_h, lflvl_ptr++) { + ff_vp9_loopfilter_sb(avctx, lflvl_ptr, i << 3, col, + yoff, uvoff); + } + } + } + return 0; +} +#endif + +static int vp9_export_enc_params(VP9Context *s, VP9Frame *frame) +{ + AVVideoEncParams *par; + unsigned int tile, nb_blocks = 0; + + if (s->s.h.segmentation.enabled) { + for (tile = 0; tile < s->active_tile_cols; tile++) + nb_blocks += s->td[tile].nb_block_structure; + } + + par = av_video_enc_params_create_side_data(frame->tf.f, + AV_VIDEO_ENC_PARAMS_VP9, nb_blocks); + if (!par) + return AVERROR(ENOMEM); + + par->qp = s->s.h.yac_qi; + par->delta_qp[0][0] = s->s.h.ydc_qdelta; + par->delta_qp[1][0] = s->s.h.uvdc_qdelta; + par->delta_qp[2][0] = s->s.h.uvdc_qdelta; + par->delta_qp[1][1] = s->s.h.uvac_qdelta; + par->delta_qp[2][1] = s->s.h.uvac_qdelta; + + if (nb_blocks) { + unsigned int block = 0; + unsigned int tile, block_tile; + + for (tile = 0; tile < s->active_tile_cols; tile++) { + VP9TileData *td = &s->td[tile]; + + for (block_tile = 0; block_tile < td->nb_block_structure; block_tile++) { + AVVideoBlockParams *b = av_video_enc_params_block(par, block++); + unsigned int row = td->block_structure[block_tile].row; + unsigned int col = td->block_structure[block_tile].col; + uint8_t seg_id = frame->segmentation_map[row * 8 * s->sb_cols + col]; + + b->src_x = col * 8; + b->src_y = row * 8; + b->w = 1 << (3 + td->block_structure[block_tile].block_size_idx_x); + b->h = 1 << (3 + td->block_structure[block_tile].block_size_idx_y); + + if (s->s.h.segmentation.feat[seg_id].q_enabled) { + b->delta_qp = s->s.h.segmentation.feat[seg_id].q_val; + if (s->s.h.segmentation.absolute_vals) + b->delta_qp -= par->qp; + } + } + } + } + + return 0; +} + +static int vp9_decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *pkt) +{ + const uint8_t *data = pkt->data; + int size = pkt->size; + VP9Context *s = avctx->priv_data; + int ret, i, j, ref; + CodedBitstreamUnit *unit; + VP9RawFrame *rf; + + int retain_segmap_ref = s->s.frames[REF_FRAME_SEGMAP].segmentation_map && + (!s->s.h.segmentation.enabled || !s->s.h.segmentation.update_map); + const VP9Frame *src; + AVFrame *f; + + ret = ff_cbs_read_packet(s->cbc, &s->current_frag, pkt); + if (ret < 0) { + ff_cbs_fragment_reset(&s->current_frag); + av_log(avctx, AV_LOG_ERROR, "Failed to read frame header.\n"); + return ret; + } + + unit = &s->current_frag.units[0]; + rf = unit->content; + + av_refstruct_replace(&s->header_ref, unit->content_ref); + s->frame_header = &rf->header; + + if ((ret = decode_frame_header(avctx, data, size, &ref)) < 0) { + ff_cbs_fragment_reset(&s->current_frag); + return ret; + } else if (ret == 0) { + if (!s->s.refs[ref].f) { + av_log(avctx, AV_LOG_ERROR, "Requested reference %d not available\n", ref); + ff_cbs_fragment_reset(&s->current_frag); + return AVERROR_INVALIDDATA; + } + for (int i = 0; i < 8; i++) + ff_progress_frame_replace(&s->next_refs[i], &s->s.refs[i]); + ff_thread_finish_setup(avctx); + ff_progress_frame_await(&s->s.refs[ref], INT_MAX); + ff_cbs_fragment_reset(&s->current_frag); + + if ((ret = av_frame_ref(frame, s->s.refs[ref].f)) < 0) + return ret; + frame->pts = pkt->pts; + frame->pkt_dts = pkt->dts; + *got_frame = 1; + return pkt->size; + } + data += ret; + size -= ret; + + src = !s->s.h.keyframe && !s->s.h.intraonly && !s->s.h.errorres ? + &s->s.frames[CUR_FRAME] : &s->s.frames[BLANK_FRAME]; + if (!retain_segmap_ref || s->s.h.keyframe || s->s.h.intraonly) + vp9_frame_replace(&s->s.frames[REF_FRAME_SEGMAP], src); + vp9_frame_replace(&s->s.frames[REF_FRAME_MVPAIR], src); + vp9_frame_unref(&s->s.frames[CUR_FRAME]); + if ((ret = vp9_frame_alloc(avctx, &s->s.frames[CUR_FRAME])) < 0) { + ff_cbs_fragment_reset(&s->current_frag); + return ret; + } + + s->s.frames[CUR_FRAME].header_ref = av_refstruct_ref(s->header_ref); + s->s.frames[CUR_FRAME].frame_header = s->frame_header; + + f = s->s.frames[CUR_FRAME].tf.f; + if (s->s.h.keyframe) + f->flags |= AV_FRAME_FLAG_KEY; + else + f->flags &= ~AV_FRAME_FLAG_KEY; + if (s->s.h.lossless) + f->flags |= AV_FRAME_FLAG_LOSSLESS; + else + f->flags &= ~AV_FRAME_FLAG_LOSSLESS; + f->pict_type = (s->s.h.keyframe || s->s.h.intraonly) ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P; + + // Non-existent frames have the implicit dimension 0x0 != CUR_FRAME + if (!s->s.frames[REF_FRAME_MVPAIR].tf.f || + (s->s.frames[REF_FRAME_MVPAIR].tf.f->width != s->s.frames[CUR_FRAME].tf.f->width || + s->s.frames[REF_FRAME_MVPAIR].tf.f->height != s->s.frames[CUR_FRAME].tf.f->height)) { + vp9_frame_unref(&s->s.frames[REF_FRAME_SEGMAP]); + } + + // ref frame setup + for (i = 0; i < 8; i++) { + ff_progress_frame_replace(&s->next_refs[i], + s->s.h.refreshrefmask & (1 << i) ? + &s->s.frames[CUR_FRAME].tf : &s->s.refs[i]); + } + + if (avctx->hwaccel) { + const FFHWAccel *hwaccel = ffhwaccel(avctx->hwaccel); + ret = hwaccel->start_frame(avctx, pkt->buf, pkt->data, pkt->size); + if (ret < 0) + return ret; + ret = hwaccel->decode_slice(avctx, pkt->data, pkt->size); + if (ret < 0) + return ret; + ret = hwaccel->end_frame(avctx); + if (ret < 0) + return ret; + + for (i = 0; i < 8; i++) { + vp9_frame_replace(&s->s.ref_frames[i], + s->s.h.refreshrefmask & (1 << i) ? + &s->s.frames[CUR_FRAME] : &s->s.ref_frames[i]); + } + + goto finish; + } + + // main tile decode loop + memset(s->above_partition_ctx, 0, s->cols); + memset(s->above_skip_ctx, 0, s->cols); + if (s->s.h.keyframe || s->s.h.intraonly) { + memset(s->above_mode_ctx, DC_PRED, s->cols * 2); + } else { + memset(s->above_mode_ctx, NEARESTMV, s->cols); + } + memset(s->above_y_nnz_ctx, 0, s->sb_cols * 16); + memset(s->above_uv_nnz_ctx[0], 0, s->sb_cols * 16 >> s->ss_h); + memset(s->above_uv_nnz_ctx[1], 0, s->sb_cols * 16 >> s->ss_h); + memset(s->above_segpred_ctx, 0, s->cols); + s->pass = s->s.frames[CUR_FRAME].uses_2pass = + avctx->active_thread_type == FF_THREAD_FRAME && s->s.h.refreshctx && !s->s.h.parallelmode; + if ((ret = update_block_buffers(avctx)) < 0) { + av_log(avctx, AV_LOG_ERROR, + "Failed to allocate block buffers\n"); + return ret; + } + if (s->s.h.refreshctx && s->s.h.parallelmode) { + int j, k, l, m; + + for (i = 0; i < 4; i++) { + for (j = 0; j < 2; j++) + for (k = 0; k < 2; k++) + for (l = 0; l < 6; l++) + for (m = 0; m < 6; m++) + memcpy(s->prob_ctx[s->s.h.framectxid].coef[i][j][k][l][m], + s->prob.coef[i][j][k][l][m], 3); + if (s->s.h.txfmmode == i) + break; + } + s->prob_ctx[s->s.h.framectxid].p = s->prob.p; + ff_thread_finish_setup(avctx); + } else if (!s->s.h.refreshctx) { + ff_thread_finish_setup(avctx); + } + +#if HAVE_THREADS + if (avctx->active_thread_type & FF_THREAD_SLICE) { + for (i = 0; i < s->sb_rows; i++) + atomic_init(&s->entries[i], 0); + } +#endif + + do { + for (i = 0; i < s->active_tile_cols; i++) { + s->td[i].b = s->td[i].b_base; + s->td[i].block = s->td[i].block_base; + s->td[i].uvblock[0] = s->td[i].uvblock_base[0]; + s->td[i].uvblock[1] = s->td[i].uvblock_base[1]; + s->td[i].eob = s->td[i].eob_base; + s->td[i].uveob[0] = s->td[i].uveob_base[0]; + s->td[i].uveob[1] = s->td[i].uveob_base[1]; + s->td[i].error_info = 0; + } + +#if HAVE_THREADS + if (avctx->active_thread_type == FF_THREAD_SLICE) { + int tile_row, tile_col; + + av_assert1(!s->pass); + + for (tile_row = 0; tile_row < s->s.h.tiling.tile_rows; tile_row++) { + for (tile_col = 0; tile_col < s->s.h.tiling.tile_cols; tile_col++) { + int64_t tile_size; + + if (tile_col == s->s.h.tiling.tile_cols - 1 && + tile_row == s->s.h.tiling.tile_rows - 1) { + tile_size = size; + } else { + tile_size = AV_RB32(data); + data += 4; + size -= 4; + } + if (tile_size > size) + return AVERROR_INVALIDDATA; + ret = ff_vpx_init_range_decoder(&s->td[tile_col].c_b[tile_row], data, tile_size); + if (ret < 0) + return ret; + if (vpx_rac_get_prob_branchy(&s->td[tile_col].c_b[tile_row], 128)) // marker bit + return AVERROR_INVALIDDATA; + data += tile_size; + size -= tile_size; + } + } + + ff_slice_thread_execute_with_mainfunc(avctx, decode_tiles_mt, loopfilter_proc, s->td, NULL, s->s.h.tiling.tile_cols); + } else +#endif + { + ret = decode_tiles(avctx, data, size); + if (ret < 0) + goto fail; + } + + // Sum all counts fields into td[0].counts for tile threading + if (avctx->active_thread_type == FF_THREAD_SLICE) + for (i = 1; i < s->s.h.tiling.tile_cols; i++) + for (j = 0; j < sizeof(s->td[i].counts) / sizeof(unsigned); j++) + ((unsigned *)&s->td[0].counts)[j] += ((unsigned *)&s->td[i].counts)[j]; + + if (s->pass < 2 && s->s.h.refreshctx && !s->s.h.parallelmode) { + ff_vp9_adapt_probs(s); + ff_thread_finish_setup(avctx); + } + } while (s->pass++ == 1); + + if (s->td->error_info < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to decode tile data\n"); + s->td->error_info = 0; + ret = AVERROR_INVALIDDATA; + goto fail; + } + if (avctx->export_side_data & AV_CODEC_EXPORT_DATA_VIDEO_ENC_PARAMS) { + ret = vp9_export_enc_params(s, &s->s.frames[CUR_FRAME]); + if (ret < 0) + goto fail; + } + +finish: + ff_cbs_fragment_reset(&s->current_frag); + + ff_progress_frame_report(&s->s.frames[CUR_FRAME].tf, INT_MAX); + // ref frame setup + for (int i = 0; i < 8; i++) + ff_progress_frame_replace(&s->s.refs[i], &s->next_refs[i]); + + if (!s->s.h.invisible) { + if ((ret = av_frame_ref(frame, s->s.frames[CUR_FRAME].tf.f)) < 0) + return ret; + *got_frame = 1; + } + + return pkt->size; +fail: + ff_cbs_fragment_reset(&s->current_frag); + ff_progress_frame_report(&s->s.frames[CUR_FRAME].tf, INT_MAX); + return ret; +} + +static av_cold void vp9_decode_flush(AVCodecContext *avctx) +{ + VP9Context *s = avctx->priv_data; + int i; + + for (i = 0; i < 3; i++) + vp9_frame_unref(&s->s.frames[i]); + + for (i = 0; i < 8; i++) { + ff_progress_frame_unref(&s->s.refs[i]); + vp9_frame_unref(&s->s.ref_frames[i]); + } + + ff_cbs_fragment_reset(&s->current_frag); + ff_cbs_flush(s->cbc); + + if (FF_HW_HAS_CB(avctx, flush)) + FF_HW_SIMPLE_CALL(avctx, flush); +} + +static av_cold int vp9_decode_init(AVCodecContext *avctx) +{ + VP9Context *s = avctx->priv_data; + int ret; + + s->last_bpp = 0; + s->s.h.filter.sharpness = -1; + + ret = ff_cbs_init(&s->cbc, AV_CODEC_ID_VP9, avctx); + if (ret < 0) + return ret; + +#if HAVE_THREADS + if (avctx->active_thread_type & FF_THREAD_SLICE) { + ret = ff_pthread_init(s, vp9_context_offsets); + if (ret < 0) + return ret; + } +#endif + + return 0; +} + +#if HAVE_THREADS +static int vp9_decode_update_thread_context(AVCodecContext *dst, const AVCodecContext *src) +{ + VP9Context *s = dst->priv_data, *ssrc = src->priv_data; + + for (int i = 0; i < 3; i++) + vp9_frame_replace(&s->s.frames[i], &ssrc->s.frames[i]); + for (int i = 0; i < 8; i++) + ff_progress_frame_replace(&s->s.refs[i], &ssrc->next_refs[i]); + av_refstruct_replace(&s->frame_extradata_pool, ssrc->frame_extradata_pool); + s->frame_extradata_pool_size = ssrc->frame_extradata_pool_size; + + av_refstruct_replace(&s->header_ref, ssrc->header_ref); + for (int i = 0; i < 8; i++) + vp9_frame_replace(&s->s.ref_frames[i], &ssrc->s.ref_frames[i]); + + s->frame_header = ssrc->frame_header; + memcpy(s->cbc->priv_data, ssrc->cbc->priv_data, sizeof(CodedBitstreamVP9Context)); + + s->s.h.invisible = ssrc->s.h.invisible; + s->s.h.keyframe = ssrc->s.h.keyframe; + s->s.h.intraonly = ssrc->s.h.intraonly; + s->ss_v = ssrc->ss_v; + s->ss_h = ssrc->ss_h; + s->s.h.segmentation.enabled = ssrc->s.h.segmentation.enabled; + s->s.h.segmentation.update_map = ssrc->s.h.segmentation.update_map; + s->s.h.segmentation.absolute_vals = ssrc->s.h.segmentation.absolute_vals; + s->bytesperpixel = ssrc->bytesperpixel; + s->gf_fmt = ssrc->gf_fmt; + s->w = ssrc->w; + s->h = ssrc->h; + s->s.h.bpp = ssrc->s.h.bpp; + s->bpp_index = ssrc->bpp_index; + s->pix_fmt = ssrc->pix_fmt; + memcpy(&s->prob_ctx, &ssrc->prob_ctx, sizeof(s->prob_ctx)); + memcpy(&s->s.h.lf_delta, &ssrc->s.h.lf_delta, sizeof(s->s.h.lf_delta)); + memcpy(&s->s.h.segmentation.feat, &ssrc->s.h.segmentation.feat, + sizeof(s->s.h.segmentation.feat)); + + return 0; +} +#endif + +const FFCodec ff_vp9_decoder = { + .p.name = "vp9", + CODEC_LONG_NAME("Google VP9"), + .p.type = AVMEDIA_TYPE_VIDEO, + .p.id = AV_CODEC_ID_VP9, + .priv_data_size = sizeof(VP9Context), + .init = vp9_decode_init, + .close = vp9_decode_free, + FF_CODEC_DECODE_CB(vp9_decode_frame), + .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS | AV_CODEC_CAP_SLICE_THREADS, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_SLICE_THREAD_HAS_MF | + FF_CODEC_CAP_USES_PROGRESSFRAMES, + .flush = vp9_decode_flush, + UPDATE_THREAD_CONTEXT(vp9_decode_update_thread_context), + .p.profiles = NULL_IF_CONFIG_SMALL(ff_vp9_profiles), + .bsfs = "vp9_superframe_split", + .hw_configs = (const AVCodecHWConfigInternal *const []) { +#if CONFIG_VP9_DXVA2_HWACCEL + HWACCEL_DXVA2(vp9), +#endif +#if CONFIG_VP9_D3D11VA_HWACCEL + HWACCEL_D3D11VA(vp9), +#endif +#if CONFIG_VP9_D3D11VA2_HWACCEL + HWACCEL_D3D11VA2(vp9), +#endif +#if CONFIG_VP9_D3D12VA_HWACCEL + HWACCEL_D3D12VA(vp9), +#endif +#if CONFIG_VP9_NVDEC_HWACCEL + HWACCEL_NVDEC(vp9), +#endif +#if CONFIG_VP9_VAAPI_HWACCEL + HWACCEL_VAAPI(vp9), +#endif +#if CONFIG_VP9_VDPAU_HWACCEL + HWACCEL_VDPAU(vp9), +#endif +#if CONFIG_VP9_VIDEOTOOLBOX_HWACCEL + HWACCEL_VIDEOTOOLBOX(vp9), +#endif +#if CONFIG_VP9_VULKAN_HWACCEL + HWACCEL_VULKAN(vp9), +#endif + NULL + }, +}; diff --git a/thirdparty/ffmpeg/libavutil/Makefile b/thirdparty/ffmpeg/libavutil/Makefile index 394812260..573a2b4c8 100644 --- a/thirdparty/ffmpeg/libavutil/Makefile +++ b/thirdparty/ffmpeg/libavutil/Makefile @@ -52,6 +52,7 @@ HEADERS = adler32.h \ hwcontext_mediacodec.h \ hwcontext_opencl.h \ hwcontext_oh.h \ + hwcontext_v4l2request.h \ hwcontext_vaapi.h \ hwcontext_videotoolbox.h \ hwcontext_vdpau.h \ @@ -216,6 +217,7 @@ OBJS-$(CONFIG_MEDIACODEC) += hwcontext_mediacodec.o OBJS-$(CONFIG_OHCODEC) += hwcontext_oh.o OBJS-$(CONFIG_OPENCL) += hwcontext_opencl.o OBJS-$(CONFIG_QSV) += hwcontext_qsv.o +OBJS-$(CONFIG_V4L2_REQUEST) += hwcontext_v4l2request.o OBJS-$(CONFIG_VAAPI) += hwcontext_vaapi.o OBJS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.o OBJS-$(CONFIG_VDPAU) += hwcontext_vdpau.o @@ -247,6 +249,8 @@ SKIPHEADERS-$(CONFIG_AMF) += hwcontext_amf.h \ hwcontext_amf_internal.h SKIPHEADERS-$(CONFIG_QSV) += hwcontext_qsv.h SKIPHEADERS-$(CONFIG_OPENCL) += hwcontext_opencl.h +SKIPHEADERS-$(CONFIG_V4L2_REQUEST) += hwcontext_v4l2request.h \ + hwcontext_v4l2request_internal.h SKIPHEADERS-$(CONFIG_VAAPI) += hwcontext_vaapi.h SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.h SKIPHEADERS-$(CONFIG_VDPAU) += hwcontext_vdpau.h diff --git a/thirdparty/ffmpeg/libavutil/Makefile.orig b/thirdparty/ffmpeg/libavutil/Makefile.orig new file mode 100644 index 000000000..394812260 --- /dev/null +++ b/thirdparty/ffmpeg/libavutil/Makefile.orig @@ -0,0 +1,333 @@ +NAME = avutil +DESC = FFmpeg utility library + +HEADERS = adler32.h \ + aes.h \ + aes_ctr.h \ + ambient_viewing_environment.h \ + attributes.h \ + audio_fifo.h \ + avassert.h \ + avstring.h \ + avutil.h \ + base64.h \ + blowfish.h \ + bprint.h \ + bswap.h \ + buffer.h \ + cast5.h \ + camellia.h \ + channel_layout.h \ + common.h \ + container_fifo.h \ + cpu.h \ + crc.h \ + csp.h \ + des.h \ + detection_bbox.h \ + dict.h \ + display.h \ + dovi_meta.h \ + downmix_info.h \ + encryption_info.h \ + error.h \ + eval.h \ + executor.h \ + fifo.h \ + file.h \ + film_grain_params.h \ + frame.h \ + hash.h \ + hdr_dynamic_metadata.h \ + hdr_dynamic_vivid_metadata.h \ + hmac.h \ + hwcontext.h \ + hwcontext_cuda.h \ + hwcontext_d3d11va.h \ + hwcontext_d3d12va.h \ + hwcontext_drm.h \ + hwcontext_dxva2.h \ + hwcontext_amf.h \ + hwcontext_qsv.h \ + hwcontext_mediacodec.h \ + hwcontext_opencl.h \ + hwcontext_oh.h \ + hwcontext_vaapi.h \ + hwcontext_videotoolbox.h \ + hwcontext_vdpau.h \ + hwcontext_vulkan.h \ + iamf.h \ + imgutils.h \ + intfloat.h \ + intreadwrite.h \ + lfg.h \ + log.h \ + lzo.h \ + macros.h \ + mathematics.h \ + mastering_display_metadata.h \ + md5.h \ + mem.h \ + motion_vector.h \ + murmur3.h \ + opt.h \ + parseutils.h \ + pixdesc.h \ + pixelutils.h \ + pixfmt.h \ + random_seed.h \ + raw_color_params.h \ + rc4.h \ + rational.h \ + refstruct.h \ + replaygain.h \ + ripemd.h \ + samplefmt.h \ + sha.h \ + sha512.h \ + spherical.h \ + stereo3d.h \ + tdrdi.h \ + threadmessage.h \ + time.h \ + timecode.h \ + timestamp.h \ + tree.h \ + twofish.h \ + uuid.h \ + version.h \ + video_enc_params.h \ + xtea.h \ + tea.h \ + tx.h \ + video_hint.h + +ARCH_HEADERS = bswap.h \ + intmath.h \ + intreadwrite.h \ + timer.h \ + +BUILT_HEADERS = avconfig.h \ + ffversion.h + +OBJS = adler32.o \ + aes.o \ + aes_ctr.o \ + ambient_viewing_environment.o \ + audio_fifo.o \ + avstring.o \ + avsscanf.o \ + base64.o \ + blowfish.o \ + bprint.o \ + buffer.o \ + cast5.o \ + camellia.o \ + channel_layout.o \ + container_fifo.o \ + cpu.o \ + crc.o \ + csp.o \ + des.o \ + detection_bbox.o \ + dict.o \ + display.o \ + dovi_meta.o \ + downmix_info.o \ + encryption_info.o \ + error.o \ + eval.o \ + executor.o \ + fifo.o \ + file.o \ + file_open.o \ + float_dsp.o \ + float_scalarproduct.o \ + film_grain_params.o \ + fixed_dsp.o \ + frame.o \ + hash.o \ + hdr_dynamic_metadata.o \ + hdr_dynamic_vivid_metadata.o \ + hmac.o \ + hwcontext.o \ + iamf.o \ + imgutils.o \ + integer.o \ + intmath.o \ + lfg.o \ + lls.o \ + log.o \ + log2_tab.o \ + lzo.o \ + mathematics.o \ + mastering_display_metadata.o \ + md5.o \ + mem.o \ + murmur3.o \ + opt.o \ + parseutils.o \ + pixdesc.o \ + pixelutils.o \ + random_seed.o \ + raw_color_params.o \ + rational.o \ + refstruct.o \ + reverse.o \ + rc4.o \ + ripemd.o \ + samplefmt.o \ + side_data.o \ + sha.o \ + sha512.o \ + slicethread.o \ + spherical.o \ + stereo3d.o \ + tdrdi.o \ + threadmessage.o \ + time.o \ + timecode.o \ + timecode_internal.o \ + timestamp.o \ + tree.o \ + twofish.o \ + utils.o \ + xga_font_data.o \ + xtea.o \ + tea.o \ + tx.o \ + tx_float.o \ + tx_double.o \ + tx_int32.o \ + uuid.o \ + version.o \ + video_enc_params.o \ + video_hint.o \ + + +OBJS-$(CONFIG_CUDA) += hwcontext_cuda.o +OBJS-$(CONFIG_D3D11VA) += hwcontext_d3d11va.o +OBJS-$(CONFIG_D3D12VA) += hwcontext_d3d12va.o +OBJS-$(CONFIG_DXVA2) += hwcontext_dxva2.o +OBJS-$(CONFIG_AMF) += hwcontext_amf.o +OBJS-$(CONFIG_LIBDRM) += hwcontext_drm.o +OBJS-$(CONFIG_MACOS_KPERF) += macos_kperf.o +OBJS-$(CONFIG_MEDIACODEC) += hwcontext_mediacodec.o +OBJS-$(CONFIG_OHCODEC) += hwcontext_oh.o +OBJS-$(CONFIG_OPENCL) += hwcontext_opencl.o +OBJS-$(CONFIG_QSV) += hwcontext_qsv.o +OBJS-$(CONFIG_VAAPI) += hwcontext_vaapi.o +OBJS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.o +OBJS-$(CONFIG_VDPAU) += hwcontext_vdpau.o +OBJS-$(CONFIG_VULKAN) += hwcontext_vulkan.o vulkan.o + +OBJS-$(!CONFIG_VULKAN) += hwcontext_stub.o + +OBJS += $(COMPAT_OBJS:%=../compat/%) + +# Object files to satisfy dependencies of other libraries for static builds +STLIBOBJS-$(CONFIG_EXR_DECODER) += half2float.o float2half.o +STLIBOBJS-$(CONFIG_EXR_ENCODER) += float2half.o +STLIBOBJS-$(CONFIG_PHM_DECODER) += half2float.o +STLIBOBJS-$(CONFIG_PHM_ENCODER) += float2half.o +STLIBOBJS-$(CONFIG_SWSCALE) += half2float.o + +# Windows resource file +SHLIBOBJS-$(HAVE_GNU_WINDRES) += avutilres.o + +SKIPHEADERS += objc.h +SKIPHEADERS-$(CONFIG_ZLIB) += zlib_utils.h +SKIPHEADERS-$(HAVE_CUDA_H) += hwcontext_cuda.h +SKIPHEADERS-$(CONFIG_CUDA) += hwcontext_cuda_internal.h \ + cuda_check.h +SKIPHEADERS-$(CONFIG_D3D11VA) += hwcontext_d3d11va.h +SKIPHEADERS-$(CONFIG_D3D12VA) += hwcontext_d3d12va.h +SKIPHEADERS-$(CONFIG_DXVA2) += hwcontext_dxva2.h +SKIPHEADERS-$(CONFIG_AMF) += hwcontext_amf.h \ + hwcontext_amf_internal.h +SKIPHEADERS-$(CONFIG_QSV) += hwcontext_qsv.h +SKIPHEADERS-$(CONFIG_OPENCL) += hwcontext_opencl.h +SKIPHEADERS-$(CONFIG_VAAPI) += hwcontext_vaapi.h +SKIPHEADERS-$(CONFIG_VIDEOTOOLBOX) += hwcontext_videotoolbox.h +SKIPHEADERS-$(CONFIG_VDPAU) += hwcontext_vdpau.h +SKIPHEADERS-$(CONFIG_VULKAN) += hwcontext_vulkan.h vulkan.h \ + vulkan_functions.h \ + vulkan_loader.h +SKIPHEADERS-$(CONFIG_SHADER_COMPRESSION) += zlib_utils.h + +TESTPROGS = adler32 \ + aes \ + aes_ctr \ + ambient_viewing_environment \ + audio_fifo \ + avstring \ + base64 \ + blowfish \ + bprint \ + buffer \ + cast5 \ + camellia \ + channel_layout \ + color_utils \ + cpu \ + crc \ + csp \ + des \ + detection_bbox \ + dict \ + display \ + encryption_info \ + error \ + eval \ + file \ + fifo \ + film_grain_params \ + hash \ + hdr_dynamic_vivid_metadata \ + hmac \ + hwdevice \ + integer \ + imgutils \ + lfg \ + lls \ + log \ + mathematics \ + md5 \ + murmur3 \ + opt \ + pca \ + parseutils \ + pixdesc \ + pixelutils \ + pixfmt_best \ + random_seed \ + rational \ + rc4 \ + ripemd \ + sha \ + sha512 \ + samplefmt \ + side_data_array \ + softfloat \ + spherical \ + stereo3d \ + tdrdi \ + timecode \ + timestamp \ + tree \ + twofish \ + utf8 \ + uuid \ + video_enc_params \ + xtea \ + tea \ + +TESTPROGS-$(HAVE_THREADS) += cpu_init +TESTPROGS-$(HAVE_LZO1X_999_COMPRESS) += lzo + +TOOLS = crypto_bench ffhash ffeval ffescape + +tools/crypto_bench$(EXESUF): ELIBS += $(if $(VERSUS),$(subst +, -l,+$(VERSUS)),) +tools/crypto_bench.o: CFLAGS += -DUSE_EXT_LIBS=0$(if $(VERSUS),$(subst +,+USE_,+$(VERSUS)),) + +$(SUBDIR)tests/lzo$(EXESUF): ELIBS = -llzo2 diff --git a/thirdparty/ffmpeg/libavutil/hwcontext.c b/thirdparty/ffmpeg/libavutil/hwcontext.c index 83bd7457e..7f93d90c8 100644 --- a/thirdparty/ffmpeg/libavutil/hwcontext.c +++ b/thirdparty/ffmpeg/libavutil/hwcontext.c @@ -71,6 +71,9 @@ static const HWContextType * const hw_table[] = { #endif #if CONFIG_OHCODEC &ff_hwcontext_type_oh, +#endif +#if CONFIG_V4L2_REQUEST + &ff_hwcontext_type_v4l2request, #endif NULL, }; @@ -83,6 +86,7 @@ static const char *const hw_type_names[] = { [AV_HWDEVICE_TYPE_D3D12VA] = "d3d12va", [AV_HWDEVICE_TYPE_OPENCL] = "opencl", [AV_HWDEVICE_TYPE_QSV] = "qsv", + [AV_HWDEVICE_TYPE_V4L2REQUEST] = "v4l2request", [AV_HWDEVICE_TYPE_VAAPI] = "vaapi", [AV_HWDEVICE_TYPE_VDPAU] = "vdpau", [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox", diff --git a/thirdparty/ffmpeg/libavutil/hwcontext.h b/thirdparty/ffmpeg/libavutil/hwcontext.h index 29374cf0a..88e47cc7f 100644 --- a/thirdparty/ffmpeg/libavutil/hwcontext.h +++ b/thirdparty/ffmpeg/libavutil/hwcontext.h @@ -41,6 +41,7 @@ enum AVHWDeviceType { AV_HWDEVICE_TYPE_AMF, /* OpenHarmony Codec device */ AV_HWDEVICE_TYPE_OHCODEC, + AV_HWDEVICE_TYPE_V4L2REQUEST, }; /** diff --git a/thirdparty/ffmpeg/libavutil/hwcontext_internal.h b/thirdparty/ffmpeg/libavutil/hwcontext_internal.h index dcfdc2016..94e4da16c 100644 --- a/thirdparty/ffmpeg/libavutil/hwcontext_internal.h +++ b/thirdparty/ffmpeg/libavutil/hwcontext_internal.h @@ -158,6 +158,7 @@ extern const HWContextType ff_hwcontext_type_drm; extern const HWContextType ff_hwcontext_type_dxva2; extern const HWContextType ff_hwcontext_type_opencl; extern const HWContextType ff_hwcontext_type_qsv; +extern const HWContextType ff_hwcontext_type_v4l2request; extern const HWContextType ff_hwcontext_type_vaapi; extern const HWContextType ff_hwcontext_type_vdpau; extern const HWContextType ff_hwcontext_type_videotoolbox; diff --git a/thirdparty/ffmpeg/libavutil/hwcontext_v4l2request.c b/thirdparty/ffmpeg/libavutil/hwcontext_v4l2request.c new file mode 100644 index 000000000..b6633d908 --- /dev/null +++ b/thirdparty/ffmpeg/libavutil/hwcontext_v4l2request.c @@ -0,0 +1,1177 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "avassert.h" +#include "hwcontext_drm.h" +#include "hwcontext_internal.h" +#include "hwcontext_v4l2request_internal.h" +#include "mem.h" + +typedef struct V4L2RequestVideoDecoder { + dev_t media_dev; + dev_t video_dev; + uint32_t *pixelformats; + int nb_pixelformats; +} V4L2RequestVideoDecoder; + +typedef struct V4L2RequestDeviceContext { + V4L2RequestVideoDecoder *decoders; + int nb_decoders; +} V4L2RequestDeviceContext; + +typedef struct V4L2RequestFramesContext { + AVV4L2RequestFramesContext p; + AVV4L2RequestFramesContextInternal internal; +} V4L2RequestFramesContext; + +typedef struct V4L2RequestFrameDescriptor { + AVDRMFrameDescriptor base; + AVBufferRef *ref; + uint32_t index; + int fd[AV_DRM_MAX_PLANES]; +} V4L2RequestFrameDescriptor; + +static const struct { + uint32_t pixelformat; + enum AVPixelFormat sw_format; + uint32_t drm_format; + uint64_t format_modifier; + uint32_t bit_depth; +} v4l2request_capture_pixelformats[] = { + { V4L2_PIX_FMT_NV12, AV_PIX_FMT_NV12, DRM_FORMAT_NV12, DRM_FORMAT_MOD_LINEAR, 8 }, +#if defined(V4L2_PIX_FMT_NV12_32L32) + { V4L2_PIX_FMT_NV12_32L32, AV_PIX_FMT_YUV420P, DRM_FORMAT_NV12, DRM_FORMAT_MOD_ALLWINNER_TILED, 8 }, +#endif +#if defined(V4L2_PIX_FMT_NV15) && defined(DRM_FORMAT_NV15) + { V4L2_PIX_FMT_NV15, AV_PIX_FMT_YUV420P10, DRM_FORMAT_NV15, DRM_FORMAT_MOD_LINEAR, 10 }, +#endif + { V4L2_PIX_FMT_NV16, AV_PIX_FMT_NV16, DRM_FORMAT_NV16, DRM_FORMAT_MOD_LINEAR, 8 }, +#if defined(V4L2_PIX_FMT_NV20) && defined(DRM_FORMAT_NV20) + { V4L2_PIX_FMT_NV20, AV_PIX_FMT_YUV422P10, DRM_FORMAT_NV20, DRM_FORMAT_MOD_LINEAR, 10 }, +#endif +#if defined(V4L2_PIX_FMT_P010) && defined(DRM_FORMAT_P010) + { V4L2_PIX_FMT_P010, AV_PIX_FMT_P010, DRM_FORMAT_P010, DRM_FORMAT_MOD_LINEAR, 10 }, +#endif +#if defined(V4L2_PIX_FMT_NV12MT_COL128) && defined(V4L2_PIX_FMT_NV12MT_10_COL128) + { V4L2_PIX_FMT_NV12MT_COL128, AV_PIX_FMT_YUV420P, DRM_FORMAT_NV12, DRM_FORMAT_MOD_BROADCOM_SAND128, 8 }, +#if defined(DRM_FORMAT_P030) + { V4L2_PIX_FMT_NV12MT_10_COL128, AV_PIX_FMT_YUV420P10, DRM_FORMAT_P030, DRM_FORMAT_MOD_BROADCOM_SAND128, 10 }, +#endif +#endif +#if defined(V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT) + { + .pixelformat = V4L2_PIX_FMT_YUV420_8_AFBC_16X16_SPLIT, + .sw_format = AV_PIX_FMT_YUV420P, + .drm_format = DRM_FORMAT_YUV420_8BIT, + .format_modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT), + .bit_depth = 8, + }, +#endif +#if defined(V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT) + { + .pixelformat = V4L2_PIX_FMT_YUV420_10_AFBC_16X16_SPLIT, + .sw_format = AV_PIX_FMT_YUV420P10, + .drm_format = DRM_FORMAT_YUV420_10BIT, + .format_modifier = DRM_FORMAT_MOD_ARM_AFBC(AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 | + AFBC_FORMAT_MOD_SPARSE | + AFBC_FORMAT_MOD_SPLIT), + .bit_depth = 10, + }, +#endif +#if defined(V4L2_PIX_FMT_NV12_COL128) && defined(V4L2_PIX_FMT_NV12_10_COL128) + { V4L2_PIX_FMT_NV12_COL128, AV_PIX_FMT_YUV420P, DRM_FORMAT_NV12, DRM_FORMAT_MOD_BROADCOM_SAND128, 8 }, +#if defined(DRM_FORMAT_P030) + { V4L2_PIX_FMT_NV12_10_COL128, AV_PIX_FMT_YUV420P10, DRM_FORMAT_P030, DRM_FORMAT_MOD_BROADCOM_SAND128, 10 }, +#endif +#endif +}; + +static int v4l2request_set_drm_descriptor(AVDRMFrameDescriptor *desc, + struct v4l2_format *format) +{ + AVDRMLayerDescriptor *layer = &desc->layers[0]; + uint32_t pixelformat = V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.pixelformat : + format->fmt.pix.pixelformat; + uint64_t format_modifier; + + layer->format = 0; + for (int i = 0; i < FF_ARRAY_ELEMS(v4l2request_capture_pixelformats); i++) { + if (pixelformat == v4l2request_capture_pixelformats[i].pixelformat) { + layer->format = v4l2request_capture_pixelformats[i].drm_format; + format_modifier = v4l2request_capture_pixelformats[i].format_modifier; + break; + } + } + if (!layer->format) + return AVERROR(ENOENT); + + for (int i = 0; i < desc->nb_objects; i++) { + desc->objects[i].format_modifier = format_modifier; + desc->objects[i].size = V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.plane_fmt[i].sizeimage : + format->fmt.pix.sizeimage; + } + + desc->nb_layers = 1; + layer->nb_planes = 1; + + layer->planes[0].object_index = 0; + layer->planes[0].offset = 0; + layer->planes[0].pitch = V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.plane_fmt[0].bytesperline : + format->fmt.pix.bytesperline; + + // AFBC formats only use 1 plane, remaining use 2 planes + if ((desc->objects[0].format_modifier >> 56) != DRM_FORMAT_MOD_VENDOR_ARM) { + layer->nb_planes = 2; + layer->planes[1].object_index = 0; + layer->planes[1].offset = layer->planes[0].pitch * + (V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.height : + format->fmt.pix.height); + layer->planes[1].pitch = layer->planes[0].pitch; + } + +#if defined(V4L2_PIX_FMT_NV12MT_COL128) && defined(V4L2_PIX_FMT_NV12MT_10_COL128) + // Raspberry Pi formats need special handling + if (pixelformat == V4L2_PIX_FMT_NV12MT_COL128 || + pixelformat == V4L2_PIX_FMT_NV12MT_10_COL128) { + layer->planes[1].object_index = 1; + layer->planes[1].offset = 0; + layer->planes[0].pitch = (V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.height : + format->fmt.pix.height); + layer->planes[1].pitch = layer->planes[0].pitch / 2; + } +#endif + +#if defined(V4L2_PIX_FMT_NV12_COL128) && defined(V4L2_PIX_FMT_NV12_10_COL128) + // Raspberry Pi formats need special handling + if (pixelformat == V4L2_PIX_FMT_NV12_COL128 || + pixelformat == V4L2_PIX_FMT_NV12_10_COL128) { + desc->objects[0].format_modifier = + DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(layer->planes[0].pitch); + layer->planes[1].offset = 128 * + (V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.height : + format->fmt.pix.height); + layer->planes[0].pitch = (V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.width : + format->fmt.pix.width); + if (pixelformat == V4L2_PIX_FMT_NV12_10_COL128) + layer->planes[0].pitch *= 2; + layer->planes[1].pitch = layer->planes[0].pitch; + } +#endif + + return 0; +} + +static void v4l2request_device_uninit(AVHWDeviceContext *hwdev) +{ + V4L2RequestDeviceContext *hwctx = hwdev->hwctx; + + av_freep(&hwctx->decoders); + hwctx->nb_decoders = 0; +} + +static int v4l2request_device_create(AVHWDeviceContext *hwdev, const char *device, + AVDictionary *opts, int flags) +{ + V4L2RequestDeviceContext *hwctx = hwdev->hwctx; + + hwctx->decoders = NULL; + hwctx->nb_decoders = 0; + + // TODO: enumerate V4L2 Request API capable video decoders + // and fill hwctx->decoders and hwctx->nb_decoders, + // limit to decoders for the media 'device' when specified + + return 0; +} + +static int v4l2request_set_format(AVHWFramesContext *hwfc, + enum v4l2_buf_type type, + uint32_t pixelformat, + uint32_t buffersize) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + struct v4l2_format format = { + .type = type, + }; + + if (V4L2_TYPE_IS_MULTIPLANAR(type)) { + format.fmt.pix_mp.width = hwfc->width; + format.fmt.pix_mp.height = hwfc->height; + format.fmt.pix_mp.pixelformat = pixelformat; + format.fmt.pix_mp.plane_fmt[0].sizeimage = buffersize; + format.fmt.pix_mp.num_planes = 1; + } else { + format.fmt.pix.width = hwfc->width; + format.fmt.pix.height = hwfc->height; + format.fmt.pix.pixelformat = pixelformat; + format.fmt.pix.sizeimage = buffersize; + } + + if (ioctl(fctxi->video_fd, VIDIOC_S_FMT, &format) < 0) + return AVERROR(errno); + + return 0; +} + +static int v4l2request_select_capture_format(AVHWFramesContext *hwfc) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + enum v4l2_buf_type type = fctxi->capture.format.type; + uint32_t pixelformat, fallback = 0; + struct v4l2_format format = { + .type = type, + }; + struct v4l2_fmtdesc fmtdesc = { + .index = 0, + .type = type, + }; + + // Get the driver preferred (or default) format + if (ioctl(fctxi->video_fd, VIDIOC_G_FMT, &format) < 0) + return AVERROR(errno); + + pixelformat = V4L2_TYPE_IS_MULTIPLANAR(type) ? + format.fmt.pix_mp.pixelformat : + format.fmt.pix.pixelformat; + + // Try to use the driver preferred format when it is a known format + for (int i = 0; i < FF_ARRAY_ELEMS(v4l2request_capture_pixelformats); i++) { + if (pixelformat == v4l2request_capture_pixelformats[i].pixelformat && + (fctx->bit_depth == v4l2request_capture_pixelformats[i].bit_depth || + !fctx->bit_depth)) + return v4l2request_set_format(hwfc, type, pixelformat, 0); + } + + // Next try to use the first known format with matching bit depth + while (ioctl(fctxi->video_fd, VIDIOC_ENUM_FMT, &fmtdesc) >= 0) { + for (int i = 0; i < FF_ARRAY_ELEMS(v4l2request_capture_pixelformats); i++) { + if (fmtdesc.pixelformat == v4l2request_capture_pixelformats[i].pixelformat) { + if (fctx->bit_depth == v4l2request_capture_pixelformats[i].bit_depth || + !fctx->bit_depth) + return v4l2request_set_format(hwfc, type, fmtdesc.pixelformat, 0); + else if (!fallback) + fallback = fmtdesc.pixelformat; + } + } + + fmtdesc.index++; + } + + // Fallback to use the first known format + if (fallback) + return v4l2request_set_format(hwfc, type, fallback, 0); + + return AVERROR(errno); +} + +static int v4l2request_try_framesize(AVHWFramesContext *hwfc, + uint32_t pixelformat) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + struct v4l2_frmsizeenum frmsize = { + .index = 0, + .pixel_format = pixelformat, + }; + + // Enumerate and check if frame size is supported + while (ioctl(fctxi->video_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) { + if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE && + hwfc->width == frmsize.discrete.width && + hwfc->height == frmsize.discrete.height) { + return 0; + } else if ((frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE || + frmsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) && + hwfc->width >= frmsize.stepwise.min_width && + hwfc->height >= frmsize.stepwise.min_height && + hwfc->width <= frmsize.stepwise.max_width && + hwfc->height <= frmsize.stepwise.max_height) { + return 0; + } + + frmsize.index++; + } + + return AVERROR(errno); +} + +static int v4l2request_try_format(AVHWFramesContext *hwfc, + enum v4l2_buf_type type, + uint32_t pixelformat) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + struct v4l2_fmtdesc fmtdesc = { + .index = 0, + .type = type, + }; + + // Enumerate and check if format is supported + while (ioctl(fctxi->video_fd, VIDIOC_ENUM_FMT, &fmtdesc) >= 0) { + if (fmtdesc.pixelformat == pixelformat) + return 0; + + fmtdesc.index++; + } + + return AVERROR(errno); +} + +static int v4l2request_set_controls(AVHWFramesContext *hwfc, + struct v4l2_ext_control *control, int count) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + struct v4l2_ext_controls controls = { + .controls = control, + .count = count, + }; + + if (!control || !count) + return 0; + + if (ioctl(fctxi->video_fd, VIDIOC_S_EXT_CTRLS, &controls) < 0) + return AVERROR(errno); + + return 0; +} + +static int v4l2request_probe_video_device(AVHWFramesContext *hwfc, + const char *path, + uint32_t pixelformat, + uint32_t buffersize) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + struct v4l2_capability capability; + struct v4l2_create_buffers buffers; + unsigned int capabilities; + int ret; + + /* + * Open video device in non-blocking mode to support decoding using + * multiple queued requests, required for e.g. multi stage decoding. + */ + fctxi->video_fd = open(path, O_RDWR | O_NONBLOCK); + if (fctxi->video_fd < 0) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, "Failed to open video device %s: %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + // Query capabilities of the video device + if (ioctl(fctxi->video_fd, VIDIOC_QUERYCAP, &capability) < 0) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, "Failed to query capabilities of %s: %s (%d)\n", + path, strerror(errno), errno); + goto fail; + } + + // Use device capabilities of the opened device when supported + capabilities = (capability.capabilities & V4L2_CAP_DEVICE_CAPS) ? + capability.device_caps : capability.capabilities; + + // Ensure streaming is supported on the video device + if ((capabilities & V4L2_CAP_STREAMING) != V4L2_CAP_STREAMING) { + ret = AVERROR(EINVAL); + av_log(hwfc, AV_LOG_VERBOSE, "Device %s is missing streaming capability\n", path); + goto fail; + } + + // Ensure multi- or single-planar API can be used + if ((capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) == V4L2_CAP_VIDEO_M2M_MPLANE) { + fctxi->output.format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + fctxi->capture.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + } else if ((capabilities & V4L2_CAP_VIDEO_M2M) == V4L2_CAP_VIDEO_M2M) { + fctxi->output.format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + fctxi->capture.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } else { + ret = AVERROR(EINVAL); + av_log(hwfc, AV_LOG_VERBOSE, "Device %s is missing mem2mem capability\n", path); + goto fail; + } + + // Query OUTPUT buffer capabilities + buffers = (struct v4l2_create_buffers) { + .count = 0, + .memory = V4L2_MEMORY_MMAP, + .format.type = fctxi->output.format.type, + }; + if (ioctl(fctxi->video_fd, VIDIOC_CREATE_BUFS, &buffers) < 0) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, + "Failed to query OUTPUT buffer capabilities of %s: %s (%d)\n", + path, strerror(errno), errno); + goto fail; + } + fctxi->output.capabilities = buffers.capabilities; + + // Ensure requests can be used + if ((buffers.capabilities & V4L2_BUF_CAP_SUPPORTS_REQUESTS) != + V4L2_BUF_CAP_SUPPORTS_REQUESTS) { + ret = AVERROR(EINVAL); + av_log(hwfc, AV_LOG_VERBOSE, "Device %s is missing support for requests\n", path); + goto fail; + } + + // Ensure the codec pixelformat can be used + ret = v4l2request_try_format(hwfc, fctxi->output.format.type, pixelformat); + if (ret < 0) { + av_log(hwfc, AV_LOG_VERBOSE, "Device %s is missing support for pixelformat %s\n", + path, av_fourcc2str(pixelformat)); + goto fail; + } + + // Ensure frame size is supported, when driver support ENUM_FRAMESIZES + ret = v4l2request_try_framesize(hwfc, pixelformat); + if (ret < 0 && ret != AVERROR(ENOTTY)) { + av_log(hwfc, AV_LOG_VERBOSE, + "Device %s is missing support for frame size %dx%d of pixelformat %s\n", + path, hwfc->width, hwfc->height, av_fourcc2str(pixelformat)); + goto fail; + } + + // Set the codec pixelformat and OUTPUT buffersize to be used + ret = v4l2request_set_format(hwfc, fctxi->output.format.type, pixelformat, buffersize); + if (ret < 0) { + av_log(hwfc, AV_LOG_ERROR, + "Failed to set OUTPUT pixelformat %s of %s: %s (%d)\n", + av_fourcc2str(pixelformat), path, strerror(errno), errno); + goto fail; + } + + // Get format details for OUTPUT buffers + if (ioctl(fctxi->video_fd, VIDIOC_G_FMT, &fctxi->output.format) < 0) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, "Failed to get OUTPUT format: %s (%d)\n", + strerror(errno), errno); + goto fail; + } + + /* + * Set any codec specific controls that can help assist the driver + * make a decision on what CAPTURE buffer format can be used. + */ + ret = v4l2request_set_controls(hwfc, fctx->init_controls, fctx->nb_init_controls); + if (ret < 0) { + av_log(hwfc, AV_LOG_VERBOSE, + "Failed to set %d control(s): %s (%d)\n", + fctx->nb_init_controls, strerror(errno), errno); + goto fail; + } + + // Select a supported CAPTURE buffer format + ret = v4l2request_select_capture_format(hwfc); + if (ret < 0) { + av_log(hwfc, AV_LOG_VERBOSE, + "Failed to select a CAPTURE format %s of %s: %s (%d)\n", + av_fourcc2str(pixelformat), path, strerror(errno), errno); + goto fail; + } + + // Query CAPTURE buffer capabilities + buffers = (struct v4l2_create_buffers) { + .count = 0, + .memory = V4L2_MEMORY_MMAP, + .format.type = fctxi->capture.format.type, + }; + if (ioctl(fctxi->video_fd, VIDIOC_CREATE_BUFS, &buffers) < 0) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, + "Failed to query CAPTURE buffer capabilities of %s: %s (%d)\n", + path, strerror(errno), errno); + goto fail; + } + fctxi->capture.capabilities = buffers.capabilities; + + // Get format details for CAPTURE buffers + if (ioctl(fctxi->video_fd, VIDIOC_G_FMT, &fctxi->capture.format) < 0) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, "Failed to get CAPTURE format: %s (%d)\n", + strerror(errno), errno); + goto fail; + } + + // All tests passed, video device should be capable + return 0; + +fail: + if (fctxi->video_fd >= 0) { + close(fctxi->video_fd); + fctxi->video_fd = -1; + } + return ret; +} + +static int v4l2request_probe_video_devices(AVHWFramesContext *hwfc, + struct udev *udev, + uint32_t pixelformat, + uint32_t buffersize) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + struct media_device_info device_info; + struct media_v2_topology topology = {0}; + struct media_v2_interface *interfaces; + struct udev_device *device; + const char *path; + dev_t devnum; + int ret; + + if (ioctl(fctxi->media_fd, MEDIA_IOC_DEVICE_INFO, &device_info) < 0) + return AVERROR(errno); + + if (ioctl(fctxi->media_fd, MEDIA_IOC_G_TOPOLOGY, &topology) < 0) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, "Failed to get media topology: %s (%d)\n", + strerror(errno), errno); + return ret; + } + + if (!topology.num_interfaces) + return AVERROR(ENOENT); + + interfaces = av_calloc(topology.num_interfaces, sizeof(struct media_v2_interface)); + if (!interfaces) + return AVERROR(ENOMEM); + + topology.ptr_interfaces = (__u64)(uintptr_t)interfaces; + if (ioctl(fctxi->media_fd, MEDIA_IOC_G_TOPOLOGY, &topology) < 0) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, "Failed to get media topology: %s (%d)\n", + strerror(errno), errno); + goto fail; + } + + ret = AVERROR(ENOENT); + for (int i = 0; i < topology.num_interfaces; i++) { + if (interfaces[i].intf_type != MEDIA_INTF_T_V4L_VIDEO) + continue; + + devnum = makedev(interfaces[i].devnode.major, interfaces[i].devnode.minor); + device = udev_device_new_from_devnum(udev, 'c', devnum); + if (!device) + continue; + + path = udev_device_get_devnode(device); + if (path) + ret = v4l2request_probe_video_device(hwfc, path, pixelformat, buffersize); + udev_device_unref(device); + + // Stop when we have found a capable video device + if (!ret) { + av_log(hwfc, AV_LOG_INFO, + "Using V4L2 media driver %s (%u.%u.%u) for %s\n", + device_info.driver, + device_info.driver_version >> 16, + (device_info.driver_version >> 8) & 0xff, + device_info.driver_version & 0xff, + av_fourcc2str(pixelformat)); + break; + } + } + +fail: + av_free(interfaces); + return ret; +} + +static int v4l2request_probe_media_device(AVHWFramesContext *hwfc, + struct udev_device *device, + uint32_t pixelformat, + uint32_t buffersize) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + const char *path; + int ret; + + path = udev_device_get_devnode(device); + if (!path) + return AVERROR(ENODEV); + + // Open enumerated media device + fctxi->media_fd = open(path, O_RDWR); + if (fctxi->media_fd < 0) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, "Failed to open media device %s: %s (%d)\n", + path, strerror(errno), errno); + return ret; + } + + // Probe video devices of current media device + ret = v4l2request_probe_video_devices(hwfc, udev_device_get_udev(device), + pixelformat, buffersize); + + // Cleanup when no capable video device was found + if (ret < 0) { + close(fctxi->media_fd); + fctxi->media_fd = -1; + } + + return ret; +} + +static int v4l2request_probe_media_devices(AVHWFramesContext *hwfc, + struct udev *udev, + uint32_t pixelformat, + uint32_t buffersize) +{ + struct udev_enumerate *enumerate; + struct udev_list_entry *devices; + struct udev_list_entry *entry; + struct udev_device *device; + int ret; + + enumerate = udev_enumerate_new(udev); + if (!enumerate) + return AVERROR(ENOMEM); + + udev_enumerate_add_match_subsystem(enumerate, "media"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + + ret = AVERROR(ENOENT); + udev_list_entry_foreach(entry, devices) { + const char *path = udev_list_entry_get_name(entry); + if (!path) + continue; + + device = udev_device_new_from_syspath(udev, path); + if (!device) + continue; + + // Probe media device for a capable video device + ret = v4l2request_probe_media_device(hwfc, device, pixelformat, buffersize); + udev_device_unref(device); + + // Stop when we have found a capable media and video device + if (!ret) + break; + } + + udev_enumerate_unref(enumerate); + return ret; +} + +static int v4l2request_open_decoder(AVHWFramesContext *hwfc) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + uint32_t buffersize; + struct udev *udev; + int ret; + + // Ensure codec pixelformat is set + if (!fctx->pixelformat) + return AVERROR(EINVAL); + + // FIXME: locate a decoder using hwdevice context decoders + + udev = udev_new(); + if (!udev) + return AVERROR(ENOMEM); + + buffersize = FFMAX(hwfc->width * hwfc->height * 3 / 2, 256 * 1024); + + // Probe all media devices (auto-detection) + ret = v4l2request_probe_media_devices(hwfc, udev, fctx->pixelformat, buffersize); + + udev_unref(udev); + return ret; +} + +static AVBufferRef *v4l2request_v4l2_buffer_alloc(AVHWFramesContext *hwfc, + struct v4l2_format *format) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + struct v4l2_create_buffers buffers = { + .count = 1, + .memory = V4L2_MEMORY_MMAP, + .format = *format, + }; + struct v4l2_buffer *buffer; + uint8_t num_planes; + AVBufferRef *ref; + + num_planes = V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.num_planes : 0; + + ref = av_buffer_allocz(sizeof(struct v4l2_buffer) + + (sizeof(struct v4l2_plane) * num_planes)); + if (!ref) + return NULL; + + buffer = (struct v4l2_buffer *)ref->data; + buffer->type = format->type; + + if (num_planes) { + buffer->length = num_planes; + buffer->m.planes = (struct v4l2_plane *)(buffer + 1); + } + + // Create the buffer + if (ioctl(fctxi->video_fd, VIDIOC_CREATE_BUFS, &buffers) < 0) { + av_log(hwfc, AV_LOG_ERROR, "Failed to create buffer of type %d: %s (%d)\n", + buffer->type, strerror(errno), errno); + goto fail; + } + + buffer->memory = buffers.memory; + buffer->index = buffers.index; + + // Query more details of the created buffer + if (ioctl(fctxi->video_fd, VIDIOC_QUERYBUF, buffer) < 0) { + av_log(hwfc, AV_LOG_ERROR, "Failed to query buffer %d of type %d: %s (%d)\n", + buffer->index, buffer->type, strerror(errno), errno); + goto fail; + } + + return ref; + +fail: + av_buffer_unref(&ref); + return NULL; +} + +static AVBufferRef *v4l2request_capture_buffer_alloc(void *opaque, size_t size) +{ + AVHWFramesContext *hwfc = opaque; + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + + return v4l2request_v4l2_buffer_alloc(hwfc, &fctxi->capture.format); +} + +static AVBufferRef *v4l2request_output_buffer_alloc(void *opaque, size_t size) +{ + AVHWFramesContext *hwfc = opaque; + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + + return v4l2request_v4l2_buffer_alloc(hwfc, &fctxi->output.format); +} + +static void v4l2request_frame_free(void *opaque, uint8_t *data) +{ + V4L2RequestFrameDescriptor *desc = (V4L2RequestFrameDescriptor *)data; + + // Close the exported CAPTURE buffer memory planes + for (int i = 0; i < FF_ARRAY_ELEMS(desc->fd); i++) { + if (desc->fd[i] >= 0) { + close(desc->fd[i]); + desc->fd[i] = -1; + } + } + + // Return the CAPTURE buffer to the frames context CAPTURE pool + av_buffer_unref(&desc->ref); + + av_free(data); +} + +static AVBufferRef *v4l2request_frame_alloc(void *opaque, size_t size) +{ + AVHWFramesContext *hwfc = opaque; + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + struct v4l2_format *format = &fctxi->capture.format; + V4L2RequestFrameDescriptor *desc; + struct v4l2_buffer *buffer; + AVBufferRef *ref; + uint8_t *data; + + data = av_mallocz(size); + if (!data) + return NULL; + + ref = av_buffer_create(data, size, v4l2request_frame_free, + hwfc, AV_BUFFER_FLAG_READONLY); + if (!ref) { + av_free(data); + return NULL; + } + + // Set initial default values + desc = (V4L2RequestFrameDescriptor *)data; + for (int i = 0; i < FF_ARRAY_ELEMS(desc->fd); i++) + desc->fd[i] = -1; + + // Get a CAPTURE buffer from frames context CAPTURE pool + desc->ref = av_buffer_pool_get(fctxi->capture.pool); + if (!desc->ref) + goto fail; + + buffer = (struct v4l2_buffer *)desc->ref->data; + desc->index = buffer->index; + + // Export CAPTURE buffer memory planes + desc->base.nb_objects = V4L2_TYPE_IS_MULTIPLANAR(format->type) ? + format->fmt.pix_mp.num_planes : 1; + av_assert0(desc->base.nb_objects <= AV_DRM_MAX_PLANES); + for (int i = 0; i < desc->base.nb_objects; i++) { + struct v4l2_exportbuffer exportbuffer = { + .type = buffer->type, + .index = buffer->index, + .plane = i, + .flags = O_RDONLY, + }; + if (ioctl(fctxi->video_fd, VIDIOC_EXPBUF, &exportbuffer) < 0) { + av_log(hwfc, AV_LOG_ERROR, "Failed to export memory plane %d (%d): %s (%d)\n", + i, buffer->index, strerror(errno), errno); + goto fail; + } + desc->base.objects[i].fd = desc->fd[i] = exportbuffer.fd; + } + + // Set AVDRMFrameDescriptor based on CAPTURE buffer format + if (v4l2request_set_drm_descriptor(&desc->base, format) < 0) + goto fail; + + return ref; + +fail: + av_buffer_unref(&ref); + return NULL; +} + +static int v4l2request_frames_init(AVHWFramesContext *hwfc) +{ + V4L2RequestFramesContext *hwctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi; + uint32_t pixelformat; + int ret; + + // Set initial default values + fctxi = &hwctx->internal; + hwctx->p.internal = fctxi; + fctxi->media_fd = -1; + fctxi->video_fd = -1; + + // Locate and open a capable video decoder device + ret = v4l2request_open_decoder(hwfc); + if (ret < 0) + return ret; + + // Reset init controls after video device is opened + hwctx->p.init_controls = NULL; + hwctx->p.nb_init_controls = 0; + + // Update frames context with CAPTURE format details + if (V4L2_TYPE_IS_MULTIPLANAR(fctxi->capture.format.type)) { + hwfc->width = fctxi->capture.format.fmt.pix_mp.width; + hwfc->height = fctxi->capture.format.fmt.pix_mp.height; + pixelformat = fctxi->capture.format.fmt.pix_mp.pixelformat; + } else { + hwfc->width = fctxi->capture.format.fmt.pix.width; + hwfc->height = fctxi->capture.format.fmt.pix.height; + pixelformat = fctxi->capture.format.fmt.pix.pixelformat; + } + + hwfc->sw_format = AV_PIX_FMT_NONE; + for (int i = 0; i < FF_ARRAY_ELEMS(v4l2request_capture_pixelformats); i++) { + if (pixelformat == v4l2request_capture_pixelformats[i].pixelformat) { + hwctx->p.bit_depth = v4l2request_capture_pixelformats[i].bit_depth; + hwfc->sw_format = v4l2request_capture_pixelformats[i].sw_format; + break; + } + } + + // Initialize buffer pool for CAPTURE buffers + fctxi->capture.pool = av_buffer_pool_init2(sizeof(struct v4l2_buffer), hwfc, + v4l2request_capture_buffer_alloc, NULL); + if (!fctxi->capture.pool) + return AVERROR(ENOMEM); + + // Initialize buffer pool for OUTPUT buffers + fctxi->output.pool = av_buffer_pool_init2(sizeof(struct v4l2_buffer), hwfc, + v4l2request_output_buffer_alloc, NULL); + if (!fctxi->output.pool) + return AVERROR(ENOMEM); + + // Initialize buffer pool for frame descriptors + ffhwframesctx(hwfc)->pool_internal = + av_buffer_pool_init2(sizeof(V4L2RequestFrameDescriptor), hwfc, + v4l2request_frame_alloc, NULL); + if (!ffhwframesctx(hwfc)->pool_internal) + return AVERROR(ENOMEM); + + av_log(hwfc, AV_LOG_VERBOSE, "Using CAPTURE buffer format %s (%dx%d)\n", + av_fourcc2str(pixelformat), hwfc->width, hwfc->height); + + return 0; +} + +static void v4l2request_frames_uninit(AVHWFramesContext *hwfc) +{ + AVV4L2RequestFramesContext *fctx = hwfc->hwctx; + AVV4L2RequestFramesContextInternal *fctxi = fctx->internal; + + av_buffer_pool_uninit(&fctxi->capture.pool); + av_buffer_pool_uninit(&fctxi->output.pool); + + if (fctxi->video_fd >= 0) { + close(fctxi->video_fd); + fctxi->video_fd = -1; + } + + if (fctxi->media_fd) { + close(fctxi->media_fd); + fctxi->media_fd = -1; + } +} + +static int v4l2request_get_buffer(AVHWFramesContext *hwfc, AVFrame *frame) +{ + V4L2RequestFrameDescriptor *desc; + + frame->buf[0] = av_buffer_pool_get(hwfc->pool); + if (!frame->buf[0]) + return AVERROR(ENOMEM); + + desc = (V4L2RequestFrameDescriptor *)frame->buf[0]->data; + frame->data[0] = (uint8_t *)&desc->base; + frame->data[1] = (uint8_t *)(uintptr_t)desc->index; + + frame->format = AV_PIX_FMT_DRM_PRIME; + frame->width = hwfc->width; + frame->height = hwfc->height; + + return 0; +} + +typedef struct V4L2RequestMapping { + // Address and length of each mmap()ed region. + int nb_regions; + int object[AV_DRM_MAX_PLANES]; + void *address[AV_DRM_MAX_PLANES]; + size_t length[AV_DRM_MAX_PLANES]; +} V4L2RequestMapping; + +static void v4l2request_unmap_frame(AVHWFramesContext *hwfc, + HWMapDescriptor *hwmap) +{ + V4L2RequestMapping *map = hwmap->priv; + + for (int i = 0; i < map->nb_regions; i++) { + struct dma_buf_sync sync = { + .flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ, + }; + ioctl(map->object[i], DMA_BUF_IOCTL_SYNC, &sync); + munmap(map->address[i], map->length[i]); + } + + av_free(map); +} + +static int v4l2request_map_frame(AVHWFramesContext *hwfc, + AVFrame *dst, const AVFrame *src) +{ + const AVDRMFrameDescriptor *desc = (AVDRMFrameDescriptor *)src->data[0]; + struct dma_buf_sync sync = { + .flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ, + }; + V4L2RequestMapping *map; + int ret, i, p, plane; + void *addr; + + map = av_mallocz(sizeof(*map)); + if (!map) + return AVERROR(ENOMEM); + + av_assert0(desc->nb_objects <= AV_DRM_MAX_PLANES); + for (i = 0; i < desc->nb_objects; i++) { + addr = mmap(NULL, desc->objects[i].size, PROT_READ, MAP_SHARED, + desc->objects[i].fd, 0); + if (addr == MAP_FAILED) { + ret = AVERROR(errno); + av_log(hwfc, AV_LOG_ERROR, "Failed to map DRM object %d to memory: %s (%d)\n", + desc->objects[i].fd, strerror(errno), errno); + goto fail; + } + + map->address[i] = addr; + map->length[i] = desc->objects[i].size; + map->object[i] = desc->objects[i].fd; + + /* + * We're not checking for errors here because the kernel may not + * support the ioctl, in which case its okay to carry on + */ + ioctl(desc->objects[i].fd, DMA_BUF_IOCTL_SYNC, &sync); + } + map->nb_regions = i; + + plane = 0; + for (i = 0; i < desc->nb_layers; i++) { + const AVDRMLayerDescriptor *layer = &desc->layers[i]; + for (p = 0; p < layer->nb_planes; p++) { + dst->data[plane] = + (uint8_t *)map->address[layer->planes[p].object_index] + + layer->planes[p].offset; + dst->linesize[plane] = layer->planes[p].pitch; + ++plane; + } + } + av_assert0(plane <= AV_DRM_MAX_PLANES); + + dst->width = src->width; + dst->height = src->height; + + ret = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, + v4l2request_unmap_frame, map); + if (ret < 0) + goto fail; + + return 0; + +fail: + for (i = 0; i < desc->nb_objects; i++) { + if (map->address[i]) + munmap(map->address[i], map->length[i]); + } + av_free(map); + return ret; +} + +static int v4l2request_transfer_get_formats(AVHWFramesContext *hwfc, + enum AVHWFrameTransferDirection dir, + enum AVPixelFormat **formats) +{ + enum AVPixelFormat *fmts; + + if (dir == AV_HWFRAME_TRANSFER_DIRECTION_TO) + return AVERROR(ENOSYS); + + fmts = av_malloc_array(2, sizeof(*fmts)); + if (!fmts) + return AVERROR(ENOMEM); + + fmts[0] = hwfc->sw_format; + fmts[1] = AV_PIX_FMT_NONE; + + if (hwfc->sw_format == AV_PIX_FMT_YUV420P || + hwfc->sw_format == AV_PIX_FMT_YUV420P10 || + hwfc->sw_format == AV_PIX_FMT_YUV422P10) + fmts[0] = AV_PIX_FMT_NONE; + + *formats = fmts; + return 0; +} + +static int v4l2request_transfer_data_from(AVHWFramesContext *hwfc, + AVFrame *dst, const AVFrame *src) +{ + AVFrame *map; + int ret; + + if (dst->width > hwfc->width || dst->height > hwfc->height) + return AVERROR(EINVAL); + + map = av_frame_alloc(); + if (!map) + return AVERROR(ENOMEM); + map->format = dst->format; + + ret = v4l2request_map_frame(hwfc, map, src); + if (ret) + goto fail; + + map->width = dst->width; + map->height = dst->height; + + ret = av_frame_copy(dst, map); + if (ret) + goto fail; + + ret = 0; +fail: + av_frame_free(&map); + return ret; +} + +static int v4l2request_map_from(AVHWFramesContext *hwfc, AVFrame *dst, + const AVFrame *src, int flags) +{ + int ret; + + if (!(flags & AV_HWFRAME_MAP_READ)) + return AVERROR(ENOSYS); + + if (hwfc->sw_format == AV_PIX_FMT_NONE || + hwfc->sw_format == AV_PIX_FMT_YUV420P || + hwfc->sw_format == AV_PIX_FMT_YUV420P10 || + hwfc->sw_format == AV_PIX_FMT_YUV422P10) + return AVERROR(ENOSYS); + else if (dst->format == AV_PIX_FMT_NONE) + dst->format = hwfc->sw_format; + else if (hwfc->sw_format != dst->format) + return AVERROR(ENOSYS); + + ret = v4l2request_map_frame(hwfc, dst, src); + if (ret) + return ret; + + return av_frame_copy_props(dst, src); +} + +const HWContextType ff_hwcontext_type_v4l2request = { + .type = AV_HWDEVICE_TYPE_V4L2REQUEST, + .name = "V4L2 Request API", + + .device_hwctx_size = sizeof(V4L2RequestDeviceContext), + .device_create = v4l2request_device_create, + .device_uninit = v4l2request_device_uninit, + + .frames_hwctx_size = sizeof(V4L2RequestFramesContext), + .frames_init = v4l2request_frames_init, + .frames_uninit = v4l2request_frames_uninit, + .frames_get_buffer = v4l2request_get_buffer, + .transfer_get_formats = v4l2request_transfer_get_formats, + .transfer_data_from = v4l2request_transfer_data_from, + .map_from = v4l2request_map_from, + + .pix_fmts = (const enum AVPixelFormat[]) { + AV_PIX_FMT_DRM_PRIME, + AV_PIX_FMT_NONE + }, +}; diff --git a/thirdparty/ffmpeg/libavutil/hwcontext_v4l2request.h b/thirdparty/ffmpeg/libavutil/hwcontext_v4l2request.h new file mode 100644 index 000000000..ec6beadd6 --- /dev/null +++ b/thirdparty/ffmpeg/libavutil/hwcontext_v4l2request.h @@ -0,0 +1,70 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_V4L2REQUEST_H +#define AVUTIL_HWCONTEXT_V4L2REQUEST_H + +#include +#include + +/** + * @file + * An API-specific header for AV_HWDEVICE_TYPE_V4L2REQUEST. + */ + +typedef struct AVV4L2RequestFramesContextInternal AVV4L2RequestFramesContextInternal; + +/** + * V4L2 Request API frames context. + * + * This struct is allocated as AVHWFramesContext.hwctx + */ +typedef struct AVV4L2RequestFramesContext { + /** + * Internal context for the initialized V4L2 stateless decoder/encoder session. + */ + AVV4L2RequestFramesContextInternal *internal; + + /** + * V4L2_PIX_FMT_* coded pixel format to set on the OUTPUT queue (decoders) + * or the CAPTURE queue (encoders) during initialization. + * + * This field must be set by caller before av_hwframe_ctx_init() is called. + */ + uint32_t pixelformat; + + /** + * Optional bit depth of the frame pixel format, e.g. 8 or 10. + * + * This field should be set by caller before av_hwframe_ctx_init() is called, + * the field will be updated to match the selected frame pixel format after + * successful initialization. + */ + uint32_t bit_depth; + + /** + * Optional codec-specific extended controls to be set during initialization. + * + * These fields should be set by caller before av_hwframe_ctx_init() is called, + * fields are reset to NULL and 0 after successful initialization. + */ + struct v4l2_ext_control *init_controls; + int nb_init_controls; +} AVV4L2RequestFramesContext; + +#endif /* AVUTIL_HWCONTEXT_V4L2REQUEST_H */ diff --git a/thirdparty/ffmpeg/libavutil/hwcontext_v4l2request_internal.h b/thirdparty/ffmpeg/libavutil/hwcontext_v4l2request_internal.h new file mode 100644 index 000000000..20436b2b2 --- /dev/null +++ b/thirdparty/ffmpeg/libavutil/hwcontext_v4l2request_internal.h @@ -0,0 +1,67 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVUTIL_HWCONTEXT_V4L2REQUEST_INTERNAL_H +#define AVUTIL_HWCONTEXT_V4L2REQUEST_INTERNAL_H + +#include "buffer.h" +#include "hwcontext_v4l2request.h" + +/** + * @file + * FFmpeg internal API-specific header for AV_HWDEVICE_TYPE_V4L2REQUEST. + */ + +/** + * Internal context for the initialized V4L2 stateless decoder/encoder session. + */ +struct AVV4L2RequestFramesContextInternal { + /** + * Media device file descriptor of the initialized session. + */ + int media_fd; + + /** + * Video device file descriptor of the initialized session. + */ + int video_fd; + + /** + * Details of the initialized CAPTURE and OUTPUT queues. + */ + struct { + /** + * V4L2 buffer format. + */ + struct v4l2_format format; + + /** + * V4L2 buffer capabilities flags. + */ + uint32_t capabilities; + + /** + * Buffer pool of allocated V4L2 buffers. + * + * AVBufferRef.data points to a struct v4l2_buffer for the created buffer. + */ + AVBufferPool *pool; + } capture, output; +}; + +#endif /* AVUTIL_HWCONTEXT_V4L2REQUEST_INTERNAL_H */